summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--matriseq.c166
1 files changed, 113 insertions, 53 deletions
diff --git a/matriseq.c b/matriseq.c
index bb9c1fe..1831599 100644
--- a/matriseq.c
+++ b/matriseq.c
@@ -33,8 +33,12 @@
#define MATRISEQ_URI "http://drobilla.net/plugins/matriseq"
-static const uint32_t STEP_TYPE = 8;
-static const uint32_t RING_SIZE = 4096;
+#define GRID_H 8
+#define GRID_W 8
+#define SEQ_H (8 * 8)
+#define NOTE_MIN 28
+#define STEP_TYPE 16
+#define RING_SIZE 4096
typedef enum {
MATRISEQ_IN = 0,
@@ -62,16 +66,19 @@ typedef struct {
// USB stuff
NaubWorld* naub;
- ZixThread thread;
ZixRing* ring;
+ ZixThread thread;
bool exit;
// State
- uint32_t time_frames;
- uint8_t grid[8][8];
- uint32_t step;
double rate;
float bpm;
+ uint32_t beats_per_bar;
+ uint32_t time_frames;
+ uint32_t step;
+ uint8_t page_x;
+ uint8_t page_y;
+ uint32_t seq[SEQ_H][STEP_TYPE];
} Matriseq;
// Log a message to the host if available, or stderr otherwise.
@@ -120,13 +127,17 @@ instantiate(const LV2_Descriptor* descriptor,
self->uris.midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
lv2_atom_forge_init(&self->forge, self->map);
- if (self) {
- self->ring = zix_ring_new(RING_SIZE);
- self->rate = rate;
- self->bpm = 140.0f;
+ // Initialise USB stuff
+ self->naub = NULL;
+ self->ring = zix_ring_new(RING_SIZE);
- zix_ring_mlock(self->ring);
- }
+ // Initialise state
+ self->rate = rate;
+ self->bpm = 140.0f;
+ self->beats_per_bar = 4;
+ self->page_y = 1; // Start at note 36 (kick)
+
+ zix_ring_mlock(self->ring);
return (LV2_Handle)self;
}
@@ -148,11 +159,19 @@ connect_port(LV2_Handle instance,
}
}
+static uint32_t*
+get_cell(Matriseq* self, NaubControlID control)
+{
+ const uint32_t x = (self->page_x * GRID_W) + control.x;
+ const uint32_t y = (self->page_y * GRID_H) + (7 - control.y);
+ return &self->seq[y][x];
+}
+
static void
set_button(Matriseq* self, NaubControlID control, bool active)
{
int32_t value = 0;
- if (self->grid[control.y][control.x]) {
+ if (*get_cell(self, control)) {
value = active ? naub_rgb(1, 0, 0) : naub_rgb(1, 1, 0);
} else {
value = active ? naub_rgb(0, 0.4, 0) : naub_rgb(0, 0, 0);
@@ -161,8 +180,27 @@ set_button(Matriseq* self, NaubControlID control, bool active)
}
static void
-naub_event(void* instance,
- const NaubEvent* event)
+set_page_indicators(Matriseq* self)
+{
+ const NaubControlID page_x_but = { 0, 1, self->page_x, 0 };
+ const NaubControlID page_y_but = { 0, 2, 0, 7 - self->page_y };
+ naub_set_control(self->naub, page_x_but, naub_rgb(0, 1, 0));
+ naub_set_control(self->naub, page_y_but, naub_rgb(0, 1, 0));
+}
+
+static void
+show_page(Matriseq* self)
+{
+ for (uint32_t y = 0; y < 8; ++y) {
+ for (uint32_t x = 0; x < 8; ++x) {
+ const NaubControlID control = { 0, 0, x, y };
+ set_button(self, control, x == self->step);
+ }
+ }
+}
+
+static void
+pad_event(void* instance, const NaubEvent* event)
{
Matriseq* self = (Matriseq*)instance;
if (event->type != NAUB_EVENT_BUTTON) { // Odd...
@@ -170,50 +208,75 @@ naub_event(void* instance,
}
const NaubControlID control = event->button.control;
- if (control.group != 0) {
- return;
- }
- /*
- fprintf(stderr, "PRESS %d:%d.%d.%d\n",
- control.device,
- control.group,
- control.x,
- control.y);
- */
+ if (control.group == 1 && event->button.pressed) {
+ const NaubControlID old_page_x_but = { 0, 1, self->page_x, 0 };
+ const NaubControlID old_page_y_but = { 0, 2, 0, 7 - self->page_y };
+ if (control.x == 0 && self->page_y < 7) {
+ ++self->page_y;
+ } else if (control.x == 1 && self->page_y > 0) {
+ --self->page_y;
+ } else if (control.x == 2 && self->page_x > 0) {
+ --self->page_x;
+ } else if (control.x == 3 && self->page_x < 1) {
+ ++self->page_x;
+ } else {
+ return;
+ }
- if (event->button.pressed) {
- naub_set_control(self->naub, control, naub_rgb(1, 0, 0));
- } else {
- uint8_t* cell = &self->grid[control.y][control.x];
- *cell = *cell ? 0 : 1;
- set_button(self, control, self->step == control.y);
+ // Turn off old page indicator buttons
+ naub_set_control(self->naub, old_page_x_but, naub_rgb(0, 0, 0));
+ naub_set_control(self->naub, old_page_y_but, naub_rgb(0, 0, 0));
+
+ // Turn on new page indicator buttons
+ set_page_indicators(self);
+
+ // Update grid display
+ show_page(self);
+ } else if (control.group == 0) {
+ if (event->button.pressed) {
+ naub_set_control(self->naub, control, naub_rgb(1, 0, 0));
+ } else {
+ uint32_t* cell = get_cell(self, control);
+ *cell = *cell ? 0 : 1;
+ set_button(self, control, self->step == control.y);
+ }
}
naub_flush(self->naub);
}
static void*
-thread_run(void* instance)
+pad_thread(void* instance)
{
Matriseq* self = (Matriseq*)instance;
uint32_t step = self->step;
+ // Initialise pad
+ set_page_indicators(self);
+
while (!naub_handle_events_timeout(self->naub, 10) && !self->exit) {
uint32_t new_step;
if (zix_ring_read_space(self->ring) >= sizeof(new_step)) {
zix_ring_read(self->ring, &new_step, sizeof(new_step));
+ const uint32_t begin = self->page_x * GRID_W;
+ const uint32_t end = (self->page_x + 1) * GRID_W;
+
// De-highlight old active row
- for (int y = 0; y < 8; ++y) {
- const NaubControlID control = { 0, 0, step, y };
- set_button(self, control, false);
+ if (step >= begin && step < end) {
+ for (int y = 0; y < 8; ++y) {
+ const NaubControlID control = { 0, 0, step % GRID_W, y };
+ set_button(self, control, false);
+ }
}
// Highlight new active row
- for (int y = 0; y < 8; ++y) {
- const NaubControlID control = { 0, 0, new_step, y };
- set_button(self, control, true);
+ if (new_step >= begin && new_step < end) {
+ for (int y = 0; y < 8; ++y) {
+ const NaubControlID control = { 0, 0, new_step % GRID_W, y };
+ set_button(self, control, true);
+ }
}
// Send bulk update to device
@@ -229,15 +292,16 @@ static void
activate(LV2_Handle instance)
{
Matriseq* self = (Matriseq*)instance;
- self->naub = naub_world_new(self, naub_event);
+ self->naub = naub_world_new(self, pad_event);
if (self->naub) {
if (!naub_world_open(
self->naub, NAUB_VENDOR_NOVATION, NAUB_PRODUCT_LAUNCHPAD)) {
- fprintf(stderr, "Opened Launchpad\n");
- if (zix_thread_create(&self->thread, 1024, thread_run, self)) {
- fprintf(stderr, "Failed to create thread\n");
+ if (zix_thread_create(&self->thread, 1024, pad_thread, self)) {
+ print(self, self->uris.log_Error, "Failed to create thread\n");
return;
}
+ } else {
+ print(self, self->uris.log_Error, "Failed to open controller\n");
}
}
}
@@ -246,10 +310,9 @@ static void
run(LV2_Handle instance, uint32_t n_frames)
{
Matriseq* self = (Matriseq*)instance;
- const float bps = self->bpm / 60.0f; // Beats per second
- const float spb = 1.0 / bps; // Seconds per beat
- //const float spt = spb / STEP_TYPE;
- const float spt = spb / 2; // Seconds per tick (step)
+
+ const float s_per_beat = 60.0f / self->bpm;
+ const float s_per_step = s_per_beat * self->beats_per_bar / STEP_TYPE;
// Prepare for writing to out port
const uint32_t out_capacity = self->out->atom.size;
@@ -259,10 +322,9 @@ run(LV2_Handle instance, uint32_t n_frames)
LV2_Atom_Forge_Frame out_frame;
lv2_atom_forge_sequence_head(&self->forge, &out_frame, 0);
- //fprintf(stderr, "BPS: %f SPB: %f\n", bps, spb);
for (uint32_t i = 0; i < n_frames; ++i) {
const double time_s = self->time_frames / self->rate;
- const uint32_t step = (uint32_t)(time_s / spt) % STEP_TYPE;
+ const uint32_t step = (uint32_t)(time_s / s_per_step) % STEP_TYPE;
if (step != self->step) {
// Update step and frame time
self->step = step;
@@ -273,14 +335,12 @@ run(LV2_Handle instance, uint32_t n_frames)
}
// Notify USB thread of new step
- //fprintf(stderr, "LV2 STEP %d @ %f\n", self->step, time_s);
zix_ring_write(self->ring, &self->step, sizeof(self->step));
// Send note ons for enabled notes this step
- for (uint32_t y = 0; y < 8; ++y) {
- if (self->grid[y][step]) {
- const uint8_t note = (7 - y) + 36;
- const uint8_t ev[] = { 0x90, note, 0x40 };
+ for (uint32_t y = 0; y < SEQ_H; ++y) {
+ if (self->seq[y][step]) {
+ const uint8_t ev[] = { 0x90, NOTE_MIN + y, 0x40 };
lv2_atom_forge_frame_time(&self->forge, i);
lv2_atom_forge_atom(&self->forge, 3, self->uris.midi_MidiEvent);
lv2_atom_forge_write(&self->forge, ev, 3);