#include "seqt.h"
#define NOPS 0x2000
#define M1 (NOPS - 1)
#define M2 ((NOPS << 1) - 1)
typedef enum OpCode {DUR, PIT, INS, DEL} OpCode;
static uint32_t stack[NOPS];
static int p, q, r;
static uint32_t
track_index(unsigned track, unsigned index)
{
return (track << 16) | (index << 4);
}
uint32_t
op_dur(unsigned track, unsigned index, unsigned delta)
{
uint32_t op = track_index(track, index) | DUR;
return op | (delta << 20);
}
uint32_t
op_pit(unsigned track, unsigned index, unsigned voice, unsigned delta)
{
uint32_t op = track_index(track, index) | PIT;
return op | (voice << 28) | (delta << 20);
}
uint32_t
op_ins(unsigned track, unsigned index, unsigned nrows)
{
uint32_t op = track_index(track, index) | INS;
return op | (nrows << 20);
}
uint32_t
op_del(unsigned track, unsigned index, unsigned nrows)
{
uint32_t op = track_index(track, index) | DEL;
return op | (nrows << 20);
}
#define inc(i) (((i) + 1) & M2)
#define dec(i) (((i) - 1) & M2)
#define isempty (p == q)
#define isfull (p == (q ^ NOPS))
static void
push(uint32_t op)
{
r = 0;
stack[q & M1] = op;
if (isfull)
p = inc(p);
q = inc(q);
}
static uint32_t
pop()
{
r++;
q = dec(q);
return stack[q & M1];
}
static uint32_t
unpop()
{
r--;
q = inc(q);
return stack[dec(q) & M1];
}
static void
apply(Matrix matrix, uint32_t op, int reverse)
{
unsigned int track, index, delta, voice, nrows;
unsigned int args = op >> 20;
unsigned char duration, cell;
track = (op >> 16) & 0xF;
index = (op >> 4) & 0xFFF;
switch ((OpCode) (op & 3)) {
case DUR:
delta = args;
duration = matrix[index][0][track >> 1];
duration = track & 1 ? duration & 0x0F : duration >> 4;
if (reverse)
duration = (duration - delta) & 0x0F;
else
duration = (duration + delta) & 0x0F;
matrix[index][0][track >> 1] |= duration << ((~track & 1) << 2);
break;
case PIT:
voice = args >> 8;
delta = args & 0xFF;
cell = matrix[index][track][voice];
if (reverse)
cell = cell - delta;
else
cell = cell + delta;
matrix[index][track][voice] = cell;
break;
case INS:
/* TODO */
(void) nrows;
break;
case DEL:
/* TODO */
(void) nrows;
}
}
void
doop(Matrix matrix, uint32_t op, int mark)
{
apply(matrix, op, 0);
push(op | (((unsigned) mark) << 2));
}
void
undo(Matrix matrix)
{
uint32_t op;
while (!isempty) {
op = pop();
apply(matrix, op, 1);
if (op & 4)
break;
}
}
void
redo(Matrix matrix)
{
uint32_t op;
unsigned int marks = 0;
while (r) {
op = unpop();
marks += op & 4;
if (marks == 8) {
(void) pop();
break;
}
apply(matrix, op, 0);
}
}