@@ 11,19 11,29 @@
#include <timely.lv2/timely.h>
#include <props.lv2/props.h>
-#define MAX_NPROPS 1
+#define MAX_NPROPS 6
typedef struct _plugstate_t plugstate_t;
typedef struct _plughandle_t plughandle_t;
struct _plugstate_t {
- //FIXME
- int32_t mode;
+ // ro
+ int32_t song;
+ int32_t pos;
+ int32_t rolling;
+ int32_t beats_per_minute;
+ // rw
+ int32_t beat_unit;
+ int32_t beats_per_bar;
};
struct _plughandle_t {
struct {
LV2_URID midi_event;
+ LV2_URID song;
+ LV2_URID pos;
+ LV2_URID rolling;
+ LV2_URID beats_per_minute;
} urid;
LV2_URID_Map *map;
@@ 32,8 42,6 @@ struct _plughandle_t {
LV2_Log_Log *log;
LV2_Log_Logger logger;
- timely_t timely;
-
const LV2_Atom_Sequence *event_in;
LV2_Atom_Sequence *event_out;
@@ 41,55 49,214 @@ struct _plughandle_t {
plugstate_t stash;
PROPS_T(props, MAX_NPROPS);
-
- bool rolling;
+
+ int64_t clocks;
+ int64_t nsamples;
+ int64_t last_clock;
+ double rate;
LV2_Atom_Forge_Ref ref;
};
static const props_def_t defs [MAX_NPROPS] = {
{
- .property = ORBIT_URI"#quantum_mode",
- .offset = offsetof(plugstate_t, mode),
- .type = LV2_ATOM__Int,
+ .property = ORBIT_URI"#song",
+ .offset = offsetof(plugstate_t, song),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = ORBIT_URI"#sync_position",
+ .offset = offsetof(plugstate_t, pos),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = ORBIT_URI"#pacemaker_beats_per_minute",
+ .offset = offsetof(plugstate_t, beats_per_minute),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = ORBIT_URI"#pacemaker_rolling",
+ .offset = offsetof(plugstate_t, rolling),
+ .type = LV2_ATOM__Bool
+ },
+ {
+ .property = ORBIT_URI"#pacemaker_beat_unit",
+ .offset = offsetof(plugstate_t, beat_unit),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = ORBIT_URI"#pacemaker_beats_per_bar",
+ .offset = offsetof(plugstate_t, beats_per_bar),
+ .type = LV2_ATOM__Int
}
};
static void
-_cb(timely_t *timely, int64_t frames UNUSED, LV2_URID type, void *data)
+_set_bpm(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, int32_t bpm)
{
- plughandle_t *handle = data;
+ if(bpm == handle->state.beats_per_minute)
+ {
+ return;
+ }
+
+ handle->state.beats_per_minute = bpm;
+
+ props_set(&handle->props, forge, frames, handle->urid.beats_per_minute, &handle->ref);
+}
- if(type == TIMELY_URI_SPEED(timely))
+static void
+_set_song(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, int32_t song)
+{
+ if(song == handle->state.song)
{
- handle->rolling = TIMELY_SPEED(timely) > 0.f ? true : false;
+ return;
+ }
- if(handle->rolling)
- {
- //FIXME
- }
- else
- {
- //FIXME
- }
+ handle->state.song = song;
+
+ props_set(&handle->props, forge, frames, handle->urid.song, &handle->ref);
+}
+
+static void
+_set_pos_raw(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, int32_t pos)
+{
+ if(pos == handle->state.pos)
+ {
+ return;
}
- else if(type == TIMELY_URI_BAR_BEAT(timely))
+
+ handle->state.pos = pos;
+
+ props_set(&handle->props, forge, frames, handle->urid.pos, &handle->ref);
+}
+
+static void
+_set_pos(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, int32_t pos)
+{
+ _set_pos_raw(handle, forge, frames, pos);
+
+ // update clocks based on song position
+ handle->clocks = handle->state.pos * 6;
+}
+
+static void
+_set_rolling(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, int32_t rolling)
+{
+ if(rolling == handle->state.rolling)
{
- if(handle->rolling)
- {
- //FIXME
- }
+ return;
}
- else if(type == TIMELY_URI_BAR(timely))
+
+ handle->state.rolling = rolling;
+
+ props_set(&handle->props, forge, frames, handle->urid.rolling, &handle->ref);
+}
+
+static void
+_handle_midi_song_select(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, const uint8_t *m)
+{
+ const int32_t song = m[1];
+
+ _set_song(handle, forge, frames, song);
+}
+
+static void
+_handle_midi_song_pos(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, const uint8_t *m)
+{
+ const int32_t lsb = m[1];
+ const int32_t msb = m[2];
+ const int32_t pos = (msb << 7) | lsb;
+
+ _set_pos(handle, forge, frames, pos);
+}
+
+static void
+_handle_midi_start(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames)
+{
+ _set_pos(handle, forge, frames, 0);
+ _set_rolling(handle, forge, frames, 1);
+}
+
+static void
+_handle_midi_continue(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames)
+{
+ // keep position
+ _set_rolling(handle, forge, frames, 1);
+}
+
+static void
+_handle_midi_stop(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames)
+{
+ _set_rolling(handle, forge, frames, 0);
+}
+
+static void
+_handle_midi_clock(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames)
+{
+ // advance clock
+ handle->clocks += 1;
+
+ const int32_t pos = handle->clocks / 6;
+ _set_pos_raw(handle, forge, frames, pos);
+
+ const int64_t this_clock = handle->nsamples + frames;
+ const int64_t nsamples = this_clock - handle->last_clock;
+ handle->last_clock = this_clock;
+
+ const double minutes = nsamples / handle->rate / 60.0;
+ const int32_t bpm = 1.0 / 24.0 / minutes; // FIXME use beat_unit
+ // FIXME filter this value
+
+ _set_bpm(handle, forge, frames, bpm);
+}
+
+static void
+_handle_midi(plughandle_t *handle, LV2_Atom_Forge *forge,
+ int64_t frames, const uint8_t *m)
+{
+ const uint8_t comm = m[0] & 0xf0;
+
+ switch(comm)
{
- if(handle->rolling)
+ case LV2_MIDI_MSG_SONG_SELECT:
{
- //FIXME
- }
+ _handle_midi_song_select(handle, forge, frames, m);
+ } break;
+ case LV2_MIDI_MSG_SONG_POS:
+ {
+ _handle_midi_song_pos(handle, forge, frames, m);
+ } break;
+ case LV2_MIDI_MSG_START:
+ {
+ _handle_midi_start(handle, forge, frames);
+ } break;
+ case LV2_MIDI_MSG_CONTINUE:
+ {
+ _handle_midi_continue(handle, forge, frames);
+ } break;
+ case LV2_MIDI_MSG_STOP:
+ {
+ _handle_midi_stop(handle, forge, frames);
+ } break;
+ case LV2_MIDI_MSG_CLOCK:
+ {
+ _handle_midi_clock(handle, forge, frames);
+ } break;
}
}
static LV2_Handle
-instantiate(const LV2_Descriptor* descriptor, double rate,
+instantiate(const LV2_Descriptor* descriptor, double rate UNUSED,
const char *bundle_path UNUSED, const LV2_Feature *const *features)
{
plughandle_t *handle = calloc(1, sizeof(plughandle_t));
@@ 99,6 266,8 @@ instantiate(const LV2_Descriptor* descriptor, double rate,
}
mlock(handle, sizeof(plughandle_t));
+ handle->rate = rate;
+
for(unsigned i=0; features[i]; i++)
{
if(!strcmp(features[i]->URI, LV2_URID__map))
@@ 126,10 295,6 @@ instantiate(const LV2_Descriptor* descriptor, double rate,
handle->urid.midi_event = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent);
- timely_mask_t mask = TIMELY_MASK_BAR_BEAT_WHOLE
- | TIMELY_MASK_BAR_WHOLE
- | TIMELY_MASK_SPEED;
- timely_init(&handle->timely, handle->map, rate, mask, _cb, handle);
lv2_atom_forge_init(&handle->forge, handle->map);
if(!props_init(&handle->props, descriptor->URI,
@@ 141,6 306,15 @@ instantiate(const LV2_Descriptor* descriptor, double rate,
return NULL;
}
+ handle->urid.song = props_map(&handle->props,
+ ORBIT_URI"#song");
+ handle->urid.pos = props_map(&handle->props,
+ ORBIT_URI"#sync_position");
+ handle->urid.beats_per_minute = props_map(&handle->props,
+ ORBIT_URI"#pacemaker_beats_per_minute");
+ handle->urid.rolling = props_map(&handle->props,
+ ORBIT_URI"#pacemaker_rolling");
+
return handle;
}
@@ 166,7 340,6 @@ static void
run(LV2_Handle instance, uint32_t nsamples)
{
plughandle_t *handle = instance;
- uint32_t last_t = 0;
const uint32_t capacity = handle->event_out->atom.size;
LV2_Atom_Forge_Frame frame;
@@ 178,16 351,19 @@ run(LV2_Handle instance, uint32_t nsamples)
LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
{
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
- if(!timely_advance(&handle->timely, obj, last_t, ev->time.frames))
+
+ if(obj->atom.type == handle->urid.midi_event)
+ {
+ const uint8_t *m = LV2_ATOM_BODY_CONST(&obj->atom);
+
+ _handle_midi(handle, &handle->forge, ev->time.frames, m);
+ }
+ else
{
props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref);
}
-
- last_t = ev->time.frames;
}
- timely_advance(&handle->timely, NULL, last_t, nsamples);
-
if(handle->ref)
{
lv2_atom_forge_pop(&handle->forge, &frame);
@@ 201,6 377,8 @@ run(LV2_Handle instance, uint32_t nsamples)
lv2_log_trace(&handle->logger, "forge buffer overflow\n");
}
}
+
+ handle->nsamples += nsamples;
}
static void