windows-nt/Source/XPSP1/NT/enduser/speech/tts/truetalk/backend/tips.cpp

807 lines
19 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/******************************************************************************
* tips.cpp *
*----------*
*
*------------------------------------------------------------------------------
* Copyright (c) 1996-1997 Entropic Research Laboratory, Inc.
* Copyright (C) 1998 Entropic, Inc
* Copyright (C) 2000 Microsoft Corporation Date: 03/02/00-12/4/00
* All Rights Reserved
*
********************************************************************* mplumpe was PACOG ***/
#include "tips.h"
#include "SynthUnit.h"
#include "sigproc.h"
#include <vapiIo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <assert.h>
#include "ftol.h"
const double CTips::m_iDefaultPeriod = .01; //10 msec.
const double CTips::m_dMinF0 = 15.0; // Absolute minimum F0 alowed
const int CTips::m_iHalfHanLen_c = 1024;
/*****************************************************************************
* CTips::CTips *
*--------------*
* Description:
*
******************************************************************* PACOG ***/
CTips::CTips (int iOptions)
{
m_fLptips = (bool) (iOptions & LpTips);
m_fRtips = (iOptions & RTips) != 0;
m_iSampFormat = 0;
m_iSampFreq = 0;
m_dGain = 1.0;
m_pfF0 = 0;
m_iNumF0 = 0;
m_dRunTime = 0.0;
m_dF0SampFreq = 0.0;
m_dF0TimeNext = 0.0;
m_dF0TimeStep = 0.0;
m_dF0Value = 0.0;
m_dAcumF0 = 0.0;
m_iF0Idx = 0;
m_dLastEpochTime = 0.0;
m_dNewEpochTime = 0.0;
m_aBuffer[0].m_pdSamples = 0;
m_aBuffer[1].m_pdSamples = 0;
m_iLpcOrder = 0;
m_pdFiltMem = 0;
m_pdInterpCoef = 0;
m_pdLastCoef = 0;
m_pUnit = 0;
m_psSynthSamples = 0;
m_iNumSynthSamples = 0;
m_adHalfHanning = 0;
}
/*****************************************************************************
* CTips::~CTips *
*---------------*
* Description:
*
******************************************************************* mplumpe ***/
CTips::~CTips ()
{
if (m_pdFiltMem)
{
delete[] m_pdFiltMem;
}
if (m_pdInterpCoef)
{
delete[] m_pdInterpCoef;
}
if (m_pdLastCoef)
{
delete[] m_pdLastCoef;
}
if (m_aBuffer[0].m_pdSamples)
{
delete[] m_aBuffer[0].m_pdSamples;
}
if (m_aBuffer[1].m_pdSamples)
{
delete[] m_aBuffer[1].m_pdSamples;
}
if (m_psSynthSamples)
{
delete[] m_psSynthSamples;
}
if (m_adHalfHanning)
{
delete [] m_adHalfHanning;
}
}
/*****************************************************************************
* CTips::Init *
*-------------*
* Description:
*
******************************************************************* mplumpe ***/
int CTips::Init (int iSampFormat, int iSampFreq)
{
int i;
assert (iSampFreq>0);
m_iSampFreq = iSampFreq;
m_iSampFormat = iSampFormat;
int iPeriodLen = (int) (iSampFreq * m_iDefaultPeriod);
// Delete old buffers, if they exist
if (m_aBuffer[0].m_pdSamples)
{
delete[] m_aBuffer[0].m_pdSamples;
}
if (m_aBuffer[1].m_pdSamples)
{
delete[] m_aBuffer[1].m_pdSamples;
}
// Allocate new ones, and initialize them to Zero.
// NOTE: Only need to allocate buffer[1], really, since they'll be rotated before
// synthesis.
if ((m_aBuffer[0].m_pdSamples = new double[2 * iPeriodLen]) == 0 )
{
return 0;
}
//memset(m_aBuffer[0].m_pdSamples, 0, sizeof(double) * 2 * iPeriodLen);
if ((m_aBuffer[1].m_pdSamples = new double [2 * iPeriodLen]) == 0)
{
return 0;
}
memset(m_aBuffer[1].m_pdSamples, 0, sizeof(double) * 2 * iPeriodLen);
m_aBuffer[0].m_iNumSamples = 2 * iPeriodLen;
m_aBuffer[0].m_iCenter = iPeriodLen;
m_aBuffer[0].m_dDelay = 0.0;
m_aBuffer[1].m_iNumSamples = 2 * iPeriodLen;
m_aBuffer[1].m_iCenter = iPeriodLen;
m_aBuffer[1].m_dDelay = 0.0;
//
// make Hanning buffer
//
if (m_adHalfHanning)
{
delete m_adHalfHanning;
}
if ((m_adHalfHanning = new double [m_iHalfHanLen_c]) == 0)
{
return 0;
}
for (i=0; i < m_iHalfHanLen_c; i++)
{
m_adHalfHanning[i] = 0.5-0.5*cos(M_PI*i/m_iHalfHanLen_c);
}
if (m_fLptips)
{
return LpcInit();
}
return 1;
}
/*****************************************************************************
* CTips::SetGain *
*----------------*
* Description:
*
******************************************************************* PACOG ***/
void CTips::SetGain (double dGain)
{
assert (dGain>=0.0);
m_dGain = dGain;
}
/*****************************************************************************
* CTips::GetGain *
*----------------*
* Description:
*
******************************************************************* PACOG ***/
double CTips::GetGain ()
{
return m_dGain;
}
/*****************************************************************************
* CTips::NewSentence *
*--------------------*
* Description:
*
******************************************************************* PACOG ***/
void CTips::NewSentence (float* pfF0, int iNumF0, int iF0SampFreq)
{
assert (pfF0);
assert (iNumF0>0);
assert (iF0SampFreq>0);
m_pfF0 = pfF0;
m_iNumF0 = iNumF0;
m_dF0SampFreq = iF0SampFreq;
m_dF0TimeStep = 1.0/iF0SampFreq;
m_dRunTime = 0.0;
m_dF0TimeNext = 0.0;
m_dAcumF0 = 0.0;
m_iF0Idx = 0;
m_dLastEpochTime = 0.0;
m_dNewEpochTime = 0.0;
}
/*****************************************************************************
* CTips::NewUnit *
*----------------*
* Description:
* Gets a new unit to synthesize, and does some analysis on it.
******************************************************************* PACOG ***/
int CTips::NewUnit (CSynth* pUnit)
{
if (pUnit)
{
m_pUnit = pUnit;
if (m_fLptips)
{
if (!m_pUnit->LpcAnalysis(m_iSampFreq, m_iLpcOrder))
{
return 0;
}
}
if ( Prosody (pUnit) == -1)
{
return 0;
}
return 1;
}
return 0;
}
/*****************************************************************************
* CTips::Prosody *
*----------------*
* Description:
* We get synthesis epochs track for a segment. F0 curve integration is
* therefore carried out here. The sampling frequency chosen is high enough
* as to reduce jitter to an umperceivable level, but that depends on the
* synthesis module being capable of synthesizing at that interval.
*
******************************************************************* PACOG ***/
int CTips::Prosody ( CSynth* pUnit )
{
double dF0IntegralTime = 0.0;
assert (m_pfF0 && m_iNumF0>0);
if (m_dNewEpochTime != m_dLastEpochTime)
{
if ((pUnit->m_pdSynEpochs = new double[2]) == 0)
{
return -1;
}
pUnit->m_pdSynEpochs[0] = m_dLastEpochTime;
pUnit->m_pdSynEpochs[1] = m_dNewEpochTime;
pUnit->m_iNumSynEpochs = 2;
}
else
{
if ((pUnit->m_pdSynEpochs = new double[1]) == 0)
{
return -1;
}
pUnit->m_pdSynEpochs[0] = m_dNewEpochTime;
pUnit->m_iNumSynEpochs = 1;
}
while ( m_dRunTime < pUnit->m_dRunTimeLimit ||
// Find an extra epoch, for the overlapping period
// if the last epoch doesn't already cross the segment limit
pUnit->m_pdSynEpochs[pUnit->m_iNumSynEpochs-1] < pUnit->m_dRunTimeLimit)
{
if (m_dRunTime >= m_dF0TimeNext)
{
m_dF0Value = m_pfF0[m_iF0Idx];
if (m_iF0Idx < m_iNumF0-1)
{
m_iF0Idx++;
}
if (m_dF0Value<=0.0)
{
m_dF0Value = 100.0; // Best choice is f0=100Hz
}
else if (m_dF0Value <= m_dMinF0)
{
m_dF0Value = m_dMinF0;
}
m_dF0TimeNext += m_dF0TimeStep;
}
dF0IntegralTime = (1.0 - m_dAcumF0) / m_dF0Value;
if (dF0IntegralTime >= m_dF0TimeStep)
{
m_dRunTime += m_dF0TimeStep;
m_dAcumF0 += m_dF0Value * m_dF0TimeStep;
}
else
{
m_dRunTime += dF0IntegralTime;
m_dAcumF0 = 0;
//Got epoch
m_dLastEpochTime = m_dNewEpochTime;
m_dNewEpochTime = m_dRunTime;
//Reallocate the synthesis epochs array
double* pdSynEpochs = new double[pUnit->m_iNumSynEpochs + 1];
if (!pdSynEpochs)
{
return -1;
}
memcpy(pdSynEpochs, pUnit->m_pdSynEpochs, pUnit->m_iNumSynEpochs * sizeof(double));
delete[] pUnit->m_pdSynEpochs;
pUnit->m_pdSynEpochs = pdSynEpochs;
// And add a new epoch
pUnit->m_pdSynEpochs[pUnit->m_iNumSynEpochs] = m_dNewEpochTime;
pUnit->m_iNumSynEpochs++;
}
}
if (pUnit->m_iNumSynEpochs <3)
{
delete[] pUnit->m_pdSynEpochs;
pUnit->m_pdSynEpochs = 0;
return 0;
}
// Synthesis epochs are in absolute synthesis time
double epStartTime = ((long)(pUnit->m_pdSynEpochs[0] * m_iSampFreq)) / (double)m_iSampFreq;
for (int i=0; i<pUnit->m_iNumSynEpochs; i++)
{
pUnit->m_pdSynEpochs[i] -= epStartTime;
}
return pUnit->FindPrecedent ();
}
/*****************************************************************************
* CTips::Pending *
*----------------*
* Description:
*
******************************************************************* PACOG ***/
int CTips::Pending ()
{
return m_pUnit != 0;
}
/*****************************************************************************
* CTips::NextPeriod *
*-------------------*
* Description:
*
******************************************************************* PACOG ***/
int CTips::NextPeriod (short** ppnSamples, int *piNumSamples)
{
int iPeriodLen;
assert (ppnSamples && piNumSamples> 0);
if (m_pUnit)
{
iPeriodLen = m_pUnit->NextBuffer (this);
if ( iPeriodLen > 0 )
{
Synthesize (iPeriodLen);
*ppnSamples = m_psSynthSamples;
*piNumSamples = iPeriodLen;
return 1;
}
else
{
delete m_pUnit;
m_pUnit = 0;
}
}
return 0;
}
/*****************************************************************************
* CTips::FillBuffer *
*-------------------*
* Description:
*
******************************************************************* PACOG ***/
int CTips::SetBuffer ( double* pdSamples, int iNumSamples, int iCenter, double dDelay, double* pdLpcCoef)
{
// Advance buffers
delete[] m_aBuffer[0].m_pdSamples;
m_aBuffer[0] = m_aBuffer[1];
m_aBuffer[1].m_pdSamples = pdSamples;
m_aBuffer[1].m_iNumSamples = iNumSamples;
m_aBuffer[1].m_iCenter = iCenter;
m_aBuffer[1].m_dDelay = dDelay;
m_pdNewCoef = pdLpcCoef;
return 1;
}
/*****************************************************************************
* CTips::Synthesize *
*-------------------*
* Description:
*
******************************************************************* PACOG ***/
int CTips::Synthesize (int iPeriodLen)
{
double* pdPeriodSamples = 0;
double* windowedLeft = 0;
int leftSize;
double* windowedRight = 0;
int rightSize;
double *p1, *p2;
int i;
assert (iPeriodLen);
if (m_fRtips)
{
NonIntegerDelay (m_aBuffer[1].m_pdSamples, m_aBuffer[1].m_iNumSamples, m_aBuffer[1].m_dDelay);
}
if (!GetWindowedSignal(0, iPeriodLen, &windowedLeft, &leftSize))
{
goto error;
}
if (!GetWindowedSignal(1, iPeriodLen, &windowedRight, &rightSize) )
{
goto error;
}
assert (windowedLeft && leftSize);
assert (windowedRight && rightSize);
if (!windowedLeft || !leftSize || !windowedRight || !rightSize )
{
goto error;
}
if ((pdPeriodSamples = new double[iPeriodLen]) == 0)
{
goto error;
}
p1=windowedLeft;
p2=windowedRight;
for (i=0; i<iPeriodLen - rightSize && i<leftSize; i++)
{
pdPeriodSamples[i] = *p1++;
}
// If windows overlap, they are added
for ( ;i<leftSize; i++)
{
pdPeriodSamples[i] = *p1++ + *p2++;
}
// Else, we fill the space with zeros
for (; i<iPeriodLen - rightSize; i++)
{
pdPeriodSamples[i] = 0.0;
}
for (;i<iPeriodLen;i++)
{
pdPeriodSamples[i] = *p2++;
}
delete[] windowedLeft;
delete[] windowedRight;
if (m_fLptips)
{
LpcSynth (pdPeriodSamples, iPeriodLen);
}
// reuse the same buffer if possible
if ( m_iNumSynthSamples < iPeriodLen )
{
if (m_psSynthSamples)
{
delete[] m_psSynthSamples;
}
if ((m_psSynthSamples = new short[iPeriodLen]) == 0)
{
goto error;
}
m_iNumSynthSamples = iPeriodLen;
}
for (i=0; i<iPeriodLen; i++)
{
m_psSynthSamples[i] = ClipData(pdPeriodSamples[i]);
}
delete[] pdPeriodSamples;
return 1;
error:
if (pdPeriodSamples)
{
delete[] pdPeriodSamples;
}
if (windowedLeft)
{
delete[] windowedLeft;
}
if (windowedRight)
{
delete[] windowedRight;
}
return 0;
}
/*****************************************************************************
* CTips::GetWindowedSignal *
*----------------------------*
* Description:
*
******************************************************************* PACOG ***/
int CTips::GetWindowedSignal (int whichBuffer, int iPeriodLen,
double** windowed, int* nWindowed)
{
double* sampPtr;
int nSamples;
int from;
if (whichBuffer==0)
{
sampPtr = m_aBuffer[0].m_pdSamples + m_aBuffer[0].m_iCenter;
nSamples = __min(iPeriodLen, (m_aBuffer[0].m_iNumSamples - m_aBuffer[0].m_iCenter));
}
else
{
from = __max(0, m_aBuffer[1].m_iCenter - iPeriodLen);
sampPtr = m_aBuffer[1].m_pdSamples + from;
nSamples = m_aBuffer[1].m_iCenter - from;
}
if (nSamples)
{
if ((*windowed = new double[nSamples]) == 0)
{
return 0;
}
*nWindowed = nSamples;
memcpy (*windowed, sampPtr, nSamples * sizeof(**windowed));
if (whichBuffer==0)
{
HalfHanning (*windowed, nSamples, 1.0, WindowSecondHalf);
}
else
{
HalfHanning (*windowed, nSamples, 1.0, WindowFirstHalf);
}
return 1;
}
else
{
fprintf (stderr, "NULL vector in GetWindowedSignal\n");
}
return 0;
}
/*****************************************************************************
* CTips::HalfHanning *
*--------------------*
* Description:
* 12/4/00 - Since ampl wasn't being used, I'm now asserting it equal
* to 1 and ignoring it. Also, a large hanning window is
* pre-computed and interpolated here, instead of being
* calculated here.
*
******************************************************************* mplumpe ***/
void CTips::HalfHanning (double* x, int xLen, double ampl, int whichHalf)
{
double delta;
double dk;
int start;
int sign;
int i;
assert (1 == ampl);
if (x && xLen)
{
delta = m_iHalfHanLen_c / xLen;
dk=0.; // FTOL function does rounding. If casting to int, need to start at 0.5 to get rounding
/*
* When multiplying by the second half, the window function is the same,
* but we multiply from the last sample in the vector to the first
* NOTE: The first sample is multiplyed by 0 in the case of the first
* half, and by 1 (*ampl, of course) in the case of the second half.
*/
switch (whichHalf)
{
case WindowSecondHalf:
start=xLen;
sign=-1;
break;
case WindowFirstHalf:
x[0]=0.0;
start=0;
sign=1;
break;
default:
fprintf(stderr, "Hanning, should especify a half window\n");
return;
}
for (i=1; i<xLen; i++)
{
dk += delta;
x[start+sign*i] *= m_adHalfHanning[FTOL(dk)];
}
}
}
/*****************************************************************************
* CTips::ClipData *
*-----------------*
* Description:
* 12/4/00 - now using the FTOL function, since the compiler doesn't do
* the conversion efficiently.
* 1/18/01 - The FTOL function rounds, whereas casting truncates. So,
* we no longer need to ad .5 for pos numbers and subtract .5
* for negative numbers.
*
******************************************************************* mplumpe ***/
short CTips::ClipData (double x)
{
if (x > SHRT_MAX )
{
return SHRT_MAX;
}
if (x < SHRT_MIN )
{
return SHRT_MIN;
}
return (short)FTOL(x);
}
/*****************************************************************************
* CTips::LpcInit *
*----------------*
* Description:
*
******************************************************************* PACOG ***/
bool CTips::LpcInit ()
{
m_iLpcOrder = LpcOrder (m_iSampFreq);
if ((m_pdFiltMem = new double [m_iLpcOrder]) == 0)
{
goto error;
}
memset( m_pdFiltMem, 0, m_iLpcOrder * sizeof (*m_pdFiltMem));
if ((m_pdInterpCoef = new double [m_iLpcOrder]) == 0)
{
goto error;
}
memset( m_pdInterpCoef, 0, m_iLpcOrder * sizeof (*m_pdInterpCoef));
if ((m_pdLastCoef = new double [m_iLpcOrder]) == 0)
{
goto error;
}
memset( m_pdLastCoef, 0, m_iLpcOrder * sizeof (*m_pdLastCoef));
return true;
error:
LpcFreeAll();
return false;
}
/*****************************************************************************
* CTips::LpcSynth *
*--------------------*
* Description:
*
******************************************************************* PACOG ***/
void CTips::LpcSynth (double* pdPeriod, int iPeriodLen)
{
double alfa;
int i;
int j;
for (i=0; i<iPeriodLen; i++)
{
alfa = i/(double)iPeriodLen;
for (j=0; j<m_iLpcOrder ; j++) {
m_pdInterpCoef[j] = (1.0 - alfa) * m_pdLastCoef[j] + alfa * m_pdNewCoef[j];
}
ParcorFilterSyn(pdPeriod+i, 1, m_pdInterpCoef, m_pdFiltMem, m_iLpcOrder );
}
memcpy( m_pdLastCoef, m_pdNewCoef, m_iLpcOrder * sizeof(*m_pdLastCoef));
}
/*****************************************************************************
* CTips::LpcFreeAll *
*----------------------*
* Description:
*
******************************************************************* PACOG ***/
void CTips::LpcFreeAll()
{
if (m_pdFiltMem)
{
delete[] m_pdFiltMem;
m_pdFiltMem = 0;
}
if (m_pdInterpCoef)
{
delete[] m_pdInterpCoef;
m_pdInterpCoef = 0;
}
if (m_pdLastCoef)
{
delete[] m_pdLastCoef;
m_pdInterpCoef = 0;
}
}