cwAudioFileProc.h/cpp, cwAudioTransforms.h/cpp, cwPvAudioFileProc.h/cpp: Initial commit.
Minor update to cwAudioFileOps.cpp.
This commit is contained in:
parent
9d8d8ee051
commit
02cd79c61f
@ -9,8 +9,10 @@
|
||||
#include "cwUtility.h"
|
||||
#include "cwFileSys.h"
|
||||
#include "cwAudioFileOps.h"
|
||||
#include "cwMath.h"
|
||||
#include "cwVectOps.h"
|
||||
#include "cwDsp.h"
|
||||
#include "cwAudioTransforms.h"
|
||||
|
||||
namespace cw
|
||||
{
|
||||
|
650
cwAudioFileProc.cpp
Normal file
650
cwAudioFileProc.cpp
Normal file
@ -0,0 +1,650 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwFile.h"
|
||||
#include "cwText.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwAudioFile.h"
|
||||
#include "cwUtility.h"
|
||||
#include "cwFileSys.h"
|
||||
#include "cwAudioFileOps.h"
|
||||
#include "cwMath.h"
|
||||
#include "cwVectOps.h"
|
||||
#include "cwDsp.h"
|
||||
#include "cwAudioTransforms.h"
|
||||
#include "cwAudioFileProc.h"
|
||||
|
||||
namespace cw
|
||||
{
|
||||
namespace afop
|
||||
{
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Template Process
|
||||
//
|
||||
namespace process
|
||||
{
|
||||
typedef struct process_str
|
||||
{
|
||||
int foo;
|
||||
double blah;
|
||||
} process_t;
|
||||
|
||||
rc_t open( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = mem::allocZ<process_t>();
|
||||
ctx->userPtr = p;
|
||||
|
||||
if((rc = ctx->args->getv( "foo", p->foo,
|
||||
"blah", p->blah)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing of 'template' args. failed.");
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t close( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
mem::release(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t process( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t main( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( ctx->procId )
|
||||
{
|
||||
case kOpenProcId:
|
||||
rc = open(ctx);
|
||||
break;
|
||||
|
||||
case kCloseProcId:
|
||||
rc = close(ctx);
|
||||
break;
|
||||
|
||||
case kProcProcId:
|
||||
rc = process(ctx);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Phase Vocoder Process
|
||||
//
|
||||
namespace pvoc
|
||||
{
|
||||
typedef struct process_str
|
||||
{
|
||||
dsp::pv_anl::fobj_t** anlA; // anlA[chCnt]
|
||||
dsp::pv_syn::fobj_t** synA; // synA[chCnt]
|
||||
unsigned chCnt; //
|
||||
unsigned procSmpN; //
|
||||
unsigned wndSmpN; //
|
||||
unsigned hopSmpN; //
|
||||
double inGain; //
|
||||
double outGain; //
|
||||
} process_t;
|
||||
|
||||
rc_t open( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = mem::allocZ<process_t>();
|
||||
ctx->userPtr = p;
|
||||
|
||||
if((rc = ctx->args->getv( "procSmpN", p->procSmpN,
|
||||
"hopSmpN", p->hopSmpN,
|
||||
"wndSmpN", p->wndSmpN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing of 'pvoc' required arguments failed.");
|
||||
}
|
||||
|
||||
|
||||
p->chCnt = std::min( ctx->srcChN, ctx->dstChN );
|
||||
p->anlA = mem::allocZ< dsp::pv_anl::fobj_t* >( p->chCnt );
|
||||
p->synA = mem::allocZ< dsp::pv_syn::fobj_t* >( p->chCnt );
|
||||
|
||||
for(unsigned i=0; i<p->chCnt; ++i)
|
||||
{
|
||||
if((rc = dsp::pv_anl::create( p->anlA[i], p->procSmpN, ctx->srcSrate, p->wndSmpN, p->hopSmpN, dsp::pv_anl::kNoCalcHzPvaFl )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"PVOC analysis component create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = dsp::pv_syn::create( p->synA[i], p->procSmpN, ctx->dstSrate, p->wndSmpN, p->hopSmpN )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"PVOC synthesis component create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t close( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
|
||||
for(unsigned i=0; i<p->chCnt; ++i)
|
||||
{
|
||||
if( p->anlA )
|
||||
dsp::pv_anl::destroy(p->anlA[i]);
|
||||
|
||||
if( p->synA )
|
||||
dsp::pv_syn::destroy(p->synA[i]);
|
||||
}
|
||||
|
||||
mem::release(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t process( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
|
||||
for(unsigned i=0; i<p->chCnt; ++i)
|
||||
{
|
||||
if( dsp::pv_anl::exec( p->anlA[i], ctx->srcChV[i], p->hopSmpN) )
|
||||
{
|
||||
|
||||
float buf[ p->anlA[i]->binCnt ];
|
||||
vop::mul( buf, p->anlA[i]->magV, p->anlA[i]->binCnt/2, p->anlA[i]->binCnt );
|
||||
|
||||
if((rc = dsp::pv_syn::exec( p->synA[i], buf, p->anlA[i]->phsV )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Pvoc synthesis failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
vop::copy( ctx->dstChV[i], p->synA[i]->ola->outV, p->hopSmpN );
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t main( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( ctx->procId )
|
||||
{
|
||||
case kOpenProcId:
|
||||
rc = open(ctx);
|
||||
break;
|
||||
|
||||
case kCloseProcId:
|
||||
rc = close(ctx);
|
||||
break;
|
||||
|
||||
case kProcProcId:
|
||||
rc = process(ctx);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Tremelo Process
|
||||
//
|
||||
namespace tremelo
|
||||
{
|
||||
|
||||
typedef struct tremelo_str
|
||||
{
|
||||
double hz;
|
||||
double depth;
|
||||
double phase;
|
||||
} tremelo_t;
|
||||
|
||||
rc_t open( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc;
|
||||
|
||||
tremelo_t* p = mem::allocZ<tremelo_t>();
|
||||
ctx->userPtr = p;
|
||||
|
||||
if((rc = ctx->args->getv( "hz", p->hz,
|
||||
"depth", p->depth)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing of 'tremelo' ctx. failed.");
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t close( proc_ctx_t* ctx )
|
||||
{
|
||||
tremelo_t* p = (tremelo_t*)ctx->userPtr;
|
||||
mem::release(p);
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t process( proc_ctx_t* ctx )
|
||||
{
|
||||
tremelo_t* p = (tremelo_t*)ctx->userPtr;
|
||||
unsigned chCnt = std::min( ctx->srcChN, ctx->dstChN );
|
||||
unsigned hopSmpN = std::min( ctx->srcHopSmpN, ctx->dstHopSmpN );
|
||||
|
||||
|
||||
for(unsigned i=0; i<hopSmpN; ++i)
|
||||
{
|
||||
float gain = p->depth * std::sin( p->phase );
|
||||
|
||||
for(unsigned j=0; j<chCnt; ++j)
|
||||
ctx->dstChV[j][i] = gain * ctx->srcChV[j][i];
|
||||
|
||||
|
||||
p->phase += 2*M_PI*p->hz / ctx->srcSrate;
|
||||
}
|
||||
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t main( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( ctx->procId )
|
||||
{
|
||||
case kOpenProcId:
|
||||
rc = open(ctx);
|
||||
break;
|
||||
|
||||
case kCloseProcId:
|
||||
rc = close(ctx);
|
||||
break;
|
||||
|
||||
case kProcProcId:
|
||||
rc = process(ctx);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Audio File Processor
|
||||
//
|
||||
|
||||
cw::rc_t cw::afop::file_processor( const char* srcFn, const char* dstFn, proc_func_t func, unsigned wndSmpN, unsigned hopSmpN, void* userPtr, const object_t* args, const object_t* recorder_cfg, unsigned recordChN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
typedef float sample_t;
|
||||
|
||||
audiofile::handle_t srcAfH;
|
||||
audiofile::handle_t dstAfH;
|
||||
audiofile::info_t info;
|
||||
proc_ctx_t proc_ctx = {0};
|
||||
|
||||
if( hopSmpN > wndSmpN )
|
||||
return cwLogError(kInvalidArgRC,"The hop sample count (%i) cannot exceed the window sample count (%i).", hopSmpN, wndSmpN );
|
||||
|
||||
proc_ctx.userPtr = userPtr;
|
||||
proc_ctx.args = args;
|
||||
|
||||
// By default wndSmpN and hopSmpN set both the src and dst wndSmpN and hopSmpN parameters
|
||||
proc_ctx.srcWndSmpN = wndSmpN;
|
||||
proc_ctx.srcHopSmpN = hopSmpN;
|
||||
proc_ctx.dstWndSmpN = wndSmpN;
|
||||
proc_ctx.dstHopSmpN = hopSmpN;
|
||||
|
||||
// if a source audio file was given
|
||||
if( srcFn != nullptr )
|
||||
{
|
||||
// open the source audio file
|
||||
if((rc = audiofile::open(srcAfH,srcFn,&info)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Unable to open the source file:%s", cwStringNullGuard(srcFn) );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// Configure the input signal
|
||||
proc_ctx.srcFn = mem::duplStr(srcFn);
|
||||
proc_ctx.srcSrate = info.srate;
|
||||
proc_ctx.srcChN = info.chCnt;
|
||||
proc_ctx.srcBits = info.bits;
|
||||
|
||||
// By default the output signal has the same configuration as the input file
|
||||
proc_ctx.dstSrate = info.srate;
|
||||
proc_ctx.dstChN = info.chCnt;
|
||||
proc_ctx.dstBits = info.bits; // TODO: allow setting output file bits as optional parameter (0=float)
|
||||
// Be sure if settting bits from input file with floating point sample format
|
||||
// that this case is correctly handled here.
|
||||
}
|
||||
|
||||
// During the 'open' call the user defined function (UDF) can override the destination file configuration
|
||||
proc_ctx.procId = kOpenProcId;
|
||||
if((rc = func( &proc_ctx )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Open processes failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// Create the output file
|
||||
if( dstFn != nullptr )
|
||||
{
|
||||
|
||||
if((rc = audiofile::create(dstAfH,dstFn, proc_ctx.dstSrate, proc_ctx.dstBits, proc_ctx.dstChN )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Unable to create the destination file:%s", cwStringNullGuard(dstFn) );
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
proc_ctx.dstFn = mem::duplStr(dstFn);
|
||||
|
||||
}
|
||||
|
||||
if( rc == kOkRC )
|
||||
{
|
||||
sample_t* srcChFileBuf[ proc_ctx.srcChN ]; // srcChFileBuf[ srcChN ][ srcHopSmpCnt ] - src file ch buffer
|
||||
sample_t* dstChFileBuf[ proc_ctx.dstChN ]; // dstChFileBuf[ dstChN ][ dstHopSmpCnt ] - dst file ch buffer
|
||||
const sample_t* iChBuf[ proc_ctx.srcChN ]; // iChBuf[ srcChN ][ srcWndSmpCnt ] - src processing buffer
|
||||
sample_t* oChBuf[ proc_ctx.dstChN ]; // oChBuf[ dstChN ][ dstWndSmpCnt ] - dst processing buffer
|
||||
|
||||
struct dsp::shift_buf::obj_str<sample_t>* srcShiftBufA[ proc_ctx.srcChN ]; // src shift buffer
|
||||
struct dsp::shift_buf::obj_str<sample_t>* dstShiftBufA[ proc_ctx.dstChN ]; // dst shift buffer
|
||||
|
||||
// Allocate memory for the source/dest file buffer
|
||||
sample_t* srcFileBuf = mem::allocZ<sample_t>( proc_ctx.srcChN * proc_ctx.srcHopSmpN );
|
||||
sample_t* dstFileBuf = mem::allocZ<sample_t>( proc_ctx.dstChN * proc_ctx.dstHopSmpN );
|
||||
sample_t* procFileBuf = mem::allocZ<sample_t>( proc_ctx.dstChN * proc_ctx.dstHopSmpN );
|
||||
|
||||
// Setup the input and output processing buffer
|
||||
proc_ctx.srcChV = iChBuf;
|
||||
proc_ctx.dstChV = oChBuf;
|
||||
|
||||
// For each source channel - setup the source file buffer and create the src shift buffer
|
||||
for(unsigned i = 0; i<proc_ctx.srcChN; ++i)
|
||||
{
|
||||
srcChFileBuf[i] = srcFileBuf + (i*proc_ctx.srcHopSmpN );
|
||||
dsp::shift_buf::create( srcShiftBufA[i], proc_ctx.srcHopSmpN, proc_ctx.srcWndSmpN, proc_ctx.srcHopSmpN );
|
||||
}
|
||||
|
||||
// For each dest. channel - setup the dest file buffer and create the dst shift buffer
|
||||
for(unsigned i = 0; i<proc_ctx.dstChN; ++i)
|
||||
{
|
||||
oChBuf[i] = procFileBuf + (i*proc_ctx.dstHopSmpN );
|
||||
dsp::shift_buf::create( dstShiftBufA[i], proc_ctx.dstHopSmpN, proc_ctx.dstWndSmpN, proc_ctx.dstHopSmpN );
|
||||
}
|
||||
|
||||
// create the data recorder
|
||||
if( recordChN )
|
||||
{
|
||||
proc_ctx.recordChA = mem::allocZ< dsp::data_recorder::fobj_t* >( recordChN );
|
||||
for(unsigned i = 0; i<recordChN; ++i)
|
||||
{
|
||||
if(dsp::data_recorder::create( proc_ctx.recordChA[i], recorder_cfg ) != kOkRC )
|
||||
{
|
||||
cwLogWarning( "Data recorder create failed." );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
while( true )
|
||||
{
|
||||
unsigned rdFrmCnt = 0;
|
||||
|
||||
// if a source file exists then read the next srcHopSmpN samples into srcChFileBuf[][]
|
||||
if( srcFn )
|
||||
{
|
||||
// Read the next srcHopSmpN samples
|
||||
rc = audiofile::readFloat(srcAfH, proc_ctx.srcHopSmpN, 0, proc_ctx.srcChN, srcChFileBuf, &rdFrmCnt );
|
||||
|
||||
// if the end of the file was encountered or no samples were returned
|
||||
if( rc == kEofRC || rdFrmCnt == 0)
|
||||
{
|
||||
rc = kOkRC;
|
||||
break;
|
||||
}
|
||||
|
||||
if( rc != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Audio process source file '%s' read failed.", srcFn );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
do // src shift-buffer processing loop
|
||||
{
|
||||
|
||||
if( srcFn )
|
||||
{
|
||||
// Shift the new source sample into the source shift buffer.
|
||||
// Note that the shift buffer must be called until they return false.
|
||||
bool rd_fl = false;
|
||||
|
||||
for(unsigned j=0; j<proc_ctx.srcChN; ++j)
|
||||
if( dsp::shift_buf::exec( srcShiftBufA[j], srcChFileBuf[j], proc_ctx.srcHopSmpN ) )
|
||||
{
|
||||
rd_fl = true;
|
||||
iChBuf[j] = srcShiftBufA[j]->outV;
|
||||
}
|
||||
|
||||
if(!rd_fl)
|
||||
break; // src shift-buf iterations are done
|
||||
}
|
||||
|
||||
// Call the processing function
|
||||
proc_ctx.procId = kProcProcId;
|
||||
if((rc = func( &proc_ctx )) != kOkRC )
|
||||
{
|
||||
// kEof isn't an error
|
||||
if( rc == kEofRC )
|
||||
rc = kOkRC;
|
||||
else
|
||||
rc = cwLogError(rc,"Audio file process reported an error.");
|
||||
|
||||
goto doneLabel;
|
||||
}
|
||||
|
||||
|
||||
// if output samples exist - shift the new samples into the output shift buffer
|
||||
if( proc_ctx.dstChN )
|
||||
{
|
||||
|
||||
do // destination shif-buffer iteration processing loop
|
||||
{
|
||||
bool wr_fl = false;
|
||||
|
||||
// Update the dst shift buffers
|
||||
// Note that the shift buffer has to be called until it returns false therefore
|
||||
// we must iterate over the file writing process writing hopSmpN frames on each call.
|
||||
for(unsigned j=0; j<proc_ctx.dstChN; ++j)
|
||||
{
|
||||
if(dsp::shift_buf::exec( dstShiftBufA[j], oChBuf[j], proc_ctx.dstHopSmpN ))
|
||||
{
|
||||
wr_fl = true;
|
||||
|
||||
dstChFileBuf[j] = dstShiftBufA[j]->outV;
|
||||
}
|
||||
}
|
||||
|
||||
// if no true's were returned by the shift-buffer (Note that all channels must return the same
|
||||
// value because they are all configured the same way.)
|
||||
if( !wr_fl )
|
||||
break;
|
||||
|
||||
// If a destination file was specified
|
||||
if( dstFn != nullptr )
|
||||
{
|
||||
if((rc = audiofile::writeFloat( dstAfH, proc_ctx.dstHopSmpN, proc_ctx.dstChN, dstChFileBuf )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Audio process source file '%s' read failed.", srcFn );
|
||||
goto doneLabel;
|
||||
}
|
||||
}
|
||||
|
||||
}while(1); // desination shift-buffer interation processing loop
|
||||
}
|
||||
|
||||
|
||||
}while(1); // source shift-buffer iteration processing loop
|
||||
|
||||
|
||||
proc_ctx.cycleIndex += 1;
|
||||
} // end while
|
||||
|
||||
doneLabel:
|
||||
|
||||
for(unsigned i=0; i<recordChN; ++i)
|
||||
dsp::data_recorder::destroy( proc_ctx.recordChA[i] );
|
||||
mem::release(proc_ctx.recordChA);
|
||||
|
||||
for(unsigned i=0; i<proc_ctx.srcChN; ++i)
|
||||
dsp::shift_buf::destroy( srcShiftBufA[i] );
|
||||
|
||||
for(unsigned i=0; i<proc_ctx.dstChN; ++i)
|
||||
dsp::shift_buf::destroy( dstShiftBufA[i] );
|
||||
|
||||
mem::release(srcFileBuf);
|
||||
mem::release(dstFileBuf);
|
||||
mem::release(procFileBuf);
|
||||
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
|
||||
proc_ctx.procId = kCloseProcId;
|
||||
func( &proc_ctx );
|
||||
|
||||
close(srcAfH);
|
||||
close(dstAfH);
|
||||
|
||||
|
||||
mem::release(proc_ctx.srcFn);
|
||||
mem::release(proc_ctx.dstFn);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
cw::rc_t cw::afop::file_processor( const object_t* cfg )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const char* srcFn = nullptr;
|
||||
const char* dstFn = nullptr;
|
||||
const char* cfgLabel = nullptr;
|
||||
const object_t* pgm = nullptr;
|
||||
unsigned wndSmpN = 0;
|
||||
unsigned hopSmpN = 0;
|
||||
const char* program = nullptr;
|
||||
const object_t* args = nullptr;
|
||||
unsigned recordChN = 0;
|
||||
const object_t* recorder = nullptr;
|
||||
proc_func_t procFunc = nullptr;
|
||||
|
||||
typedef struct labelFunc_str
|
||||
{
|
||||
const char* label;
|
||||
proc_func_t func;
|
||||
} labelFunc_t;
|
||||
|
||||
labelFunc_t labelFuncA[] =
|
||||
{
|
||||
{ "tremelo",tremelo::main },
|
||||
{ "pvoc", pvoc::main },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
// parse the main audio file processor cfg record
|
||||
if((rc = cfg->getv("srcFn", srcFn,
|
||||
"dstFn", dstFn,
|
||||
"cfg", cfgLabel)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Error parsing the main audio file proc configuration record.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// locate the cfg for the specific process function to run
|
||||
if((rc = cfg->getv(cfgLabel, pgm)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The audio file proc. configuration '%s' was not found.",cwStringNullGuard(cfgLabel));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// parse the specific process function configuration record
|
||||
if((rc = pgm->getv("wndSmpN", wndSmpN,
|
||||
"hopSmpN", hopSmpN,
|
||||
"program", program,
|
||||
"args", args)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The audio file proc. configuration '%s' parse failed.",cwStringNullGuard(cfgLabel));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// parse the recorder spec
|
||||
if((rc = cfg->getv_opt("recordChN", recordChN,
|
||||
"recorder", recorder)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Error parsing the main audio file proc optional configuration fields.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// TODO: add optional args: inGain,outGain,dstBits
|
||||
|
||||
// locate the executable function associated with the specified process function
|
||||
for(unsigned i=0; true; ++i)
|
||||
{
|
||||
// if the function was not found
|
||||
if( labelFuncA[i].func == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The audio processing program '%s' could not be found.", cwStringNullGuard(program));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if this is the specified function
|
||||
if( textCompare(labelFuncA[i].label,program ) == 0 )
|
||||
{
|
||||
procFunc = labelFuncA[i].func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// run the processr
|
||||
if((rc = file_processor( srcFn, dstFn, procFunc, wndSmpN, hopSmpN, nullptr, args, recorder, recordChN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The audio file proc. failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
63
cwAudioFileProc.h
Normal file
63
cwAudioFileProc.h
Normal file
@ -0,0 +1,63 @@
|
||||
namespace cw
|
||||
{
|
||||
namespace afop
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
kOpenProcId,
|
||||
kProcProcId,
|
||||
kCloseProcId
|
||||
};
|
||||
|
||||
typedef struct proc_ctx_str
|
||||
{
|
||||
unsigned procId;
|
||||
|
||||
void* userPtr;
|
||||
const object_t* args; // cfg. for the selected process func
|
||||
unsigned cycleIndex;
|
||||
|
||||
char* srcFn;
|
||||
float srcSrate;
|
||||
unsigned srcChN;
|
||||
unsigned srcBits;
|
||||
const float** srcChV; // srcChV[ srcChN ][ srcWndSmpN ] - read incoming samples from this buffer
|
||||
unsigned srcWndSmpN; //
|
||||
unsigned srcHopSmpN; //
|
||||
|
||||
char* dstFn;
|
||||
float dstSrate;
|
||||
unsigned dstChN;
|
||||
unsigned dstBits;
|
||||
float** dstChV; // dstChV[ dstChN ][ dstWndSmpN ]
|
||||
unsigned dstWndSmpN; //
|
||||
unsigned dstHopSmpN; //
|
||||
|
||||
|
||||
dsp::data_recorder::fobj_t** recordChA; // recordChA[ recordChN ]
|
||||
|
||||
} proc_ctx_t;
|
||||
|
||||
// Open
|
||||
// Accept or modify the destination configuration. The src signal parameters are definted by the driver program.
|
||||
//
|
||||
// Proc:
|
||||
// If srcChN is non-zero then srcChV will be valid. The last srcHopSmpN will contain new samples for this iteration
|
||||
// If dstChN is non-zero then fill at least the first dstHopSmpN samples in dstChV[][].
|
||||
//
|
||||
// Note that the srcChV[][] and dstChV[][] both point to buffers of lengh src/dstWndSmpN but only the first src/dstHopSmpN
|
||||
// are finalized for a given cycle. The training wndSmpN-hopSmpN will be available (shifted right by hopSmpN samples)
|
||||
// on the next call.
|
||||
//
|
||||
// Return kEofRC if no input file is given and processing is complete.
|
||||
|
||||
|
||||
typedef rc_t (*proc_func_t)( proc_ctx_t* ctx );
|
||||
rc_t file_processor( const char* srcFn, const char* dstFn, proc_func_t func, unsigned wndSmpN, unsigned hopSmpN, void* userArg, const object_t* args, const object_t* recorder_cfg, unsigned recordChN=0 );
|
||||
rc_t file_processor( const object_t* cfg );
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
262
cwAudioTransforms.cpp
Normal file
262
cwAudioTransforms.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwFile.h"
|
||||
#include "cwText.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwAudioFile.h"
|
||||
#include "cwUtility.h"
|
||||
#include "cwFileSys.h"
|
||||
#include "cwAudioFileOps.h"
|
||||
#include "cwVectOps.h"
|
||||
#include "cwMath.h"
|
||||
#include "cwDsp.h"
|
||||
#include "cwAudioTransforms.h"
|
||||
|
||||
namespace cw
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
namespace wnd_func
|
||||
{
|
||||
|
||||
idLabelPair_t wndLabelArray[] =
|
||||
{
|
||||
{ kHannWndId, "hann" },
|
||||
{ kHammingWndId, "hamming" },
|
||||
{ kTriangleWndId, "triangle" },
|
||||
{ kKaiserWndId, "kaiser" },
|
||||
{ kHannMatlabWndId, "hann_matlab" },
|
||||
{ kUnityWndId, "unity" },
|
||||
{ kInvalidWndId, "<invalid>" }
|
||||
};
|
||||
|
||||
const char* wndIdToLabel( unsigned id )
|
||||
{ return cw::idToLabel( wndLabelArray, id, kInvalidWndId ); }
|
||||
|
||||
unsigned wndLabelToId( const char* label )
|
||||
{ return cw::labelToId( wndLabelArray, label, kInvalidWndId ); }
|
||||
|
||||
rc_t _test( const char* windowLabel, const double* wndV, unsigned wndN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
wnd_func::fobj_t* p = nullptr;
|
||||
|
||||
unsigned wndId = wndLabelToId( windowLabel );
|
||||
|
||||
if((rc = create(p,wndId,wndN,3)) == kOkRC )
|
||||
{
|
||||
vop::print(p->wndV, p->wndN, "%f ", windowLabel);
|
||||
|
||||
printf("diff: %f\n", vop::sum_sq_diff( wndV, p->wndV, wndN));
|
||||
|
||||
destroy(p);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t test( const cw::object_t* args )
|
||||
{
|
||||
double hann_15[] = { 0.0, 0.04951557, 0.1882551 , 0.38873953, 0.61126047, 0.8117449, 0.95048443, 1.0, 0.95048443, 0.8117449, 0.61126047, 0.38873953, 0.1882551, 0.04951557, 0.0 };
|
||||
double hann_16[] = { 0.0, 0.04322727, 0.1654347 , 0.3454915 , 0.55226423, 0.75, 0.9045085 , 0.9890738, 0.9890738, 0.9045085, 0.75, 0.55226423, 0.3454915, 0.1654347 , 0.04322727, 0.0 };
|
||||
double hamm_15[] = { 0.08,0.12555432, 0.25319469, 0.43764037, 0.64235963, 0.82680531, 0.95444568, 1.0, 0.95444568, 0.82680531,0.64235963, 0.43764037, 0.25319469, 0.12555432, 0.08 };
|
||||
double hamm_16[] = { 0.08,0.11976909, 0.23219992, 0.39785218, 0.58808309, 0.77, 0.91214782, 0.9899479, 0.9899479 , 0.91214782,0.77, 0.58808309, 0.39785218, 0.23219992, 0.11976909, 0.08};
|
||||
double tri_15[] = { 0.0, 0.14285714, 0.28571429, 0.42857143, 0.57142857, 0.71428571, 0.85714286, 1.0, 0.85714286, 0.71428571,0.57142857, 0.42857143, 0.28571429, 0.14285714, 0.0 };
|
||||
double tri_16[] = { 0.0, 0.13333333, 0.26666667, 0.4, 0.53333333, 0.66666667, 0.8, 0.93333333, 0.93333333, 0.8, 0.66666667, 0.53333333, 0.4, 0.26666667, 0.13333333, 0.0 };
|
||||
double ones_5[] = { 1.0, 1.0, 1.0, 1.0, 1.0 };
|
||||
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
_test( "hann", hann_15, 15);
|
||||
_test( "hann", hann_16, 16);
|
||||
_test( "hamming", hamm_15, 15);
|
||||
_test( "hamming", hamm_16, 16);
|
||||
_test( "triangle", tri_15, 15);
|
||||
_test( "triangle", tri_16, 16);
|
||||
_test( "unity", ones_5, 5);
|
||||
|
||||
if( rc != kOkRC )
|
||||
cwLogError(rc,"Window test failed.");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ola
|
||||
{
|
||||
rc_t test( const cw::object_t* args )
|
||||
{
|
||||
typedef float sample_t;
|
||||
|
||||
rc_t rc = kOkRC;
|
||||
unsigned wndSmpCnt = 16;
|
||||
unsigned hopSmpCnt = 4;
|
||||
unsigned procSmpCnt = 2;
|
||||
unsigned wndTypeId = wnd_func::kUnityWndId;
|
||||
unsigned hopCnt = 8;
|
||||
unsigned oSmpCnt = hopCnt * hopSmpCnt;
|
||||
unsigned i,j,k;
|
||||
|
||||
sample_t cV[] = { 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4 };
|
||||
sample_t* x = mem::allocZ<sample_t>(wndSmpCnt);
|
||||
sample_t* y = mem::allocZ<sample_t>(oSmpCnt);
|
||||
vop::fill(x,wndSmpCnt,1.0);
|
||||
|
||||
ola::fobj_t* p = nullptr;
|
||||
|
||||
if((rc = ola::create(p, wndSmpCnt, hopSmpCnt, procSmpCnt, wndTypeId )) == kOkRC )
|
||||
{
|
||||
|
||||
// Each iteration represents a single audio cylce
|
||||
// which sources/sinks procSmpCnt samples
|
||||
for(i=0,j=0,k=0; k<oSmpCnt; i+=procSmpCnt)
|
||||
{
|
||||
j += procSmpCnt;
|
||||
|
||||
// if there are hopSmpCnt new samples available then there must
|
||||
// be a new window of samples available -
|
||||
if( j > hopSmpCnt )
|
||||
{
|
||||
j -= hopSmpCnt;
|
||||
ola::exec(p,x,p->wndSmpCnt);
|
||||
}
|
||||
|
||||
const sample_t* op;
|
||||
|
||||
// Get procSmpCnt samples from the output.
|
||||
if( (op=ola::execOut(p)) != NULL )
|
||||
{
|
||||
assert( y + k + p->procSmpCnt <= y + oSmpCnt );
|
||||
vop::copy<sample_t>(y+k, op, p->procSmpCnt);
|
||||
k += p->procSmpCnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vop::print(cV,oSmpCnt,"%f ","Correct ");
|
||||
vop::print(y, oSmpCnt,"%f ","Computed");
|
||||
printf("diff:%f\n", vop::sum_sq_diff(cV,y,oSmpCnt));
|
||||
|
||||
ola::destroy(p);
|
||||
mem::release(x);
|
||||
mem::release(y);
|
||||
return rc;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace shift_buf
|
||||
{
|
||||
rc_t test( const object_t* args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
typedef float sample_t;
|
||||
|
||||
sample_t m[] =
|
||||
{
|
||||
1, 2, 3, 4, 5, 6, 7,
|
||||
7, 8, 9,10,11,12,13,
|
||||
13,14,15,16,17,18,19,
|
||||
19,20,21,22,23,24,25,
|
||||
25,26,27,28,29,30,31,
|
||||
31,32,33,34,35,36,37,
|
||||
37,38,39,40,41,42,43,
|
||||
43,44,45,46,47,48,49
|
||||
};
|
||||
|
||||
unsigned procSmpCnt = 5; // count of samples to be fed to the shift buffer on each cycle
|
||||
unsigned hopSmpCnt = 6; // count of samples between shift buffer outputs
|
||||
unsigned wndSmpCnt = 7; // count of samples in each shift buffer output
|
||||
|
||||
unsigned iSmpCnt = 49; // count of samples in the input test signal
|
||||
unsigned oColCnt = iSmpCnt / hopSmpCnt;
|
||||
unsigned oSmpCnt = oColCnt * wndSmpCnt; // count of samples in the output test signal
|
||||
unsigned i,j;
|
||||
|
||||
shift_buf::fobj_t* p = nullptr;
|
||||
|
||||
if((rc = shift_buf::create(p,procSmpCnt,wndSmpCnt,hopSmpCnt)) == kOkRC )
|
||||
{
|
||||
sample_t* x = mem::allocZ<sample_t>(iSmpCnt);
|
||||
sample_t* y = mem::allocZ<sample_t>(oSmpCnt);
|
||||
|
||||
vop::seq(x,iSmpCnt,1.0f,1.0f);
|
||||
|
||||
for(i=0,j=0; i<iSmpCnt; i+=procSmpCnt)
|
||||
{
|
||||
// Give the shift buffer a block of procSmpCnt samples.
|
||||
// If it returns 'true' then it has hopSmpCnt new samples and
|
||||
// a buffer of at least wndSmpCnt.
|
||||
for(; shift_buf::exec( p, x + i, procSmpCnt ); j+=wndSmpCnt )
|
||||
{
|
||||
|
||||
// cmShiftBufExec() returned true then there are wndSmpCnt samples available.
|
||||
assert( y + j + wndSmpCnt <= y + oSmpCnt );
|
||||
vop::copy(y + j, p->outV, wndSmpCnt );
|
||||
|
||||
vop::print(p->outV, wndSmpCnt, "%f ");
|
||||
vop::print(m + j, wndSmpCnt, "%f ");
|
||||
|
||||
if( !vop::is_equal(p->outV, m+j, wndSmpCnt ))
|
||||
{
|
||||
rc = cwLogError(kTestFailRC,"shift_buf test failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mem::release(x);
|
||||
mem::release(y);
|
||||
shift_buf::destroy(p);
|
||||
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pv_anl
|
||||
{
|
||||
rc_t test( const object_t* args )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
pv_anl::fobj_t* pva = nullptr;
|
||||
pv_syn::fobj_t* pvs = nullptr;
|
||||
unsigned procSmpCnt = 0;
|
||||
float srate = 0;
|
||||
float out_srate = 0;
|
||||
unsigned wndSmpCnt = 0;
|
||||
unsigned hopSmpCnt = 0;
|
||||
unsigned flags = kCalcHzPvaFl;
|
||||
unsigned wndTypeId = wnd_func::kHannWndId;
|
||||
|
||||
if((rc = create( pva, procSmpCnt, srate, wndSmpCnt, hopSmpCnt, flags )) != kOkRC )
|
||||
{
|
||||
}
|
||||
|
||||
if((rc = create( pvs, procSmpCnt, out_srate, wndSmpCnt, hopSmpCnt, wndTypeId )) != kOkRC )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
destroy(pva);
|
||||
destroy(pvs);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rc_t test( const cw::object_t* args )
|
||||
{
|
||||
wnd_func::test(args);
|
||||
ola::test(args);
|
||||
shift_buf::test(args);
|
||||
return kOkRC;
|
||||
}
|
||||
}
|
||||
}
|
1117
cwAudioTransforms.h
Normal file
1117
cwAudioTransforms.h
Normal file
File diff suppressed because it is too large
Load Diff
495
cwPvAudioFileProc.cpp
Normal file
495
cwPvAudioFileProc.cpp
Normal file
@ -0,0 +1,495 @@
|
||||
#include "cwCommon.h"
|
||||
#include "cwLog.h"
|
||||
#include "cwCommonImpl.h"
|
||||
#include "cwMem.h"
|
||||
#include "cwFile.h"
|
||||
#include "cwText.h"
|
||||
#include "cwObject.h"
|
||||
#include "cwAudioFile.h"
|
||||
#include "cwUtility.h"
|
||||
#include "cwFileSys.h"
|
||||
#include "cwAudioFileOps.h"
|
||||
#include "cwMath.h"
|
||||
#include "cwVectOps.h"
|
||||
#include "cwDsp.h"
|
||||
#include "cwAudioTransforms.h"
|
||||
#include "cwAudioFileProc.h"
|
||||
#include "cwPvAudioFileProc.h"
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Phase Vocoder File Processor
|
||||
//
|
||||
namespace cw
|
||||
{
|
||||
namespace afop
|
||||
{
|
||||
// PV Template
|
||||
namespace pvoc_template
|
||||
{
|
||||
typedef struct process_str
|
||||
{
|
||||
int foo;
|
||||
int blah;
|
||||
} process_t;
|
||||
|
||||
rc_t open( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = mem::allocZ<process_t>();
|
||||
|
||||
if((rc = ctx->args->getv( "foo", p->foo,
|
||||
"blah", p->blah)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing of 'pvoc_template' args. failed.");
|
||||
}
|
||||
|
||||
ctx->userPtr = p;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t close( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
if( p != nullptr )
|
||||
{
|
||||
mem::release(ctx->userPtr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t process( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
unsigned chCnt = std::min(ctx->srcChN,ctx->dstChN);
|
||||
unsigned binN = ctx->binN;
|
||||
|
||||
for(unsigned i=0; i<chCnt; ++i)
|
||||
for(unsigned j=0; j<binN; ++j)
|
||||
{
|
||||
ctx->dstMagChA[i][j] = ctx->srcMagChA[i][j];
|
||||
ctx->dstPhsChA[i][j] = ctx->srcPhsChA[i][j];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t main( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( ctx->procId )
|
||||
{
|
||||
case kOpenProcId:
|
||||
rc = open(ctx);
|
||||
break;
|
||||
|
||||
case kCloseProcId:
|
||||
rc = close(ctx);
|
||||
break;
|
||||
|
||||
case kProcProcId:
|
||||
rc = process(ctx);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PV Spec Dist
|
||||
namespace pvoc_spec_dist
|
||||
{
|
||||
typedef struct process_str
|
||||
{
|
||||
dsp::spec_dist::fobj_t** sdChA;
|
||||
} process_t;
|
||||
|
||||
rc_t open( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
process_t* p = mem::allocZ<process_t>();
|
||||
ctx->userPtr = p;
|
||||
|
||||
float ceiling = 30;
|
||||
float expo = 2;
|
||||
float thresh = 60;
|
||||
float uprSlope = -0.7;
|
||||
float lwrSlope = 2;
|
||||
float mix = 0;
|
||||
|
||||
|
||||
|
||||
if((rc = ctx->args->getv( "ceiling", ceiling,
|
||||
"expo", expo,
|
||||
"thresh", thresh,
|
||||
"uprSlope", uprSlope,
|
||||
"lwrSlope", lwrSlope,
|
||||
"mix", mix)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing of 'pvoc_template' args. failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->sdChA = mem::allocZ< dsp::spec_dist::fobj_t* >( ctx->srcChN );
|
||||
|
||||
for(unsigned i=0; i<ctx->srcChN; ++i)
|
||||
if((rc = dsp::spec_dist::create( p->sdChA[i], ctx->binN, ceiling, expo, thresh, uprSlope, lwrSlope, mix )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Spec Dist processor channel create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t close( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
|
||||
if( p != nullptr )
|
||||
{
|
||||
if( p->sdChA )
|
||||
{
|
||||
for(unsigned i=0; i<ctx->srcChN; ++i)
|
||||
dsp::spec_dist::destroy( p->sdChA[i] );
|
||||
mem::release(p->sdChA);
|
||||
}
|
||||
|
||||
mem::release( p );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t process( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
unsigned chCnt = std::min(ctx->srcChN,ctx->dstChN);
|
||||
|
||||
for(unsigned i=0; i<chCnt; ++i)
|
||||
{
|
||||
dsp::spec_dist::exec( p->sdChA[i], ctx->srcMagChA[i], ctx->srcPhsChA[i], ctx->binN );
|
||||
|
||||
for(unsigned j=0; j<ctx->binN; ++j)
|
||||
{
|
||||
ctx->dstMagChA[i][j] = p->sdChA[i]->outMagV[j];
|
||||
ctx->dstPhsChA[i][j] = p->sdChA[i]->outPhsV[j];
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t main( pvoc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( ctx->procId )
|
||||
{
|
||||
case kOpenProcId:
|
||||
rc = open(ctx);
|
||||
break;
|
||||
|
||||
case kCloseProcId:
|
||||
rc = close(ctx);
|
||||
break;
|
||||
|
||||
case kProcProcId:
|
||||
rc = process(ctx);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace pv_file_proc
|
||||
{
|
||||
typedef struct process_str
|
||||
{
|
||||
dsp::pv_anl::fobj_t** anlA; // anlA[chCnt]
|
||||
dsp::pv_syn::fobj_t** synA; // synA[chCnt]
|
||||
pvoc_ctx_t pvoc_ctx; //
|
||||
const char* functionLabel;; //
|
||||
pvoc_func_t function; //
|
||||
float* dstBuf;
|
||||
float* srcBuf;
|
||||
} process_t;
|
||||
|
||||
typedef struct labelFunc_str
|
||||
{
|
||||
const char* label;
|
||||
pvoc_func_t func;
|
||||
} labelFunc_t;
|
||||
|
||||
labelFunc_t labelFuncA[] =
|
||||
{
|
||||
{ "pvoc_template",pvoc_template::main },
|
||||
{ "spec_dist", pvoc_spec_dist::main },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
rc_t open( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = mem::allocZ<process_t>();
|
||||
ctx->userPtr = p;
|
||||
|
||||
|
||||
// parse the specific process function configuration record
|
||||
if((rc = ctx->args->getv( "wndSmpN", p->pvoc_ctx.wndSmpN,
|
||||
"hopSmpN", p->pvoc_ctx.hopSmpN,
|
||||
"procSmpN", p->pvoc_ctx.procSmpN,
|
||||
"function", p->functionLabel,
|
||||
"args", p->pvoc_ctx.args)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The pvoc file proc. configuration parse failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if((rc = ctx->args->getv_opt( "inGain", p->pvoc_ctx.inGain,
|
||||
"outGain", p->pvoc_ctx.outGain )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Parsing of pvoc file optional arguments failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
|
||||
// locate the executable function associated with the specified process function
|
||||
for(unsigned i=0; true; ++i)
|
||||
{
|
||||
// if the function was not found
|
||||
if( labelFuncA[i].func == nullptr )
|
||||
{
|
||||
rc = cwLogError(kInvalidArgRC,"The audio processing program '%s' could not be found.", cwStringNullGuard(p->functionLabel));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if this is the specified function
|
||||
if( textCompare(labelFuncA[i].label,p->functionLabel ) == 0 )
|
||||
{
|
||||
p->function = labelFuncA[i].func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p->pvoc_ctx.td_ctx = ctx;
|
||||
p->pvoc_ctx.srcChN = ctx->srcChN;
|
||||
p->pvoc_ctx.dstChN = ctx->dstChN;
|
||||
|
||||
|
||||
p->pvoc_ctx.srcMagChA = mem::allocZ< const float * >( p->pvoc_ctx.srcChN );
|
||||
p->pvoc_ctx.srcPhsChA = mem::allocZ< const float * >( p->pvoc_ctx.srcChN );
|
||||
|
||||
p->pvoc_ctx.dstMagChA = mem::allocZ< float* >( p->pvoc_ctx.dstChN );
|
||||
p->pvoc_ctx.dstPhsChA = mem::allocZ< float* >( p->pvoc_ctx.dstChN );
|
||||
|
||||
p->anlA = mem::allocZ< dsp::pv_anl::fobj_t* >( p->pvoc_ctx.srcChN );
|
||||
p->synA = mem::allocZ< dsp::pv_syn::fobj_t* >( p->pvoc_ctx.dstChN );
|
||||
|
||||
|
||||
for(unsigned i=0; i<p->pvoc_ctx.srcChN; ++i)
|
||||
{
|
||||
if((rc = dsp::pv_anl::create( p->anlA[i], p->pvoc_ctx.procSmpN, ctx->srcSrate, p->pvoc_ctx.wndSmpN, p->pvoc_ctx.hopSmpN, dsp::pv_anl::kNoCalcHzPvaFl )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"PVOC analysis component create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->pvoc_ctx.binN = p->anlA[i]->binCnt; // All input and ouput frames have the same bin count
|
||||
}
|
||||
|
||||
// Call the open function
|
||||
p->pvoc_ctx.procId = kOpenProcId;
|
||||
if((rc = p->function( &p->pvoc_ctx )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
|
||||
// Allocate the vector memory for the src/dst buffer
|
||||
p->srcBuf = mem::allocZ< float >( p->pvoc_ctx.srcChN * p->pvoc_ctx.binN );
|
||||
p->dstBuf = mem::allocZ< float >( 2* p->pvoc_ctx.dstChN * p->pvoc_ctx.binN );
|
||||
|
||||
for(unsigned i=0; i<p->pvoc_ctx.dstChN; ++i)
|
||||
{
|
||||
if((rc = dsp::pv_syn::create( p->synA[i], p->pvoc_ctx.procSmpN, ctx->dstSrate, p->pvoc_ctx.wndSmpN, p->pvoc_ctx.hopSmpN )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"PVOC synthesis component create failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
p->pvoc_ctx.dstMagChA[i] = p->dstBuf + (2*i*p->pvoc_ctx.binN);
|
||||
p->pvoc_ctx.dstPhsChA[i] = p->pvoc_ctx.dstMagChA[i] + p->pvoc_ctx.binN;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t close( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
|
||||
if( p != nullptr )
|
||||
{
|
||||
|
||||
p->pvoc_ctx.procId = kCloseProcId;
|
||||
p->function( &p->pvoc_ctx );
|
||||
|
||||
if( p->anlA )
|
||||
for(unsigned i=0; i<p->pvoc_ctx.srcChN; ++i)
|
||||
dsp::pv_anl::destroy(p->anlA[i]);
|
||||
|
||||
if( p->synA )
|
||||
for(unsigned i=0; i<p->pvoc_ctx.dstChN; ++i)
|
||||
dsp::pv_syn::destroy(p->synA[i]);
|
||||
|
||||
mem::release( p->anlA );
|
||||
mem::release( p->synA );
|
||||
mem::release( p->pvoc_ctx.srcMagChA );
|
||||
mem::release( p->pvoc_ctx.srcPhsChA );
|
||||
mem::release( p->pvoc_ctx.dstMagChA );
|
||||
mem::release( p->pvoc_ctx.dstPhsChA );
|
||||
mem::release( p->dstBuf );
|
||||
mem::release( p->srcBuf );
|
||||
mem::release(p);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t process( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
process_t* p = (process_t*)ctx->userPtr;
|
||||
bool fl = false;
|
||||
|
||||
// Setup the source spectral data
|
||||
for(unsigned i=0; i<p->pvoc_ctx.srcChN; ++i)
|
||||
{
|
||||
p->pvoc_ctx.srcMagChA[i] = nullptr;
|
||||
p->pvoc_ctx.srcPhsChA[i] = nullptr;
|
||||
|
||||
if( dsp::pv_anl::exec( p->anlA[i], ctx->srcChV[i], p->pvoc_ctx.hopSmpN) )
|
||||
{
|
||||
float* srcChV = p->srcBuf + (i*p->pvoc_ctx.binN);
|
||||
|
||||
// apply input gain
|
||||
vop::mul( srcChV, p->anlA[i]->magV, p->pvoc_ctx.inGain * p->pvoc_ctx.binN, p->pvoc_ctx.binN );
|
||||
|
||||
p->pvoc_ctx.srcMagChA[i] = (const float*)srcChV;
|
||||
p->pvoc_ctx.srcPhsChA[i] = (const float*)p->anlA[i]->phsV;
|
||||
|
||||
fl = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( fl )
|
||||
{
|
||||
p->pvoc_ctx.procId = kProcProcId;
|
||||
p->function( &p->pvoc_ctx );
|
||||
|
||||
// Get the dest. spectral data.
|
||||
for(unsigned i=0; i<p->pvoc_ctx.dstChN; ++ i)
|
||||
{
|
||||
if((rc = dsp::pv_syn::exec( p->synA[i], p->pvoc_ctx.dstMagChA[i], p->pvoc_ctx.dstPhsChA[i] )) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"Pvoc synthesis failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// apply output gain
|
||||
vop::mul( ctx->dstChV[i], p->synA[i]->ola->outV, p->pvoc_ctx.outGain, p->pvoc_ctx.hopSmpN );
|
||||
//vop::copy( ctx->dstChV[i], p->synA[i]->ola->outV, p->pvoc_ctx.hopSmpN );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc_t main( proc_ctx_t* ctx )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
switch( ctx->procId )
|
||||
{
|
||||
case kOpenProcId:
|
||||
rc = open(ctx);
|
||||
break;
|
||||
|
||||
case kCloseProcId:
|
||||
rc = close(ctx);
|
||||
break;
|
||||
|
||||
case kProcProcId:
|
||||
rc = process(ctx);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cw::rc_t cw::afop::pvoc_file_processor( const object_t* cfg )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
const char* srcFn = nullptr;
|
||||
const char* dstFn = nullptr;
|
||||
const char* pgmLabel = nullptr;
|
||||
const object_t* pgm = nullptr;
|
||||
unsigned hopSmpN = 0;
|
||||
unsigned recordChN = 0;
|
||||
const object_t* recorder = nullptr;
|
||||
|
||||
// parse the main audio file processor cfg record
|
||||
if((rc = cfg->getv("srcFn", srcFn,
|
||||
"dstFn", dstFn,
|
||||
"program", pgmLabel)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Error parsing the main audio file proc configuration record.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// parse the recorder spec
|
||||
if((rc = cfg->getv_opt("recordChN", recordChN,
|
||||
"recorder", recorder)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"Error parsing the main audio file proc optional configuration fields.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// locate the cfg for the specific process function to run
|
||||
if((rc = cfg->getv(pgmLabel, pgm)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The audio file proc. configuration '%s' was not found.",cwStringNullGuard(pgmLabel));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// parse the specific process function configuration record
|
||||
if((rc = pgm->getv("hopSmpN", hopSmpN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(kSyntaxErrorRC,"The audio file proc. configuration '%s' parse failed.",cwStringNullGuard(pgmLabel));
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// run the processr
|
||||
if((rc = file_processor( srcFn, dstFn, pv_file_proc::main, hopSmpN, hopSmpN, nullptr, pgm, recorder, recordChN)) != kOkRC )
|
||||
{
|
||||
rc = cwLogError(rc,"The audio file proc. failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
errLabel:
|
||||
return rc;
|
||||
}
|
41
cwPvAudioFileProc.h
Normal file
41
cwPvAudioFileProc.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef cwPvAudioFileProc_h
|
||||
#define cwPvAudioFileProc_h
|
||||
|
||||
namespace cw
|
||||
{
|
||||
namespace afop
|
||||
{
|
||||
|
||||
typedef struct pvoc_ctx_str
|
||||
{
|
||||
unsigned procId;
|
||||
|
||||
proc_ctx_t* td_ctx; // time domain context (userPtr = pgmLabel)
|
||||
const object_t* args; // program args
|
||||
void* userPtr;
|
||||
|
||||
unsigned wndSmpN; // TODO: change thise to src and dst variables
|
||||
unsigned hopSmpN;
|
||||
unsigned procSmpN;
|
||||
unsigned binN;
|
||||
|
||||
double inGain;
|
||||
double outGain;
|
||||
|
||||
unsigned srcChN;
|
||||
const float** srcMagChA; // srcMagChA[ chN ][ binN ]
|
||||
const float** srcPhsChA; // srcPhsChA[ chN ][ binN ]
|
||||
|
||||
unsigned dstChN;
|
||||
float** dstMagChA; // dstMagChA[ chN ][ binN ]
|
||||
float** dstPhsChA; // dstPhsChA[ chN ][ binN ]
|
||||
|
||||
} pvoc_ctx_t;
|
||||
|
||||
typedef rc_t (*pvoc_func_t)( pvoc_ctx_t* ctx );
|
||||
|
||||
rc_t pvoc_file_processor( const object_t* cfg );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user