4. VM Components
4.1. Main Struct
typedef struct seqvm seqvm;
struct seqvm {
<<seqvm>>
};
4.2. Cell
Commands are contained inside of cells, called seqvm_cell
.
typedef struct seqvm_cell seqvm_cell;
struct seqvm_cell {
unsigned char cmd;
unsigned p[3];
};
Initialize with seqvm_cell_init
.
void seqvm_cell_init(seqvm_cell *c);
void seqvm_cell_init(seqvm_cell *c)
{
c->cmd = SEQVM_NONE;
c->p[0] = 0;
c->p[1] = 0;
c->p[2] = 0;
}
4.3. Cell Pool
seqvm_cell *pool;
int size;
svm->pool = pool;
svm->size = size;
{
int i;
for (i = 0; i < svm->size; i++) {
seqvm_cell_init(&svm->pool[i]);
}
}
The pool can be retrieved with seqvm_get_pool
.
seqvm_cell* seqvm_get_pool(seqvm *svm);
seqvm_cell* seqvm_get_pool(seqvm *svm)
{
return svm->pool;
}
4.4. Channel
Output signals are written to something known as a channel,
or seqvm_chan
.
typedef struct seqvm_chan seqvm_chan;
An output channel has 4 parameters: sequence, tick, gate, and aux.
struct seqvm_chan {
int val;
int tick;
int gate;
int aux;
};
val stores the sequence value. This should be fed into a table lookup algorithm to get converted into a note.
tick is a flag that tells seqvm to send a tick signal when a clock tick happens. This gets reset every block. Ticks can be fed into triggerable envelope generators for more percussive sounds.
gate sends a gate signal, and is generally used to indicate if a note is on or off.
aux doesn't do anything yet, but will be reserved for later.
Initialize with seqvm_chan_init
.
void seqvm_chan_init(seqvm_chan *chan);
void seqvm_chan_init(seqvm_chan *chan)
{
chan->val = 0;
chan->tick = 0;
chan->gate = 0;
chan->aux = 0;
}
4.5. Channel Bank
A set of 4 channels makes a channel bank, known as
seqvm_chanbank
.
typedef struct seqvm_chanbank seqvm_chanbank;
struct seqvm_chanbank {
seqvm_chan chan[4];
};
Initialize with seqvm_chanbank_init
.
void seqvm_chanbank_init(seqvm_chanbank *bnk);
void seqvm_chanbank_init(seqvm_chanbank *bnk)
{
int i;
for (i = 0; i < 4; i++) seqvm_chan_init(&bnk->chan[i]);
}
There are two channel banks, known as main
and next
.
This is done to ensure sample-precision for clocked output
signals.
seqvm_chanbank main;
seqvm_chanbank next;
seqvm_chanbank_init(&svm->main);
seqvm_chanbank_init(&svm->next);
When a tick happens inside a block of audio, the state is
split between main
and next
. Everything before pos
uses main
, and everything after uses next
.
At the next block after the tick, main
is updated to be
the state in next
.
Explicitely update the two with svm_update_states
. The
block position blkpos
is where a tick would happen
in the block.
Get the appropriate channel from a channel bank using
seqvm_get_chan
.
seqvm_chan * seqvm_get_chan(seqvm *svm, int chan, int pos);
seqvm_chan * seqvm_get_chan(seqvm *svm, int chan, int pos)
{
if (pos < svm->blkpos || svm->blkpos < 0) {
return &svm->main.chan[chan];
}
return &svm->next.chan[chan];
}
4.6. Register
Registers are integer values addressable via index. These can be read and written to in the commands.
int r[16];
{
int i;
for (i = 0; i < 16; i++) svm->r[i] = 0;
}
4.7. Program Position
The current program position is stored in a variable
called pos
. It starts at 0 at startup.
int pos;
svm->pos = 0;
4.8. Block Position
In order to keep track of clocks with sample precision, a block position is stored in the VM. This is where the tick happens inside the computed block of audio rendered.
This adds a sparseness limitation to seqvm, only one clock signal event can happen per block. For most needs, this is acceptable.
blkpos
is initialized to be negative in order to indicate
no event has happened.
int blkpos;
svm->blkpos = -1;
4.9. Halt Flag
A halt
flag is used to tell the VM to stop computing.
int halt;
svm->halt = 0;
4.10. Random Number Generator
An internal random number generator is implemented for portability purposes. It is a pretty typical LCG.
unsigned long rng;
Can be initialized with seqvm_srand
.
void seqvm_srand(seqvm *svm, unsigned long val);
void seqvm_srand(seqvm *svm, unsigned long val)
{
svm->rng = val;
}
Initalized to be 0 by default.
seqvm_srand(svm, 0);
A random number is produce with seqvm_rand
.
unsigned long seqvm_rand(seqvm *svm);
A pretty standard LGC process.
unsigned long seqvm_rand(seqvm *svm)
{
unsigned long rng;
rng = (1103515245 * svm->rng + 12345) % SEQVM_RANDMAX;
svm->rng = rng;
return rng;
}
#define SEQVM_RANDMAX 2147483648
prev | home | next