Skip to content
Permalink
dev
Go to file
 
 
Cannot retrieve contributors at this time
1505 lines (1283 sloc) 35.4 KB
/* net.c
* bees
* aleph
*
* definition of a network of control operators
* and points of IO connection between them
*/
// std
#include <stdio.h>
#include <string.h>
// asf
#include "print_funcs.h"
#include "delay.h"
// aleph-avr32
#include "app.h"
#include "bfin.h"
#include "control.h"
#include "memory.h"
#include "types.h"
// bees
#include "net.h"
#include "net_protected.h"
#include "op.h"
#include "op_derived.h"
#include "op_gfx.h"
#include "pages.h"
#include "param.h"
#include "play.h"
#include "preset.h"
#include "util.h"
#include "op_pool.h"
//=========================================
//===== constants
// define for serialization debugging
// #define PRINT_PICKLE 1
//=========================================
//===== variables
//----- static
// when unset, node activation will not propagate
static u8 netActive = 0;
//---- external
ctlnet_t* net;
// pointers to system-created ops
// encoders
op_enc_t* opSysEnc[4] = { NULL, NULL, NULL, NULL };
// function keys and footswitches
op_sw_t* opSysSw[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
// adc
op_adc_t* opSysAdc = NULL;
// preset
op_preset_t* opSysPreset = NULL;
static const char emptystring[] = " ";
//===============================================
//========= static functions
/// stupid hack function to identify switch input
/// returns switch index in [1, numSwitches]
/// otherwise 0
/// FIXME: obviously this is magic # bs
static inline int in_get_switch_index(s16 in) {
if(in > 3 && in < 10) {
return in - 3;
} else {
return 0;
}
}
// create all system operators
static void add_sys_ops(void);
static void update_sys_op_pointers(void);
static void add_sys_ops(void) {
/// FIXME:
/* dangerous for scene storage,
will break in the unlikely event that op pool is assigned differently.
should either:
a) reassign these pointers after unpickling
probably by index like the old hack, or
b) don't pickle system ops at all, only their inputs.
still needs to make a fixed assumption about order.
... i dunno
*/
// 4 encoders
net_add_op(eOpEnc);
net_add_op(eOpEnc);
net_add_op(eOpEnc);
net_add_op(eOpEnc);
// 4 function switches
net_add_op(eOpSwitch);
net_add_op(eOpSwitch);
net_add_op(eOpSwitch);
net_add_op(eOpSwitch);
// 2 footswitches
net_add_op(eOpSwitch);
net_add_op(eOpSwitch);
// 1 adc
net_add_op(eOpAdc);
// 1 preset receiver
net_add_op(eOpPreset);
update_sys_op_pointers();
}
static void update_sys_op_pointers(void) {
// 4 encoders
opSysEnc[0] = (op_enc_t*)net->ops[0];
opSysEnc[1] = (op_enc_t*)net->ops[1];
opSysEnc[2] = (op_enc_t*)net->ops[2];
opSysEnc[3] = (op_enc_t*)net->ops[3];
// 4 function switches
opSysSw[0] = (op_sw_t*)net->ops[4];
opSysSw[1] = (op_sw_t*)net->ops[5];
opSysSw[2] = (op_sw_t*)net->ops[6];
opSysSw[3] = (op_sw_t*)net->ops[7];
opSysSw[4] = (op_sw_t*)net->ops[8];
opSysSw[5] = (op_sw_t*)net->ops[9];
opSysAdc = (op_adc_t*)net->ops[10];
opSysPreset = (op_preset_t*)net->ops[11];
}
///----- node pickling
static u8* onode_pickle(onode_t* out, u8* dst) {
// target
dst = pickle_32((u32)(out->target), dst);
return dst;
}
static const u8* onode_unpickle(const u8* src, onode_t* out) {
u32 v32;
// output target
src = unpickle_32(src, &v32);
out->target = (s16)v32;
#ifdef PRINT_PICKLE
print_dbg(" ; opIdx: ");
print_dbg_ulong(out->opIdx);
print_dbg(" ; opOutIdx: ");
print_dbg_ulong(out->opOutIdx);
print_dbg(" ; target: ");
print_dbg_ulong(out->target);
#endif
return src;
}
static u8* inode_pickle(inode_t* in, u8* dst) {
/// don't need to pickle indices because we recreate the op list from scratch
#ifdef PRINT_PICKLE
print_dbg("\r\n pickling input node, op index: ");
print_dbg_ulong(in->opIdx);
print_dbg(" , input idx: ");
print_dbg_ulong(in->opInIdx);
print_dbg(" , play flag: ");
print_dbg_ulong(in->play);
#endif
// play inclusion flag
*dst++ = in->play;
// dummy byte for alignment
*dst++ = 0;
// dummy byte for alignment
*dst++ = 0;
// dummy byte for alignment
*dst++ = 0;
return dst;
}
static const u8* inode_unpickle(const u8* src, inode_t* in) {
/// don't need to pickle indices because we recreate the op list from scratch
// only need these flags:
// in->preset = *src++;
// play inclusion flag
in->play = *src++;
#ifdef PRINT_PICKLE
print_dbg(" ; opIdx: ");
print_dbg_ulong(in->opIdx);
print_dbg(" ; opInIdx: ");
print_dbg_ulong(in->opInIdx);
print_dbg("; got flag: ");
print_dbg_ulong(in->play);
#endif
// dummy byte for alignment
++src;
// dummy byte for alignment
++src;
// dummy byte for alignment
++src;
return src;
}
//==================================================
//========= public functions
// initialize network at pre-allocated memory
void net_init(void) {
u32 i;
net = (ctlnet_t*)alloc_mem(sizeof(ctlnet_t));
net->numOps = 0;
net->numIns = 0;
net->numOuts = 0;
net->numParams = 0;
// unassign all I/O nodes
for(i=0; i<NET_INS_MAX; i++) {
net_init_inode(i);
}
for(i=0; i<NET_OUTS_MAX; i++) {
net_init_onode(i);
}
print_dbg("\r\n initialized ctlnet, byte count: ");
print_dbg_hex(sizeof(ctlnet_t));
add_sys_ops();
// ???
netActive = 1;
}
// de-initialize network
void net_deinit(void) {
u32 i;
print_dbg("\r\n deinitializing network");
for(i=0; i<net->numOps; i++) {
op_deinit(net->ops[i]);
freeOp((u8*)net->ops[i]);
}
print_dbg("\r\n finished de-initializing network");
net->numOps = 0;
net->numIns = 0;
net->numOuts = 0;
net->numParams = 0;
// unassign all I/O nodes
for(i=0; i<NET_INS_MAX; i++) {
net_init_inode(i);
}
for(i=0; i<NET_OUTS_MAX; i++) {
net_init_onode(i);
}
// make sure to get out of op-graphics mode
op_gfx_reset();
}
// clear ops and i/o
void net_clear_user_ops(void) {
/// no... this seems wrong.
net_deinit();
add_sys_ops();
}
// initialize an input node
void net_init_inode(u16 idx) {
net->ins[idx].opIdx = -1;
net->ins[idx].play = 0;
}
// initialize an output node
void net_init_onode(u16 idx) {
net->outs[idx].opIdx = -1;
net->outs[idx].target = -1;
}
#ifndef PD
// activate an input node with a value
void net_activate(void *op_void, s16 outIdx, const io_t val) {
static inode_t* pIn;
s16 pIndex;
u8 visIn, visOut;
op_t *op = (op_t *)op_void;
s16 inIdx = op->out[outIdx];
/* print_dbg("\r\n net_activate, input idx: "); */
/* print_dbg_hex(inIdx); */
/* print_dbg(" , value: "); */
/* print_dbg_hex(val); */
/* print_dbg(" , op index: "); */
/* print_dbg_ulong(net->ins[inIdx].opIdx); */
/* print_dbg(" , input idx: "); */
/* print_dbg_ulong(net->ins[inIdx].opInIdx); */
if(!netActive) {
if(op != NULL) {
// if the net isn't active, dont respond to requests from operators
print_dbg(" ... ignoring node activation from op.");
return;
}
}
if(outIdx < MAX_PLAY_OUTS) {
visOut = op->playOuts[outIdx];
}
else {
visOut = 0;
}
if(pageIdx == ePagePlay) {
if(opPlay) {
// operators have focus, do nothing
} else {
if(visOut) {
play_output(op, outIdx, val);
}
}
}
if(inIdx < 0) {
return;
}
visIn = net_get_in_play(inIdx);
if(inIdx < net->numIns) {
// this is an op input
pIn = &(net->ins[inIdx]);
op_set_in_val(net->ops[pIn->opIdx],
pIn->opInIdx,
val);
} else {
// this is a parameter
//// FIXME this is horrible
pIndex = inIdx - net->numIns;
if (pIndex >= net->numParams) { return; }
set_param_value(pIndex, val);
}
/// only process for play mode if we're in play mode
if(pageIdx == ePagePlay) {
if(opPlay) {
// operators have focus, do nothing
} else {
// process if play-mode-visibility is set on this input
if(visIn) {
play_input(inIdx);
}
}
}
}
// activate an input node with a value
void net_activate_in(s16 inIdx, const io_t val, void* op) {
static inode_t* pIn;
s16 pIndex;
u8 vis;
/* print_dbg("\r\n net_activate, input idx: "); */
/* print_dbg_hex(inIdx); */
/* print_dbg(" , value: "); */
/* print_dbg_hex(val); */
/* print_dbg(" , op index: "); */
/* print_dbg_ulong(net->ins[inIdx].opIdx); */
/* print_dbg(" , input idx: "); */
/* print_dbg_ulong(net->ins[inIdx].opInIdx); */
if(!netActive) {
if(op != NULL) {
// if the net isn't active, dont respond to requests from operators
print_dbg(" ... ignoring node activation from op.");
return;
}
}
if(inIdx < 0) {
return;
}
vis = net_get_in_play(inIdx);
if(inIdx < net->numIns) {
// this is an op input
pIn = &(net->ins[inIdx]);
op_set_in_val(net->ops[pIn->opIdx],
pIn->opInIdx,
val);
} else {
// this is a parameter
//// FIXME this is horrible
pIndex = inIdx - net->numIns;
if (pIndex >= net->numParams) { return; }
set_param_value(pIndex, val);
}
/// only process for play mode if we're in play mode
if(pageIdx == ePagePlay) {
if(opPlay) {
// operators have focus, do nothing
} else {
// process if play-mode-visibility is set on this input
if(vis) {
play_input(inIdx);
}
}
}
}
#endif
// attempt to allocate a new operator from the static memory pool, return index
s16 net_add_op(op_id_t opId) {
u16 ins, outs;
int i, j;
int idxOld, idxNew;
op_t* op = NULL;
s32 numInsSave = net->numIns;
s32 numOutsSave = net->numOuts;
print_dbg("\r\n adding operator; old input count: ");
print_dbg_ulong(numInsSave);
if (net->numOps >= NET_OPS_MAX) {
return -1;
}
print_dbg(" , op class: ");
print_dbg_ulong(opId);
print_dbg(" , size: ");
print_dbg_ulong(op_registry[opId].size);
print_dbg(" ; allocating... ");
size_t opChunk = op_registry[opId].size;
if (opChunk <= SMALL_OP_SIZE) {
op = (op_t*)allocSmallOp();
}
else if (opChunk <= BIG_OP_SIZE) {
op = (op_t*)allocBigOp();
}
if (op == NULL) {
print_dbg("\r\ncouldn't get enough memory for new op");
return -1;
}
op_init(op, opId);
ins = op->numInputs;
outs = op->numOutputs;
if (ins > (NET_INS_MAX - net->numIns)) {
print_dbg("\r\n op creation failed; too many inputs in network.");
op_deinit(op);
freeOp((u8*)op);
return -1;
}
if (outs > (NET_OUTS_MAX - net->numOuts)) {
print_dbg("\r\n op creation failed; too many outputs in network.");
op_deinit(op);
freeOp((u8*)op);
return -1;
}
// add op pointer to list
net->ops[net->numOps] = op;
// advance offset for next allocation
//---- add inputs and outputs to node list
for(i=0; i<ins; ++i) {
net->ins[net->numIns].opIdx = net->numOps;
net->ins[net->numIns].opInIdx = i;
++(net->numIns);
}
for(i=0; i<outs; i++) {
net->outs[net->numOuts].opIdx = net->numOps;
net->outs[net->numOuts].opOutIdx = i;
net->outs[net->numOuts].target = -1;
++(net->numOuts);
}
if(net->numOps > 0) {
// if we added input nodes, need to adjust connections to DSP params
for(i=0; i < numOutsSave; i++) {
if(net->outs[i].target >= numInsSave) {
// preset target, add offset for new inputs
net_connect(i, net->outs[i].target + ins);
}
/// do the same in all presets!
for(j=0; j<NET_PRESETS_MAX; j++) {
if(preset_out_enabled(j, i)) {
s16 tar = presets[j].outs[i].target;
if(tar >= numInsSave) {
tar = tar + ins;
presets[j].outs[i].target = tar;
}
}
} // preset loop
} // outs loop
for(i=0; i<NET_PRESETS_MAX; i++) {
// shift parameter nodes in preset data
for(j=net->numParams - 1; j>=0; j--) {
// this was the old param index
idxOld = j + numInsSave;
// copy to new param index
idxNew = idxOld + ins;
if(idxNew >= PRESET_INODES_COUNT) {
print_dbg("\r\n out of preset input nodes in new op creation! ");
continue;
} else {
presets[i].ins[idxNew].value = presets[i].ins[idxOld].value;
presets[i].ins[idxNew].enabled = presets[i].ins[idxOld].enabled;
// clear the old data. it may correspond to new operator inputs.
presets[i].ins[idxOld].enabled = 0;
presets[i].ins[idxOld].value = 0;
}
}
}
}
++(net->numOps);
return net->numOps - 1;
}
// attempt to allocate a new operator from the static memory pool, return index
s16 net_add_op_at(op_id_t opId, int opIdx) {
u16 ins, outs;
int i, j;
op_t* op = NULL;
opIdx +=1;
if (opIdx < 12) {
opIdx = 12;
}
if (opIdx > net->numOps) {
opIdx = net->numOps;
}
if (net->numOps >= NET_OPS_MAX) {
return -1;
}
print_dbg(" , op class: ");
print_dbg_ulong(opId);
print_dbg(" , size: ");
print_dbg_ulong(op_registry[opId].size);
print_dbg(" ; allocating... ");
size_t opChunk = op_registry[opId].size;
if (opChunk <= SMALL_OP_SIZE) {
op = (op_t*)allocSmallOp();
}
else if (opChunk <= BIG_OP_SIZE) {
op = (op_t*)allocBigOp();
}
if (op == NULL) {
print_dbg("\r\ncouldn't get enough memory for new op");
return -1;
}
op_init(op, opId);
ins = op->numInputs;
outs = op->numOutputs;
int opFirstIn =0;
int opFirstOut = 0;
i=0;
while (i < opIdx) {
opFirstIn += net->ops[i]->numInputs;
opFirstOut += net->ops[i]->numOutputs;
i++;
}
if (ins > (NET_INS_MAX - net->numIns)) {
print_dbg("\r\n op creation failed; too many inputs in network.");
op_deinit(op);
freeOp((u8*)op);
return -1;
}
if (outs > (NET_OUTS_MAX - net->numOuts)) {
print_dbg("\r\n op creation failed; too many outputs in network.");
op_deinit(op);
freeOp((u8*)op);
return -1;
}
net->numIns +=ins;
net->numOuts += outs;
net->numOps += 1;
for(i=net->numOps - 1; i >= opIdx; i--) {
net->ops[i] = net->ops[i-1];
}
for(i=net->numOuts - 1; i >= opFirstOut; i--) {
net->outs[i] = net->outs[i-outs];
net->outs[i].opIdx += 1;
}
for(i=net->numIns - 1; i >= opFirstIn; i--) {
net->ins[i] = net->ins[i-ins];
net->ins[i].opIdx += 1;
}
// add op pointer to list
// advance offset for next allocation
net->ops[opIdx] = op;
//---- add inputs and outputs to node list
for (i=0; i<ins; ++i) {
net->ins[opFirstIn + i].opIdx = opIdx;
net->ins[opFirstIn + i].opInIdx = i;
}
for (i=0; i<outs; i++) {
net->outs[opFirstOut + i].opIdx = opIdx;
net->outs[opFirstOut + i].opOutIdx = i;
net->outs[opFirstOut + i].target = -1;
}
if(net->numOps > 0) {
for(i=0; i < net->numOuts; i++) {
// if we added input nodes, need to adjust connections to
// subsequent ins (including DSP params)
if (net->outs[i].target >= opFirstIn) {
net_connect(i, net->outs[i].target + ins);
}
} // outs loop
for(i=0; i<NET_PRESETS_MAX; i++) {
// shift input nodes in preset data
for(j=net->numParams + net->numIns - 1; j>=opFirstIn + ins; j--) {
presets[i].ins[j].value = presets[i].ins[j - ins].value;
presets[i].ins[j].enabled = presets[i].ins[j - ins].enabled;
// disable preset ins for new op
presets[i].ins[j - ins].enabled = 0;
}
// shift output nodes in preset data
for(j=net->numOuts - 1 ; j >= opFirstOut + outs; j--) {
presets[i].outs[j].target = presets[i].outs[j-outs].target;
presets[i].outs[j].enabled = presets[i].outs[j-outs].enabled;
// disable preset outs for new op
presets[i].outs[j - outs].enabled = 0;
}
for(j=0; j < net->numOuts; j++) {
if(presets[i].outs[j].enabled) {
s16 tar = presets[i].outs[j].target;
if(tar >= opFirstIn) {
presets[i].outs[j].target = tar + ins;
}
}
}
}
}
return opIdx;
}
// destroy last operator created
s16 net_pop_op(void) {
const s16 opIdx = net->numOps - 1;
op_t* op = net->ops[opIdx];
int i, j;
int x, y;
int ins;
int numInsSave = net->numIns;
int idxOld, idxNew;
app_pause();
// bail if system op
if(net_op_flag (opIdx, eOpFlagSys)) {
app_resume();
return 1;
}
// de-init
op_deinit(op);
freeOp((u8*)op);
ins = op->numInputs;
// store the global index of the first input
x = net_op_in_idx(opIdx, 0);
y = x + ins;
// check if anything connects here
for(i=0; i<net->numOuts; i++) {
// this check works b/c we know this is last op in list
if( net->outs[i].target >= x ) {
if( net->outs[i].target < y) {
net_disconnect(i);
} else {
/// this should take care of both param and op input targets.
net_connect(i, net->outs[i].target - op->numInputs);
}
}
}
// erase input nodes
while(x < y) {
net_init_inode(x++);
}
// store the global index of the first output
x = net_op_out_idx(opIdx, 0);
y = x + op->numOutputs;
// erase output nodes
while(x < y) {
net_init_onode(x++);
}
net->numIns -= op->numInputs;
net->numOuts -= op->numOutputs;
net->numOps -= 1;
// FIXME: shift preset param data and connections to params,
// since they share an indexing list with inputs and we just changed it.
for(i=0; i<NET_PRESETS_MAX; ++i) {
// shift parameter nodes in preset data
for(j=0; j<net->numParams; ++j) {
// this was the old param index
idxOld = j + numInsSave;
// copy to new param index
idxNew = idxOld - ins;
presets[i].ins[idxNew].value = presets[i].ins[idxOld].value;
presets[i].ins[idxNew].enabled = presets[i].ins[idxOld].enabled;
// clear the old data.
presets[i].ins[idxOld].enabled = 0;
presets[i].ins[idxOld].value = 0;
}
}
app_resume();
return 0;
}
s16 net_remove_op(const u32 opIdx) {
op_t* op = net->ops[opIdx];
int opNumInputs = op->numInputs;
int opNumOutputs = op->numOutputs;
int i, j;
app_pause();
// bail if system op
if(net_op_flag (opIdx, eOpFlagSys)) {
app_resume();
return 1;
}
// bail if out of range
if(opIdx < 0 || opIdx >= net->numOps) {
print_dbg("\r\nout-of-range op deletion requested");
print_dbg("\r\nnumOps = ");
print_dbg_ulong(net->numOps);
app_resume();
return 1;
}
print_dbg("\r\ndeinit-ing op");
// de-init
op_deinit(op);
freeOp((u8*)op);
print_dbg("\r\nde-inited op");
// store the global index of the first input
int opFirstIn = net_op_in_idx(opIdx, 0);
int opFirstOut = net_op_out_idx(opIdx, 0);
// check each output, break connections to removed op,
// adjust output indices after removed op down for the gap
for(i=0; i<net->numOuts; i++) {
// break connections to removed op
if( net->outs[i].target >= opFirstIn &&
net->outs[i].target < opFirstIn + opNumInputs) {
net_disconnect(i);
} else if (net->outs[i].target >= opFirstIn + opNumInputs) {
/// shuffle op indexes down past removed op
net_connect(i, net->outs[i].target - opNumInputs);
}
}
print_dbg("\r\nreshuffling...");
// reshuffle input indices & associated op indices above
// the removed op
for(i = opFirstIn; i < net->numIns - opNumInputs; i++) {
net->ins[i] = net->ins[i + opNumInputs];
net->ins[i].opIdx -= 1;
}
// reshuffle output indices & associated op indices above
// the removed op
for(i = 0; i < net->numOuts - opNumOutputs; i++) {
if (net->outs[i].opIdx >= opIdx) {
net->outs[i] = net->outs[i + opNumOutputs];
net->outs[i].opIdx -= 1;
}
}
net->numIns -= opNumInputs;
net->numOuts -= opNumOutputs;
net->numOps -= 1;
for(i=opIdx; i < net->numOps; i++) {
net->ops[i] = net->ops[i+1];
}
//HACK try re-indexing all outputs
for(i=0; i<net->numOuts; i++) {
if (net->outs[i].target >= 0) {
net_connect(i, net->outs[i].target);
}
}
for(i=0; i<NET_PRESETS_MAX; ++i) {
// shift parameter nodes in preset data
for(j=opFirstIn; j<net->numParams + net->numIns; ++j) {
presets[i].ins[j].value = presets[i].ins[j + opNumInputs].value;
presets[i].ins[j].enabled = presets[i].ins[j + opNumInputs].enabled;
}
for(j=opFirstOut; j < net->numOuts; ++j) {
presets[i].outs[j].target = presets[i].outs[j + opNumOutputs].target;
presets[i].outs[j].enabled = presets[i].outs[j + opNumOutputs].enabled;
}
for(j=0; j < net->numOuts; j++) {
if(presets[i].outs[j].enabled) {
s16 tar = presets[i].outs[j].target;
if(tar >= opFirstIn + opNumInputs) { // target above deleted op, reshuffle
presets[i].outs[j].target = tar - opNumInputs;
}
else if (tar >= opFirstIn) { // targetting deleted op, forget
presets[i].outs[j].enabled = 0;
}
}
}
}
app_resume();
return 0;
}
// create a connection between given idx pairs
void net_connect(u32 oIdx, u32 iIdx) {
const s32 srcOpIdx = net->outs[oIdx].opIdx;
const s32 dstOpIdx = net->ins[iIdx].opIdx;
net->outs[oIdx].target = iIdx;
// FIXME: this could be smarter.
// but for now, just don't allow an op to connect to itself
// (keep the target in the onode for UI purposes,
// but don't actually update the operator output variable)
if(srcOpIdx == dstOpIdx) {
return;
}
/// something weird is happening!
// value seems to drift on each disconnect/reconnect...?
if((srcOpIdx >=0) && (srcOpIdx < net->numOps)) {
net->ops[srcOpIdx]->out[net->outs[oIdx].opOutIdx] = iIdx;
} else {
print_dbg(" !!!!!! WARNING ! invalid source operator index in net_connect() ");
}
}
// disconnect given output
void net_disconnect(u32 outIdx) {
net->ops[net->outs[outIdx].opIdx]->out[net->outs[outIdx].opOutIdx] = -1;
net->outs[outIdx].target = -1;
}
//---- queries
// get current count of operators
u16 net_num_ops(void) {
return net->numOps;
}
// get current count of inputs (including DSP parameters!)
u16 net_num_ins(void) {
return net->numIns + net->numParams;
}
// get current count of outputs
u16 net_num_outs(void) {
return net->numOuts;
}
// get num params (subset of inputs)
u16 net_num_params(void) {
return net->numParams;
}
// get param index given idx in (input + params)
s16 net_param_idx(u16 inIdx) {
return inIdx - net->numIns;
}
// get string for operator at given idx
const char* net_op_name(const s16 idx) {
// int sw;
if (idx < 0) {
return (const char*)emptystring;
}
/// dirty hack for switch labels
switch(in_get_switch_index(idx)) {
case 0:
return net->ops[idx]->opString;
case 1:
return "SW1";
break;
case 2:
return "SW2";
break;
case 3:
return "SW3";
break;
case 4:
return "SW4";
break;
case 5:
return "FS1";
break;
case 6:
return "FS2";
break;
default:
return "!!!";
}
}
// get name for input at given idx
const char* net_in_name(u16 idx) {
if (idx >= net->numIns) {
// not an operator input
idx -= net->numIns;
if (idx >= net->numParams) {
// not a param input either, whoops
return (const char*)emptystring;
} else {
// param input
return net->params[idx].desc.label;
}
} else {
return op_in_name(net->ops[net->ins[idx].opIdx], net->ins[idx].opInIdx);
}
}
// get name for output at given idx
const char* net_out_name(const u16 idx) {
if(idx < net->numOuts) {
return op_out_name(net->ops[net->outs[idx].opIdx], net->outs[idx].opOutIdx);
} else {
return (const char*)emptystring;
}
}
// get op index for input at given idx
s16 net_in_op_idx(const u16 idx) {
if (idx >= net->numIns) return -1;
return net->ins[idx].opIdx;
}
// get op index for output at given idx
s16 net_out_op_idx(const u16 idx) {
if (idx > net->numOuts) return -1;
return net->outs[idx].opIdx;
}
// get global index for a given input of given op
u16 net_op_in_idx(const u16 opIdx, const u16 inIdx) {
u16 which;
for(which=0; which < (net->numIns); which++) {
if(net->ins[which].opIdx == opIdx) {
return (which + inIdx);
}
}
return 0; // get here if op has no inputs
}
// get global index for a given output of given op
u16 net_op_out_idx(const u16 opIdx, const u16 outIdx) {
u16 which;
for(which=0; which<net->numOuts; which++) {
if(net->outs[which].opIdx == opIdx) {
return (which + outIdx);
}
}
return 0; // shouldn't get here
}
// get connection index for output
s16 net_get_target(u16 outIdx) {
return net->outs[outIdx].target;
}
// is this input connected to anything?
u8 net_in_connected(s32 iIdx) {
u8 f=0;
u16 i;
for(i=0; i<net->numOuts; i++) {
if(net->outs[i].target == iIdx) {
f = 1;
break;
}
}
return f;
}
u8 net_op_flag(const u16 opIdx, op_flag_t flag) {
return net->ops[opIdx]->flags & (1 << flag);
}
// populate an array with indices of all connected outputs for a given index
// returns count of connections
u32 net_gather(s32 iIdx, u32(*outs)[NET_OUTS_MAX]) {
u32 iTest;
u32 iOut=0;
for(iTest=0; iTest<NET_OUTS_MAX; iTest++) {
if(net->outs[iTest].target == iIdx) {
(*outs)[iOut] = iTest;
iOut++;
}
}
return iOut;
}
//--- get / set / increment input value
io_t net_get_in_value(s32 inIdx) {
if(inIdx < 0) {
return 0;
}
if (inIdx >= net->numIns) {
inIdx -= net->numIns;
return get_param_value(inIdx);
} else {
return op_get_in_val(net->ops[net->ins[inIdx].opIdx], net->ins[inIdx].opInIdx);
}
}
void net_set_in_value(s32 inIdx, io_t val) {
if (inIdx < 0) return;
if (inIdx < net->numIns) {
op_set_in_val(net->ops[net->ins[inIdx].opIdx], net->ins[inIdx].opInIdx, val);
} else {
// parameter
inIdx -= net->numIns;
set_param_value(inIdx, val);
}
}
// probably only called from UI,
// can err on the side of caution vs speed
io_t net_inc_in_value(s32 inIdx, io_t inc) {
op_t* op;
if(inIdx >= net->numIns) {
// hack to get param idx
inIdx -= net->numIns;
return inc_param_value(inIdx, inc);
} else {
op = net->ops[net->ins[inIdx].opIdx];
op_inc_in_val(op, net->ins[inIdx].opInIdx, inc);
return net_get_in_value(inIdx);
}
}
// toggle preset inclusion for input
u8 net_toggle_in_preset(u32 id) {
return preset_get_selected()->ins[id].enabled ^= 1;
}
// toggle preset inclusion for output
u8 net_toggle_out_preset(u32 id) {
u8 tmp = preset_out_enabled(preset_get_select(), id) ^ 1;
preset_get_selected()->outs[id].enabled = tmp;
return tmp;
}
// set preset inclusion for input
void net_set_in_preset(u32 id, u8 val) {
preset_get_selected()->ins[id].enabled = val;
}
// set preset inclusion for output
void net_set_out_preset(u32 id, u8 val) {
preset_get_selected()->outs[id].enabled = val;
}
// get preset inclusion for input
u8 net_get_in_preset(u32 id) {
return preset_get_selected()->ins[id].enabled;
}
// get preset inclusion for output
u8 net_get_out_preset(u32 id) {
return preset_get_selected()->outs[id].enabled;
}
// toggle play inclusion for input
u8 net_toggle_in_play(u32 inIdx) {
u32 pidx;
if(inIdx < net->numIns) {
net->ins[inIdx].play ^= 1;
return net->ins[inIdx].play;
} else {
pidx = inIdx - net->numIns;
net->params[pidx].play ^= 1;
return net->params[pidx].play;
}
}
// set play inclusion for input
void net_set_in_play(u32 inIdx, u8 val) {
if(inIdx < net->numIns) {
net->ins[inIdx].play = val;
} else {
net->params[inIdx - net->numIns].play = val;
}
}
// get play inclusion for input
u8 net_get_in_play(u32 inIdx) {
if(inIdx < net->numIns) {
return net->ins[inIdx].play;
} else {
return net->params[inIdx - net->numIns].play;
}
}
// toggle play inclusion for output
u8 net_toggle_out_play(u32 outIdx) {
if(outIdx > net->numOuts) {
print_dbg("\r\nrequested out-of-range output for play screen display toggling");
return 0;
}
else {
s32 opIdx = net->outs[outIdx].opIdx;
s32 opOutIdx = net->outs[outIdx].opOutIdx;
if(opOutIdx < MAX_PLAY_OUTS) {
net->ops[opIdx]->playOuts[opOutIdx] ^= 1;
} else {
print_dbg("\r\nrequested out-of-range op output for play screen display toggling");
return 0;
}
}
return 0;
}
// set play inclusion for output
void net_set_out_play(u32 outIdx, u8 val) {
if(outIdx > net->numOuts) {
print_dbg("\r\nrequested out-of-range output for play screen display setting");
return;
}
else {
s32 opIdx = net->outs[outIdx].opIdx;
s32 opOutIdx = net->outs[outIdx].opOutIdx;
if(opOutIdx < MAX_PLAY_OUTS) {
net->ops[opIdx]->playOuts[opOutIdx] = val;
}
else {
print_dbg("\r\nrequested out-of-range op output for play screen display setting");
}
}
}
// get play inclusion for output
u8 net_get_out_play(u32 outIdx) {
if(outIdx > net->numOuts) {
print_dbg("\r\nrequested out-of-range output for play screen display getting");
return 0;
}
else {
s32 opIdx = net->outs[outIdx].opIdx;
s32 opOutIdx = net->outs[outIdx].opOutIdx;
if(opOutIdx < MAX_PLAY_OUTS) {
return net->ops[opIdx]->playOuts[opOutIdx];
} else {
print_dbg("\r\nrequested out-of-range op output for play screen display getting");
return 0;
}
}
}
//------------------------------------
//------ params
// add a new parameter
void net_add_param(u32 idx, const ParamDesc * pdesc) {
s32 val;
// copy descriptor, hm
memcpy( &(net->params[net->numParams].desc), (const void*)pdesc, sizeof(ParamDesc) );
// initialize scaler
scaler_init(&(net->params[net->numParams].scaler),
&(net->params[net->numParams].desc));
//... FIXME: can't remember what this is about.
// net->params[net->numParams].idx = idx;
net->params[net->numParams].play = 1;
// net->params[net->numParams].preset = 0;
net->numParams += 1;
// query initial value
val = bfin_get_param(idx);
net->params[net->numParams - 1].data.value =
scaler_get_in( &(net->params[net->numParams - 1].scaler), val);
net->params[net->numParams - 1].data.changed = 0;
}
// clear existing parameters
void net_clear_params(void) {
print_dbg("\r\n clearing parameter list... ");
net->numParams = 0;
}
// resend existing parameter values
void net_send_params(void) {
u32 i;
for(i=0; i<net->numParams; i++) {
set_param_value(i, net->params[i].data.value);
}
}
// retrigger all inputs
/* void net_retrigger_ins(void) { */
/* u32 i; */
/* netActive = 0; */
/* for(i=0; i<net->numIns; i++) { */
/* net_activate(i, net_get_in_value(i), NULL); */
/* } */
/* netActive = 1; */
/* } */
// pickle the network!
u8* net_pickle(u8* dst) {
u32 i;
op_t* op;
u32 val = 0;
// write count of operators
// ( 4 bytes for alignment)
dst = pickle_32((u32)(net->numOps), dst);
// loop over operators
for(i=0; i<net->numOps; ++i) {
op = net->ops[i];
// store type id
dst = pickle_32(op->type, dst);
// pickle the operator state (if needed)
if(op->pickle != NULL) {
dst = (*(op->pickle))(op, dst);
}
}
// write input nodes
//// all nodes, even unused
for(i=0; i < (NET_INS_MAX); ++i) {
dst = inode_pickle(&(net->ins[i]), dst);
}
// write output nodes
for(i=0; i < NET_OUTS_MAX; ++i) {
dst = onode_pickle(&(net->outs[i]), dst);
}
// write count of parameters
// 4 bytes for alignment
val = (u32)(net->numParams);
dst = pickle_32(val, dst);
// write parameter nodes (includes value and descriptor)
for(i=0; i<net->numParams; ++i) {
dst = param_pickle(&(net->params[i]), dst);
}
return dst;
}
// XXX HACK - we need this global flag to tell grid ops not to grab
// focus on init during scene recall
u8 recallingScene = 0;
// unpickle the network!
u8* net_unpickle(const u8* src) {
u32 i, count, val;
op_id_t id;
op_t* op;
// reset operator count, param count, pool offset, etc
// no system operators after this
net_deinit();
// confusingly though, we don't call deinit() after this.
// system ops are in the pickled scene data along with everything else.
// net_deinit() should maybe be renamed.
// get count of operators
// (use 4 bytes for alignment)
src = unpickle_32(src, &count);
#ifdef PRINT_PICKLE
print_dbg("\r\n count of ops: ");
print_dbg_ulong(count);
#endif
// loop over operators
for(i=0; i<count; ++i) {
// get operator class id
src = unpickle_32(src, &val);
id = (op_id_t)val;
#ifdef PRINT_PICKLE
print_dbg("\r\n adding op, class id: ");
print_dbg_ulong(id);
#endif
// add and initialize from class id
/// .. this should update the operator count, inodes and onodes
recallingScene = 1;
net_add_op(id);
recallingScene = 0;
// unpickle operator state (if needed)
op = net->ops[net->numOps - 1];
if(op->unpickle != NULL) {
#ifdef PRINT_PICKLE
print_dbg(" ... unpickling op .... ");
print_dbg_ulong(id);
#endif
src = (*(op->unpickle))(op, src);
}
}
/// copy ALL i/o nodes, even unused!
print_dbg("\r\n reading all input nodes ");
for(i=0; i < (NET_INS_MAX); ++i) {
src = inode_unpickle(src, &(net->ins[i]));
}
// read output nodes
for(i=0; i < NET_OUTS_MAX; ++i) {
src = onode_unpickle(src, &(net->outs[i]));
if(i < net->numOuts) {
if(net->outs[i].target >= 0) {
// reconnect so the parent operator knows what to do
net_connect(i, net->outs[i].target);
}
}
}
// get count of parameters
src = unpickle_32(src, &val);
net->numParams = (u16)val;
#ifdef PRINT_PICKLE
print_dbg("\r\n reading params, count: ");
print_dbg_ulong(net->numParams);
#endif
// read parameter nodes (includes value and descriptor)
for(i=0; i<(net->numParams); ++i) {
#ifdef PRINT_PICKLE
print_dbg("\r\n unpickling param, idx: ");
print_dbg_ulong(i);
#endif
src = param_unpickle(&(net->params[i]), src);
}
update_sys_op_pointers();
return (u8*)src;
}
// get parameter string representations,
// given string buffer and index in inputs list
void net_get_param_value_string(char* dst, u32 idx) {
//// FIXME
/// get param index! rrrgg
idx -= net->numIns;
/// lookup representation from stored input value and print to buf
scaler_get_str( dst,
&(net->params[idx].scaler),
net->params[idx].data.value
);
}
// same, with arbitrary value
void net_get_param_value_string_conversion(char* dst, u32 idx, s32 val) {
/// lookup representation from stored input value and print to buf
scaler_get_str( dst,
&(net->params[idx].scaler),
val
);
}
// disconnect from parameters
void net_disconnect_params(void) {
int i;
int j;
int t = net->numIns; // test target
for(i=0; i<net->numParams; ++i) {
for(j=0; j<net->numOuts; ++j) {
if(net->outs[j].target == t) {
net_disconnect(j);
}
}
t++;
}
}
// insert a split after an output node
// return out11 of split if original out was unconnected,
// otherwise connect out1 of split to old target and return out2
s16 net_split_out(s16 outIdx) {
// saved target
s16 target = net->outs[outIdx].target;
s16 opIdx = net->outs[outIdx].opIdx;
// index of added split operator
s16 split;
if( target < 0) {
// no target
split = net_add_op_at(eOpSplit, opIdx);
if(split < 0) {
// failed to add, do nothing
return outIdx;
} else {
// FIXME: net_op_in_idx is pretty slow
net_connect(outIdx, net_op_in_idx(split, 0));
return net_op_out_idx(split, 0);
} // add ok
} else {
// had target; reroute
split = net_add_op_at(eOpSplit, opIdx);
// get the target again, because maybe it was a DSP param
// (if it was, its index will have shifted.
// patch and presets have been updated, but local var has not.)
target = net->outs[outIdx].target;
if(split < 0) {
// failed to add, do nothing
return outIdx;
} else {
// FIXME: net_op_in_idx is pretty slow
net_connect(outIdx, net_op_in_idx(split, 0));
net_connect(net_op_out_idx(split, 0), target);
return net_op_out_idx(split, 1);
} // add ok
}
}
///////////////
// test / dbg
#if 0
void net_print(void) {
print_dbg("\r\n net address: 0x");
print_dbg_hex((u32)(net));
print_dbg("\r\n net input count: ");
print_dbg_ulong(net->numIns);
print_dbg("\r\n net output count: ");
print_dbg_ulong(net->numOuts);
print_dbg("\r\n net op count: ");
print_dbg_ulong(net->numOps);
}
#endif
// set active
void net_set_active(bool v) {
netActive = v;
}