#include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" #include "cwFile.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwUtility.h" #include "cwFileSys.h" #include "cwAudioFileOps.h" cw::rc_t cw::afop::sine( const char* fn, double srate, unsigned bits, double hz, double gain, double secs ) { rc_t rc = kOkRC; unsigned bN = srate * secs; float* b = mem::alloc(bN); unsigned chCnt = 1; unsigned i; for(i=0; igetv("fn",fn,"srate",srate,"bits",bits,"hz",hz,"gain",gain,"secs",secs)) != kOkRC ) return cwLogError(kSyntaxErrorRC,"Invalid parameter to audio file sine test."); char* afn = filesys::expandPath(fn); rc = sine( afn, srate, bits, hz, gain, secs ); mem::release(afn); return rc; } cw::rc_t cw::afop::mix( const char* fnV[], const float* gainV, unsigned srcN, const char* outFn, unsigned outBits ) { rc_t rc = kOkRC; if( srcN == 0 ) return rc; unsigned maxFrmN = 0; unsigned maxChN = 0; double srate = 0; audiofile::handle_t hV[ srcN ]; audiofile::handle_t oH; // open each source file and determine the output file audio format for(unsigned i=0; i maxActualFrmN ) maxActualFrmN = actualFrmN; } // write the mixed output buffer if((rc = audiofile::writeFloat(oH, maxActualFrmN, maxChN, oChBuf )) != kOkRC ) { rc = cwLogError(kOpFailRC,"Write failed on output file '%s'.", outFn ); goto errLabel; } } } errLabel: if( rc != kOkRC ) cwLogError(kOpFailRC,"Mix failed."); // close the source audio files for(unsigned i=0; igetv("outFn",oFn,"outBits",outBits,"srcL",srcL)) != kOkRC ) goto errLabel; else { char* outFn = filesys::expandPath(oFn); unsigned srcN = srcL->child_count(); const char* fnV[ srcN ]; float gainV[ srcN ]; memset(fnV,0,sizeof(fnV)); // read each source record for(unsigned i=0; ichild_ele(i)->getv("gain",gainV[i],"src",fn)) != kOkRC ) rc = cwLogError(kSyntaxErrorRC,"Mix source index %i syntax error."); else fnV[i] = filesys::expandPath(fn); } if( rc == kOkRC ) rc = mix( fnV, gainV, srcN, outFn, outBits); mem::free(outFn); for(unsigned i=0; i= endSec ) { rc = cwLogError(kInvalidArgRC,"Invalid time selection. Begin time (%f) is greater than end time (%f). ", begSec, endSec ); goto errLabel; } // create the output file if((rc = audiofile::create( oH, oFn, info.srate, outBits, info.chCnt)) != kOkRC ) goto errLabel; else { unsigned begFrmIdx = (unsigned)floor(begSec * info.srate); unsigned endFrmIdx = endSec==-1 ? info.frameCnt : (unsigned)floor(endSec * info.srate); unsigned ttlFrmN = endFrmIdx - begFrmIdx; unsigned actualBufFrmN = 0; const unsigned bufFrmN = 8196; // read/write buffer size float buf[ bufFrmN*info.chCnt ]; float* chBuf[ info.chCnt ]; // set up the read/write channel buffer for(unsigned i = 0; i ttlFrmN ) actualBufFrmN -= ttlFrmN - curFrmN; // write the buffer to the output file if((rc = audiofile::writeFloat(oH, actualBufFrmN, info.chCnt, chBuf )) != kOkRC ) { rc = cwLogError(kOpFailRC,"Write failed on output file '%s'.", oFn ); goto errLabel; } } } errLabel: if( rc != kOkRC ) cwLogError(rc,"selectToFile failed."); audiofile::close(oH); audiofile::close(iH); mem::release(iFn); mem::release(dstDir); mem::release(oFn); return rc; } cw::rc_t cw::afop::selectToFile( const object_t* cfg ) { rc_t rc = kOkRC; const object_t* selectL = nullptr; const char* oDir = nullptr; unsigned outBits = 16; // read the top level cfg record if((rc = cfg->getv("outDir",oDir,"outBits",outBits,"selectL",selectL)) != kOkRC ) goto errLabel; else { unsigned selN = selectL->child_count(); for(unsigned i=0; ichild_ele(i)->getv("begSec",begSec,"endSec",endSec,"dst",dstFn, "src",srcFn)) != kOkRC ) { rc = cwLogError(kSyntaxErrorRC,"'Select to file' index %i syntax error."); goto errLabel; } if((rc = selectToFile( srcFn, begSec, endSec, outBits, oDir, dstFn )) != kOkRC ) goto errLabel; } } errLabel: return rc; } namespace cw { namespace afop { typedef struct _cutMixArg_str { const cutMixArg_t* arg; char* srcFn; unsigned srcFrmIdx; unsigned srcFrmN; unsigned srcFadeInFrmN; unsigned srcFadeOutFrmN; unsigned dstFrmIdx; audiofile::handle_t afH; audiofile::info_t afInfo; } _cutMixArg_t; rc_t _cutAndMixOpen( const char* srcDir, const cutMixArg_t* argL, _cutMixArg_t* xArgL, unsigned argN, unsigned& chN_Ref, double& srate_Ref, unsigned& dstFrmN_Ref, unsigned& maxSrcFrmN_Ref ) { rc_t rc = kOkRC; maxSrcFrmN_Ref = 0; chN_Ref = 0; for(unsigned i = 0; i argL[i].srcEndSec ) { rc = cwLogError(kInvalidArgRC,"The end time is prior to the begin time on source file '%s'.", xArgL[i].srcFn); goto errLabel; } xArgL[i].arg = argL + i; xArgL[i].srcFrmIdx = floor(argL[i].srcBegSec * srate_Ref); xArgL[i].srcFrmN = floor(argL[i].srcEndSec * srate_Ref) - xArgL[i].srcFrmIdx; xArgL[i].srcFadeInFrmN = floor(argL[i].srcBegFadeSec * srate_Ref); xArgL[i].srcFadeOutFrmN = floor(argL[i].srcEndFadeSec * srate_Ref); xArgL[i].dstFrmIdx = floor(argL[i].dstBegSec * srate_Ref); chN_Ref = std::max( chN_Ref, xArgL[i].afInfo.chCnt ); maxSrcFrmN_Ref = std::max( maxSrcFrmN_Ref, xArgL[i].srcFrmN ); dstFrmN_Ref = std::max( dstFrmN_Ref, xArgL[i].dstFrmIdx + xArgL[i].srcFrmN ); } errLabel: return rc; } rc_t _cutAndMixClose( _cutMixArg_t* xArgL, unsigned argN ) { rc_t rc = kOkRC; for(unsigned i = 0; i(dstFrmN*dstChN); // output signal buffer srcV = mem::alloc(maxSrcFrmN*dstChN); // source signal buffer // create the src read buffer for(unsigned i=0; igetv("dstFn",dstFn,"dstBits",dstBits,"srcDir",srcDir,"crossFadeSec",crossFadeSec,"argL",argNodeL)) != kOkRC ) goto errLabel; if( argNodeL == nullptr ) { rc = cwLogError(kInvalidArgRC,"No crossfades were specified."); } else { unsigned argN = argNodeL->child_count(); cutMixArg_t argL[ argN ]; printf("cm: %s %s %f\n",dstFn,srcDir,crossFadeSec); // for each source file for(i=0; ichild_count(); ++i) { const object_t* o = argNodeL->child_ele(i); // parse the non-optional parameters if((rc = o->getv("srcBegSec", argL[i].srcBegSec, "srcEndSec", argL[i].srcEndSec, "srcFn", argL[i].srcFn )) != kOkRC ) { rc = cwLogError(kInvalidArgRC,"Invalid crossfade argument at argument index %i.",i); } else { argL[i].dstBegSec = argL[i].srcBegSec; // By default the src is moved to the same location argL[i].srcBegFadeSec = crossFadeSec; // By default the beg/end fade is the global fade time. argL[i].srcEndFadeSec = crossFadeSec; // parse the optional parameters if((rc = o->getv_opt("dstBegSec", argL[i].dstBegSec, "srcBegFadeSec", argL[i].srcBegFadeSec, "srcEndFadeSec", argL[i].srcEndFadeSec )) != kOkRC ) { rc = cwLogError(kInvalidArgRC,"Invalid crossfade optional argument at argument index %i.",i); } } printf("cm beg:%f end:%f dst:%f %s\n", argL[i].srcBegSec, argL[i].srcEndSec, argL[i].dstBegSec, argL[i].srcFn ); } afSrcDir = filesys::expandPath(srcDir); afDstFn = filesys::expandPath(dstFn); // call cross-fader if((rc = cutAndMix( afDstFn, dstBits, afSrcDir, argL, argN )) != kOkRC ) { rc = cwLogError(rc,""); goto errLabel; } } errLabel: mem::release(afSrcDir); mem::release(afDstFn); if( rc != kOkRC ) rc = cwLogError(rc,"Cut and mix failed."); return rc; } cw::rc_t cw::afop::parallelMix( const char* dstFn, unsigned dstBits, const char* srcDir, const parallelMixArg_t* argL, unsigned argN ) { cutMixArg_t cmArgL[ argN ]; double fadeInSec = 0; double dstBegSec = 0; for(unsigned i=0; igetv("dstFn",dstFn,"dstBits",dstBits,"srcDir",srcDir,"argL",argNodeL)) != kOkRC ) goto errLabel; if( argNodeL == nullptr ) { rc = cwLogError(kInvalidArgRC,"No crossfades were specified."); } else { unsigned argN = argNodeL->child_count(); parallelMixArg_t argL[ argN ]; printf("%s %s\n",dstFn,srcDir); // for each source file for(i=0; ichild_count(); ++i) { const object_t* o = argNodeL->child_ele(i); // parse the non-optional parameters if((rc = o->getv("srcBegSec", argL[i].srcBegSec, "srcEndSec", argL[i].srcEndSec, "fadeOutSec", argL[i].fadeOutSec, "srcFn", argL[i].srcFn )) != kOkRC ) { rc = cwLogError(kInvalidArgRC,"Invalid crossfade argument at argument index %i.",i); } else { /* // parse the optional parameters if((rc = o->getv_opt("dstBegSec", argL[i].dstBegSec, "srcBegFadeSec", argL[i].srcBegFadeSec, "srcEndFadeSec", argL[i].srcEndFadeSec )) != kOkRC ) { rc = cwLogError(kInvalidArgRC,"Invalid crossfade optional argument at argument index %i.",i); } */ } printf("beg:%f end:%f dst:%f %s\n", argL[i].srcBegSec, argL[i].srcEndSec, argL[i].fadeOutSec, argL[i].srcFn ); } afSrcDir = filesys::expandPath(srcDir); afDstFn = filesys::expandPath(dstFn); // call cross-fader if((rc = parallelMix( afDstFn, dstBits, afSrcDir, argL, argN )) != kOkRC ) { rc = cwLogError(rc,"Parallel mix failed."); goto errLabel; } } errLabel: mem::release(afSrcDir); mem::release(afDstFn); if( rc != kOkRC ) rc = cwLogError(rc,"Crossfade failed."); return rc; } cw::rc_t cw::afop::test( const object_t* cfg ) { rc_t rc = kOkRC; const object_t* o; if((o = cfg->find("sine")) != nullptr ) rc = sine(o); return rc; }