ps_alignment.c
Go to the documentation of this file.
/* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* ====================================================================
* Copyright (c) 2010 Carnegie Mellon University. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* This work was supported in part by funding from the Defense Advanced
* Research Projects Agency and the National Science Foundation of the
* United States of America, and the CMU Sphinx Speech Consortium.
*
* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
* NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ====================================================================
*
*/
/* System headers. */
/* SphinxBase headers. */
#include <sphinxbase/ckd_alloc.h>
/* Local headers. */
#include "ps_alignment.h"
ps_alignment_t *
ps_alignment_init(dict2pid_t *d2p)
{
ps_alignment_t *al = ckd_calloc(1, sizeof(*al));
al->d2p = dict2pid_retain(d2p);
return al;
}
int
ps_alignment_free(ps_alignment_t *al)
{
if (al == NULL)
return 0;
dict2pid_free(al->d2p);
ckd_free(al->word.seq);
ckd_free(al->sseq.seq);
ckd_free(al->state.seq);
ckd_free(al);
return 0;
}
#define VECTOR_GROW 10
static void *
vector_grow_one(void *ptr, uint16 *n_alloc, uint16 *n, size_t item_size)
{
int newsize = *n + 1;
if (newsize < *n_alloc) {
*n += 1;
return ptr;
}
newsize += VECTOR_GROW;
if (newsize > 0xffff)
return NULL;
ptr = ckd_realloc(ptr, newsize * item_size);
*n += 1;
*n_alloc = newsize;
return ptr;
}
static ps_alignment_entry_t *
ps_alignment_vector_grow_one(ps_alignment_vector_t *vec)
{
void *ptr;
ptr = vector_grow_one(vec->seq, &vec->n_alloc,
&vec->n_ent, sizeof(*vec->seq));
if (ptr == NULL)
return NULL;
vec->seq = ptr;
return vec->seq + vec->n_ent - 1;
}
static void
ps_alignment_vector_empty(ps_alignment_vector_t *vec)
{
vec->n_ent = 0;
}
int
ps_alignment_add_word(ps_alignment_t *al,
int32 wid, int duration)
{
ps_alignment_entry_t *ent;
if ((ent = ps_alignment_vector_grow_one(&al->word)) == NULL)
return 0;
ent->id.wid = wid;
if (al->word.n_ent > 1)
ent->start = ent[-1].start + ent[-1].duration;
else
ent->start = 0;
ent->duration = duration;
ent->score = 0;
ent->parent = PS_ALIGNMENT_NONE;
ent->child = PS_ALIGNMENT_NONE;
return al->word.n_ent;
}
int
ps_alignment_populate(ps_alignment_t *al)
{
dict2pid_t *d2p;
dict_t *dict;
bin_mdef_t *mdef;
int i, lc;
/* Clear phone and state sequences. */
ps_alignment_vector_empty(&al->sseq);
ps_alignment_vector_empty(&al->state);
/* For each word, expand to phones/senone sequences. */
d2p = al->d2p;
dict = d2p->dict;
mdef = d2p->mdef;
lc = bin_mdef_silphone(mdef);
for (i = 0; i < al->word.n_ent; ++i) {
ps_alignment_entry_t *went = al->word.seq + i;
ps_alignment_entry_t *sent;
int wid = went->id.wid;
int len = dict_pronlen(dict, wid);
int j, rc;
if (i < al->word.n_ent - 1)
rc = dict_first_phone(dict, al->word.seq[i+1].id.wid);
else
rc = bin_mdef_silphone(mdef);
/* First phone. */
if ((sent = ps_alignment_vector_grow_one(&al->sseq)) == NULL) {
E_ERROR("Failed to add phone entry!\n");
return -1;
}
sent->id.pid.cipid = dict_first_phone(dict, wid);
sent->id.pid.tmatid = bin_mdef_pid2tmatid(mdef, sent->id.pid.cipid);
sent->start = went->start;
sent->duration = went->duration;
sent->score = 0;
sent->parent = i;
went->child = (uint16)(sent - al->sseq.seq);
if (len == 1)
sent->id.pid.ssid
= dict2pid_lrdiph_rc(d2p, sent->id.pid.cipid, lc, rc);
else
sent->id.pid.ssid
= dict2pid_ldiph_lc(d2p, sent->id.pid.cipid,
dict_second_phone(dict, wid), lc);
assert(sent->id.pid.ssid != BAD_SSID);
/* Internal phones. */
for (j = 1; j < len - 1; ++j) {
if ((sent = ps_alignment_vector_grow_one(&al->sseq)) == NULL) {
E_ERROR("Failed to add phone entry!\n");
return -1;
}
sent->id.pid.cipid = dict_pron(dict, wid, j);
sent->id.pid.tmatid = bin_mdef_pid2tmatid(mdef, sent->id.pid.cipid);
sent->id.pid.ssid = dict2pid_internal(d2p, wid, j);
assert(sent->id.pid.ssid != BAD_SSID);
sent->start = went->start;
sent->duration = went->duration;
sent->score = 0;
sent->parent = i;
}
/* Last phone. */
if (j < len) {
xwdssid_t *rssid;
assert(j == len - 1);
if ((sent = ps_alignment_vector_grow_one(&al->sseq)) == NULL) {
E_ERROR("Failed to add phone entry!\n");
return -1;
}
sent->id.pid.cipid = dict_last_phone(dict, wid);
sent->id.pid.tmatid = bin_mdef_pid2tmatid(mdef, sent->id.pid.cipid);
rssid = dict2pid_rssid(d2p, sent->id.pid.cipid,
dict_second_last_phone(dict, wid));
sent->id.pid.ssid = rssid->ssid[rssid->cimap[rc]];
assert(sent->id.pid.ssid != BAD_SSID);
sent->start = went->start;
sent->duration = went->duration;
sent->score = 0;
sent->parent = i;
}
/* Update lc. Could just use sent->id.pid.cipid here but that
* seems needlessly obscure. */
lc = dict_last_phone(dict, wid);
}
/* For each senone sequence, expand to senones. (we could do this
* nested above but this makes it more clear and easier to
* refactor) */
for (i = 0; i < al->sseq.n_ent; ++i) {
ps_alignment_entry_t *pent = al->sseq.seq + i;
ps_alignment_entry_t *sent;
int j;
for (j = 0; j < bin_mdef_n_emit_state(mdef); ++j) {
if ((sent = ps_alignment_vector_grow_one(&al->state)) == NULL) {
E_ERROR("Failed to add state entry!\n");
return -1;
}
sent->id.senid = bin_mdef_sseq2sen(mdef, pent->id.pid.ssid, j);
assert(sent->id.senid != BAD_SENID);
sent->start = pent->start;
sent->duration = pent->duration;
sent->score = 0;
sent->parent = i;
if (j == 0)
pent->child = (uint16)(sent - al->state.seq);
}
}
return 0;
}
/* FIXME: Somewhat the same as the above function, needs refactoring */
int
ps_alignment_populate_ci(ps_alignment_t *al)
{
dict2pid_t *d2p;
dict_t *dict;
bin_mdef_t *mdef;
int i;
/* Clear phone and state sequences. */
ps_alignment_vector_empty(&al->sseq);
ps_alignment_vector_empty(&al->state);
/* For each word, expand to phones/senone sequences. */
d2p = al->d2p;
dict = d2p->dict;
mdef = d2p->mdef;
for (i = 0; i < al->word.n_ent; ++i) {
ps_alignment_entry_t *went = al->word.seq + i;
ps_alignment_entry_t *sent;
int wid = went->id.wid;
int len = dict_pronlen(dict, wid);
int j;
for (j = 0; j < len; ++j) {
if ((sent = ps_alignment_vector_grow_one(&al->sseq)) == NULL) {
E_ERROR("Failed to add phone entry!\n");
return -1;
}
sent->id.pid.cipid = dict_pron(dict, wid, j);
sent->id.pid.tmatid = bin_mdef_pid2tmatid(mdef, sent->id.pid.cipid);
sent->id.pid.ssid = bin_mdef_pid2ssid(mdef, sent->id.pid.cipid);
assert(sent->id.pid.ssid != BAD_SSID);
sent->start = went->start;
sent->duration = went->duration;
sent->score = 0;
sent->parent = i;
}
}
/* For each senone sequence, expand to senones. (we could do this
* nested above but this makes it more clear and easier to
* refactor) */
for (i = 0; i < al->sseq.n_ent; ++i) {
ps_alignment_entry_t *pent = al->sseq.seq + i;
ps_alignment_entry_t *sent;
int j;
for (j = 0; j < bin_mdef_n_emit_state(mdef); ++j) {
if ((sent = ps_alignment_vector_grow_one(&al->state)) == NULL) {
E_ERROR("Failed to add state entry!\n");
return -1;
}
sent->id.senid = bin_mdef_sseq2sen(mdef, pent->id.pid.ssid, j);
assert(sent->id.senid != BAD_SENID);
sent->start = pent->start;
sent->duration = pent->duration;
sent->score = 0;
sent->parent = i;
if (j == 0)
pent->child = (uint16)(sent - al->state.seq);
}
}
return 0;
}
int
ps_alignment_propagate(ps_alignment_t *al)
{
ps_alignment_entry_t *last_ent = NULL;
int i;
/* Propagate duration up from states to phones. */
for (i = 0; i < al->state.n_ent; ++i) {
ps_alignment_entry_t *sent = al->state.seq + i;
ps_alignment_entry_t *pent = al->sseq.seq + sent->parent;
if (pent != last_ent) {
pent->start = sent->start;
pent->duration = 0;
pent->score = 0;
}
pent->duration += sent->duration;
pent->score += sent->score;
last_ent = pent;
}
/* Propagate duration up from phones to words. */
last_ent = NULL;
for (i = 0; i < al->sseq.n_ent; ++i) {
ps_alignment_entry_t *pent = al->sseq.seq + i;
ps_alignment_entry_t *went = al->word.seq + pent->parent;
if (went != last_ent) {
went->start = pent->start;
went->duration = 0;
went->score = 0;
}
went->duration += pent->duration;
went->score += pent->score;
last_ent = went;
}
return 0;
}
int
ps_alignment_n_words(ps_alignment_t *al)
{
return (int)al->word.n_ent;
}
int
ps_alignment_n_phones(ps_alignment_t *al)
{
return (int)al->sseq.n_ent;
}
int
ps_alignment_n_states(ps_alignment_t *al)
{
return (int)al->state.n_ent;
}
ps_alignment_iter_t *
ps_alignment_words(ps_alignment_t *al)
{
ps_alignment_iter_t *itor;
if (al->word.n_ent == 0)
return NULL;
itor = ckd_calloc(1, sizeof(*itor));
itor->al = al;
itor->vec = &al->word;
itor->pos = 0;
return itor;
}
ps_alignment_iter_t *
ps_alignment_phones(ps_alignment_t *al)
{
ps_alignment_iter_t *itor;
if (al->sseq.n_ent == 0)
return NULL;
itor = ckd_calloc(1, sizeof(*itor));
itor->al = al;
itor->vec = &al->sseq;
itor->pos = 0;
return itor;
}
ps_alignment_iter_t *
ps_alignment_states(ps_alignment_t *al)
{
ps_alignment_iter_t *itor;
if (al->state.n_ent == 0)
return NULL;
itor = ckd_calloc(1, sizeof(*itor));
itor->al = al;
itor->vec = &al->state;
itor->pos = 0;
return itor;
}
ps_alignment_entry_t *
ps_alignment_iter_get(ps_alignment_iter_t *itor)
{
return itor->vec->seq + itor->pos;
}
int
ps_alignment_iter_free(ps_alignment_iter_t *itor)
{
ckd_free(itor);
return 0;
}
ps_alignment_iter_t *
ps_alignment_iter_goto(ps_alignment_iter_t *itor, int pos)
{
if (itor == NULL)
return NULL;
if (pos >= itor->vec->n_ent) {
ps_alignment_iter_free(itor);
return NULL;
}
itor->pos = pos;
return itor;
}
ps_alignment_iter_t *
ps_alignment_iter_next(ps_alignment_iter_t *itor)
{
if (itor == NULL)
return NULL;
if (++itor->pos >= itor->vec->n_ent) {
ps_alignment_iter_free(itor);
return NULL;
}
return itor;
}
ps_alignment_iter_t *
ps_alignment_iter_prev(ps_alignment_iter_t *itor)
{
if (itor == NULL)
return NULL;
if (--itor->pos < 0) {
ps_alignment_iter_free(itor);
return NULL;
}
return itor;
}
ps_alignment_iter_t *
ps_alignment_iter_up(ps_alignment_iter_t *itor)
{
ps_alignment_iter_t *itor2;
if (itor == NULL)
return NULL;
if (itor->vec == &itor->al->word)
return NULL;
if (itor->vec->seq[itor->pos].parent == PS_ALIGNMENT_NONE)
return NULL;
itor2 = ckd_calloc(1, sizeof(*itor2));
itor2->al = itor->al;
itor2->pos = itor->vec->seq[itor->pos].parent;
if (itor->vec == &itor->al->sseq)
itor2->vec = &itor->al->word;
else
itor2->vec = &itor->al->sseq;
return itor2;
}
ps_alignment_iter_t *
ps_alignment_iter_down(ps_alignment_iter_t *itor)
{
ps_alignment_iter_t *itor2;
if (itor == NULL)
return NULL;
if (itor->vec == &itor->al->state)
return NULL;
if (itor->vec->seq[itor->pos].child == PS_ALIGNMENT_NONE)
return NULL;
itor2 = ckd_calloc(1, sizeof(*itor2));
itor2->al = itor->al;
itor2->pos = itor->vec->seq[itor->pos].child;
if (itor->vec == &itor->al->word)
itor2->vec = &itor->al->sseq;
else
itor2->vec = &itor->al->state;
return itor2;
}
BAD_SSID
#define BAD_SSID
Invalid senone sequence ID (limited to 16 bits for PocketSphinx).
Definition: bin_mdef.h:94
BAD_SENID
#define BAD_SENID
Invalid senone ID (limited to 16 bits for PocketSphinx).
Definition: bin_mdef.h:98
dict2pid_free
int dict2pid_free(dict2pid_t *d2p)
Free the memory dict2pid structure.
Definition: dict2pid.c:507
dict2pid_internal
s3ssid_t dict2pid_internal(dict2pid_t *d2p, int32 wid, int pos)
Return the senone sequence ID for the given word position.
Definition: dict2pid.c:367
dict2pid_retain
dict2pid_t * dict2pid_retain(dict2pid_t *d2p)
Retain a pointer to dict2pid.
Definition: dict2pid.c:500
dict2pid_rssid
#define dict2pid_rssid(d, ci, lc)
Access macros; not designed for arbitrary use.
Definition: dict2pid.h:115
dict_pron
#define dict_pron(d, w, p)
The CI phones of the word w at position p.
Definition: dict.h:165
ps_alignment_populate
int ps_alignment_populate(ps_alignment_t *al)
Populate lower layers using available word information.
Definition: ps_alignment.c:129
ps_alignment_phones
ps_alignment_iter_t * ps_alignment_phones(ps_alignment_t *al)
Iterate over the alignment starting at the first phone.
Definition: ps_alignment.c:383
ps_alignment_iter_down
ps_alignment_iter_t * ps_alignment_iter_down(ps_alignment_iter_t *itor)
Get a new iterator starting at the first child of the current node.
Definition: ps_alignment.c:481
ps_alignment_iter_prev
ps_alignment_iter_t * ps_alignment_iter_prev(ps_alignment_iter_t *itor)
Move an alignment iterator back.
Definition: ps_alignment.c:449
ps_alignment_iter_goto
ps_alignment_iter_t * ps_alignment_iter_goto(ps_alignment_iter_t *itor, int pos)
Move alignment iterator to given index.
Definition: ps_alignment.c:424
ps_alignment_iter_free
int ps_alignment_iter_free(ps_alignment_iter_t *itor)
Release an iterator before completing all iterations.
Definition: ps_alignment.c:417
ps_alignment_n_words
int ps_alignment_n_words(ps_alignment_t *al)
Number of words.
Definition: ps_alignment.c:351
ps_alignment_states
ps_alignment_iter_t * ps_alignment_states(ps_alignment_t *al)
Iterate over the alignment starting at the first state.
Definition: ps_alignment.c:397
ps_alignment_words
ps_alignment_iter_t * ps_alignment_words(ps_alignment_t *al)
Iterate over the alignment starting at the first word.
Definition: ps_alignment.c:369
ps_alignment_iter_get
ps_alignment_entry_t * ps_alignment_iter_get(ps_alignment_iter_t *itor)
Get the alignment entry pointed to by an iterator.
Definition: ps_alignment.c:411
ps_alignment_iter_next
ps_alignment_iter_t * ps_alignment_iter_next(ps_alignment_iter_t *itor)
Move an alignment iterator forward.
Definition: ps_alignment.c:437
ps_alignment_n_phones
int ps_alignment_n_phones(ps_alignment_t *al)
Number of phones.
Definition: ps_alignment.c:357
ps_alignment_add_word
int ps_alignment_add_word(ps_alignment_t *al, int32 wid, int duration)
Append a word.
Definition: ps_alignment.c:108
ps_alignment_free
int ps_alignment_free(ps_alignment_t *al)
Release an alignment.
Definition: ps_alignment.c:59
ps_alignment_init
ps_alignment_t * ps_alignment_init(dict2pid_t *d2p)
Create a new, empty alignment.
Definition: ps_alignment.c:51
ps_alignment_n_states
int ps_alignment_n_states(ps_alignment_t *al)
Number of states.
Definition: ps_alignment.c:363
ps_alignment_propagate
int ps_alignment_propagate(ps_alignment_t *al)
Propagate timing information up from state sequence.
Definition: ps_alignment.c:313
ps_alignment_populate_ci
int ps_alignment_populate_ci(ps_alignment_t *al)
Populate lower layers using context-independent phones.
Definition: ps_alignment.c:247
ps_alignment_iter_up
ps_alignment_iter_t * ps_alignment_iter_up(ps_alignment_iter_t *itor)
Get a new iterator starting at the parent of the current node.
Definition: ps_alignment.c:461
ps_alignment.h
Multi-level alignment structure.
bin_mdef_s
Definition: bin_mdef.h:117
dict2pid_t
Building composite triphone (as well as word internal triphones) with the dictionary.
Definition: dict2pid.h:84
dict2pid_t::mdef
bin_mdef_t * mdef
Model definition, used to generate internal ssids on the fly.
Definition: dict2pid.h:87
dict2pid_t::dict
dict_t * dict
Dictionary this table refers to.
Definition: dict2pid.h:89
dict_t
a structure for a dictionary.
Definition: dict.h:76
ps_alignment_entry_s
Definition: ps_alignment.h:56
ps_alignment_iter_s
Definition: ps_alignment.h:88
ps_alignment_s
Definition: ps_alignment.h:80
ps_alignment_vector_s
Definition: ps_alignment.h:74
xwdssid_t
cross word triphone model structure
Definition: dict2pid.h:73
xwdssid_t::cimap
s3cipid_t * cimap
Index into ssid[] above for each ci phone.
Definition: dict2pid.h:75
xwdssid_t::ssid
s3ssid_t * ssid
Senone Sequence ID list for all context ciphones.
Definition: dict2pid.h:74