|
|
/******************************************************************************
* 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; } }
|