aboutsummaryrefslogtreecommitdiff
path: root/cyclone
diff options
context:
space:
mode:
Diffstat (limited to 'cyclone')
-rw-r--r--cyclone/hammer/seq.c263
1 files changed, 182 insertions, 81 deletions
diff --git a/cyclone/hammer/seq.c b/cyclone/hammer/seq.c
index a9d58b6..5997d31 100644
--- a/cyclone/hammer/seq.c
+++ b/cyclone/hammer/seq.c
@@ -16,10 +16,14 @@
#include "common/mifi.h"
#include "hammer/file.h"
-#define SEQ_DEBUG
+//#define SEQ_DEBUG
-#define SEQ_INISIZE 256 /* LATER rethink */
-#define SEQ_EOM 255 /* end of message marker, LATER rethink */
+#define SEQ_INISIZE 256 /* LATER rethink */
+#define SEQ_EOM 255 /* end of message marker, LATER rethink */
+#define SEQ_TICKSPERSEC 48
+#define SEQ_MINTICKDELAY 1. /* LATER rethink */
+
+enum { SEQ_IDLEMODE, SEQ_RECMODE, SEQ_PLAYMODE, SEQ_SLAVEMODE };
typedef struct _seqevent
{
@@ -33,11 +37,12 @@ typedef struct _seq
t_canvas *x_canvas;
t_symbol *x_defname;
t_hammerfile *x_filehandle;
- int x_isplaying;
- int x_isrecording;
+ int x_mode;
int x_playhead;
float x_tempo;
+ float x_newtempo;
double x_prevtime;
+ double x_slaveprevtime;
double x_clockdelay;
unsigned char x_status;
int x_evesize;
@@ -47,6 +52,7 @@ typedef struct _seq
t_seqevent *x_sequence;
t_seqevent x_seqini[SEQ_INISIZE];
t_clock *x_clock;
+ t_clock *x_slaveclock;
t_outlet *x_bangout;
} t_seq;
@@ -75,8 +81,7 @@ static void seq_complete(t_seq *x)
else
{
t_seqevent *ep = &x->x_sequence[x->x_nevents];
- double elapsed = clock_gettimesince(x->x_prevtime);
- ep->e_delta = (int)elapsed;
+ ep->e_delta = (int)clock_gettimesince(x->x_prevtime);
x->x_prevtime = clock_getlogicaltime();
if (x->x_evesize < 4)
ep->e_bytes[x->x_evesize] = SEQ_EOM;
@@ -155,6 +160,19 @@ static void seq_endofsysex(t_seq *x)
x->x_status = 0;
}
+static void seq_stoprecording(t_seq *x)
+{
+ if (x->x_status == 240)
+ {
+ post("seq: incomplete sysex"); /* CHECKED */
+ seq_endofsysex(x); /* CHECKED 247 added implicitly */
+ }
+ else if (x->x_status)
+ seq_complete(x);
+ /* CHECKED running status used in recording, but not across recordings */
+ x->x_status = 0;
+}
+
static void seq_stopplayback(t_seq *x)
{
/* FIXME */
@@ -163,26 +181,118 @@ static void seq_stopplayback(t_seq *x)
/* CHECKED bang not sent if playback stopped early */
clock_unset(x->x_clock);
x->x_playhead = 0;
- x->x_isplaying = 0;
}
-static void seq_stoprecording(t_seq *x)
+static void seq_stopslavery(t_seq *x)
{
- if (x->x_status == 240)
+ /* FIXME */
+ clock_unset(x->x_clock);
+ clock_unset(x->x_slaveclock);
+ x->x_playhead = 0;
+}
+
+static void seq_startrecording(t_seq *x, int modechanged)
+{
+ x->x_prevtime = clock_getlogicaltime();
+ x->x_status = 0;
+ x->x_evesize = 0;
+ x->x_expectedsize = -1; /* LATER rethink */
+}
+
+/* CHECKED running status not used in playback */
+static void seq_startplayback(t_seq *x, int modechanged)
+{
+ /* CHECKED bang not sent if sequence is empty */
+ if (x->x_nevents)
{
- post("seq: incomplete sysex"); /* CHECKED */
- seq_endofsysex(x); /* CHECKED 247 added implicitly */
+ if (modechanged)
+ {
+ x->x_playhead = 0;
+ /* playback data never sent within the scheduler event of
+ a start message (even for the first delta <= 0), LATER rethink */
+ x->x_clockdelay = x->x_sequence->e_delta * x->x_newtempo;
+ }
+ else
+ {
+ /* CHECKED tempo change */
+ x->x_clockdelay -= clock_gettimesince(x->x_prevtime);
+ x->x_clockdelay *= x->x_newtempo / x->x_tempo;
+ }
+ if (x->x_clockdelay < 0.)
+ x->x_clockdelay = 0.;
+ clock_delay(x->x_clock, x->x_clockdelay);
+ x->x_prevtime = clock_getlogicaltime();
+ x->x_tempo = x->x_newtempo;
}
- else if (x->x_status)
- seq_complete(x);
- /* CHECKED running status used in recording, but not across recordings */
- x->x_status = 0;
- x->x_isrecording = 0;
+ else x->x_mode = SEQ_IDLEMODE;
+}
+
+static void seq_startslavery(t_seq *x, int modechanged)
+{
+ if (x->x_nevents)
+ {
+ x->x_playhead = 0;
+ x->x_prevtime = 0.;
+ x->x_slaveprevtime = 0.;
+ }
+ else x->x_mode = SEQ_IDLEMODE;
+}
+
+static void seq_setmode(t_seq *x, int newmode)
+{
+ int changed = (x->x_mode != newmode);
+ if (changed)
+ {
+ switch (x->x_mode)
+ {
+ case SEQ_IDLEMODE:
+ break;
+ case SEQ_RECMODE:
+ seq_stoprecording(x);
+ break;
+ case SEQ_PLAYMODE:
+ seq_stopplayback(x);
+ break;
+ case SEQ_SLAVEMODE:
+ seq_stopslavery(x);
+ break;
+ default:
+ bug("seq_setmode (old)");
+ return;
+ }
+ x->x_mode = newmode;
+ }
+ switch (newmode)
+ {
+ case SEQ_IDLEMODE:
+ break;
+ case SEQ_RECMODE:
+ seq_startrecording(x, changed);
+ break;
+ case SEQ_PLAYMODE:
+ seq_startplayback(x, changed);
+ break;
+ case SEQ_SLAVEMODE:
+ seq_startslavery(x, changed);
+ break;
+ default:
+ bug("seq_setmode (new)");
+ }
+}
+
+static void seq_settempo(t_seq *x, float newtempo)
+{
+ if (newtempo < 1e-20)
+ x->x_newtempo = 1e-20;
+ else if (newtempo > 1e20)
+ x->x_newtempo = 1e20;
+ else
+ x->x_newtempo = newtempo;
}
static void seq_clocktick(t_seq *x)
{
- if (x->x_isplaying)
+ if (x->x_mode == SEQ_PLAYMODE || x->x_mode == SEQ_SLAVEMODE)
{
t_seqevent *ep = &x->x_sequence[x->x_playhead++];
unsigned char *bp = ep->e_bytes;
@@ -198,8 +308,8 @@ nextevent:
outlet_float(((t_object *)x)->ob_outlet, *bp++);
}
}
- if (!x->x_isplaying) /* reentrancy protection */
- return;
+ if (x->x_mode != SEQ_PLAYMODE && x->x_mode != SEQ_SLAVEMODE)
+ return; /* protecting against outlet -> 'stop' etc. */
if (x->x_playhead < x->x_nevents)
{
ep++;
@@ -221,63 +331,66 @@ nextevent:
}
else
{
- seq_stopplayback(x);
+ seq_setmode(x, SEQ_IDLEMODE);
/* CHECKED bang sent immediately _after_ last byte */
outlet_bang(x->x_bangout); /* LATER think about reentrancy */
}
}
}
-static void seq_tick(t_seq *x)
+/* timeout handler ('tick' is late) */
+static void seq_slaveclocktick(t_seq *x)
{
- /* FIXME */
+ if (x->x_mode == SEQ_SLAVEMODE) clock_unset(x->x_clock);
}
-/* CHECKED running status not used in playback */
-static void seq_dostart(t_seq *x, float tempo)
-{
- if (x->x_isplaying)
- {
- /* CHECKED tempo change */
- double elapsed = clock_gettimesince(x->x_prevtime);
- double left = x->x_clockdelay - elapsed;
- if (left < 0)
- left = 0;
- else
- left *= tempo / x->x_tempo;
- clock_delay(x->x_clock, x->x_clockdelay = left);
- x->x_prevtime = clock_getlogicaltime();
- x->x_tempo = tempo;
- }
- else
+/* LATER dealing with self-invokation (outlet -> 'tick') */
+static void seq_tick(t_seq *x)
+{
+ if (x->x_mode == SEQ_SLAVEMODE)
{
- if (x->x_isrecording) /* CHECKED 'start' stops recording */
- seq_stoprecording(x);
- /* CHECKED bang not sent if a sequence is empty */
- if (x->x_nevents)
+ if (x->x_slaveprevtime > 0)
{
- x->x_tempo = tempo;
- x->x_playhead = 0;
- x->x_isplaying = 1;
- /* playback data never sent within the scheduler event of
- a start message (even for the first delta <= 0), LATER rethink */
- x->x_clockdelay = x->x_sequence->e_delta * tempo;
+ double elapsed = clock_gettimesince(x->x_slaveprevtime);
+ if (elapsed < SEQ_MINTICKDELAY)
+ return;
+ clock_delay(x->x_slaveclock, elapsed);
+ seq_settempo(x, (float)(elapsed * (SEQ_TICKSPERSEC / 1000.)));
+ if (x->x_prevtime > 0)
+ {
+ x->x_clockdelay -= clock_gettimesince(x->x_prevtime);
+ x->x_clockdelay *= x->x_newtempo / x->x_tempo;
+ }
+ else x->x_clockdelay =
+ x->x_sequence[x->x_playhead].e_delta * x->x_newtempo;
if (x->x_clockdelay < 0.)
x->x_clockdelay = 0.;
clock_delay(x->x_clock, x->x_clockdelay);
x->x_prevtime = clock_getlogicaltime();
+ x->x_slaveprevtime = x->x_prevtime;
+ x->x_tempo = x->x_newtempo;
+ }
+ else
+ {
+ x->x_clockdelay = 0.; /* redundant */
+ x->x_prevtime = 0.; /* redundant */
+ x->x_slaveprevtime = clock_getlogicaltime();
+ x->x_tempo = 1.; /* redundant */
}
}
}
+/* CHECKED bang does the same as 'start 1024', not 'start <current-tempo>'
+ (also if already in SEQ_PLAYMODE) */
static void seq_bang(t_seq *x)
{
- seq_dostart(x, 1.);
+ seq_settempo(x, 1.);
+ seq_setmode(x, SEQ_PLAYMODE); /* CHECKED 'bang' stops recording */
}
static void seq_float(t_seq *x, t_float f)
{
- if (x->x_isrecording)
+ if (x->x_mode == SEQ_RECMODE)
{
/* CHECKED noninteger and out of range silently truncated */
unsigned char c = (unsigned char)f;
@@ -314,28 +427,18 @@ static void seq_list(t_seq *x, t_symbol *s, int ac, t_atom *av)
/* CHECKED anything else/more silently ignored */
}
-static void seq_dorecord(t_seq *x)
-{
- if (x->x_isplaying) /* CHECKED 'record' and 'append' stops playback */
- seq_stopplayback(x);
- x->x_isrecording = 1;
- x->x_prevtime = clock_getlogicaltime();
- x->x_status = 0;
- x->x_evesize = 0;
- x->x_expectedsize = -1; /* LATER rethink */
-}
-
static void seq_record(t_seq *x)
{
- /* CHECKED 'record' resets recording */
+ /* CHECKED 'record' stops playback, resets recording */
seq_doclear(x, 0);
- seq_dorecord(x);
+ seq_setmode(x, SEQ_RECMODE);
}
static void seq_append(t_seq *x)
{
- /* CHECKED if isrecording, 'append' resets the timer */
- seq_dorecord(x);
+ /* CHECKED 'append' stops playback */
+ /* CHECKED if in SEQ_RECMODE, 'append' resets the timer */
+ seq_setmode(x, SEQ_RECMODE);
}
static void seq_start(t_seq *x, t_floatarg f)
@@ -343,31 +446,25 @@ static void seq_start(t_seq *x, t_floatarg f)
if (f < 0)
{
/* FIXME */
+ seq_setmode(x, SEQ_SLAVEMODE);
}
else
{
- float tempo = (f == 0 ? 1. : 1024. / f);
- if (tempo < 1e-20)
- tempo = 1e-20;
- else if (tempo > 1e20)
- tempo = 1e20;
- seq_dostart(x, tempo);
+ seq_settempo(x, (f == 0 ? 1. : 1024. / f));
+ seq_setmode(x, SEQ_PLAYMODE); /* CHECKED 'start' stops recording */
}
}
static void seq_stop(t_seq *x)
{
- if (x->x_isplaying)
- seq_stopplayback(x);
- else if (x->x_isrecording)
- seq_stoprecording(x);
+ seq_setmode(x, SEQ_IDLEMODE);
}
/* CHECKED first delta time is set permanently (it is stored in a file) */
static void seq_delay(t_seq *x, t_floatarg f)
{
if (x->x_nevents)
- /* CHECKED signed/unsigned bug, not emulated */
+ /* CHECKED signed/unsigned bug (not emulated) */
x->x_sequence->e_delta = (f > 0 ? f : 0);
}
@@ -379,7 +476,7 @@ static void seq_hook(t_seq *x, t_floatarg f)
{
t_seqevent *ev = x->x_sequence;
if (f < 0)
- f = 0; /* CHECKED signed/unsigned bug, not emulated */
+ f = 0; /* CHECKED signed/unsigned bug (not emulated) */
while (nevents--) ev++->e_delta *= f;
}
}
@@ -823,6 +920,7 @@ static void seq_print(t_seq *x)
static void seq_free(t_seq *x)
{
if (x->x_clock) clock_free(x->x_clock);
+ if (x->x_slaveclock) clock_free(x->x_slaveclock);
hammerfile_free(x->x_filehandle);
if (x->x_sequence != x->x_seqini)
freebytes(x->x_sequence, x->x_size * sizeof(*x->x_sequence));
@@ -841,7 +939,9 @@ static void *seq_new(t_symbol *s)
x->x_filehandle = hammerfile_new((t_pd *)x, 0,
seq_readhook, seq_writehook, 0);
x->x_tempo = 1.;
- x->x_prevtime = 0;
+ x->x_newtempo = 1.;
+ x->x_prevtime = 0.;
+ x->x_slaveprevtime = 0.;
x->x_size = SEQ_INISIZE;
x->x_nevents = 0;
x->x_sequence = x->x_seqini;
@@ -854,6 +954,7 @@ static void *seq_new(t_symbol *s)
}
else x->x_defname = &s_;
x->x_clock = clock_new(x, (t_method)seq_clocktick);
+ x->x_slaveclock = clock_new(x, (t_method)seq_slaveclocktick);
return (x);
}
@@ -883,7 +984,7 @@ void seq_setup(void)
class_addmethod(seq_class, (t_method)seq_delay,
gensym("delay"), A_FLOAT, 0); /* CHECKED arg obligatory */
class_addmethod(seq_class, (t_method)seq_hook,
- gensym("hook"), A_FLOAT, 0); /* CHECKED arg obligatory */
+ gensym("hook"), A_FLOAT, 0); /* CHECKED arg obligatory */
class_addmethod(seq_class, (t_method)seq_read,
gensym("read"), A_DEFSYM, 0);
class_addmethod(seq_class, (t_method)seq_write,