/**
* @file calc_tied_mix.c
*
*
* @brief 混合ガウス分布の重みつき和の計算:tied-mixture用,キャッシュ有り
*
* Tied-mixture 用のガウス混合分布計算ではキャッシュが考慮されます.
* 計算された混合分布の音響尤度はコードブック単位でフレームごとに
* キャッシュされ,同じコードブックが同じ時間でアクセスされた場合は
* そのキャッシュから値を返します.
*
*
*
* @brief Compute weighed sum of Gaussian mixture for tied-mixture model (cache enabled)
*
* In tied-mixture computation, the computed output probability of each
* Gaussian component will be cache per codebook, for each input frame.
* If the same codebook of the same time is accessed later, the cached
* value will be returned.
*
*
* @author Akinobu LEE
* @date Thu Feb 17 14:22:44 2005
*
* $Revision: 1.7 $
*
*/
/*
* Copyright (c) 1991-2012 Kawahara Lab., Kyoto University
* Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
* Copyright (c) 2005-2012 Julius project team, Nagoya Institute of Technology
* All rights reserved
*/
#include
#include
#include
#include
#include
#include
/**
* Initialize codebook cache area.
*
* @param wrk [i/o] HMM computation work area
*
* @return TRUE on success, FALSE on failure.
*/
boolean
calc_tied_mix_init(HMMWork *wrk)
{
wrk->mixture_cache = NULL;
wrk->mixture_cache_num = NULL;
wrk->tmix_allocframenum = 0;
wrk->mroot = NULL;
wrk->tmix_last_id = (int *)mymalloc(sizeof(int) * wrk->OP_hmminfo->maxmixturenum * wrk->OP_nstream);
return TRUE;
}
/**
* Setup codebook cache for the next incoming input.
*
* @param wrk [i/o] HMM computation work area
* @param framenum [in] length of the next input.
*
* @return TRUE on success, FALSE on failure.
*/
boolean
calc_tied_mix_prepare(HMMWork *wrk, int framenum)
{
int bid, t;
/* clear */
for(t=0;ttmix_allocframenum;t++) {
for(bid=0;bidOP_hmminfo->codebooknum;bid++) {
wrk->mixture_cache_num[t][bid] = 0;
}
}
return TRUE;
}
/**
* Expand the cache to time axis if needed.
*
* @param wrk [i/o] HMM computation work area
* @param reqframe [in] required frame length
*/
static void
calc_tied_mix_extend(HMMWork *wrk, int reqframe)
{
int newnum;
int bid, t, size;
/* if enough length are already allocated, return immediately */
if (reqframe < wrk->tmix_allocframenum) return;
/* allocate per certain period */
newnum = reqframe + 1;
if (newnum < wrk->tmix_allocframenum + OUTPROB_CACHE_PERIOD)
newnum = wrk->tmix_allocframenum + OUTPROB_CACHE_PERIOD;
if (wrk->mixture_cache == NULL) {
wrk->mixture_cache = (MIXCACHE ***)mymalloc(sizeof(MIXCACHE **) * newnum);
wrk->mixture_cache_num = (short **)mymalloc(sizeof(short *) * newnum);
} else {
wrk->mixture_cache = (MIXCACHE ***)myrealloc(wrk->mixture_cache, sizeof(MIXCACHE **) * newnum);
wrk->mixture_cache_num = (short **)myrealloc(wrk->mixture_cache_num, sizeof(short *) * newnum);
}
size = wrk->OP_gprune_num * wrk->OP_hmminfo->codebooknum;
for(t = wrk->tmix_allocframenum; t < newnum; t++) {
wrk->mixture_cache[t] = (MIXCACHE **)mybmalloc2(sizeof(MIXCACHE *) * wrk->OP_hmminfo->codebooknum, &(wrk->mroot));
wrk->mixture_cache_num[t] = (short *)mybmalloc2(sizeof(short) * wrk->OP_hmminfo->codebooknum, &(wrk->mroot));
wrk->mixture_cache[t][0] = (MIXCACHE *)mybmalloc2(sizeof(MIXCACHE) * size, &(wrk->mroot));
for(bid=1;bidOP_hmminfo->codebooknum;bid++) {
wrk->mixture_cache[t][bid] = &(wrk->mixture_cache[t][0][wrk->OP_gprune_num * bid]);
}
/* clear the new part */
for(bid=0;bidOP_hmminfo->codebooknum;bid++) {
wrk->mixture_cache_num[t][bid] = 0;
}
}
wrk->tmix_allocframenum = newnum;
}
/**
* Free work area for tied-mixture calculation.
*
* @param wrk [i/o] HMM computation work area
*
*/
void
calc_tied_mix_free(HMMWork *wrk)
{
if (wrk->mroot != NULL) mybfree2(&(wrk->mroot));
if (wrk->mixture_cache_num != NULL) free(wrk->mixture_cache_num);
if (wrk->mixture_cache != NULL) free(wrk->mixture_cache);
free(wrk->tmix_last_id);
wrk->mroot = NULL;
wrk->mixture_cache_num = NULL;
wrk->mixture_cache = NULL;
}
/**
* @brief Compute the output probability of current state OP_State on
* tied-mixture model
*
* This function assumes that the OP_state is assigned to a tied-mixture
* codebook. Here the output probability of Gaussian mixture component
* referred by OP_state is consulted to the book level cache, and if not
* computed yet on that input frame time, it will be computed here.
*
* @param wrk [i/o] HMM computation work area
*
* @return the computed output probability in log10.
*/
LOGPROB
calc_tied_mix(HMMWork *wrk)
{
GCODEBOOK *book;
LOGPROB logprob, logprobsum;
int i, id;
MIXCACHE *ttcache;
short ttcachenum;
MIXCACHE *last_ttcache;
short last_ttcachenum;
PROB *weight;
PROB stream_weight;
int s;
int num;
logprobsum = 0.0;
for(s=0;sOP_nstream;s++) {
book = (GCODEBOOK *)(wrk->OP_state->pdf[s]->b);
weight = wrk->OP_state->pdf[s]->bweight;
/* set stream weight */
if (wrk->OP_state->w) stream_weight = wrk->OP_state->w->weight[s];
else stream_weight = 1.0;
/* setup storage pointer for this mixture pdf */
wrk->OP_vec = wrk->OP_vec_stream[s];
wrk->OP_veclen = wrk->OP_veclen_stream[s];
/* extend cache if needed */
calc_tied_mix_extend(wrk, wrk->OP_time);
/* prepare cache for this codebook at this time */
ttcache = wrk->mixture_cache[wrk->OP_time][book->id];
ttcachenum = wrk->mixture_cache_num[wrk->OP_time][book->id];
/* consult cache */
if (ttcachenum > 0) {
/* calculate using cache and weight */
for (i=0;iOP_calced_score[i] = ttcache[i].score + weight[ttcache[i].id];
}
num = ttcachenum;
} else {
/* compute Gaussian set */
/* computed Gaussians will be set in:
score ... OP_calced_score[0..OP_calced_num]
id ... OP_calced_id[0..OP_calced_num] */
if (wrk->OP_time >= 1) {
last_ttcache = wrk->mixture_cache[wrk->OP_time-1][book->id];
last_ttcachenum = wrk->mixture_cache_num[wrk->OP_time-1][book->id];
if (last_ttcachenum > 0) {
for(i=0;itmix_last_id[i] = last_ttcache[i].id;
/* tell last calced best */
(*(wrk->compute_gaussset))(wrk, book->d, book->num, wrk->tmix_last_id, last_ttcachenum);
} else {
(*(wrk->compute_gaussset))(wrk, book->d, book->num, NULL, 0);
}
} else {
(*(wrk->compute_gaussset))(wrk, book->d, book->num, NULL, 0);
}
/* store to cache */
wrk->mixture_cache_num[wrk->OP_time][book->id] = wrk->OP_calced_num;
for (i=0;iOP_calced_num;i++) {
id = wrk->OP_calced_id[i];
ttcache[i].id = id;
ttcache[i].score = wrk->OP_calced_score[i];
/* now OP_calced_{id|score} can be used for work area */
/* add weights */
wrk->OP_calced_score[i] += weight[id];
}
num = wrk->OP_calced_num;
}
/* add log probs */
logprob = addlog_array(wrk->OP_calced_score, num);
/* if outprob of a stream is zero, skip this stream */
if (logprob <= LOG_ZERO) continue;
/* sum all the obtained mixture scores */
logprobsum += logprob * stream_weight;
}
if (logprobsum == 0.0) return(LOG_ZERO); /* no valid stream */
if (logprobsum <= LOG_ZERO) return(LOG_ZERO); /* lowest == LOG_ZERO */
return (logprobsum * INV_LOG_TEN);
}
/**
* @brief Compute the output probability of current state OP_State,
* regardless of tied-mixture model or state-level mixture PDF.
*
* This function switches calculation function of calc_mix() and
* calc_tied_mix() based on the mixture PDF information.
* This will be used on a system which has tied-mixture codebook
* but some states still has their own mixture PDF.
*
* The initialization functions should be the same as calc_tied_mix(),
* since calc_mix() has no specific initialization.
*
* @param wrk [i/o] HMM computation work area
*
* @return the computed output probability in log10.
*/
LOGPROB
calc_compound_mix(HMMWork *wrk)
{
HTK_HMM_PDF *m;
GCODEBOOK *book;
LOGPROB logprob, logprobsum;
int i, id;
MIXCACHE *ttcache;
short ttcachenum;
MIXCACHE *last_ttcache;
short last_ttcachenum;
PROB *weight;
PROB stream_weight;
int s;
int num;
logprobsum = 0.0;
for(s=0;sOP_nstream;s++) {
/* set stream weight */
if (wrk->OP_state->w) stream_weight = wrk->OP_state->w->weight[s];
else stream_weight = 1.0;
m = wrk->OP_state->pdf[s];
/* setup storage pointer for this mixture pdf */
wrk->OP_vec = wrk->OP_vec_stream[s];
wrk->OP_veclen = wrk->OP_veclen_stream[s];
weight = wrk->OP_state->pdf[s]->bweight;
if (m->tmix) {
/* tied-mixture PDF */
book = (GCODEBOOK *)(m->b);
/* extend cache if needed */
calc_tied_mix_extend(wrk, wrk->OP_time);
/* prepare cache for this codebook at this time */
ttcache = wrk->mixture_cache[wrk->OP_time][book->id];
ttcachenum = wrk->mixture_cache_num[wrk->OP_time][book->id];
/* consult cache */
if (ttcachenum > 0) {
/* calculate using cache and weight */
for (i=0;iOP_calced_score[i] = ttcache[i].score + weight[ttcache[i].id];
}
num = ttcachenum;
} else {
/* compute Gaussian set */
/* computed Gaussians will be set in:
score ... OP_calced_score[0..OP_calced_num]
id ... OP_calced_id[0..OP_calced_num] */
if (wrk->OP_time >= 1) {
last_ttcache = wrk->mixture_cache[wrk->OP_time-1][book->id];
last_ttcachenum = wrk->mixture_cache_num[wrk->OP_time-1][book->id];
if (last_ttcachenum > 0) {
for(i=0;itmix_last_id[i] = last_ttcache[i].id;
/* tell last calced best */
(*(wrk->compute_gaussset))(wrk, book->d, book->num, wrk->tmix_last_id, last_ttcachenum);
} else {
(*(wrk->compute_gaussset))(wrk, book->d, book->num, NULL, 0);
}
} else {
(*(wrk->compute_gaussset))(wrk, book->d, book->num, NULL, 0);
}
/* store to cache */
wrk->mixture_cache_num[wrk->OP_time][book->id] = wrk->OP_calced_num;
for (i=0;iOP_calced_num;i++) {
id = wrk->OP_calced_id[i];
ttcache[i].id = id;
ttcache[i].score = wrk->OP_calced_score[i];
/* now OP_calced_{id|score} can be used for work area */
/* add weights */
wrk->OP_calced_score[i] += weight[id];
}
num = wrk->OP_calced_num;
}
} else {
/* normal state */
(*(wrk->compute_gaussset))(wrk, m->b, m->mix_num, NULL, 0);
/* add weights */
for(i=0;iOP_calced_num;i++) {
wrk->OP_calced_score[i] += weight[wrk->OP_calced_id[i]];
}
num = wrk->OP_calced_num;
}
/* add log probs */
logprob = addlog_array(wrk->OP_calced_score, num);
/* if outprob of a stream is zero, skip this stream */
if (logprob <= LOG_ZERO) continue;
/* sum all the obtained mixture scores */
logprobsum += logprob * stream_weight;
}
if (logprobsum == 0.0) return(LOG_ZERO); /* no valid stream */
if (logprobsum <= LOG_ZERO) return(LOG_ZERO); /* lowest == LOG_ZERO */
return (logprobsum * INV_LOG_TEN);
}