#include #include #include #include #include #include "cmPrefix.h" #include "cmGlobal.h" #include "cmFloatTypes.h" #include "cmRpt.h" #include "cmErr.h" #include "cmCtx.h" #include "cmMem.h" #include "cmMallocDebug.h" #include "cmLinkedHeap.h" #include "cmThread.h" #include "cmText.h" #include "cmFileSys.h" #include "cmJson.h" #include "cmPrefs.h" #include "cmSymTbl.h" #include "cmTime.h" #include "cmMidi.h" #include "cmMidiFile.h" #include "cmAudioFile.h" #include "cmTimeLine.h" #include "cmScore.h" #include "cmProcObj.h" #include "cmProc4.h" #include "cmAudioFileMgr.h" #include "cmdIf.h" #include //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- cmdIf::cmdIf( cmCtx_t* ctx, cmdIfRspdr* rspdr, const cmChar_t* audioPath ) : _ctx(ctx),_thH(cmThreadNullHandle), _cmdQueH(cmTs1p1cNullHandle),_outQueH(cmTs1p1cNullHandle), _tlH(cmTimeLineNullHandle),_afmH(cmAfmNullHandle),_scH(cmScNullHandle), _afPath(NULL),_rspdr(rspdr),_curSeqId(cmInvalidId) { cmErrSetup(&_err,&ctx->rpt,"cmdIf"); cmAfmCreate(ctx,&_afmH); cmThreadCreate( &_thH, _thFunc, this, &ctx->rpt ); cmTs1p1cCreate( &_cmdQueH, 4*64536, NULL, NULL, &_ctx->rpt ); cmTs1p1cCreate( &_outQueH, 4*64536, NULL, NULL, &_ctx->rpt ); if( audioPath != NULL ) setAudioFilePath(audioPath); } cmdIf::~cmdIf() { cmThreadDestroy( &_thH ); // stop the thread to prevent interfering with que release _releaseQue(&_cmdQueH); _releaseQue(&_outQueH); cmTimeLineFinalize(&_tlH); cmAfmDestroy(&_afmH); cmScoreFinalize(&_scH); cmMemFree(_afPath); } cmdIf::rc_t cmdIf::open( const cmChar_t* fn ) { return _sendCmd(kOpenCmdId,0,fn); } cmdIf::rc_t cmdIf::close( ) { return _sendCmd(kCloseCmdId); } const cmChar_t* cmdIf::tlFileName() const { if( cmTimeLineIsValid(_tlH) == false ) return NULL; return cmTimeLineFileName(_tlH); } const cmTlMidiFile_t* cmdIf::tlMidiFileObjPtr( const cmTlObj_t* op ) const { return cmTimeLineMidiFileObjPtr(_tlH,const_cast(op)); } const cmTlAudioFile_t* cmdIf::tlAudioFileObjPtr( const cmTlObj_t* op ) const { return cmTimeLineAudioFileObjPtr(_tlH,const_cast(op)); } const cmTlMidiEvt_t* cmdIf::tlMidiEvtObjPtr( const cmTlObj_t* op ) const { return cmTimeLineMidiEvtObjPtr(_tlH,const_cast(op)); } const cmTlAudioEvt_t* cmdIf::tlAudioEvtObjPtr( const cmTlObj_t* op ) const { return cmTimeLineAudioEvtObjPtr(_tlH,const_cast(op)); } const cmTlMarker_t* cmdIf::tlMarkerObjPtr( const cmTlObj_t* op ) const { return cmTimeLineMarkerObjPtr(_tlH,const_cast(op)); } const cmChar_t* cmdIf::scoreFileName() const { if( cmScoreIsValid(_scH) == false ) return NULL; return cmScoreFileName(_scH); } const cmScoreEvt_t* cmdIf::scoreEventIdToPtr( unsigned scEvtId ) const { if( cmScoreIsValid(_scH)==false ) return NULL; return cmScoreEvt(_scH,scEvtId); } const cmScoreSection_t* cmdIf::scoreSectionIdToPtr( unsigned scSectId ) const { if( cmScoreIsValid(_scH)==false) return NULL; return cmScoreSection(_scH,scSectId); } const cmTlObj_t* cmdIf::tlObjIdToPtr( unsigned tlObjId ) const { return cmTlIdToObjPtr( _tlH, tlObjId ); } cmdIf::rc_t cmdIf::selectSequence( unsigned id ) { return _sendCmd(kSelectSeqCmdId,id); } cmdIf::rc_t cmdIf::audioFileLoad( const cmChar_t* fn, unsigned appFileId ) { const cmChar_t* afFn = fn; //cmFileSysPathPart_t* pp = NULL ; /* if( _afPath != NULL ) { pp = cmFsPathParts(fn); afFn = cmFsMakeFn(_afPath,pp->fnStr,pp->extStr,NULL); } */ rc_t rc = _sendCmd(kAfLoadCmdId,appFileId,afFn); /* if( _afPath != NULL ) { cmFsFreeFn(afFn); cmFsFreePathParts(pp); } */ return rc; } cmAfmFileH_t cmdIf::audioFileHandle( unsigned appFileId ) { return cmAfmIdToHandle(_afmH,appFileId); } void cmdIf::setAudioFilePath( const cmChar_t* path ) { _afPath = cmMemResizeStr(_afPath,path); } cmdIf::rc_t cmdIf::setScore( const cmChar_t* scoreFn ) { return _sendCmd(kScoreCmdId,0,scoreFn); } void cmdIf::setScoreLocation( unsigned locIdx, unsigned smpIdx, unsigned pitch, unsigned vel ) { cmScoreSetPerfEvent(_scH,locIdx,smpIdx,pitch,vel); } void cmdIf::setScoreVarValue( unsigned locIdx, unsigned varId, double value ) { cmScoreSetPerfValue(_scH,locIdx,varId,value); } void cmdIf::setScoreDynLevel( unsigned evtIdx, unsigned dynLvl ) { cmScoreSetPerfDynLevel(_scH,evtIdx,dynLvl); } void cmdIf::onTimeLineMarkerSelected( unsigned markerTlId ) { if( _rspdr != NULL ) _rspdr->cmdIfOnTimeLineMarkerSelect(markerTlId); _onTimeLineObjSelected(markerTlId); } void cmdIf::onTimeLineMidiEvtSelected( unsigned midiEvtTlId ) { if( _rspdr != NULL ) _rspdr->cmdIfOnTimeLineMidiEvtSelect(midiEvtTlId); _onTimeLineObjSelected(midiEvtTlId); } void cmdIf::onScoreBarSelected( unsigned scoreIdx ) { if( cmScoreIsValid(_scH) ) cmScoreClearPerfInfo(_scH); if( _rspdr != NULL ) _rspdr->cmdIfOnScoreBarSelect(scoreIdx); } cmdIf::rc_t cmdIf::generateOnsetMarks() { return _sendCmd( kGenOnsetMarksCmdId); } cmdIf::rc_t cmdIf::deleteOnsetMarks() { return _sendCmd( kDelOnsetMarksCmdId); } bool cmdIf::isBusy() const { return cmThreadIsValid(_thH) && cmThreadState(_thH)==kRunningThId; } void cmdIf::onIdle() { if( !cmTs1p1cIsValid(_outQueH) ) return; // pick up msg's sent from the worker thread while( cmTs1p1cMsgWaiting(_outQueH) ) { cmd_t c; cmThRC_t thRC; if((thRC = cmTs1p1cDequeueMsg(_outQueH,&c,sizeof(c))) != kOkThRC ) { _thErrorMsg("Deque response failed."); continue; } //printf("deq th->app id:%i val:%i n:%i msg:%p\n",c.id,c.value,c.byteCnt,c.u.msg); switch( c.id ) { // the worker thread is busy - show a modal progress window case kShowStatusCmdId: _rspdr->cmdIfShowStatusMsg(c.u.string); break; // the worker thread is idle - remove the modal progress window case kHideStatusCmdId: _rspdr->cmdIfHideStatus(); break; // report an error which occured during a worker thread operation case kErrMsgCmdId: _rspdr->cmdIfErrorMsg(c.u.string); break; // send a msg to the time-line UI case kTimeLineMsgCmdId: _rspdr->cmdIfTimeLineMsg(c.u.msg,c.byteCnt); break; case kAfLoadCmdId: _rspdr->cmdIfAudioFileLoad(c.value); break; case kScoreMsgCmdId: _rspdr->cmdIfScoreMsg(c.u.msg,c.byteCnt); break; default: break; } if( c.byteCnt ) { cmMemFree(c.u.msg); } } } //---------------------------------------------------------------------------------------- // App Thread Functions //---------------------------------------------------------------------------------------- cmdIf::rc_t cmdIf::_sendCmd( cmdId_t id, unsigned value, const char* str ) { rc_t rc; if((rc = _enqueue( _cmdQueH, id, value, str, str==NULL ? 0 : strlen(str)+1 )) == kOkRC ) cmThreadPause(_thH, 0 ); else { cmErrMsg(&_err,kCmdEnqueueFailRC,"Command enque failed."); } return rc; } void cmdIf::_releaseQue( cmTs1p1cH_t* queHPtr ) { while( cmTs1p1cMsgWaiting(*queHPtr) ) { cmd_t c; cmThRC_t thRC; if((thRC = cmTs1p1cDequeueMsg(*queHPtr,&c,sizeof(c))) != kOkThRC ) { // TODO: PRINT ERROR MSG HERE USING APP THREAD ERROR HANDLER //_thErrorMsg("Deque command failed during queue draining."); continue; } if( c.byteCnt ) cmMemFree(c.u.msg); } cmTs1p1cDestroy(queHPtr); } //---------------------------------------------------------------------------------------- // Worker Thread Functions //---------------------------------------------------------------------------------------- bool cmdIf::_thFunc( void* arg ) { cmdIf* p = (cmdIf*)arg; while( cmTs1p1cMsgWaiting(p->_cmdQueH) ) { cmd_t c; cmThRC_t thRC; if((thRC = cmTs1p1cDequeueMsg(p->_cmdQueH,&c,sizeof(c))) != kOkThRC ) { p->_thErrorMsg("Deque command failed."); continue; } switch(c.id ) { case kOpenCmdId: p->_thDoOpen(&c); break; case kCloseCmdId: p->_thDoClose(&c); break; case kSelectSeqCmdId: p->_thDoSelectSeq(&c); break; case kAfLoadCmdId: p->_thDoAfLoad(&c); break; case kScoreCmdId: p->_thDoScore(&c); break; case kGenOnsetMarksCmdId: p->_thDoGenOnsetMarks(&c); break; case kDelOnsetMarksCmdId: p->_thDoDelOnsetMarks(&c); break; default: break; } if( c.byteCnt ) cmMemFree(c.u.msg); } cmThreadPause( p->_thH, kPauseThFl ); return true; } //void cmdIfRptFunc( void* user, const cmChar_t* text ) //{ printf("%s",text); } void cmdIf::_thDoOpen( const cmd_t* cmd ) { _thStatusMsg("Loading: '%s'",cmd->u.string); if( cmTimeLineInitializeFromFile(_ctx,&_tlH,_thSendTimeLineMsg,this,cmd->u.string,_afPath) != kOkTlRC ) _thErrorMsg("Load failed on '%s'.",cmd->u.string); else { _curSeqId = 0; // Make notification callbacks for all time line records to _thSendTimeLineMsg() // _thSendTimeLineMsg() then enqueues msg's into _outQueH which are picked // up by the idle handler cmTimeLineSeqNotify(_tlH,_curSeqId); } _thSendResponse(kHideStatusCmdId); } void cmdIf::_thDoClose( const cmd_t* cmd ) { _thStatusMsg("Closing ... "); if( cmTimeLineFinalize(&_tlH) != kOkTlRC ) { _thErrorMsg("Time line finalize failed."); } _thSendResponse(kHideStatusCmdId); } void cmdIf::_thDoSelectSeq( const cmd_t* cmd ) { _thStatusMsg("Selecting Sequence:%i ",cmd->value); _curSeqId = cmInvalidId; if( cmTimeLineSeqNotify(_tlH,cmd->value) != kOkTlRC ) _thErrorMsg("Sequence selection failed."); else _curSeqId = cmd->value; _thSendResponse(kHideStatusCmdId); } void cmdIf::_thDoAfLoad( const cmd_t* cmd ) { _thStatusMsg("Loading Audio File: '%s'",cmd->u.string); cmAfmFileH_t afH = cmAfmFileNullHandle; if( cmAfmFileOpen(_afmH,&afH,cmd->u.string, cmd->value, NULL ) != kOkAfmRC ) _thErrorMsg("Audio file load failed on '%s'.",cmd->u.string); else { double msPerSummaryPt = 50.0; unsigned samplesPerSummaryPt = (unsigned)floor(cmAfmFileInfo(afH)->srate * msPerSummaryPt / 1000.0); if( cmAfmFileSummarize(afH,samplesPerSummaryPt) != kOkAfmRC ) _thErrorMsg("Audio file summarization failed on '%s'.",cmd->u.string); else _thSendResponse(kAfLoadCmdId,cmd->u.string,cmd->value); } _thSendResponse(kHideStatusCmdId); } void cmdIf::_thDoScore( const cmd_t* cmd ) { _thStatusMsg("Loading Score File: '%s'",cmd->u.string); if( cmScoreInitialize(_ctx,&_scH,cmd->u.string,0,NULL,0,_thSendScoreMsg,this,cmSymTblNullHandle) != kOkScRC ) _thErrorMsg("Score open failed on '%s'.",cmStringNullGuard(cmd->u.string)); else { // Make notification callbacks for all score records to _thSendScoreMsg() // _thSendScoreMsg() then enqueues msg's into _outQueH which are picked // up by the idle handler cmScoreSeqNotify(_scH); } _thSendResponse(kHideStatusCmdId); } void cmdIf::_thDoGenOnsetMarks( const cmd_t* cmd ) { if( !cmTimeLineIsValid(_tlH) ) return; _thStatusMsg("Generating Onset Markers."); // makes notification callbacks to _thSendTimeLineMsg() if( cmTimeLineGenOnsetMarks(_tlH,_curSeqId) != kOkTlRC ) _thErrorMsg("Onset marker generation failed."); else cmTimeLineSeqNotify(_tlH,_curSeqId); _thSendResponse(kHideStatusCmdId); } void cmdIf::_thDoDelOnsetMarks( const cmd_t* cmd ) { if( !cmTimeLineIsValid(_tlH) ) return; _thStatusMsg("Deleting Onset Markers."); // makes notification callbacks to _thSendTimeLineMsg() if( cmTimeLineDeleteOnsetMarks(_tlH,_curSeqId) != kOkTlRC ) _thErrorMsg("Onset marker deletion failed."); _thSendResponse(kHideStatusCmdId); } /* void cmdIf::_thErrorMsg( const char* fmt, va_list vl ) { const cmChar_t* s = cmTsVPrintf(fmt,vl); _thSendResponse(kErrMsgCmdId,s); cmTsFreeStr(s); } */ void cmdIf::_thErrorMsg( const char* fmt, ... ) { va_list vl,vl1; va_start(vl,fmt); va_copy(vl1,vl); int n = vsnprintf(NULL,0,fmt,vl); char b[n+1]; vsnprintf(b,n+1,fmt,vl1); _thSendResponse(kErrMsgCmdId,b); va_end(vl1); va_end(vl); } /* void cmdIf::_thStatusMsg( const char* fmt, va_list vl ) { const cmChar_t* s = cmTsVPrintf(fmt,vl); _thSendResponse(kShowStatusCmdId,s); cmTsFreeStr(s); } */ void cmdIf::_thStatusMsg( const char* fmt, ... ) { va_list vl,vl1; va_start(vl,fmt); va_copy(vl1,vl); int n = vsnprintf(NULL,0,fmt,vl); char b[n+1]; vsnprintf(b,n+1,fmt,vl1); _thSendResponse(kShowStatusCmdId,b); va_end(vl1); va_end(vl); } void cmdIf::_thSendResponse( cmdId_t id, const char* str, unsigned value ) { if( _enqueue( _outQueH, id, value, str, str==NULL ? 0 : strlen(str)+1 ) != kOkRC ) { _thErrorMsg("Response send failed."); } } void cmdIf::_thSendTimeLineMsg( void* arg, const void* msg, unsigned byteCnt ) { cmdIf* p = (cmdIf*)arg; if( cmTs1p1cIsValid( p->_outQueH ) ) if( p->_enqueue( p->_outQueH, kTimeLineMsgCmdId, 0, msg, byteCnt ) != kOkRC ) p->_thErrorMsg("Time Line enqueue failed on response queue."); } void cmdIf::_thSendScoreMsg( void* arg, const void* msg, unsigned byteCnt ) { cmdIf* p = (cmdIf*)arg; if( cmTs1p1cIsValid( p->_outQueH ) ) if( p->_enqueue( p->_outQueH, kScoreMsgCmdId, 0, msg, byteCnt ) != kOkRC ) p->_thErrorMsg("Score msg enqueue failed on response queue."); } //---------------------------------------------------------------------------------------- // Thread Independent Functions //---------------------------------------------------------------------------------------- cmdIf::rc_t cmdIf::_enqueue( cmTs1p1cH_t qH, cmdId_t id, unsigned value, const void* data, unsigned byteCnt ) { cmThRC_t thRC; rc_t rc = kOkRC; cmd_t c; assert( (byteCnt==0 && data==NULL) || (byteCnt!=0 && data!=NULL) ); c.id = id; c.byteCnt = byteCnt; c.value = value; c.u.msg = NULL; if( byteCnt ) { // memory allocated here must be deleted after the record is dequeued c.u.string = cmMemAlloc(char,byteCnt); memcpy(c.u.msg,data,byteCnt); } //printf("enq %s id:%i n:%i msg:%p\n", cmHandlesAreEqual(qH,_outQueH) ? "thr->app" : "app->thr", c.id,c.byteCnt,c.u.string); if((thRC = cmTs1p1cEnqueueMsg(qH,&c,sizeof(c))) != kOkThRC ) { if( byteCnt ) cmMemFree(c.u.string); rc = kCmdFailRC; } return rc; } //---------------------------------------------------------------------------------------- void cmdIf::_onTimeLineObjSelected( unsigned tlObjId ) { const cmTlObj_t* mop; if((mop = cmTimeLineIdToObj(_tlH,cmInvalidId,tlObjId)) == NULL ) { cmErrMsg(&_err,kCmdFailRC,"Unexpected invalid time line marker id '%i'.",tlObjId); return; } const cmTlAudioFile_t* afp = cmTimeLineAudioFileAtTime(_tlH, mop->seqId, mop->seqSmpIdx ); const cmTlMidiFile_t* mfp = cmTimeLineMidiFileAtTime( _tlH, mop->seqId, mop->seqSmpIdx ); const cmTlMidiEvt_t* mep = cmTimeLineMidiEvtAtTime( _tlH, mop->seqId, mop->seqSmpIdx ); if( afp != NULL ) printf("%s\n",afp->fn); if( mfp != NULL ) printf("%s : %i\n",mfp->fn,mop->seqSmpIdx); if( mep != NULL ) { if( mep->msg->status == kNoteOnMdId ) printf("%s : %i\n",cmMidiToSciPitch(mep->msg->u.chMsgPtr->d0,NULL,0),mep->obj.seqSmpIdx); else printf("midi:%i\n",mep->msg->status); } } //---------------------------------------------------------------------------------------- void cmdIf::testStub() { //ed_main(); //cmScAlignScanMarkers(_err.rpt, _tlH, _scH ); //sc_main(_scH,_tlH); cmScorePrintLoc(_scH); }