cwFlow* : Added MIDI input and output devices to 'flow' framework.

This commit is contained in:
kevin 2024-04-06 16:07:46 -04:00
parent c4d518db47
commit e49df496e6
8 changed files with 577 additions and 72 deletions

View File

@ -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; i<deviceN; ++i)
if( deviceA[i].typeId == kAudioDevTypeId )
{
if( deviceA[i].u.a.abuf == NULL )
{
rc = cwLogError(kInvalidArgRC,"The audio '%s' device does not have a valid audio buffer.",cwStringNullGuard(deviceA[i].devLabel));
goto errLabel;
}
else
if( deviceA[i].u.a.abuf->frameN != 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;

View File

@ -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;

View File

@ -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; i<devN; ++i)
if( devA[i].typeId == flow::kAudioDevTypeId )
devA[i].u.a.abuf = _clone_abuf( srcDevA[i].u.a.abuf );
{
switch( devA[i].typeId )
{
case flow::kAudioDevTypeId:
devA[i].u.a.abuf = _clone_abuf( srcDevA[i].u.a.abuf );
break;
case flow::kMidiDevTypeId:
devA[i].u.m = srcDevA[i].u.m;
break;
default:
break;
}
}
return devA;
}
@ -203,9 +219,20 @@ namespace cw
if( net->stateId == 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; j<p->deviceN; ++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; j<p->deviceN; ++j)
if( p->deviceA[j].typeId == flow::kAudioDevTypeId && cwIsFlag(p->deviceA[j].flags, flow::kOutFl ) )

View File

@ -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<inst_t>();
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,"<all>") )
{
inst->dev_filt_fl = false;
dev_label = nullptr;
}
if( textIsEqual(dev_label,"<all>") )
{
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<midi::ch_msg_t>( 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; i<inst->ext_dev->u.m.msgCnt && j<inst->bufN; ++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<inst_t>(); //
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; i<src_mbuf->msgN; ++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
};
}
//------------------------------------------------------------------------------------------------------------------
//

View File

@ -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; }

View File

@ -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: <null>");
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<mbuf_t*>( 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<fbuf_t*>( 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<mbuf_t>();
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; i<p->deviceN; ++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;

View File

@ -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)

View File

@ -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; i<midiDevN; ++i)
devN += midiDevicePortCount(p->ioH,i,true) + midiDevicePortCount(p->ioH,i,false);
for(unsigned i=0; i<p->audioGroupN; ++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; i<p->deviceN && di<serialDeviceCount(p->ioH); ++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; i<p->deviceN && di<midiDeviceCount(p->ioH); ++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; i<p->deviceN && di<socketCount(p->ioH); ++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; i<p->deviceN && di<midiDeviceCount(p->ioH); ++di)
{
// input port setup
for(unsigned pi=0; pi<midiDevicePortCount(p->ioH,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; pi<midiDevicePortCount(p->ioH,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; gi<p->audioGroupN; ++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; i<p->deviceN; ++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;
}