summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-11-09 06:58:14 +0000
committerDavid Robillard <d@drobilla.net>2015-11-09 06:58:14 +0000
commit92d4404abbe1373386e7fa7bd42a3acd1e4eeba3 (patch)
tree2e87d6ecccfeaef07f39b57e21421d54dfc5e873
parentd75f8b95fca74098d8b939e7236acde7c6b3a9ae (diff)
downloadmatriseq.lv2-92d4404abbe1373386e7fa7bd42a3acd1e4eeba3.tar.gz
matriseq.lv2-92d4404abbe1373386e7fa7bd42a3acd1e4eeba3.tar.bz2
matriseq.lv2-92d4404abbe1373386e7fa7bd42a3acd1e4eeba3.zip
Rewrite Matriseq for the Launchpad Pro
git-svn-id: http://svn.drobilla.net/lad/trunk/plugins/matriseq@5817 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--matriseq.c483
-rw-r--r--matriseq.ttl21
-rw-r--r--wscript20
-rw-r--r--zix/common.h83
-rw-r--r--zix/ring.c225
-rw-r--r--zix/ring.h130
-rw-r--r--zix/thread.h133
7 files changed, 307 insertions, 788 deletions
diff --git a/matriseq.c b/matriseq.c
index 4290709..5a111ae 100644
--- a/matriseq.c
+++ b/matriseq.c
@@ -1,6 +1,6 @@
/*
- This file is part of Matriseq.
- Copyright 2007-2012 David Robillard <http://drobilla.net/>
+ Matriseq, a simple step sequencer for the Launchpad Pro.
+ Copyright 2007-2015 David Robillard <http://drobilla.net/>
Matriseq is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
@@ -21,12 +21,9 @@
#include <stdlib.h>
#include <string.h>
-#include "naub/naub.h"
-#include "zix/thread.h"
-#include "zix/ring.h"
-
#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
#include "lv2/lv2plug.in/ns/ext/log/log.h"
+#include "lv2/lv2plug.in/ns/ext/log/logger.h"
#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
#include "lv2/lv2plug.in/ns/ext/time/time.h"
#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
@@ -37,15 +34,69 @@
#define GRID_H 8
#define GRID_W 8
#define SEQ_H (8 * 8)
+#define SEQ_W 16
#define NOTE_MIN 28
#define STEP_TYPE 16
#define RING_SIZE 4096
+static const uint8_t msg_set_col[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xC };
+static const uint8_t msg_set_row[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xD };
+static const uint8_t msg_show_text[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0x14 };
+static const uint8_t msg_light_led[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xA };
+
typedef enum {
- MATRISEQ_IN = 0,
- MATRISEQ_OUT = 1
+ BLACK = 0,
+ DARK_GREY = 1,
+ LIGHT_GREY = 2,
+ WHITE = 3,
+ RED = 5,
+ YELLOW = 13,
+ DARK_GREEN = 15,
+ GREEN = 21
+} Color;
+
+typedef enum {
+ MATRISEQ_CONTROL_IN = 0,
+ MATRISEQ_CONTROL_OUT = 1,
+ MATRISEQ_PLAYBACK = 2
} PortIndex;
+typedef enum {
+ BOTTOM_1 = 1,
+ BOTTOM_2 = 2,
+ BOTTOM_3 = 3,
+ BOTTOM_4 = 4,
+ BOTTOM_5 = 5,
+ BOTTOM_6 = 6,
+ BOTTOM_7 = 7,
+ BOTTOM_8 = 8,
+ LEFT_1 = 10,
+ LEFT_2 = 20,
+ LEFT_3 = 30,
+ LEFT_4 = 40,
+ LEFT_5 = 50,
+ LEFT_6 = 60,
+ LEFT_7 = 70,
+ LEFT_8 = 80,
+ RIGHT_1 = 19,
+ RIGHT_2 = 29,
+ RIGHT_3 = 39,
+ RIGHT_4 = 49,
+ RIGHT_5 = 59,
+ RIGHT_6 = 69,
+ RIGHT_7 = 79,
+ RIGHT_8 = 89,
+ TOP_1 = 91,
+ TOP_2 = 92,
+ TOP_3 = 93,
+ TOP_4 = 94,
+ TOP_5 = 95,
+ TOP_6 = 96,
+ TOP_7 = 97,
+ TOP_8 = 98,
+ TOP_9 = 99
+} ButtonID;
+
// URIDs used by this plugin
typedef struct {
LV2_URID atom_Blank;
@@ -60,23 +111,19 @@ typedef struct {
typedef struct {
// Port buffers
- LV2_Atom_Sequence* in;
- LV2_Atom_Sequence* out;
+ LV2_Atom_Sequence* control_in;
+ LV2_Atom_Sequence* control_out;
+ LV2_Atom_Sequence* playback;
// Features
LV2_URID_Map* map;
LV2_Log_Log* log;
- // LV2 stuff
- LV2_Atom_Forge forge;
+ LV2_Log_Logger logger;
+ LV2_Atom_Forge control_forge;
+ LV2_Atom_Forge playback_forge;
MatriseqURIs uris;
- // USB stuff
- NaubWorld* naub;
- ZixRing* ring;
- ZixThread thread;
- bool exit;
-
// State
double rate;
float bpm;
@@ -86,24 +133,12 @@ typedef struct {
uint32_t step;
uint8_t page_x;
uint8_t page_y;
- uint32_t seq[SEQ_H][STEP_TYPE];
+ uint32_t seq[SEQ_H][SEQ_W];
+ uint32_t last_inquiry;
+ bool refresh;
+ bool attached;
} Matriseq;
-// Log a message to the host if available, or stderr otherwise.
-LV2_LOG_FUNC(3, 4)
-static void
-print(Matriseq* self, LV2_URID type, const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- if (self->log) {
- self->log->vprintf(self->log->handle, type, fmt, args);
- } else {
- vfprintf(stderr, fmt, args);
- }
- va_end(args);
-}
-
static LV2_Handle
instantiate(const LV2_Descriptor* descriptor,
double rate,
@@ -124,7 +159,7 @@ instantiate(const LV2_Descriptor* descriptor,
}
}
if (!self->map) {
- print(self, self->uris.log_Error, "Missing feature urid:map.\n");
+ lv2_log_error(&self->logger, "Missing feature urid:map\n");
free(self);
return NULL;
}
@@ -139,21 +174,18 @@ instantiate(const LV2_Descriptor* descriptor,
self->uris.time_barBeat = map->map(map->handle, LV2_TIME__barBeat);
self->uris.time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);
self->uris.time_speed = map->map(map->handle, LV2_TIME__speed);
- lv2_atom_forge_init(&self->forge, self->map);
-
- // Initialise USB stuff
- self->naub = NULL;
- self->ring = zix_ring_new(RING_SIZE);
+ lv2_atom_forge_init(&self->control_forge, self->map);
+ lv2_atom_forge_init(&self->playback_forge, self->map);
+ lv2_log_logger_init(&self->logger, self->map, self->log);
// Initialise state
self->rate = rate;
self->bpm = 140.0f;
- self->speed = 0.0f;
+ //self->speed = 0.0f;
+ self->speed = 1.0f;
self->beats_per_bar = 4;
self->page_y = 1; // Start at note 36 (kick)
- zix_ring_mlock(self->ring);
-
return (LV2_Handle)self;
}
@@ -165,165 +197,198 @@ connect_port(LV2_Handle instance,
Matriseq* self = (Matriseq*)instance;
switch ((PortIndex)port) {
- case MATRISEQ_IN:
- self->in = (LV2_Atom_Sequence*)data;
+ case MATRISEQ_CONTROL_IN:
+ self->control_in = (LV2_Atom_Sequence*)data;
break;
- case MATRISEQ_OUT:
- self->out = (LV2_Atom_Sequence*)data;
+ case MATRISEQ_CONTROL_OUT:
+ self->control_out = (LV2_Atom_Sequence*)data;
+ break;
+ case MATRISEQ_PLAYBACK:
+ self->playback = (LV2_Atom_Sequence*)data;
break;
}
}
static uint32_t*
-get_cell(Matriseq* self, NaubControlID control)
+get_cell(Matriseq* self, uint8_t pad_x, uint8_t pad_y)
{
- 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];
+ const uint32_t seq_x = (self->page_x * GRID_W) + pad_x;
+ const uint32_t seq_y = (self->page_y * GRID_H) + pad_y;
+ return &self->seq[seq_y][seq_x];
}
static void
-set_button(Matriseq* self, NaubControlID control, bool active)
+on_button(Matriseq* self, uint8_t num, uint8_t value)
{
- int32_t value = 0;
- 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);
+ if (!value) {
+ return; // Ignore button release
}
- naub_set_control(self->naub, control, value);
-}
-static void
-set_column(Matriseq* self, uint32_t step, bool active)
-{
- for (int y = 0; y < 8; ++y) {
- const NaubControlID control = { 0, 0, step % GRID_W, y };
- set_button(self, control, active);
+ if (num == TOP_1 && self->page_y < 7) { // Page up
+ ++self->page_y;
+ self->refresh = true;
+ } else if (num == TOP_2 && self->page_y > 0) { // Page down
+ --self->page_y;
+ self->refresh = true;
+ } else if (num == TOP_3 && self->page_x > 0) { // Page left
+ --self->page_x;
+ self->refresh = true;
+ } else if (num == TOP_4 && self->page_x < 1) { // Page right
+ ++self->page_x;
+ self->refresh = true;
+ } else if (num % 10 == 9) { // Right button column
+ self->page_y = num / 10 - 1;
+ self->refresh = true;
+ } else if (num <= BOTTOM_8) { // Bottom button row
+ if ((num - 1) * GRID_W < SEQ_W) {
+ self->page_x = num - 1;
+ self->refresh = true;
+ }
}
}
static void
-set_page_indicators(Matriseq* self)
+write_control(Matriseq* self, uint32_t t, const uint8_t* msg, uint32_t n)
{
- 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));
+ lv2_atom_forge_frame_time(&self->control_forge, t);
+ lv2_atom_forge_atom(&self->control_forge, n, self->uris.midi_MidiEvent);
+ lv2_atom_forge_write(&self->control_forge, msg, n);
}
+/** Pad on the central 8x8 grid pressed. */
static void
-show_page(Matriseq* self)
+on_pad(Matriseq* self, uint32_t t, uint8_t x, uint8_t y, uint8_t vel)
{
- 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);
- }
+ if (vel > 0) {
+ // Flip sequence cell
+ uint32_t* cell = get_cell(self, x, y);
+ *cell = !*cell;
+
+ // Illuminate pad
+ const uint8_t msg[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xA,
+ (y + 1) * 10 + x + 1,
+ *cell ? GREEN : BLACK,
+ 0xF7 };
+
+ write_control(self, t, msg, sizeof(msg));
}
}
-static void
-pad_event(void* instance, const NaubEvent* event)
+static uint8_t
+step_button_color(Matriseq* self, uint8_t step, uint8_t col)
{
- Matriseq* self = (Matriseq*)instance;
- if (event->type != NAUB_EVENT_BUTTON) { // Odd...
- return;
- }
-
- const NaubControlID control = event->button.control;
-
- 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;
- }
+ const uint32_t page_begin = self->page_x * 8;
+ const uint32_t page_end = (self->page_x + 1) * 8;
- // 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);
- }
+ if (step >= page_begin && step < page_end && step % 8 == col) {
+ return GREEN;
+ } else if (self->page_x == col) {
+ return DARK_GREY;
}
-
- naub_flush(self->naub);
+ return BLACK;
}
-static void*
-pad_thread(void* instance)
+/** Display scrolling text banner. */
+static void
+pad_show_text(Matriseq* self, uint32_t t, const char* text)
{
- Matriseq* self = (Matriseq*)instance;
- uint32_t step = self->step;
-
- // Initialise pad
- set_page_indicators(self);
- set_column(self, step, true);
- naub_flush(self->naub);
-
- 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 size_t len = strlen(text);
+ uint8_t msg[sizeof(msg_show_text) + 3 + len + 1];
+ memcpy(msg, msg_show_text, sizeof(msg_show_text));
+
+ uint8_t* ptr = msg + sizeof(msg_show_text);
+ *ptr++ = DARK_GREY; // Text color
+ *ptr++ = 0; // Loop
+ *ptr++ = 7; // Speed (1-7)
+ memcpy((char*)ptr, text, len);
+ ptr += len;
+ *ptr++ = 0xF7;
+ write_control(self, t, msg, sizeof(msg));
+}
- const uint32_t begin = self->page_x * GRID_W;
- const uint32_t end = (self->page_x + 1) * GRID_W;
+/** Update step indicators when the page has not changed. */
+static void
+pad_update_step(Matriseq* self, uint32_t t, uint32_t old_step, uint32_t new_step)
+{
+ // Turn off old step indicator
+ const uint8_t off_msg[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xA,
+ old_step % 8 + 1,
+ step_button_color(self, new_step, old_step % 8),
+ 0xF7 };
+ write_control(self, t, off_msg, sizeof(off_msg));
+
+ // Turn on new step indicator
+ const uint8_t on_msg[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xA,
+ new_step % 8 + 1,
+ step_button_color(self, new_step, new_step % 8),
+ 0xF7 };
+ write_control(self, t, on_msg, sizeof(on_msg));
+}
- // De-highlight old active row
- if (step >= begin && step < end) {
- set_column(self, step, false);
- }
+/** Refresh page and step indicators. */
+static void
+pad_refresh_position(Matriseq* self, uint32_t t)
+{
+ // Set bottom horizontal tick/page indicators
+ uint8_t xmsg[sizeof(msg_set_row) + 11];
+ memcpy(xmsg, msg_set_row, sizeof(msg_set_row));
+
+ uint8_t* ptr = xmsg + sizeof(msg_set_row);
+ *ptr++ = 0; // Row number 0 (bottom)
+ *ptr++ = 0; // Non-existent bottom left button color
+ for (int i = 0; i < 8; ++i) {
+ *ptr++ = step_button_color(self, self->step, i);
+ }
+ *ptr++ = 0xF7;
+ write_control(self, t, xmsg, sizeof(xmsg));
+
+ // Set right vertical page indicators
+ uint8_t ymsg[sizeof(msg_set_col) + 11];
+ memcpy(ymsg, msg_set_col, sizeof(msg_set_col));
+
+ ptr = ymsg + sizeof(msg_set_col);
+ *ptr++ = 9; // Column number 9 (right)
+ *ptr++ = 0; // Non-existent bottom right button color
+ for (int i = 0; i < 8; ++i) {
+ *ptr++ = self->page_y == i ? DARK_GREY : BLACK;
+ }
+ *ptr++ = 0xF7;
+ write_control(self, t, ymsg, sizeof(ymsg));
+}
- // Highlight new active row
- if (new_step >= begin && new_step < end) {
- set_column(self, new_step, true);
- }
+static void
+pad_clear(Matriseq* self, uint32_t t)
+{
+ const uint8_t msg[] = { 0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0xE, 0, 0xF7 };
+ write_control(self, t, msg, sizeof(msg));
+}
- // Send bulk update to device
- naub_flush(self->naub);
+static void
+pad_refresh_grid(Matriseq* self, uint32_t t)
+{
+ uint8_t msg[136];
+ memcpy(msg, msg_light_led, sizeof(msg_light_led));
+ uint8_t* ptr = msg + sizeof(msg_light_led);
+ for (uint32_t y = 0; y < 8; ++y) {
+ for (uint32_t x = 0; x < 8; ++x) {
+ const uint32_t gx = (self->page_x * GRID_W) + x;
+ const uint32_t gy = (self->page_y * GRID_H) + y;
- step = new_step;
+ *ptr++ = (y + 1) * 10 + x + 1;
+ *ptr++ = self->seq[gy][gx] ? GREEN : BLACK;
}
}
- return NULL;
+
+ *ptr++ = 0xF7;
+ write_control(self, t, msg, sizeof(msg));
}
static void
-activate(LV2_Handle instance)
+send_device_inquiry(Matriseq* self, uint32_t t)
{
- Matriseq* self = (Matriseq*)instance;
- self->naub = naub_world_new(self, pad_event);
- if (self->naub) {
- if (!naub_world_open(
- self->naub, NAUB_VENDOR_NOVATION, NAUB_PRODUCT_LAUNCHPAD)) {
- 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");
- }
- }
+ const uint8_t msg[] = { 0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7 };
+ write_control(self, t, msg, sizeof(msg));
+ self->last_inquiry = self->time_frames;
}
static void
@@ -335,16 +400,25 @@ run(LV2_Handle instance, uint32_t n_frames)
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;
- lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->out, out_capacity);
-
- // Initialise output port to empty sequence
- LV2_Atom_Forge_Frame out_frame;
- lv2_atom_forge_sequence_head(&self->forge, &out_frame, 0);
+ // Set output buffers for forges
+ lv2_atom_forge_set_buffer(
+ &self->control_forge, (uint8_t*)self->control_out, self->control_out->atom.size);
+ lv2_atom_forge_set_buffer(
+ &self->playback_forge, (uint8_t*)self->playback, self->playback->atom.size);
+
+ // Initialise outputs to empty sequence
+ LV2_Atom_Forge_Frame control_frame;
+ LV2_Atom_Forge_Frame playback_frame;
+ lv2_atom_forge_sequence_head(&self->control_forge, &control_frame, 0);
+ lv2_atom_forge_sequence_head(&self->playback_forge, &playback_frame, 0);
+
+ if (!self->attached && self->time_frames - self->last_inquiry > self->rate) {
+ // Haven't heard from Launchpad, send device inquiry once a second
+ send_device_inquiry(self, 0);
+ }
// Work forwards in time frame by frame, handling events as we go
- const LV2_Atom_Sequence* in = self->in;
+ const LV2_Atom_Sequence* in = self->control_in;
const LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body);
for (uint32_t t = 0; t < n_frames; ++t) {
while (!lv2_atom_sequence_is_end(&in->body, in->atom.size, ev) &&
@@ -371,6 +445,27 @@ run(LV2_Handle instance, uint32_t n_frames)
self->speed = ((LV2_Atom_Float*)speed)->body;
}
}
+ } else if (ev->body.type == uris->midi_MidiEvent) {
+ const uint8_t* const msg = (const uint8_t*)(ev + 1);
+ switch (lv2_midi_message_type(msg)) {
+ case LV2_MIDI_MSG_NOTE_ON:
+ on_pad(self, t, msg[1] % 10 - 1, msg[1] / 10 - 1, msg[2]);
+ break;
+ case LV2_MIDI_MSG_CONTROLLER:
+ on_button(self, msg[1], msg[2]);
+ break;
+ case LV2_MIDI_MSG_SYSTEM_EXCLUSIVE:
+ if (msg[1] == 0x7E) {
+ // Launchpad responded to device inquiry, initialise
+ pad_clear(self, t);
+ pad_refresh_position(self, t);
+ pad_refresh_grid(self, t);
+ pad_show_text(self, t, "Matriseq");
+ self->attached = true;
+ }
+ default:
+ break;
+ }
}
ev = lv2_atom_sequence_next(ev);
@@ -380,21 +475,30 @@ run(LV2_Handle instance, uint32_t n_frames)
const uint32_t step = (uint32_t)(time_s / s_per_step) % STEP_TYPE;
if (step != self->step) {
// Update step
+ const uint32_t last_step = self->step;
+ pad_update_step(self, t, self->step, step);
self->step = step;
if (step == 0) {
self->time_frames = 0;
}
- // Notify USB thread of new step
- zix_ring_write(self->ring, &self->step, sizeof(self->step));
+ // Send note offs for enabled notes last step
+ for (uint32_t y = 0; y < SEQ_H; ++y) {
+ if (self->seq[y][last_step]) {
+ const uint8_t off[] = { 0x80, NOTE_MIN + y, 0x40 };
+ lv2_atom_forge_frame_time(&self->playback_forge, t);
+ lv2_atom_forge_atom(&self->playback_forge, 3, self->uris.midi_MidiEvent);
+ lv2_atom_forge_write(&self->playback_forge, off, 3);
+ }
+ }
// Send note ons for enabled notes this step
for (uint32_t y = 0; y < SEQ_H; ++y) {
if (self->seq[y][step]) {
const uint8_t on[] = { 0x90, NOTE_MIN + y, 0x40 };
- lv2_atom_forge_frame_time(&self->forge, t);
- lv2_atom_forge_atom(&self->forge, 3, self->uris.midi_MidiEvent);
- lv2_atom_forge_write(&self->forge, on, 3);
+ lv2_atom_forge_frame_time(&self->playback_forge, t);
+ lv2_atom_forge_atom(&self->playback_forge, 3, self->uris.midi_MidiEvent);
+ lv2_atom_forge_write(&self->playback_forge, on, 3);
}
}
}
@@ -403,25 +507,18 @@ run(LV2_Handle instance, uint32_t n_frames)
++self->time_frames;
}
}
-}
-static void
-deactivate(LV2_Handle instance)
-{
- Matriseq* self = (Matriseq*)instance;
- self->exit = true;
- void* thread_ret = NULL;
- zix_thread_join(self->thread, &thread_ret);
- naub_world_free(self->naub);
- self->naub = NULL;
+ if (self->refresh) {
+ pad_refresh_position(self, n_frames - 1);
+ pad_refresh_grid(self, n_frames - 1);
+ self->refresh = false;
+ }
}
static void
cleanup(LV2_Handle instance)
{
- Matriseq* self = (Matriseq*)instance;
- zix_ring_free(self->ring);
- free(self);
+ free(instance);
}
static const void*
@@ -434,21 +531,15 @@ static const LV2_Descriptor descriptor = {
MATRISEQ_URI,
instantiate,
connect_port,
- activate,
+ NULL,
run,
- deactivate,
+ NULL,
cleanup,
extension_data
};
-LV2_SYMBOL_EXPORT
-const LV2_Descriptor*
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return (index == 0) ? &descriptor : NULL;
}
diff --git a/matriseq.ttl b/matriseq.ttl
index a1c5dd8..a39b2df 100644
--- a/matriseq.ttl
+++ b/matriseq.ttl
@@ -5,6 +5,7 @@
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rsz: <http://lv2plug.in/ns/ext/resize-port#> .
@prefix time: <http://lv2plug.in/ns/ext/time#> .
<http://drobilla.net/drobilla#me>
@@ -24,16 +25,26 @@
a lv2:InputPort ,
atom:AtomPort ;
atom:bufferType atom:Sequence ;
- atom:supports time:Position ;
+ atom:supports time:Position, midi:MidiEvent ;
lv2:index 0 ;
- lv2:symbol "in" ;
- lv2:name "In"
+ lv2:symbol "control_in" ;
+ lv2:name "Control In"
] , [
a lv2:OutputPort ,
atom:AtomPort ;
atom:bufferType atom:Sequence ;
atom:supports midi:MidiEvent ;
+ rsz:minimumSize 4096 ;
lv2:index 1 ;
- lv2:symbol "out" ;
- lv2:name "Out"
+ lv2:symbol "control_out" ;
+ lv2:name "Control Out" ;
+ ] , [
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports midi:MidiEvent ;
+ rsz:minimumSize 4096 ;
+ lv2:index 2 ;
+ lv2:symbol "playback" ;
+ lv2:name "Playback"
] .
diff --git a/wscript b/wscript
index 4506bfd..cd6f91d 100644
--- a/wscript
+++ b/wscript
@@ -1,5 +1,6 @@
#!/usr/bin/env python
import waflib.extras.autowaf as autowaf
+import re
MATRISEQ_VERSION = '1.0.0'
@@ -22,13 +23,6 @@ def configure(conf):
autowaf.display_header('Matriseq Configuration')
autowaf.check_pkg(conf, 'lv2', atleast_version='1.0.0', uselib_store='LV2')
- autowaf.check_pkg(conf, 'naub-0', atleast_version='0.0.0', uselib_store='NAUB')
-
- # Check for mlock
- conf.check(function_name='mlock',
- header_name='sys/mman.h',
- define_name='HAVE_MLOCK',
- mandatory=False)
# Set env.pluginlib_PATTERN
pat = conf.env.cshlib_PATTERN
@@ -58,20 +52,14 @@ def build(bld):
install_path = '${LV2DIR}/%s' % bundle,
LIB_EXT = bld.env.pluginlib_EXT)
- # Create a build environment that builds module-style library names
- # e.g. matriseq.so instead of libmatriseq.so
- # Note for C++ you must set cxxshlib_PATTERN instead
- penv = bld.env.derive()
- penv.cshlib_PATTERN = bld.env.pluginlib_PATTERN
-
# Build plugin library
obj = bld(features = 'c cshlib',
- env = penv,
- source = ['matriseq.c', 'zix/ring.c'],
+ source = ['matriseq.c'],
name = 'matriseq',
target = '%s/matriseq' % bundle,
install_path = '${LV2DIR}/%s' % bundle,
includes = ['.'],
lib = ['pthread'])
- autowaf.use_lib(bld, obj, 'LV2 NAUB')
+ autowaf.use_lib(bld, obj, 'LV2')
+ obj.env.cshlib_PATTERN = re.sub('^lib', '', bld.env.cshlib_PATTERN)
diff --git a/zix/common.h b/zix/common.h
deleted file mode 100644
index 59e1f55..0000000
--- a/zix/common.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- Copyright 2011 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef ZIX_COMMON_H
-#define ZIX_COMMON_H
-
-/**
- @addtogroup zix
- @{
-*/
-
-/** @cond */
-#ifdef ZIX_SHARED
-# ifdef _WIN32
-# define ZIX_LIB_IMPORT __declspec(dllimport)
-# define ZIX_LIB_EXPORT __declspec(dllexport)
-# else
-# define ZIX_LIB_IMPORT __attribute__((visibility("default")))
-# define ZIX_LIB_EXPORT __attribute__((visibility("default")))
-# endif
-# ifdef ZIX_INTERNAL
-# define ZIX_API ZIX_LIB_EXPORT
-# else
-# define ZIX_API ZIX_LIB_IMPORT
-# endif
-#else
-# define ZIX_API
-#endif
-/** @endcond */
-
-#ifdef __cplusplus
-extern "C" {
-#else
-# include <stdbool.h>
-#endif
-
-typedef enum {
- ZIX_STATUS_SUCCESS,
- ZIX_STATUS_ERROR,
- ZIX_STATUS_NO_MEM,
- ZIX_STATUS_NOT_FOUND,
- ZIX_STATUS_EXISTS,
- ZIX_STATUS_BAD_ARG,
- ZIX_STATUS_BAD_PERMS,
-} ZixStatus;
-
-/**
- Function for comparing two elements.
-*/
-typedef int (*ZixComparator)(const void* a, const void* b, void* user_data);
-
-/**
- Function for testing equality of two elements.
-*/
-typedef bool (*ZixEqualFunc)(const void* a, const void* b);
-
-/**
- Function to destroy an element.
-*/
-typedef void (*ZixDestroyFunc)(void* ptr);
-
-/**
- @}
-*/
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* ZIX_COMMON_H */
diff --git a/zix/ring.c b/zix/ring.c
deleted file mode 100644
index b701497..0000000
--- a/zix/ring.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- Copyright 2011 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_MLOCK
-# include <sys/mman.h>
-# define ZIX_MLOCK(ptr, size) mlock((ptr), (size))
-#elif defined(_WIN32)
-# include <windows.h>
-# define ZIX_MLOCK(ptr, size) VirtualLock((ptr), (size))
-#else
-# pragma message("warning: No memory locking, possible RT violations")
-# define ZIX_MLOCK(ptr, size)
-#endif
-
-#if defined(__APPLE__)
-# include <libkern/OSAtomic.h>
-# define ZIX_FULL_BARRIER() OSMemoryBarrier()
-#elif defined(_WIN32)
-# include <windows.h>
-# define ZIX_FULL_BARRIER() MemoryBarrier()
-#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
-# define ZIX_FULL_BARRIER() __sync_synchronize()
-#else
-# pragma message("warning: No memory barriers, possible SMP bugs")
-# define ZIX_FULL_BARRIER()
-#endif
-
-/* No support for any systems with separate read and write barriers */
-#define ZIX_READ_BARRIER() ZIX_FULL_BARRIER()
-#define ZIX_WRITE_BARRIER() ZIX_FULL_BARRIER()
-
-#include "zix/ring.h"
-
-struct ZixRingImpl {
- uint32_t write_head; ///< Read index into buf
- uint32_t read_head; ///< Write index into buf
- uint32_t size; ///< Size (capacity) in bytes
- uint32_t size_mask; ///< Mask for fast modulo
- char* buf; ///< Contents
-};
-
-static inline uint32_t
-next_power_of_two(uint32_t size)
-{
- // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
- size--;
- size |= size >> 1;
- size |= size >> 2;
- size |= size >> 4;
- size |= size >> 8;
- size |= size >> 16;
- size++;
- return size;
-}
-
-ZixRing*
-zix_ring_new(uint32_t size)
-{
- ZixRing* ring = (ZixRing*)malloc(sizeof(ZixRing));
- ring->write_head = 0;
- ring->read_head = 0;
- ring->size = next_power_of_two(size);
- ring->size_mask = ring->size - 1;
- ring->buf = (char*)malloc(ring->size);
- return ring;
-}
-
-void
-zix_ring_free(ZixRing* ring)
-{
- free(ring->buf);
- free(ring);
-}
-
-void
-zix_ring_mlock(ZixRing* ring)
-{
- ZIX_MLOCK(ring, sizeof(ZixRing));
- ZIX_MLOCK(ring->buf, ring->size);
-}
-
-void
-zix_ring_reset(ZixRing* ring)
-{
- ring->write_head = 0;
- ring->read_head = 0;
-}
-
-static inline uint32_t
-read_space_internal(const ZixRing* ring, uint32_t r, uint32_t w)
-{
- if (r < w) {
- return w - r;
- } else {
- return (w - r + ring->size) & ring->size_mask;
- }
-}
-
-uint32_t
-zix_ring_read_space(const ZixRing* ring)
-{
- return read_space_internal(ring, ring->read_head, ring->write_head);
-}
-
-static inline uint32_t
-write_space_internal(const ZixRing* ring, uint32_t r, uint32_t w)
-{
- if (r == w) {
- return ring->size - 1;
- } else if (r < w) {
- return ((r - w + ring->size) & ring->size_mask) - 1;
- } else {
- return (r - w) - 1;
- }
-}
-
-uint32_t
-zix_ring_write_space(const ZixRing* ring)
-{
- return write_space_internal(ring, ring->read_head, ring->write_head);
-}
-
-uint32_t
-zix_ring_capacity(const ZixRing* ring)
-{
- return ring->size - 1;
-}
-
-static inline uint32_t
-peek_internal(const ZixRing* ring, uint32_t r, uint32_t w,
- uint32_t size, void* dst)
-{
- if (read_space_internal(ring, r, w) < size) {
- return 0;
- }
-
- if (r + size < ring->size) {
- memcpy(dst, &ring->buf[r], size);
- } else {
- const uint32_t first_size = ring->size - r;
- memcpy(dst, &ring->buf[r], first_size);
- memcpy((char*)dst + first_size, &ring->buf[0], size - first_size);
- }
-
- return size;
-}
-
-uint32_t
-zix_ring_peek(ZixRing* ring, void* dst, uint32_t size)
-{
- const uint32_t r = ring->read_head;
- const uint32_t w = ring->write_head;
-
- return peek_internal(ring, r, w, size, dst);
-}
-
-uint32_t
-zix_ring_read(ZixRing* ring, void* dst, uint32_t size)
-{
- const uint32_t r = ring->read_head;
- const uint32_t w = ring->write_head;
-
- if (peek_internal(ring, r, w, size, dst)) {
- ZIX_READ_BARRIER();
- ring->read_head = (r + size) & ring->size_mask;
- return size;
- } else {
- return 0;
- }
-}
-
-uint32_t
-zix_ring_skip(ZixRing* ring, uint32_t size)
-{
- const uint32_t r = ring->read_head;
- const uint32_t w = ring->write_head;
- if (read_space_internal(ring, r, w) < size) {
- return 0;
- }
-
- ZIX_READ_BARRIER();
- ring->read_head = (r + size) & ring->size_mask;
- return size;
-}
-
-uint32_t
-zix_ring_write(ZixRing* ring, const void* src, uint32_t size)
-{
- const uint32_t r = ring->read_head;
- const uint32_t w = ring->write_head;
- if (write_space_internal(ring, r, w) < size) {
- return 0;
- }
-
- if (w + size <= ring->size) {
- memcpy(&ring->buf[w], src, size);
- ZIX_WRITE_BARRIER();
- ring->write_head = (w + size) & ring->size_mask;
- } else {
- const uint32_t this_size = ring->size - w;
- memcpy(&ring->buf[w], src, this_size);
- memcpy(&ring->buf[0], (const char*)src + this_size, size - this_size);
- ZIX_WRITE_BARRIER();
- ring->write_head = size - this_size;
- }
-
- return size;
-}
diff --git a/zix/ring.h b/zix/ring.h
deleted file mode 100644
index f173b6f..0000000
--- a/zix/ring.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- Copyright 2011-2014 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef ZIX_RING_H
-#define ZIX_RING_H
-
-#include <stdint.h>
-
-#include "zix/common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- @addtogroup zix
- @{
- @name Ring
- @{
-*/
-
-/**
- A lock-free ring buffer.
-
- Thread-safe with a single reader and single writer, and realtime safe
- on both ends.
-*/
-typedef struct ZixRingImpl ZixRing;
-
-/**
- Create a new ring.
- @param size Size in bytes (note this may be rounded up).
-
- At most `size - 1` bytes may be stored in the ring at once.
-*/
-ZixRing*
-zix_ring_new(uint32_t size);
-
-/**
- Destroy a ring.
-*/
-void
-zix_ring_free(ZixRing* ring);
-
-/**
- Lock the ring data into physical memory.
-
- This function is NOT thread safe or real-time safe, but it should be called
- after zix_ring_new() to lock all ring memory to avoid page faults while
- using the ring (i.e. this function MUST be called first in order for the
- ring to be truly real-time safe).
-
-*/
-void
-zix_ring_mlock(ZixRing* ring);
-
-/**
- Reset (empty) a ring.
-
- This function is NOT thread-safe, it may only be called when there are no
- readers or writers.
-*/
-void
-zix_ring_reset(ZixRing* ring);
-
-/**
- Return the number of bytes of space available for reading.
-*/
-uint32_t
-zix_ring_read_space(const ZixRing* ring);
-
-/**
- Return the number of bytes of space available for writing.
-*/
-uint32_t
-zix_ring_write_space(const ZixRing* ring);
-
-/**
- Return the capacity (i.e. total write space when empty).
-*/
-uint32_t
-zix_ring_capacity(const ZixRing* ring);
-
-/**
- Read from the ring without advancing the read head.
-*/
-uint32_t
-zix_ring_peek(ZixRing* ring, void* dst, uint32_t size);
-
-/**
- Read from the ring and advance the read head.
-*/
-uint32_t
-zix_ring_read(ZixRing* ring, void* dst, uint32_t size);
-
-/**
- Skip data in the ring (advance read head without reading).
-*/
-uint32_t
-zix_ring_skip(ZixRing* ring, uint32_t size);
-
-/**
- Write data to the ring.
-*/
-uint32_t
-zix_ring_write(ZixRing* ring, const void* src, uint32_t size);
-
-/**
- @}
- @}
-*/
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* ZIX_RING_H */
diff --git a/zix/thread.h b/zix/thread.h
deleted file mode 100644
index b007efa..0000000
--- a/zix/thread.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- Copyright 2012-2014 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef ZIX_THREAD_H
-#define ZIX_THREAD_H
-
-#ifdef _WIN32
-# include <windows.h>
-#else
-# include <errno.h>
-# include <pthread.h>
-#endif
-
-#include "zix/common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#else
-# include <stdbool.h>
-#endif
-
-/**
- @addtogroup zix
- @{
- @name Thread
- @{
-*/
-
-#ifdef _WIN32
-typedef HANDLE ZixThread;
-#else
-typedef pthread_t ZixThread;
-#endif
-
-/**
- Initialize `thread` to a new thread.
-
- The thread will immediately be launched, calling `function` with `arg`
- as the only parameter.
-*/
-static inline ZixStatus
-zix_thread_create(ZixThread* thread,
- size_t stack_size,
- void* (*function)(void*),
- void* arg);
-
-/**
- Join `thread` (block until `thread` exits).
-*/
-static inline ZixStatus
-zix_thread_join(ZixThread thread, void** retval);
-
-#ifdef _WIN32
-
-static inline ZixStatus
-zix_thread_create(ZixThread* thread,
- size_t stack_size,
- void* (*function)(void*),
- void* arg)
-{
- *thread = CreateThread(NULL, stack_size,
- (LPTHREAD_START_ROUTINE)function, arg,
- 0, NULL);
- return *thread ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR;
-}
-
-static inline ZixStatus
-zix_thread_join(ZixThread thread, void** retval)
-{
- return WaitForSingleObject(thread, INFINITE)
- ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR;
-}
-
-#else /* !defined(_WIN32) */
-
-static inline ZixStatus
-zix_thread_create(ZixThread* thread,
- size_t stack_size,
- void* (*function)(void*),
- void* arg)
-{
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setstacksize(&attr, stack_size);
-
- const int ret = pthread_create(thread, NULL, function, arg);
- pthread_attr_destroy(&attr);
-
- if (ret == EAGAIN) {
- return ZIX_STATUS_NO_MEM;
- } else if (ret == EINVAL) {
- return ZIX_STATUS_BAD_ARG;
- } else if (ret == EPERM) {
- return ZIX_STATUS_BAD_PERMS;
- } else if (ret) {
- return ZIX_STATUS_ERROR;
- }
-
- return ZIX_STATUS_SUCCESS;
-}
-
-static inline ZixStatus
-zix_thread_join(ZixThread thread, void** retval)
-{
- return pthread_join(thread, retval)
- ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS;
-}
-
-#endif
-
-/**
- @}
- @}
-*/
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* ZIX_THREAD_H */