diff --git a/cwFlow.cpp b/cwFlow.cpp index 09a1e6e..8f61939 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -8,6 +8,8 @@ #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -24,6 +26,8 @@ namespace cw } library_t; library_t g_library[] = { + { "midi_in", &midi_in::members }, + { "midi_out", &midi_out::members }, { "audio_in", &audio_in::members }, { "audio_out", &audio_out::members }, { "audioFileIn", &audioFileIn::members }, @@ -292,7 +296,7 @@ namespace cw if( src_var->value == nullptr ) { - rc = cwLogError(kSyntaxErrorRC,"The source value is null on the connection input:%s %s source:%s %s .", in_inst->label, in_var_label, src_inst->label, suffix); + rc = cwLogError(kSyntaxErrorRC,"The source value is null on the connection input:'%s' %s source:'%s' '%s' .", in_inst->label, in_var_label, src_inst->label, suffix); goto errLabel; } @@ -1549,7 +1553,7 @@ void cw::flow::print_abuf( const abuf_t* abuf ) void cw::flow::print_external_device( const external_device_t* dev ) { - printf("Dev: %10s id:%3i type:%3i fl:0x%x : ", cwStringNullGuard(dev->label),dev->ioDevId,dev->typeId,dev->flags); + printf("Dev: %10s type:%3i fl:0x%x : ", cwStringNullGuard(dev->devLabel),dev->typeId,dev->flags); if( dev->typeId == kAudioDevTypeId ) print_abuf(dev->u.a.abuf); printf("\n"); @@ -1603,6 +1607,19 @@ cw::rc_t cw::flow::create( handle_t& hRef, goto errLabel; } + for(unsigned i=0; iframeN != p->framesPerCycle ) + cwLogWarning("The audio frame count (%i) for audio device '%s' does not match the Flow framesPerCycle (%i).",deviceA[i].u.a.abuf->frameN,p->framesPerCycle); + } + // print the class dict if( printClassDictFl ) class_dict_print( p ); @@ -1692,7 +1709,10 @@ cw::rc_t cw::flow::exec( handle_t h ) p->cycleIndex += 1; if( p->maxCycleCount > 0 && p->cycleIndex >= p->maxCycleCount ) + { + cwLogInfo("'maxCycleCnt' reached: %i. Shutting down flow.",p->maxCycleCount); break; + } } return rc; diff --git a/cwFlow.h b/cwFlow.h index d45df47..945840b 100644 --- a/cwFlow.h +++ b/cwFlow.h @@ -31,17 +31,38 @@ namespace cw // The audio_in/audio_out proc's locate and use these buffers. } audio_dev_cfg_t; + struct external_device_str; + + typedef rc_t (*send_midi_triple_func_t)( struct external_device_str* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 ); + + typedef struct midi_dev_cfg_str + { + // msgArray[] contains the current msgs for all devices NOT just the device that this record is embedded in. + // We do this so that the order of messages as they arrived is maintained. Otherwise, to achieve this ordering, + // the messages for all devices would need to be collected and sorted by time. + const midi::ch_msg_t* msgArray; + unsigned msgCnt; + + unsigned maxMsgCnt; // max possible value of msgCnt + send_midi_triple_func_t sendTripleFunc; + } midi_dev_cfg_t; + // Generate external device record typedef struct external_device_str { - const char* label; // IO framework device label - unsigned ioDevId; // IO framework device id - unsigned typeId; // see ???DevTypeId above - unsigned flags; // see ???Fl above + void* reserved; + const char* devLabel; // IO framework device label + const char* portLabel; // IO framework MIDI port label (only used by MIDI devices) + unsigned typeId; // see ???DevTypeId above + unsigned flags; // see ???Fl above + + unsigned ioDevIdx; // IO framework device index + unsigned ioPortIdx; // IO framework MIDI port index (only used by MIDI devices) union { - audio_dev_cfg_t a; // audio devices include this additional record + audio_dev_cfg_t a; // audio devices use this record + midi_dev_cfg_t m; // MIDI " " " " } u; } external_device_t; diff --git a/cwFlowCross.cpp b/cwFlowCross.cpp index 2839273..c47afc7 100644 --- a/cwFlowCross.cpp +++ b/cwFlowCross.cpp @@ -9,6 +9,8 @@ #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t #include "cwDspTransforms.h" +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -28,14 +30,15 @@ namespace cw kFadeOutStateId, }; + // Each duplicated network is represented by a flow_netword_t record in flow_cross_t.netA[]. typedef struct flow_network_str { dsp::recorder::obj_t* recorder; - flow::external_device_t* deviceA; + flow::external_device_t* deviceA; // deviceA[ deviceN ] - cloned exteranl device array unsigned deviceN; - flow::handle_t flowH; - + flow::handle_t flowH; + unsigned stateId; // inactive, fade-in, fade-out double fadeGain; // 0 0->1 1->0 unsigned fadeSmpN; // @@ -47,7 +50,7 @@ namespace cw typedef struct flow_cross_str { - unsigned cur_idx; + unsigned cur_idx; // index of the network currently receiving parameter updates double srate; unsigned netN; @@ -106,8 +109,21 @@ namespace cw memcpy(devA,srcDevA,devN * sizeof(flow::external_device_t)); for(unsigned i=0; istateId == kFadeOutStateId && ef == 0.0 ) net->stateId = kInactiveStateId; - } + // Copy audio from the actual external audio device to a cloned audio device + void _update_midi_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx ) + { + flow::midi_dev_cfg_t& src = p->deviceA[devIdx].u.m; // src MIDI device + flow::midi_dev_cfg_t& dst = net->deviceA[devIdx].u.m; // dst MIDI device clone + + // redirect the MIDI msg list array to the clones + dst.msgArray = src.msgArray; + dst.msgCnt = src.msgCnt; + } + + // Copy audio from the actual external audio device to a cloned audio device void _update_audio_input( flow_cross_t* p, flow_network_t* net, unsigned devIdx ) { flow::abuf_t* src = p->deviceA[devIdx].u.a.abuf; @@ -216,6 +243,7 @@ namespace cw //_fade_audio( src, dst, net ); } + void _zero_audio_output( flow_cross_t* p, flow_network_t* net, unsigned devIdx ) { flow::abuf_t* dst = net->deviceA[devIdx].u.a.abuf; @@ -378,13 +406,27 @@ cw::rc_t cw::flow_cross::exec_cycle( handle_t h ) { flow_network_t* net = p->netA + i; - // We generally don't want to fade the input because the state - // of the network delay lines would then be invalid when the - // network is eventually made active again for(unsigned j=0; jdeviceN; ++j) - if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kInFl ) ) - _update_audio_input( p, p->netA + i, j ); + if( cwIsFlag(p->deviceA[j].flags, flow::kInFl ) ) + { + switch( p->deviceA[j].typeId) + { + case flow::kAudioDevTypeId: + // We generally don't want to fade the input because the state + // of the network delay lines would then be invalid when the + // network is eventually made active again + + // copy audio from the actual audio device to the cloned audio devices + _update_audio_input( p, p->netA + i, j ); + break; + case flow::kMidiDevTypeId: + // update the cloned MIDI devices from the master device + _update_midi_input( p, p->netA + i, j ); + + } + } + // zero the audio device output buffers because we are about to sum into them for(unsigned j=0; jdeviceN; ++j) if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) ) diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index 0e25254..5eff84e 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -2,12 +2,18 @@ #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" +#include "cwText.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t + +#include "cwTime.h" +#include "cwMidiDecls.h" + + #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -164,6 +170,251 @@ namespace cw } + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_in + // + + namespace midi_in + { + enum + { + kDevLabelPId, + kPortLabelPId, + kOutPId + }; + + typedef struct + { + midi::ch_msg_t* buf; + unsigned bufN; + bool dev_filt_fl; + bool port_filt_fl; + external_device_t* ext_dev; + } inst_t; + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; + const char* dev_label = nullptr; + const char* port_label = nullptr; + inst_t* inst = mem::allocZ(); + + ctx->userPtr = inst; + + // Register variable and get their current value + if((rc = var_register_and_get( ctx, kAnyChIdx, + kDevLabelPId, "dev_label", dev_label, + kPortLabelPId, "port_label", port_label )) != kOkRC ) + + { + goto errLabel; + } + + if((rc = var_register( ctx, kAnyChIdx,kOutPId, "out")) != kOkRC ) + { + goto errLabel; + } + + + inst->dev_filt_fl = true; + inst->port_filt_fl = true; + + if( textIsEqual(dev_label,"") ) + { + inst->dev_filt_fl = false; + dev_label = nullptr; + } + + if( textIsEqual(dev_label,"") ) + { + inst->port_filt_fl = false; + port_label = nullptr; + } + + + + if((inst->ext_dev = external_device_find( ctx->ctx, dev_label, kMidiDevTypeId, kInFl, port_label )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The MIDI input device '%s' port '%s' could not be found.", cwStringNullGuard(dev_label), cwStringNullGuard(port_label)); + goto errLabel; + } + + // Allocate a buffer large enough to hold the max. number of messages arriving on a single call to exec(). + inst->bufN = inst->ext_dev->u.m.maxMsgCnt; + inst->buf = mem::allocZ( inst->bufN ); + + // create one output audio buffer + rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, nullptr, 0 ); + + + errLabel: + return rc; + } + + rc_t destroy( instance_t* ctx ) + { + rc_t rc = kOkRC; + + inst_t* inst = (inst_t*)ctx->userPtr; + mem::release(inst->buf); + + mem::release(inst); + + return rc; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { return kOkRC; } + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + + inst_t* inst = (inst_t*)ctx->userPtr; + mbuf_t* mbuf = nullptr; + + + // get the output variable + if((rc = var_get(ctx,kOutPId,kAnyChIdx,mbuf)) != kOkRC ) + { + rc = cwLogError(kInvalidStateRC,"The MIDI file instance '%s' does not have a valid MIDI output buffer.",ctx->label); + } + else + { + // if the device filter is not set + if( !inst->dev_filt_fl) + { + mbuf->msgA = inst->ext_dev->u.m.msgArray; + mbuf->msgN = inst->ext_dev->u.m.msgCnt; + } + else // the device filter is set + { + const midi::ch_msg_t* m = inst->ext_dev->u.m.msgArray; + unsigned j = 0; + for(unsigned i=0; iext_dev->u.m.msgCnt && jbufN; ++i) + if( m->devIdx == inst->ext_dev->ioDevIdx && (!inst->port_filt_fl || m->portIdx == inst->ext_dev->ioPortIdx) ) + inst->buf[j++] = m[i]; + + mbuf->msgN = j; + mbuf->msgA = inst->buf; + } + + + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + //------------------------------------------------------------------------------------------------------------------ + // + // midi_out + // + + namespace midi_out + { + enum + { + kInPId, + kDevLabelPId, + kPortLabelPId + }; + + typedef struct + { + external_device_t* ext_dev; + } inst_t; + + rc_t create( instance_t* ctx ) + { + rc_t rc = kOkRC; // + inst_t* inst = mem::allocZ(); // + const char* dev_label = nullptr; + const char* port_label = nullptr; + mbuf_t* mbuf = nullptr; + + ctx->userPtr = inst; + + // Register variables and get their current value + if((rc = var_register_and_get( ctx, kAnyChIdx, + kDevLabelPId, "dev_label", dev_label, + kPortLabelPId,"port_label", port_label, + kInPId, "in", mbuf)) != kOkRC ) + { + goto errLabel; + } + + if((inst->ext_dev = external_device_find( ctx->ctx, dev_label, kMidiDevTypeId, kOutFl, port_label )) == nullptr ) + { + rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(dev_label)); + goto errLabel; + } + + errLabel: + return rc; + } + + rc_t destroy( instance_t* ctx ) + { + rc_t rc = kOkRC; + inst_t* inst = (inst_t*)ctx->userPtr; + + mem::release(inst); + + return rc; + } + + rc_t value( instance_t* ctx, variable_t* var ) + { + rc_t rc = kOkRC; + return rc; + } + + rc_t exec( instance_t* ctx ) + { + rc_t rc = kOkRC; + inst_t* inst = (inst_t*)ctx->userPtr; + const mbuf_t* src_mbuf = nullptr; + + if((rc = var_get(ctx,kInPId,kAnyChIdx,src_mbuf)) != kOkRC ) + rc = cwLogError(kInvalidStateRC,"The MIDI output instance '%s' does not have a valid input connection.",ctx->label); + else + { + for(unsigned i=0; imsgN; ++i) + { + const midi::ch_msg_t* m = src_mbuf->msgA + i; + inst->ext_dev->u.m.sendTripleFunc( inst->ext_dev, m->ch, m->status, m->d0, m->d1 ); + } + } + + return rc; + } + + class_members_t members = { + .create = create, + .destroy = destroy, + .value = value, + .exec = exec, + .report = nullptr + }; + + } + + + + //------------------------------------------------------------------------------------------------------------------ // diff --git a/cwFlowProc.h b/cwFlowProc.h index 15fee86..026bbed 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -2,6 +2,8 @@ namespace cw { namespace flow { + namespace midi_in { extern class_members_t members; } + namespace midi_out { extern class_members_t members; } namespace audio_in { extern class_members_t members; } namespace audio_out { extern class_members_t members; } namespace audioFileIn { extern class_members_t members; } diff --git a/cwFlowTypes.cpp b/cwFlowTypes.cpp index 02ccf1f..477b5f1 100644 --- a/cwFlowTypes.cpp +++ b/cwFlowTypes.cpp @@ -7,6 +7,8 @@ #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwFlowDecl.h" #include "cwFlow.h" #include "cwFlowTypes.h" @@ -18,21 +20,22 @@ namespace cw { idLabelPair_t typeLabelFlagsA[] = { - { kBoolTFl, "bool" }, - { kUIntTFl, "uint" }, - { kIntTFl, "int", }, - { kFloatTFl, "float"}, - { kRealTFl, "real"}, - { kDoubleTFl, "double"}, + { kBoolTFl, "bool" }, + { kUIntTFl, "uint" }, + { kIntTFl, "int", }, + { kFloatTFl, "float"}, + { kRealTFl, "real"}, + { kDoubleTFl,"double"}, - { kBoolMtxTFl, "bool_mtx" }, - { kUIntMtxTFl, "uint_mtx" }, - { kIntMtxTFl, "int_mtx" }, - { kFloatMtxTFl, "float_mtx" }, - { kDoubleMtxTFl, "double_mtx" }, + { kBoolMtxTFl, "bool_mtx" }, + { kUIntMtxTFl, "uint_mtx" }, + { kIntMtxTFl, "int_mtx" }, + { kFloatMtxTFl, "float_mtx" }, + { kDoubleMtxTFl,"double_mtx" }, { kABufTFl, "audio" }, { kFBufTFl, "spectrum" }, + { kMBufTFl, "midi" }, { kStringTFl, "string" }, { kTimeTFl, "time" }, { kInvalidTFl, nullptr } @@ -73,6 +76,9 @@ namespace cw fbuf_destroy( v->u.fbuf ); break; + case kMBufTFl: + mbuf_destroy( v->u.mbuf ); + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -99,8 +105,7 @@ namespace cw } void _value_duplicate( value_t& dst, const value_t& src ) - { - + { switch( src.flags & kTypeMask ) { case kInvalidTFl: @@ -125,6 +130,10 @@ namespace cw dst.flags = src.flags; break; + case kMBufTFl: + dst.u.mbuf = src.u.mbuf == nullptr ? nullptr : mbuf_duplicate(src.u.mbuf); + dst.flags = src.flags; + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -182,6 +191,15 @@ namespace cw printf("(binN:%i hopSmpN:%i) ", v->u.fbuf->binN_V[i], v->u.fbuf->hopSmpN_V[i] ); } break; + + case kMBufTFl: + if( v->u.mbuf == nullptr ) + printf("mbuf: "); + else + { + printf("mbuf: cnt: %i", v->u.mbuf->msgN ); + } + break; case kBoolMtxTFl: case kUIntMtxTFl: @@ -345,6 +363,28 @@ namespace cw return rc; } + rc_t _val_get( value_t* val, mbuf_t*& valRef ) + { + rc_t rc = kOkRC; + if( cwIsFlag(val->flags & kTypeMask, kMBufTFl) ) + valRef = val->u.mbuf; + else + { + valRef = nullptr; + rc = cwLogError(kTypeMismatchRC,"The type 0x%x could not be converted to an mbuf_t.",val->flags); + } + return rc; + } + + rc_t _val_get( value_t* val, const mbuf_t*& valRef ) + { + mbuf_t* non_const_val; + rc_t rc = kOkRC; + if((rc = _val_get(val,non_const_val)) == kOkRC ) + valRef = non_const_val; + return rc; + } + template< typename T > rc_t _val_get_driver( const variable_t* var, T& valRef ) @@ -492,6 +532,14 @@ namespace cw var->local_value[ local_value_idx ].flags = kABufTFl; cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,abuf==nullptr ? "null" : "valid"); } + + template<> + void _var_setter( variable_t* var, unsigned local_value_idx, mbuf_t* val ) + { + var->local_value[ local_value_idx ].u.mbuf = val; + var->local_value[ local_value_idx ].flags = kMBufTFl; + cwLogMod("%s.%s ch:%i %s (abuf).",var->inst->label,var->label,var->chIdx,mbuf==nullptr ? "null" : "valid"); + } template<> void _var_setter( variable_t* var, unsigned local_value_idx, fbuf_t* val ) @@ -609,6 +657,19 @@ namespace cw return rc; } + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, mbuf_t* mbuf ) + { + rc_t rc; + variable_t* var = nullptr; + if((rc = var_register_and_set( inst, var_label, vid, chIdx, var)) != kOkRC ) + return rc; + + if( var != nullptr ) + _var_set_driver( var, kMBufTFl, mbuf ); + + return rc; + } + rc_t _var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, fbuf_t* fbuf ) { rc_t rc; @@ -1010,6 +1071,25 @@ cw::flow::fbuf_t* cw::flow::fbuf_duplicate( const fbuf_t* src ) } +cw::flow::mbuf_t* cw::flow::mbuf_create( const midi::ch_msg_t* msgA, unsigned msgN ) +{ + mbuf_t* m = mem::allocZ(); + m->msgA = msgA; + m->msgN = msgN; + return m; +} + +void cw::flow::mbuf_destroy( mbuf_t*& buf ) +{ + mem::release(buf); +} + +cw::flow::mbuf_t* cw::flow::mbuf_duplicate( const mbuf_t* src ) +{ + return mbuf_create(src->msgA,src->msgN); +} + + unsigned cw::flow::value_type_label_to_flag( const char* s ) { unsigned flags = labelToId(typeLabelFlagsA,s,kInvalidTFl); @@ -1089,10 +1169,13 @@ cw::rc_t cw::flow::instance_find( flow_t* p, const char* inst_label, instance_t* return cwLogError(kInvalidArgRC,"The instance '%s' was not found.", inst_label ); } -cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl ) +cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel ) { for(unsigned i=0; ideviceN; ++i) - if( cw::textIsEqual(p->deviceA[i].label,device_label) && p->deviceA[i].typeId==typeId && cwIsFlag(p->deviceA[i].flags,inOrOutFl )) + if( (device_label==nullptr || cw::textIsEqual(p->deviceA[i].devLabel,device_label)) + && p->deviceA[i].typeId==typeId + && cwIsFlag(p->deviceA[i].flags,inOrOutFl) + && (midiPortLabel==nullptr || cw::textIsEqual(p->deviceA[i].portLabel,midiPortLabel)) ) return p->deviceA + i; cwLogError(kInvalidArgRC,"The %s device named '%s' could not be found.", cwIsFlag(inOrOutFl,kInFl) ? "in" : "out", device_label ); @@ -1381,6 +1464,21 @@ cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label return rc; } +cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, midi::ch_msg_t* msgA, unsigned msgN ) +{ + rc_t rc = kOkRC; + mbuf_t* mbuf; + + if((mbuf = mbuf_create(msgA,msgN)) == nullptr ) + return cwLogError(kOpFailRC,"mbuf create failed on instance:'%s' variable:'%s'.", inst->label, var_label); + + if((rc = _var_register_and_set( inst, var_label, vid, chIdx, mbuf )) != kOkRC ) + mbuf_destroy(mbuf); + + return rc; +} + + cw::rc_t cw::flow::var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV, const fd_real_t** phsV, const fd_real_t** hzV ) { unsigned maxBinN_V[ chN ]; @@ -1423,6 +1521,13 @@ cw::rc_t cw::flow::var_get( const variable_t* var, const fbuf_t*& valRef ) cw::rc_t cw::flow::var_get( variable_t* var, fbuf_t*& valRef ) { return _val_get_driver(var,valRef); } +cw::rc_t cw::flow::var_get( const variable_t* var, const mbuf_t*& valRef ) +{ return _val_get_driver(var,valRef); } + +cw::rc_t cw::flow::var_get( variable_t* var, mbuf_t*& valRef ) +{ return _val_get_driver(var,valRef); } + + cw::rc_t cw::flow::var_set( instance_t* inst, unsigned vid, unsigned chIdx, bool val ) { rc_t rc = kOkRC; diff --git a/cwFlowTypes.h b/cwFlowTypes.h index eb1ffbf..0fe4ce5 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -4,16 +4,22 @@ namespace cw { #define kRealTFl kFloatTFl - typedef dsp::real_t real_t; - typedef dsp::sample_t sample_t; + typedef dsp::real_t real_t; + typedef dsp::sample_t sample_t; typedef dsp::fd_real_t fd_real_t; - typedef dsp::srate_t srate_t; - typedef unsigned uint_t; - typedef int int_t; + typedef dsp::srate_t srate_t; + typedef unsigned uint_t; + typedef int int_t; typedef unsigned vid_t; + enum { + kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz) + kAnyChIdx = kInvalidIdx, + kLocalValueN = 2 + }; + typedef struct abuf_str { struct value_str* base; @@ -24,12 +30,6 @@ namespace cw } abuf_t; - enum { - kFbufVectN = 3, // count of signal vectors in fbuf (mag,phs,hz) - kAnyChIdx = kInvalidIdx, - kLocalValueN = 2 - }; - typedef struct fbuf_str { struct value_str* base; @@ -39,13 +39,20 @@ namespace cw unsigned* maxBinN_V; // max value that binN_V[i] is allowed to take unsigned* binN_V; // binN_V[ chN ] count of sample frames per channel unsigned* hopSmpN_V; // hopSmpN_V[ chN ] hop sample count - fd_real_t** magV; // magV[ chN ][ binN ] - fd_real_t** phsV; // phsV[ chN ][ binN ] - fd_real_t** hzV; // hzV[ chN ][ binN ] + fd_real_t** magV; // magV[ chN ][ binN ] + fd_real_t** phsV; // phsV[ chN ][ binN ] + fd_real_t** hzV; // hzV[ chN ][ binN ] bool* readyFlV; // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate) - fd_real_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer) + fd_real_t* buf; // memory used by this buffer (or NULL if magV,phsV,hzV point are proxied to another buffer) } fbuf_t; + typedef struct mbuf_str + { + struct value_str* base; + const midi::ch_msg_t* msgA; + unsigned msgN; + } mbuf_t; + enum { kInvalidTFl = 0x00000000, @@ -64,10 +71,11 @@ namespace cw kABufTFl = 0x00000800, kFBufTFl = 0x00001000, - kStringTFl = 0x00002000, - kTimeTFl = 0x00004000, + kMBufTFl = 0x00002000, + kStringTFl = 0x00004000, + kTimeTFl = 0x00008000, - kTypeMask = 0x00007fff, + kTypeMask = 0x0000ffff, }; @@ -96,6 +104,7 @@ namespace cw abuf_t* abuf; fbuf_t* fbuf; + mbuf_t* mbuf; char* s; char* fname; @@ -115,11 +124,11 @@ namespace cw typedef rc_t (*member_func_t)( struct instance_str* ctx ); typedef rc_t (*member_value_func_t)( struct instance_str* ctx, struct variable_str* var ); + enum { - kSrcVarFl = 0x01, - kSrcOptVarFl = 0x02 - + kSrcVarFl = 0x01, + kSrcOptVarFl = 0x02 }; typedef struct class_members_str @@ -242,6 +251,10 @@ namespace cw fbuf_t* fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); void fbuf_destroy( fbuf_t*& buf ); fbuf_t* fbuf_duplicate( const fbuf_t* src ); + + mbuf_t* mbuf_create( const midi::ch_msg_t* msgA=nullptr, unsigned msgN=0 ); + void mbuf_destroy( mbuf_t*& buf ); + mbuf_t* mbuf_duplicate( const mbuf_t* src ); inline bool value_is_abuf( const value_t* v ) { return v->flags & kABufTFl; } inline bool value_is_fbuf( const value_t* v ) { return v->flags & kFBufTFl; } @@ -274,7 +287,7 @@ namespace cw instance_t* instance_find( flow_t* p, const char* inst_label ); rc_t instance_find( flow_t* p, const char* inst_label, instance_t*& instPtrRef ); - external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl ); + external_device_t* external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel=nullptr ); void instance_print( instance_t* inst ); @@ -374,6 +387,7 @@ namespace cw rc_t var_register_and_set( instance_t* inst, const char* label, unsigned vid, unsigned chIdx, variable_t*& varRef ); rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN ); + rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, midi::ch_msg_t* midiA, unsigned midiN ); rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); rc_t var_register_and_set( instance_t* inst, const char* var_label, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_real_t** magV=nullptr, const fd_real_t** phsV=nullptr, const fd_real_t** hzV=nullptr ); @@ -427,6 +441,8 @@ namespace cw rc_t var_get( variable_t* var, abuf_t*& valRef ); rc_t var_get( const variable_t* var, const fbuf_t*& valRef ); rc_t var_get( variable_t* var, fbuf_t*& valRef ); + rc_t var_get( const variable_t* var, const mbuf_t*& valRef ); + rc_t var_get( variable_t* var, mbuf_t*& valRef ); template< typename T> rc_t var_get( instance_t* inst, unsigned vid, unsigned chIdx, T& valRef) diff --git a/cwIoFlow.cpp b/cwIoFlow.cpp index 7d3bf79..2ac38b1 100644 --- a/cwIoFlow.cpp +++ b/cwIoFlow.cpp @@ -8,6 +8,8 @@ #include "cwTime.h" #include "cwVectOps.h" #include "cwMtx.h" +#include "cwTime.h" +#include "cwMidiDecls.h" #include "cwDspTypes.h" #include "cwFlowDecl.h" @@ -87,7 +89,7 @@ namespace cw mem::release(p->audioGroupA); mem::release(p); - + return kOkRC; } @@ -95,10 +97,13 @@ namespace cw { unsigned devN = 0; - //devN += midiDeviceCount(p->ioH); devN += socketCount(p->ioH); devN += serialDeviceCount(p->ioH); + unsigned midiDevN = midiDeviceCount(p->ioH); + for(unsigned i=0; iioH,i,true) + midiDevicePortCount(p->ioH,i,false); + for(unsigned i=0; iaudioGroupN; ++i) devN += p->audioGroupA[i].iDeviceN + p->audioGroupA[i].oDeviceN; @@ -146,23 +151,38 @@ namespace cw } } - - void _setup_device_cfg( flow::external_device_t* d, const char* devLabel, unsigned ioDevId, unsigned typeId, unsigned flags ) + rc_t _send_midi_triple( flow::external_device_t* dev, uint8_t ch, uint8_t status, uint8_t d0, uint8_t d1 ) { - d->label = devLabel; - d->ioDevId = ioDevId; - d->typeId = typeId; - d->flags = flags; + return midiDeviceSend(((io_flow_t*)dev->reserved)->ioH, dev->ioDevIdx, dev->ioPortIdx, status |= ch, d0, d1); } + void _setup_device_cfg( io_flow_t* p, flow::external_device_t* d, const char* devLabel, unsigned ioDevIdx, unsigned typeId, unsigned flags, const char* midiPortLabel=nullptr, unsigned midiPortIdx=kInvalidIdx ) + { + d->reserved = p; + d->devLabel = devLabel; + d->portLabel = midiPortLabel; + d->typeId = typeId; + d->flags = flags; + d->ioDevIdx = ioDevIdx; + d->ioPortIdx = midiPortIdx; + } + + void _setup_midi_device_cfg( io_flow_t* p, flow::external_device_t* d, const char* devLabel, unsigned ioDevIdx, unsigned flags, unsigned ioMidiPortIdx ) + { + const char* midiPortLabel = io::midiDevicePortName(p->ioH,ioDevIdx, flags & flow::kInFl ? true : false,ioMidiPortIdx); + _setup_device_cfg( p, d, devLabel, ioDevIdx, flow::kMidiDevTypeId, flags, midiPortLabel, ioMidiPortIdx ); + d->u.m.maxMsgCnt = io::midiDeviceMaxBufferMsgCount(p->ioH); + d->u.m.sendTripleFunc = _send_midi_triple; + } + void _setup_audio_device_cfg( io_flow_t* p, flow::external_device_t* d, audio_group_t* ag, audio_dev_t* ad, unsigned flags ) { - _setup_device_cfg( d, io::audioDeviceLabel(p->ioH,ad->ioDevIdx), ad->ioDevId, flow::kAudioDevTypeId, flags ); + _setup_device_cfg( p, d, io::audioDeviceLabel(p->ioH,ad->ioDevIdx), ad->ioDevIdx, flow::kAudioDevTypeId, flags ); // Each audio device is given a flow::abuf to hold incoming or outgoing audio. // This buffer also allows the 'audio_in' and 'audio_out' flow procs to configure themselves. d->u.a.abuf = &ad->abuf; - } + } void _setup_generic_device_array( io_flow_t* p ) { @@ -175,17 +195,27 @@ namespace cw // get serial devices for(unsigned di=0; ideviceN && diioH); ++di,++i) - _setup_device_cfg( p->deviceA + i, io::serialDeviceLabel(p->ioH,di), io::serialDeviceId(p->ioH,di), flow::kSerialDevTypeId, flow::kInFl | flow::kOutFl ); - - // get midi devices - //for(unsigned di=0; ideviceN && diioH); ++di,++i) - // _setup_device_cfg( p->deviceA + i, io::midiDeviceLabel(p->ioH,di), di, flow::kMidiDevTypeId, flow::kInFl | flow::kOutFl ); + _setup_device_cfg( p, p->deviceA + i, io::serialDeviceLabel(p->ioH,di), di, flow::kSerialDevTypeId, flow::kInFl | flow::kOutFl ); // get sockets for(unsigned di=0; ideviceN && diioH); ++di,++i) - _setup_device_cfg( p->deviceA + i, io::socketLabel(p->ioH,di), io::socketUserId(p->ioH,di), flow::kSocketDevTypeId, flow::kInFl | flow::kOutFl ); + _setup_device_cfg( p, p->deviceA + i, io::socketLabel(p->ioH,di), di, flow::kSocketDevTypeId, flow::kInFl | flow::kOutFl ); + // get midi devices + for(unsigned di=0; ideviceN && diioH); ++di) + { + // input port setup + for(unsigned pi=0; piioH,di,true); ++pi,++i) + _setup_midi_device_cfg( p, p->deviceA + i, io::midiDeviceName(p->ioH,di), di, flow::kInFl, pi); + + // output port setup + for(unsigned pi=0; piioH,di,false); ++pi,++i) + _setup_midi_device_cfg( p, p->deviceA + i, io::midiDeviceName(p->ioH,di), di, flow::kOutFl, pi); + + } + + // get the audio devices for(unsigned gi=0; giaudioGroupN; ++gi) { @@ -198,6 +228,9 @@ namespace cw _setup_audio_device_cfg( p, p->deviceA + i, ag, ag->oDeviceA + di, flow::kOutFl ); } + + assert( i == p->deviceN ); + } rc_t _device_index_to_abuf( io_flow_t* p, unsigned ioGroupIdx, unsigned ioDevIdx, unsigned inOrOutFl, flow::abuf_t*& abuf_ref ) @@ -254,6 +287,18 @@ namespace cw rc_t rc = kOkRC; flow::abuf_t* abuf = nullptr; + // Get an array of incoming MIDI events which have occurred since the last call to 'io::midiDeviceBuffer()' + unsigned midiBufMsgCnt = 0; + const midi::ch_msg_t* midiBuf = midiDeviceBuffer(p->ioH,midiBufMsgCnt); + + // Give each MIDI input device a pointer to the incoming MIDI msgs + for(unsigned i=0; ideviceN; ++i) + if( p->deviceA[i].typeId == flow::kMidiDevTypeId && cwIsFlag(p->deviceA[i].flags,flow::kInFl) ) + { + p->deviceA[i].u.m.msgArray = midiBuf; + p->deviceA[i].u.m.msgCnt = midiBufMsgCnt; + } + // if there is incoming (recorded) audio if( m.iBufChCnt > 0 ) { @@ -318,6 +363,9 @@ namespace cw } errLabel: + // Drop the MIDI messages that were processed on this call. + midiDeviceClearBuffer(p->ioH,midiBufMsgCnt); + return rc; }