soundeditor/qatsh/ATSSound.cpp

630 lines
18 KiB
C++
Raw Permalink Normal View History

// ATSSound class implementation
//
// C++ interface to ATS_SOUND C structure and functions from atsa
//
// QATSH Copyright 2009 Jean-Philippe MEURET <jpmeuret@free.fr>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <ctime>
#include <string>
#include "mathexpr.h"
#include "ATSSound.h"
// Constructors / Destructor ===============================================
ATSSound::ATSSound(ATS_SOUND* pSoundData)
: _pSoundData(pSoundData), _eSoundType((ESoundTypeId)ATSA_TYPE),
_nFileSize(-1), _bNeedFree(false)
{
if (_pSoundData)
{
// We'll need to free _pSoundData internals in destructor.
_bNeedFree = true;
// Backward compatibility : ATSH didn't save it.
updateResidualMaxEnergy();
}
}
ATSSound::~ATSSound()
{
if ( _pSoundData)
{
if (_bNeedFree) //
free_sound(_pSoundData);
free(_pSoundData);
}
}
// Static accessors ========================================================
bool ATSSound::soundHasPhase(ATSSound::ESoundTypeId eType)
{
return (eType == eSoundTypePartialsAmplFreqPhase
|| eType == eSoundTypePartialsAmplFreqPhaseNoise);
}
bool ATSSound::soundHasNoise(ATSSound::ESoundTypeId eType)
{
return (eType == eSoundTypePartialsAmplFreqNoise
|| eType == eSoundTypePartialsAmplFreqPhaseNoise);
}
// Properties get/setters ==================================================
double ATSSound::samplingRate() const
{
return _pSoundData->srate;
}
//void ATSSound::setSamplingRate(double dSamplingRate)
//{
//}
int ATSSound::frameSize() const
{
return _pSoundData->frame_size;
}
//void ATSSound::setFrameSize(int nbSamples)
//{
//}
int ATSSound::windowSize() const
{
return _pSoundData->window_size;
}
//void ATSSound::setWindowSize(int nbSamples)
//{
//}
int ATSSound::nbFrames() const
{
return _pSoundData->frames;
}
//void ATSSound::setNbFrames(int nbFrames)
//{
//}
//void ATSSound::setMaxFrequency(double dFreq)
//{
//}
double ATSSound::duration() const
{
return _pSoundData->dur;
}
//void ATSSound::setDuration(double dDuration)
//{
//}
int ATSSound::fileSize() const
{
return _nFileSize;
}
ATSSound::ESoundTypeId ATSSound::type() const
{
return _eSoundType;
}
bool ATSSound::hasPhase() const
{
return soundHasPhase(_eSoundType);
}
bool ATSSound::hasNoise() const
{
return soundHasNoise(_eSoundType);
}
int ATSSound::nbPartialProperties() const
{
return 3 + (hasPhase() ? 1 : 0) + (hasNoise() ? 1 : 0);
}
//void ATSSound::setType(ESoundTypeId eType)
//{
//}
bool ATSSound::isOptimized() const
{
return _pSoundData->optimized != NIL;
}
// Partials properties ===================================================
int ATSSound::nbPartials() const
{
return _pSoundData->partials;
}
double ATSSound::partialsMaxAmplitude() const
{
return _pSoundData->ampmax;
}
double ATSSound::partialsMaxFrequency() const
{
return _pSoundData->frqmax;
}
double ATSSound::partialTime(int nFrameInd) const
{
return _pSoundData->time[0][nFrameInd];
}
double ATSSound::partialFrequency(int nPartInd, int nFrameInd) const
{
return _pSoundData->frq[_pSoundData->av[nPartInd].track][nFrameInd];
}
double ATSSound::partialAmplitude(int nPartInd, int nFrameInd) const
{
return _pSoundData->amp[_pSoundData->av[nPartInd].track][nFrameInd];
}
double ATSSound::partialPhase(int nPartInd, int nFrameInd) const
{
return _pSoundData->pha[_pSoundData->av[nPartInd].track][nFrameInd];
}
double ATSSound::partialSMR(int nPartInd, int nFrameInd) const
{
return _pSoundData->smr[_pSoundData->av[nPartInd].track][nFrameInd];
}
// Residual properties ===================================================
int ATSSound::nbResidualBands() const
{
return ATSA_CRITICAL_BANDS;
}
double ATSSound::residualBandMinFrequency(int nBandInd) const
{
return ATSA_CRITICAL_BAND_EDGES[nBandInd];
}
double ATSSound::residualBandMaxFrequency(int nBandInd) const
{
return ATSA_CRITICAL_BAND_EDGES[nBandInd+1];
}
double ATSSound::residualBandsMaxFrequency() const
{
return ATSA_CRITICAL_BAND_EDGES[ATSA_CRITICAL_BANDS];
}
double ATSSound::residualBandsMaxEnergy() const
{
return _dResBandFrameMaxEnergy;
}
double ATSSound::residualBandEnergy(int nBandInd, int nFrameInd) const
{
return _pSoundData->band_energy[nBandInd][nFrameInd];
}
double ATSSound::residualBandTime(int nFrameInd) const
{
return _pSoundData->time[0][nFrameInd];
}
// Add/Remove partials ===================================================
void ATSSound::addPartial(int nSrcPartIndex)
{
std::cout << "ATSSound::addPartial(" << nSrcPartIndex << ")" << std::endl;
add_partial(_pSoundData, nSrcPartIndex);
}
void ATSSound::removePartials(const int aPartIndexes[], int nParts)
{
std::cout << "ATSSound::removePartials(";
for (int nInd = 0; nInd < nParts; nInd++)
std::cout << aPartIndexes[nInd] << ',';
std::cout << ')' << std::endl;
remove_partials(_pSoundData, const_cast<int*>(aPartIndexes), nParts);
}
// ESoundFunction class ====================================================
// MathExpr EFunction-derived class for access to sound data from partial and frame index.
class ESoundFunction : public EFunction
{
public:
ESoundFunction(ATS_SOUND* pSoundData, TypesAndConstants::EPartialMagnitude eMagnitude)
: EFunction(2), _eMagnitude(eMagnitude), _pSoundData(pSoundData)
{
}
// Function evaluation function.
double Val()
{
// Force partial index inside the right interval.
int nPartInd = (int)pvars[0];
if (nPartInd < 0)
nPartInd = 0;
else if (nPartInd >= _pSoundData->partials)
nPartInd = _pSoundData->partials - 1;
// Force frame index inside the right interval.
int nFrameInd = (int)pvars[1];
if (nFrameInd < 0)
nFrameInd = 0;
else if (nFrameInd >= _pSoundData->frames)
nFrameInd = _pSoundData->frames - 1;
// Get the target magnitude value.
switch (_eMagnitude)
{
case TypesAndConstants::eAmplitude:
return _pSoundData->amp[_pSoundData->av[nPartInd].track][nFrameInd];
case TypesAndConstants::eFrequency:
return _pSoundData->frq[_pSoundData->av[nPartInd].track][nFrameInd];
case TypesAndConstants::ePhase:
return _pSoundData->pha[_pSoundData->av[nPartInd].track][nFrameInd];
default: // N/A.
return 0;
}
}
private:
TypesAndConstants::EPartialMagnitude _eMagnitude;
ATS_SOUND* _pSoundData;
};
// Modify partials ========================================================
std::string ATSSound::modifyPartials(const int aPartIndexes[], int nParts,
const std::string& strFormula,
TypesAndConstants::EPartialMagnitude eTargetMagnitude)
{
static const int nMaxVars = 20;
RVar* aVars[nMaxVars];
int nVars = 0; // Warning: MUST never be larger that nMaxVars.
// Define the Partial number constant "variable".
double dPartNum = (int)nbPartials();
RVar varPartials("P", &dPartNum);
aVars[nVars++] = &varPartials;
// Define the Frame number constant "variable".
double dFrameNum = (int)nbFrames();
RVar varFrames("T", &dFrameNum);
aVars[nVars++] = &varFrames;
// Define the Partial index variable.
double dPartInd;
RVar varPartial("p", &dPartInd);
aVars[nVars++] = &varPartial;
// Define the Frame index variable.
double dFrameInd;
RVar varFrame("t", &dFrameInd);
aVars[nVars++] = &varFrame;
// Define the amplitude, frequency and phase variables
// (current partial and frame, then min, max and mean for the partial among all its frames).
double dAmpl;
RVar varAmpl("a", &dAmpl);
aVars[nVars++] = &varAmpl;
double dMinAmpl;
RVar varMinAmpl("amin", &dMinAmpl);
aVars[nVars++] = &varMinAmpl;
double dMaxAmpl;
RVar varMaxAmpl("amax", &dMaxAmpl);
aVars[nVars++] = &varMaxAmpl;
double dMeanAmpl;
RVar varMeanAmpl("amean", &dMeanAmpl);
aVars[nVars++] = &varMeanAmpl;
double dFreq;
RVar varFreq("f", &dFreq);
aVars[nVars++] = &varFreq;
double dMinFreq;
RVar varMinFreq("fmin", &dMinFreq);
aVars[nVars++] = &varMinFreq;
double dMaxFreq;
RVar varMaxFreq("fmax", &dMaxFreq);
aVars[nVars++] = &varMaxFreq;
double dMeanFreq;
RVar varMeanFreq("fmean", &dMeanFreq);
aVars[nVars++] = &varMeanFreq;
double dPhase;
RVar varPhase("h", &dPhase);
aVars[nVars++] = &varPhase;
double dMinPhase;
RVar varMinPhase("hmin", &dMinPhase);
aVars[nVars++] = &varMinPhase;
double dMaxPhase;
RVar varMaxPhase("hmax", &dMaxPhase);
aVars[nVars++] = &varMaxPhase;
double dMeanPhase;
RVar varMeanPhase("hmean", &dMeanPhase);
aVars[nVars++] = &varMeanPhase;
// Target magnitude (value(frame), min/frames, max/frames, mean/frames).
double** pTgtMagnitude = 0;
double* pTgtValue = 0;
double* pTgtMinValue = 0;
double* pTgtMaxValue = 0;
double* pTgtMeanValue = 0;
switch (eTargetMagnitude)
{
case TypesAndConstants::eAmplitude:
pTgtMagnitude = _pSoundData->amp;
pTgtValue = &dAmpl;
pTgtMinValue = &dMinAmpl;
pTgtMaxValue = &dMaxAmpl;
pTgtMeanValue = &dMeanAmpl;
break;
case TypesAndConstants::eFrequency:
pTgtMagnitude = _pSoundData->frq;
pTgtValue = &dFreq;
pTgtMinValue = &dMinFreq;
pTgtMaxValue = &dMaxFreq;
pTgtMeanValue = &dMeanFreq;
break;
case TypesAndConstants::ePhase:
pTgtMagnitude = _pSoundData->pha;
pTgtValue = &dPhase;
pTgtMinValue = &dMinPhase;
pTgtMaxValue = &dMaxPhase;
pTgtMeanValue = &dMeanPhase;
break;
default: // N/A.
break;
}
// Define the target magnitude variable (2nd possible name).
RVar varValue("v", pTgtValue);
aVars[nVars++] = &varValue;
// Define the target magnitude min, max, mean, ... "constant" variables (2nd possible name).
RVar varMinValue("vmin", pTgtMinValue);
aVars[nVars++] = &varMinValue;
RVar varMaxValue("vmax", pTgtMaxValue);
aVars[nVars++] = &varMaxValue;
RVar varMeanValue("vmean", pTgtMeanValue);
aVars[nVars++] = &varMeanValue;
clock_t start_time = clock();
// Make a copy of the ATS_SOUND to process : write operations will process _pSoundData,
// while read operations will process this virgin copy (necessary for some formulas).
ATS_SOUND* pSourceSoundData = copy_sound(_pSoundData);
// Define the amplitude, frequency and phase functions : p,t => ampl/freq/pha[p][t]
static const int nMaxFuncs = 5;
RFunction* aFuncs[nMaxFuncs];
int nFuncs = 0; // Warning: MUST never be larger that nMaxFuncs.
ESoundFunction eFuncAmpl(pSourceSoundData, TypesAndConstants::eAmplitude);
RFunction funcAmpl(eFuncAmpl);
funcAmpl.SetName("A");
aFuncs[nFuncs++] = &funcAmpl;
ESoundFunction eFuncFreq(pSourceSoundData, TypesAndConstants::eFrequency);
RFunction funcFreq(eFuncFreq);
funcFreq.SetName("F");
aFuncs[nFuncs++] = &funcFreq;
ESoundFunction eFuncPhase(pSourceSoundData, TypesAndConstants::ePhase);
RFunction funcPhase(eFuncPhase);
funcPhase.SetName("H");
aFuncs[nFuncs++] = &funcPhase;
ESoundFunction* pTgtValueEFunc = 0;
switch (eTargetMagnitude)
{
case TypesAndConstants::eAmplitude:
pTgtValueEFunc = &eFuncAmpl;
break;
case TypesAndConstants::eFrequency:
pTgtValueEFunc = &eFuncFreq;
break;
case TypesAndConstants::ePhase:
pTgtValueEFunc = &eFuncPhase;
break;
default: // N/A.
break;
}
RFunction funcValue(*pTgtValueEFunc);
funcValue.SetName("V");
aFuncs[nFuncs++] = &funcValue;
// Check nFuncs and nVars (in case we didn't crash before ;-)
if (nVars > nMaxVars)
{
std::cout << "ATSSound::modifyPartials(" << nParts
<< ") : Too many variables ; memory overwritten !" << std::endl;
return "Error: Variables memory overwritten";
}
if (nFuncs > nMaxFuncs)
{
std::cout << "ATSSound::modifyPartials(" << nParts
<< ") : Too many functions ; memory overwritten !" << std::endl;
return "Error: Functions memory overwritten";
}
// Define and check the formula.
ROperation opFormula(strFormula.c_str(), nVars, aVars, nFuncs, aFuncs);
char* pszParsedFormula = opFormula.Expr();
const std::string strParsedFormula(pszParsedFormula);
delete [] pszParsedFormula;
if (strParsedFormula.find("Error") != std::string::npos)
{
std::cout << "ATSSound::modifyPartials(" << nParts
<< ") : Skipping on erroneous formula "
<< strParsedFormula << std::endl;
return strParsedFormula;
}
// Apply the formula.
for (int nPartIndInd = 0; nPartIndInd < nParts; nPartIndInd++)
{
// Set the value of the Partial index variable.
const int nPartInd = pSourceSoundData->av[aPartIndexes[nPartIndInd]].track;
dPartInd = (double)nPartInd;
// Compute amin, amax, amean (vmin set at the same time).
dMinAmpl = std::numeric_limits<double>::max();
dMaxAmpl = -dMinAmpl;
dMeanAmpl = 0.0;
for (int nFrameInd = 0; nFrameInd < nbFrames(); nFrameInd++)
{
dAmpl = pSourceSoundData->amp[nPartInd][nFrameInd];
dMeanAmpl += dAmpl;
if (dMaxAmpl < dAmpl)
dMaxAmpl = dAmpl;
if (dMinAmpl > dAmpl)
dMinAmpl = dAmpl;
}
dMeanAmpl /= nbFrames();
// Compute fmin, fmax, fmean (vmin set at the same time).
dMinFreq = std::numeric_limits<double>::max();
dMaxFreq = -dMinFreq;
dMeanFreq = 0.0;
for (int nFrameInd = 0; nFrameInd < nbFrames(); nFrameInd++)
{
dFreq = pSourceSoundData->frq[nPartInd][nFrameInd];
dMeanFreq += dFreq;
if (dMaxFreq < dFreq)
dMaxFreq = dFreq;
if (dMinFreq > dFreq)
dMinFreq = dFreq;
}
dMeanFreq /= nbFrames();
// Compute hmin, hmax, hmean (vmin set at the same time).
dMinPhase = std::numeric_limits<double>::max();
dMaxPhase = -dMinPhase;
dMeanPhase = 0.0;
for (int nFrameInd = 0; nFrameInd < nbFrames(); nFrameInd++)
{
dPhase = pSourceSoundData->pha[nPartInd][nFrameInd];
dMeanPhase += dPhase;
if (dMaxPhase < dPhase)
dMaxPhase = dPhase;
if (dMinPhase > dPhase)
dMinPhase = dPhase;
}
dMeanPhase /= nbFrames();
// Apply the formula to each frame.
for (int nFrameInd = 0; nFrameInd < nbFrames(); nFrameInd++)
{
// Set the value of the Frame index variable.
dFrameInd = (double)nFrameInd;
// Set the value of the a, f and h variables (v also set at the same time).
dAmpl = pSourceSoundData->amp[nPartInd][nFrameInd];
dFreq = pSourceSoundData->frq[nPartInd][nFrameInd];
dPhase = pSourceSoundData->pha[nPartInd][nFrameInd];
// Evaluate the formula and change the frame magnitude.
pTgtMagnitude[nPartInd][nFrameInd] = opFormula.Val();
}
}
// Free sound copy.
free_sound(pSourceSoundData);
std::cout << "ATSSound::modifyPartials(" << nParts << ") : "
<< clock() - start_time << std::endl;
// << (double)(clock() - start_time) / (double)CLOCKS_PER_SEC << " s" << std::endl;
return strParsedFormula;
}
// .ats file input ========================================================
bool ATSSound::load(const char* pszATSFileName)
{
// Re-initialize sound data structure if it has already been used.
if (_pSoundData)
{
if (_bNeedFree)
{
free_sound(_pSoundData);
_bNeedFree = false;
}
}
else
_pSoundData = (ATS_SOUND*)malloc(sizeof(ATS_SOUND));
// Loads the .ats file.
_nFileSize = ats_load(_pSoundData, pszATSFileName, (int*)&_eSoundType);
// Compute the maximum of energy in residual band frames if successfully loaded.
if (_nFileSize >= 0)
{
// We'll need to free _pSoundData internals in destructor.
_bNeedFree = true;
// Backward compatibility : ATSH didn't save it.
updateResidualMaxEnergy();
}
return _nFileSize >= 0;
}
// .ats file output =======================================================
bool ATSSound::store(const char* pszATSFileName) const
{
// TODO : Customizable partial amplitude threshold (0 for the moment).
return ats_save(_pSoundData, pszATSFileName, 0.0, (int)_eSoundType) > 0;
}
// Compute the maximum of energy in residual band frames ==================
void ATSSound::updateResidualMaxEnergy()
{
_dResBandFrameMaxEnergy = 0.0;
for (int nBandInd = 0; nBandInd < nbResidualBands() ; nBandInd++)
for (int nFrameInd = 0; nFrameInd < nbFrames() ; nFrameInd++)
if (_pSoundData->band_energy[nBandInd][nFrameInd] > _dResBandFrameMaxEnergy)
_dResBandFrameMaxEnergy = _pSoundData->band_energy[nBandInd][nFrameInd];
}
// Direct access to the internal ATS_SOUND structure ======================
ATS_SOUND* ATSSound::data()
{
return _pSoundData;
}
// Tools ==================================================================
void ATSSound::dump(const char* pszHeader, std::ostream& oStream) const
{
oStream << pszHeader << "ATSSound :" << std::endl;
oStream << " File type : " << type() << std::endl;
oStream << " File size (bytes) : " << fileSize() << std::endl;
oStream << " Duration (s) : " << duration() << std::endl;
oStream << " Sampling rate (Hz) : " << samplingRate() << std::endl;
oStream << " Nb partials : " << nbPartials() << std::endl;
oStream << " Max. part. freq. (Hz) : " << partialsMaxFrequency() << std::endl;
oStream << " Max. part. ampl. (?) : " << partialsMaxAmplitude() << std::endl;
oStream << " With noise : " << (hasNoise() ? "Yes" : "No") << std::endl;
oStream << " With phase : " << (hasPhase() ? "Yes" : "No") << std::endl;
oStream << " Nb frames : " << nbFrames() << std::endl;
oStream << " Frame size (samples) : " << frameSize() << std::endl;
oStream << " Window size (samples) : " << windowSize() << std::endl;
oStream << " Optimized : " << (isOptimized() ? "Yes" : "No") << std::endl;
}