diff options
author | David Robillard <d@drobilla.net> | 2020-12-26 19:21:17 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-12-26 19:21:17 +0100 |
commit | 882b9446cbf7316345de391188e68c2a7333da5b (patch) | |
tree | 5baf31e0a28313b380cc7e8694b09a2a517d14f3 /plugins | |
parent | 8d2251749da9e0ae4254502edfc8917236a9b8c0 (diff) | |
download | lv2-882b9446cbf7316345de391188e68c2a7333da5b.tar.xz |
Format all code with clang-format
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/eg-amp.lv2/amp.c | 110 | ||||
-rw-r--r-- | plugins/eg-fifths.lv2/fifths.c | 235 | ||||
-rw-r--r-- | plugins/eg-fifths.lv2/uris.h | 38 | ||||
-rw-r--r-- | plugins/eg-metro.lv2/metro.c | 461 | ||||
-rw-r--r-- | plugins/eg-midigate.lv2/midigate.c | 224 | ||||
-rw-r--r-- | plugins/eg-params.lv2/params.c | 702 | ||||
-rw-r--r-- | plugins/eg-params.lv2/state_map.h | 80 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/atom_sink.h | 12 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/peaks.h | 290 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 904 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler_ui.c | 586 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/uris.h | 189 | ||||
-rw-r--r-- | plugins/eg-scope.lv2/examploscope.c | 573 | ||||
-rw-r--r-- | plugins/eg-scope.lv2/examploscope_ui.c | 954 | ||||
-rw-r--r-- | plugins/eg-scope.lv2/uris.h | 70 |
15 files changed, 2679 insertions, 2749 deletions
diff --git a/plugins/eg-amp.lv2/amp.c b/plugins/eg-amp.lv2/amp.c index 77cf83f..244825f 100644 --- a/plugins/eg-amp.lv2/amp.c +++ b/plugins/eg-amp.lv2/amp.c @@ -42,11 +42,7 @@ In code, ports are referred to by index. An enumeration of port indices should be defined for readability. */ -typedef enum { - AMP_GAIN = 0, - AMP_INPUT = 1, - AMP_OUTPUT = 2 -} PortIndex; +typedef enum { AMP_GAIN = 0, AMP_INPUT = 1, AMP_OUTPUT = 2 } PortIndex; /** Every plugin defines a private structure for the plugin instance. All data @@ -55,10 +51,10 @@ typedef enum { stored, since there is no additional instance data. */ typedef struct { - // Port buffers - const float* gain; - const float* input; - float* output; + // Port buffers + const float* gain; + const float* input; + float* output; } Amp; /** @@ -77,9 +73,9 @@ instantiate(const LV2_Descriptor* descriptor, const char* bundle_path, const LV2_Feature* const* features) { - Amp* amp = (Amp*)calloc(1, sizeof(Amp)); + Amp* amp = (Amp*)calloc(1, sizeof(Amp)); - return (LV2_Handle)amp; + return (LV2_Handle)amp; } /** @@ -91,23 +87,21 @@ instantiate(const LV2_Descriptor* descriptor, context as run(). */ static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) +connect_port(LV2_Handle instance, uint32_t port, void* data) { - Amp* amp = (Amp*)instance; - - switch ((PortIndex)port) { - case AMP_GAIN: - amp->gain = (const float*)data; - break; - case AMP_INPUT: - amp->input = (const float*)data; - break; - case AMP_OUTPUT: - amp->output = (float*)data; - break; - } + Amp* amp = (Amp*)instance; + + switch ((PortIndex)port) { + case AMP_GAIN: + amp->gain = (const float*)data; + break; + case AMP_INPUT: + amp->input = (const float*)data; + break; + case AMP_OUTPUT: + amp->output = (float*)data; + break; + } } /** @@ -121,11 +115,10 @@ connect_port(LV2_Handle instance, */ static void activate(LV2_Handle instance) -{ -} +{} /** Define a macro for converting a gain in dB to a coefficient. */ -#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) +#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g)*0.05f) : 0.0f) /** The `run()` method is the main process function of the plugin. It processes @@ -136,34 +129,33 @@ activate(LV2_Handle instance) static void run(LV2_Handle instance, uint32_t n_samples) { - const Amp* amp = (const Amp*)instance; + const Amp* amp = (const Amp*)instance; - const float gain = *(amp->gain); - const float* const input = amp->input; - float* const output = amp->output; + const float gain = *(amp->gain); + const float* const input = amp->input; + float* const output = amp->output; - const float coef = DB_CO(gain); + const float coef = DB_CO(gain); - for (uint32_t pos = 0; pos < n_samples; pos++) { - output[pos] = input[pos] * coef; - } + for (uint32_t pos = 0; pos < n_samples; pos++) { + output[pos] = input[pos] * coef; + } } /** - The `deactivate()` method is the counterpart to `activate()`, and is called by - the host after running the plugin. It indicates that the host will not call - `run()` again until another call to `activate()` and is mainly useful for more - advanced plugins with ``live'' characteristics such as those with auxiliary - processing threads. As with `activate()`, this plugin has no use for this - information so this method does nothing. + The `deactivate()` method is the counterpart to `activate()`, and is called + by the host after running the plugin. It indicates that the host will not + call `run()` again until another call to `activate()` and is mainly useful + for more advanced plugins with ``live'' characteristics such as those with + auxiliary processing threads. As with `activate()`, this plugin has no use + for this information so this method does nothing. This method is in the ``instantiation'' threading class, so no other methods on this instance will be called concurrently with it. */ static void deactivate(LV2_Handle instance) -{ -} +{} /** Destroy a plugin instance (counterpart to `instantiate()`). @@ -174,7 +166,7 @@ deactivate(LV2_Handle instance) static void cleanup(LV2_Handle instance) { - free(instance); + free(instance); } /** @@ -190,7 +182,7 @@ cleanup(LV2_Handle instance) static const void* extension_data(const char* uri) { - return NULL; + return NULL; } /** @@ -198,19 +190,17 @@ extension_data(const char* uri) descriptors statically to avoid leaking memory and non-portable shared library constructors and destructors to clean up properly. */ -static const LV2_Descriptor descriptor = { - AMP_URI, - instantiate, - connect_port, - activate, - run, - deactivate, - cleanup, - extension_data -}; +static const LV2_Descriptor descriptor = {AMP_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data}; /** - The `lv2_descriptor()` function is the entry point to the plugin library. The + The `lv2_descriptor()` function is the entry point to the plugin library. The host will load the library and call this function repeatedly with increasing indices to find all the plugins defined in the library. The index is not an indentifier, the URI of the returned descriptor is used to determine the @@ -223,5 +213,5 @@ LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-fifths.lv2/fifths.c b/plugins/eg-fifths.lv2/fifths.c index bafd0d9..7527895 100644 --- a/plugins/eg-fifths.lv2/fifths.c +++ b/plugins/eg-fifths.lv2/fifths.c @@ -31,40 +31,35 @@ #include <stdio.h> #include <stdlib.h> -enum { - FIFTHS_IN = 0, - FIFTHS_OUT = 1 -}; +enum { FIFTHS_IN = 0, FIFTHS_OUT = 1 }; typedef struct { - // Features - LV2_URID_Map* map; - LV2_Log_Logger logger; + // Features + LV2_URID_Map* map; + LV2_Log_Logger logger; - // Ports - const LV2_Atom_Sequence* in_port; - LV2_Atom_Sequence* out_port; + // Ports + const LV2_Atom_Sequence* in_port; + LV2_Atom_Sequence* out_port; - // URIs - FifthsURIs uris; + // URIs + FifthsURIs uris; } Fifths; static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) +connect_port(LV2_Handle instance, uint32_t port, void* data) { - Fifths* self = (Fifths*)instance; - switch (port) { - case FIFTHS_IN: - self->in_port = (const LV2_Atom_Sequence*)data; - break; - case FIFTHS_OUT: - self->out_port = (LV2_Atom_Sequence*)data; - break; - default: - break; - } + Fifths* self = (Fifths*)instance; + switch (port) { + case FIFTHS_IN: + self->in_port = (const LV2_Atom_Sequence*)data; + break; + case FIFTHS_OUT: + self->out_port = (LV2_Atom_Sequence*)data; + break; + default: + break; + } } static LV2_Handle @@ -73,119 +68,115 @@ instantiate(const LV2_Descriptor* descriptor, const char* path, const LV2_Feature* const* features) { - // Allocate and initialise instance structure. - Fifths* self = (Fifths*)calloc(1, sizeof(Fifths)); - if (!self) { - return NULL; - } - - // Scan host features for URID map - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - map_fifths_uris(self->map, &self->uris); - - return (LV2_Handle)self; + // Allocate and initialise instance structure. + Fifths* self = (Fifths*)calloc(1, sizeof(Fifths)); + if (!self) { + return NULL; + } + + // Scan host features for URID map + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &self->logger.log, false, + LV2_URID__map, &self->map, true, + NULL); + // clang-format on + + lv2_log_logger_set_map(&self->logger, self->map); + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + free(self); + return NULL; + } + + map_fifths_uris(self->map, &self->uris); + + return (LV2_Handle)self; } static void cleanup(LV2_Handle instance) { - free(instance); + free(instance); } static void -run(LV2_Handle instance, - uint32_t sample_count) +run(LV2_Handle instance, uint32_t sample_count) { - Fifths* self = (Fifths*)instance; - FifthsURIs* uris = &self->uris; - - // Struct for a 3 byte MIDI event, used for writing notes - typedef struct { - LV2_Atom_Event event; - uint8_t msg[3]; - } MIDINoteEvent; - - // Initially self->out_port contains a Chunk with size set to capacity - - // Get the capacity - const uint32_t out_capacity = self->out_port->atom.size; - - // Write an empty Sequence header to the output - lv2_atom_sequence_clear(self->out_port); - self->out_port->atom.type = self->in_port->atom.type; - - // Read incoming events - LV2_ATOM_SEQUENCE_FOREACH(self->in_port, ev) { - if (ev->body.type == uris->midi_Event) { - const uint8_t* const msg = (const uint8_t*)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - case LV2_MIDI_MSG_NOTE_OFF: - // Forward note to output - lv2_atom_sequence_append_event( - self->out_port, out_capacity, ev); - - if (msg[1] <= 127 - 7) { - // Make a note one 5th (7 semitones) higher than input - MIDINoteEvent fifth; - - // Could simply do fifth.event = *ev here instead... - fifth.event.time.frames = ev->time.frames; // Same time - fifth.event.body.type = ev->body.type; // Same type - fifth.event.body.size = ev->body.size; // Same size - - fifth.msg[0] = msg[0]; // Same status - fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones - fifth.msg[2] = msg[2]; // Same velocity - - // Write 5th event - lv2_atom_sequence_append_event( - self->out_port, out_capacity, &fifth.event); - } - break; - default: - // Forward all other MIDI events directly - lv2_atom_sequence_append_event( - self->out_port, out_capacity, ev); - break; - } - } - } + Fifths* self = (Fifths*)instance; + FifthsURIs* uris = &self->uris; + + // Struct for a 3 byte MIDI event, used for writing notes + typedef struct { + LV2_Atom_Event event; + uint8_t msg[3]; + } MIDINoteEvent; + + // Initially self->out_port contains a Chunk with size set to capacity + + // Get the capacity + const uint32_t out_capacity = self->out_port->atom.size; + + // Write an empty Sequence header to the output + lv2_atom_sequence_clear(self->out_port); + self->out_port->atom.type = self->in_port->atom.type; + + // Read incoming events + LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) { + if (ev->body.type == uris->midi_Event) { + const uint8_t* const msg = (const uint8_t*)(ev + 1); + switch (lv2_midi_message_type(msg)) { + case LV2_MIDI_MSG_NOTE_ON: + case LV2_MIDI_MSG_NOTE_OFF: + // Forward note to output + lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); + + if (msg[1] <= 127 - 7) { + // Make a note one 5th (7 semitones) higher than input + MIDINoteEvent fifth; + + // Could simply do fifth.event = *ev here instead... + fifth.event.time.frames = ev->time.frames; // Same time + fifth.event.body.type = ev->body.type; // Same type + fifth.event.body.size = ev->body.size; // Same size + + fifth.msg[0] = msg[0]; // Same status + fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones + fifth.msg[2] = msg[2]; // Same velocity + + // Write 5th event + lv2_atom_sequence_append_event( + self->out_port, out_capacity, &fifth.event); + } + break; + default: + // Forward all other MIDI events directly + lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); + break; + } + } + } } static const void* extension_data(const char* uri) { - return NULL; + return NULL; } -static const LV2_Descriptor descriptor = { - EG_FIFTHS_URI, - instantiate, - connect_port, - NULL, // activate, - run, - NULL, // deactivate, - cleanup, - extension_data -}; +static const LV2_Descriptor descriptor = {EG_FIFTHS_URI, + instantiate, + connect_port, + NULL, // activate, + run, + NULL, // deactivate, + cleanup, + extension_data}; LV2_SYMBOL_EXPORT -const LV2_Descriptor* lv2_descriptor(uint32_t index) +const LV2_Descriptor* +lv2_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-fifths.lv2/uris.h b/plugins/eg-fifths.lv2/uris.h index 54a723d..d26577e 100644 --- a/plugins/eg-fifths.lv2/uris.h +++ b/plugins/eg-fifths.lv2/uris.h @@ -26,29 +26,29 @@ #define EG_FIFTHS_URI "http://lv2plug.in/plugins/eg-fifths" typedef struct { - LV2_URID atom_Path; - LV2_URID atom_Resource; - LV2_URID atom_Sequence; - LV2_URID atom_URID; - LV2_URID atom_eventTransfer; - LV2_URID midi_Event; - LV2_URID patch_Set; - LV2_URID patch_property; - LV2_URID patch_value; + LV2_URID atom_Path; + LV2_URID atom_Resource; + LV2_URID atom_Sequence; + LV2_URID atom_URID; + LV2_URID atom_eventTransfer; + LV2_URID midi_Event; + LV2_URID patch_Set; + LV2_URID patch_property; + LV2_URID patch_value; } FifthsURIs; static inline void map_fifths_uris(LV2_URID_Map* map, FifthsURIs* uris) { - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); - uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); - uris->patch_property = map->map(map->handle, LV2_PATCH__property); - uris->patch_value = map->map(map->handle, LV2_PATCH__value); + uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); + uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); + uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); + uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); + uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); + uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); + uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); + uris->patch_property = map->map(map->handle, LV2_PATCH__property); + uris->patch_value = map->map(map->handle, LV2_PATCH__value); } -#endif /* FIFTHS_URIS_H */ +#endif /* FIFTHS_URIS_H */ diff --git a/plugins/eg-metro.lv2/metro.c b/plugins/eg-metro.lv2/metro.c index 6182a74..46ca41b 100644 --- a/plugins/eg-metro.lv2/metro.c +++ b/plugins/eg-metro.lv2/metro.c @@ -32,37 +32,34 @@ #include <string.h> #ifndef M_PI -# define M_PI 3.14159265 +# define M_PI 3.14159265 #endif #define EG_METRO_URI "http://lv2plug.in/plugins/eg-metro" typedef struct { - LV2_URID atom_Blank; - LV2_URID atom_Float; - LV2_URID atom_Object; - LV2_URID atom_Path; - LV2_URID atom_Resource; - LV2_URID atom_Sequence; - LV2_URID time_Position; - LV2_URID time_barBeat; - LV2_URID time_beatsPerMinute; - LV2_URID time_speed; + LV2_URID atom_Blank; + LV2_URID atom_Float; + LV2_URID atom_Object; + LV2_URID atom_Path; + LV2_URID atom_Resource; + LV2_URID atom_Sequence; + LV2_URID time_Position; + LV2_URID time_barBeat; + LV2_URID time_beatsPerMinute; + LV2_URID time_speed; } MetroURIs; static const double attack_s = 0.005; static const double decay_s = 0.075; -enum { - METRO_CONTROL = 0, - METRO_OUT = 1 -}; +enum { METRO_CONTROL = 0, METRO_OUT = 1 }; /** During execution this plugin can be in one of 3 states: */ typedef enum { - STATE_ATTACK, // Envelope rising - STATE_DECAY, // Envelope lowering - STATE_OFF // Silent + STATE_ATTACK, // Envelope rising + STATE_DECAY, // Envelope lowering + STATE_OFF // Silent } State; /** @@ -77,50 +74,48 @@ typedef enum { the user to modify these parameters, the frequency of the wave, and so on. */ typedef struct { - LV2_URID_Map* map; // URID map feature - LV2_Log_Logger logger; // Logger API - MetroURIs uris; // Cache of mapped URIDs - - struct { - LV2_Atom_Sequence* control; - float* output; - } ports; - - // Variables to keep track of the tempo information sent by the host - double rate; // Sample rate - float bpm; // Beats per minute (tempo) - float speed; // Transport speed (usually 0=stop, 1=play) - - uint32_t elapsed_len; // Frames since the start of the last click - uint32_t wave_offset; // Current play offset in the wave - State state; // Current play state - - // One cycle of a sine wave - float* wave; - uint32_t wave_len; - - // Envelope parameters - uint32_t attack_len; - uint32_t decay_len; + LV2_URID_Map* map; // URID map feature + LV2_Log_Logger logger; // Logger API + MetroURIs uris; // Cache of mapped URIDs + + struct { + LV2_Atom_Sequence* control; + float* output; + } ports; + + // Variables to keep track of the tempo information sent by the host + double rate; // Sample rate + float bpm; // Beats per minute (tempo) + float speed; // Transport speed (usually 0=stop, 1=play) + + uint32_t elapsed_len; // Frames since the start of the last click + uint32_t wave_offset; // Current play offset in the wave + State state; // Current play state + + // One cycle of a sine wave + float* wave; + uint32_t wave_len; + + // Envelope parameters + uint32_t attack_len; + uint32_t decay_len; } Metro; static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) +connect_port(LV2_Handle instance, uint32_t port, void* data) { - Metro* self = (Metro*)instance; - - switch (port) { - case METRO_CONTROL: - self->ports.control = (LV2_Atom_Sequence*)data; - break; - case METRO_OUT: - self->ports.output = (float*)data; - break; - default: - break; - } + Metro* self = (Metro*)instance; + + switch (port) { + case METRO_CONTROL: + self->ports.control = (LV2_Atom_Sequence*)data; + break; + case METRO_OUT: + self->ports.output = (float*)data; + break; + default: + break; + } } /** @@ -130,11 +125,11 @@ connect_port(LV2_Handle instance, static void activate(LV2_Handle instance) { - Metro* self = (Metro*)instance; + Metro* self = (Metro*)instance; - self->elapsed_len = 0; - self->wave_offset = 0; - self->state = STATE_OFF; + self->elapsed_len = 0; + self->wave_offset = 0; + self->state = STATE_OFF; } /** @@ -149,64 +144,64 @@ instantiate(const LV2_Descriptor* descriptor, const char* path, const LV2_Feature* const* features) { - Metro* self = (Metro*)calloc(1, sizeof(Metro)); - if (!self) { - return NULL; - } - - // Scan host features for URID map - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIS - MetroURIs* const uris = &self->uris; - LV2_URID_Map* const map = self->map; - uris->atom_Blank = map->map(map->handle, LV2_ATOM__Blank); - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Object = map->map(map->handle, LV2_ATOM__Object); - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->time_Position = map->map(map->handle, LV2_TIME__Position); - uris->time_barBeat = map->map(map->handle, LV2_TIME__barBeat); - uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute); - uris->time_speed = map->map(map->handle, LV2_TIME__speed); - - // Initialise instance fields - self->rate = rate; - self->bpm = 120.0f; - self->attack_len = (uint32_t)(attack_s * rate); - self->decay_len = (uint32_t)(decay_s * rate); - self->state = STATE_OFF; - - // Generate one cycle of a sine wave at the desired frequency - const double freq = 440.0 * 2.0; - const double amp = 0.5; - self->wave_len = (uint32_t)(rate / freq); - self->wave = (float*)malloc(self->wave_len * sizeof(float)); - for (uint32_t i = 0; i < self->wave_len; ++i) { - self->wave[i] = (float)(sin(i * 2 * M_PI * freq / rate) * amp); - } - - return (LV2_Handle)self; + Metro* self = (Metro*)calloc(1, sizeof(Metro)); + if (!self) { + return NULL; + } + + // Scan host features for URID map + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &self->logger.log, false, + LV2_URID__map, &self->map, true, + NULL); + // clang-format on + + lv2_log_logger_set_map(&self->logger, self->map); + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + free(self); + return NULL; + } + + // Map URIS + MetroURIs* const uris = &self->uris; + LV2_URID_Map* const map = self->map; + uris->atom_Blank = map->map(map->handle, LV2_ATOM__Blank); + uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); + uris->atom_Object = map->map(map->handle, LV2_ATOM__Object); + uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); + uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); + uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); + uris->time_Position = map->map(map->handle, LV2_TIME__Position); + uris->time_barBeat = map->map(map->handle, LV2_TIME__barBeat); + uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute); + uris->time_speed = map->map(map->handle, LV2_TIME__speed); + + // Initialise instance fields + self->rate = rate; + self->bpm = 120.0f; + self->attack_len = (uint32_t)(attack_s * rate); + self->decay_len = (uint32_t)(decay_s * rate); + self->state = STATE_OFF; + + // Generate one cycle of a sine wave at the desired frequency + const double freq = 440.0 * 2.0; + const double amp = 0.5; + self->wave_len = (uint32_t)(rate / freq); + self->wave = (float*)malloc(self->wave_len * sizeof(float)); + for (uint32_t i = 0; i < self->wave_len; ++i) { + self->wave[i] = (float)(sin(i * 2 * M_PI * freq / rate) * amp); + } + + return (LV2_Handle)self; } static void cleanup(LV2_Handle instance) { - free(instance); + free(instance); } /** @@ -216,47 +211,47 @@ cleanup(LV2_Handle instance) static void play(Metro* self, uint32_t begin, uint32_t end) { - float* const output = self->ports.output; - const uint32_t frames_per_beat = 60.0f / self->bpm * self->rate; - - if (self->speed == 0.0f) { - memset(output, 0, (end - begin) * sizeof(float)); - return; - } - - for (uint32_t i = begin; i < end; ++i) { - switch (self->state) { - case STATE_ATTACK: - // Amplitude increases from 0..1 until attack_len - output[i] = self->wave[self->wave_offset] * - self->elapsed_len / (float)self->attack_len; - if (self->elapsed_len >= self->attack_len) { - self->state = STATE_DECAY; - } - break; - case STATE_DECAY: - // Amplitude decreases from 1..0 until attack_len + decay_len - output[i] = 0.0f; - output[i] = self->wave[self->wave_offset] * - (1 - ((float)(self->elapsed_len - self->attack_len) / - (float)self->decay_len)); - if (self->elapsed_len >= self->attack_len + self->decay_len) { - self->state = STATE_OFF; - } - break; - case STATE_OFF: - output[i] = 0.0f; - } - - // We continuously play the sine wave regardless of envelope - self->wave_offset = (self->wave_offset + 1) % self->wave_len; - - // Update elapsed time and start attack if necessary - if (++self->elapsed_len == frames_per_beat) { - self->state = STATE_ATTACK; - self->elapsed_len = 0; - } - } + float* const output = self->ports.output; + const uint32_t frames_per_beat = 60.0f / self->bpm * self->rate; + + if (self->speed == 0.0f) { + memset(output, 0, (end - begin) * sizeof(float)); + return; + } + + for (uint32_t i = begin; i < end; ++i) { + switch (self->state) { + case STATE_ATTACK: + // Amplitude increases from 0..1 until attack_len + output[i] = self->wave[self->wave_offset] * self->elapsed_len / + (float)self->attack_len; + if (self->elapsed_len >= self->attack_len) { + self->state = STATE_DECAY; + } + break; + case STATE_DECAY: + // Amplitude decreases from 1..0 until attack_len + decay_len + output[i] = 0.0f; + output[i] = self->wave[self->wave_offset] * + (1 - ((float)(self->elapsed_len - self->attack_len) / + (float)self->decay_len)); + if (self->elapsed_len >= self->attack_len + self->decay_len) { + self->state = STATE_OFF; + } + break; + case STATE_OFF: + output[i] = 0.0f; + } + + // We continuously play the sine wave regardless of envelope + self->wave_offset = (self->wave_offset + 1) % self->wave_len; + + // Update elapsed time and start attack if necessary + if (++self->elapsed_len == frames_per_beat) { + self->state = STATE_ATTACK; + self->elapsed_len = 0; + } + } } /** @@ -266,94 +261,92 @@ play(Metro* self, uint32_t begin, uint32_t end) static void update_position(Metro* self, const LV2_Atom_Object* obj) { - const MetroURIs* uris = &self->uris; - - // Received new transport position/speed - LV2_Atom* beat = NULL; - LV2_Atom* bpm = NULL; - LV2_Atom* speed = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->time_barBeat, &beat, - uris->time_beatsPerMinute, &bpm, - uris->time_speed, &speed, - NULL); - // clang-format on - - if (bpm && bpm->type == uris->atom_Float) { - // Tempo changed, update BPM - self->bpm = ((LV2_Atom_Float*)bpm)->body; - } - if (speed && speed->type == uris->atom_Float) { - // Speed changed, e.g. 0 (stop) to 1 (play) - self->speed = ((LV2_Atom_Float*)speed)->body; - } - if (beat && beat->type == uris->atom_Float) { - // Received a beat position, synchronise - // This hard sync may cause clicks, a real plugin would be more graceful - const float frames_per_beat = (float)(60.0 / self->bpm * self->rate); - const float bar_beats = ((LV2_Atom_Float*)beat)->body; - const float beat_beats = bar_beats - floorf(bar_beats); - self->elapsed_len = beat_beats * frames_per_beat; - if (self->elapsed_len < self->attack_len) { - self->state = STATE_ATTACK; - } else if (self->elapsed_len < self->attack_len + self->decay_len) { - self->state = STATE_DECAY; - } else { - self->state = STATE_OFF; - } - } + const MetroURIs* uris = &self->uris; + + // Received new transport position/speed + LV2_Atom* beat = NULL; + LV2_Atom* bpm = NULL; + LV2_Atom* speed = NULL; + // clang-format off + lv2_atom_object_get(obj, + uris->time_barBeat, &beat, + uris->time_beatsPerMinute, &bpm, + uris->time_speed, &speed, + NULL); + // clang-format on + + if (bpm && bpm->type == uris->atom_Float) { + // Tempo changed, update BPM + self->bpm = ((LV2_Atom_Float*)bpm)->body; + } + if (speed && speed->type == uris->atom_Float) { + // Speed changed, e.g. 0 (stop) to 1 (play) + self->speed = ((LV2_Atom_Float*)speed)->body; + } + if (beat && beat->type == uris->atom_Float) { + // Received a beat position, synchronise + // This hard sync may cause clicks, a real plugin would be more graceful + const float frames_per_beat = (float)(60.0 / self->bpm * self->rate); + const float bar_beats = ((LV2_Atom_Float*)beat)->body; + const float beat_beats = bar_beats - floorf(bar_beats); + self->elapsed_len = beat_beats * frames_per_beat; + if (self->elapsed_len < self->attack_len) { + self->state = STATE_ATTACK; + } else if (self->elapsed_len < self->attack_len + self->decay_len) { + self->state = STATE_DECAY; + } else { + self->state = STATE_OFF; + } + } } static void run(LV2_Handle instance, uint32_t sample_count) { - Metro* self = (Metro*)instance; - const MetroURIs* uris = &self->uris; - - // Work forwards in time frame by frame, handling events as we go - const LV2_Atom_Sequence* in = self->ports.control; - uint32_t last_t = 0; - for (const LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body); - !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev); - ev = lv2_atom_sequence_next(ev)) { - - // Play the click for the time slice from last_t until now - play(self, last_t, ev->time.frames); - - // Check if this event is an Object - // (or deprecated Blank to tolerate old hosts) - if (ev->body.type == uris->atom_Object || - ev->body.type == uris->atom_Blank) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->time_Position) { - // Received position information, update - update_position(self, obj); - } - } - - // Update time for next iteration and move to next event - last_t = ev->time.frames; - } - - // Play for remainder of cycle - play(self, last_t, sample_count); + Metro* self = (Metro*)instance; + const MetroURIs* uris = &self->uris; + + // Work forwards in time frame by frame, handling events as we go + const LV2_Atom_Sequence* in = self->ports.control; + uint32_t last_t = 0; + for (const LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body); + !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev); + ev = lv2_atom_sequence_next(ev)) { + // Play the click for the time slice from last_t until now + play(self, last_t, ev->time.frames); + + // Check if this event is an Object + // (or deprecated Blank to tolerate old hosts) + if (ev->body.type == uris->atom_Object || + ev->body.type == uris->atom_Blank) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; + if (obj->body.otype == uris->time_Position) { + // Received position information, update + update_position(self, obj); + } + } + + // Update time for next iteration and move to next event + last_t = ev->time.frames; + } + + // Play for remainder of cycle + play(self, last_t, sample_count); } static const LV2_Descriptor descriptor = { - EG_METRO_URI, - instantiate, - connect_port, - activate, - run, - NULL, // deactivate, - cleanup, - NULL, // extension_data + EG_METRO_URI, + instantiate, + connect_port, + activate, + run, + NULL, // deactivate, + cleanup, + NULL, // extension_data }; LV2_SYMBOL_EXPORT const LV2_Descriptor* -lv2_descriptor(uint32_t index) + lv2_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-midigate.lv2/midigate.c b/plugins/eg-midigate.lv2/midigate.c index 5bffeea..5379440 100644 --- a/plugins/eg-midigate.lv2/midigate.c +++ b/plugins/eg-midigate.lv2/midigate.c @@ -32,27 +32,27 @@ #define MIDIGATE_URI "http://lv2plug.in/plugins/eg-midigate" typedef enum { - MIDIGATE_CONTROL = 0, - MIDIGATE_IN = 1, - MIDIGATE_OUT = 2 + MIDIGATE_CONTROL = 0, + MIDIGATE_IN = 1, + MIDIGATE_OUT = 2 } PortIndex; typedef struct { - // Port buffers - const LV2_Atom_Sequence* control; - const float* in; - float* out; + // Port buffers + const LV2_Atom_Sequence* control; + const float* in; + float* out; - // Features - LV2_URID_Map* map; - LV2_Log_Logger logger; + // Features + LV2_URID_Map* map; + LV2_Log_Logger logger; - struct { - LV2_URID midi_MidiEvent; - } uris; + struct { + LV2_URID midi_MidiEvent; + } uris; - unsigned n_active_notes; - unsigned program; // 0 = normal, 1 = inverted + unsigned n_active_notes; + unsigned program; // 0 = normal, 1 = inverted } Midigate; static LV2_Handle @@ -61,59 +61,57 @@ instantiate(const LV2_Descriptor* descriptor, const char* bundle_path, const LV2_Feature* const* features) { - Midigate* self = (Midigate*)calloc(1, sizeof(Midigate)); - if (!self) { - return NULL; - } - - // Scan host features for URID map - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - self->uris.midi_MidiEvent = self->map->map( - self->map->handle, LV2_MIDI__MidiEvent); - - return (LV2_Handle)self; + Midigate* self = (Midigate*)calloc(1, sizeof(Midigate)); + if (!self) { + return NULL; + } + + // Scan host features for URID map + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &self->logger.log, false, + LV2_URID__map, &self->map, true, + NULL); + // clang-format on + + lv2_log_logger_set_map(&self->logger, self->map); + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + free(self); + return NULL; + } + + self->uris.midi_MidiEvent = + self->map->map(self->map->handle, LV2_MIDI__MidiEvent); + + return (LV2_Handle)self; } static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) +connect_port(LV2_Handle instance, uint32_t port, void* data) { - Midigate* self = (Midigate*)instance; - - switch ((PortIndex)port) { - case MIDIGATE_CONTROL: - self->control = (const LV2_Atom_Sequence*)data; - break; - case MIDIGATE_IN: - self->in = (const float*)data; - break; - case MIDIGATE_OUT: - self->out = (float*)data; - break; - } + Midigate* self = (Midigate*)instance; + + switch ((PortIndex)port) { + case MIDIGATE_CONTROL: + self->control = (const LV2_Atom_Sequence*)data; + break; + case MIDIGATE_IN: + self->in = (const float*)data; + break; + case MIDIGATE_OUT: + self->out = (float*)data; + break; + } } static void activate(LV2_Handle instance) { - Midigate* self = (Midigate*)instance; - self->n_active_notes = 0; - self->program = 0; + Midigate* self = (Midigate*)instance; + self->n_active_notes = 0; + self->program = 0; } /** @@ -124,14 +122,13 @@ activate(LV2_Handle instance) static void write_output(Midigate* self, uint32_t offset, uint32_t len) { - const bool active = (self->program == 0) - ? (self->n_active_notes > 0) - : (self->n_active_notes == 0); - if (active) { - memcpy(self->out + offset, self->in + offset, len * sizeof(float)); - } else { - memset(self->out + offset, 0, len * sizeof(float)); - } + const bool active = (self->program == 0) ? (self->n_active_notes > 0) + : (self->n_active_notes == 0); + if (active) { + memcpy(self->out + offset, self->in + offset, len * sizeof(float)); + } else { + memset(self->out + offset, 0, len * sizeof(float)); + } } /** @@ -159,40 +156,41 @@ write_output(Midigate* self, uint32_t offset, uint32_t len) static void run(LV2_Handle instance, uint32_t sample_count) { - Midigate* self = (Midigate*)instance; - uint32_t offset = 0; - - LV2_ATOM_SEQUENCE_FOREACH(self->control, ev) { - if (ev->body.type == self->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: - ++self->n_active_notes; - break; - case LV2_MIDI_MSG_NOTE_OFF: - if (self->n_active_notes > 0) { - --self->n_active_notes; - } - break; - case LV2_MIDI_MSG_CONTROLLER: - if (msg[1] == LV2_MIDI_CTL_ALL_NOTES_OFF) { - self->n_active_notes = 0; - } - break; - case LV2_MIDI_MSG_PGM_CHANGE: - if (msg[1] == 0 || msg[1] == 1) { - self->program = msg[1]; - } - break; - default: break; - } - } - - write_output(self, offset, ev->time.frames - offset); - offset = (uint32_t)ev->time.frames; - } - - write_output(self, offset, sample_count - offset); + Midigate* self = (Midigate*)instance; + uint32_t offset = 0; + + LV2_ATOM_SEQUENCE_FOREACH (self->control, ev) { + if (ev->body.type == self->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: + ++self->n_active_notes; + break; + case LV2_MIDI_MSG_NOTE_OFF: + if (self->n_active_notes > 0) { + --self->n_active_notes; + } + break; + case LV2_MIDI_MSG_CONTROLLER: + if (msg[1] == LV2_MIDI_CTL_ALL_NOTES_OFF) { + self->n_active_notes = 0; + } + break; + case LV2_MIDI_MSG_PGM_CHANGE: + if (msg[1] == 0 || msg[1] == 1) { + self->program = msg[1]; + } + break; + default: + break; + } + } + + write_output(self, offset, ev->time.frames - offset); + offset = (uint32_t)ev->time.frames; + } + + write_output(self, offset, sample_count - offset); } /** @@ -207,7 +205,7 @@ deactivate(LV2_Handle instance) static void cleanup(LV2_Handle instance) { - free(instance); + free(instance); } /** @@ -216,23 +214,21 @@ cleanup(LV2_Handle instance) static const void* extension_data(const char* uri) { - return NULL; + return NULL; } -static const LV2_Descriptor descriptor = { - MIDIGATE_URI, - instantiate, - connect_port, - activate, - run, - deactivate, - cleanup, - extension_data -}; +static const LV2_Descriptor descriptor = {MIDIGATE_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data}; LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-params.lv2/params.c b/plugins/eg-params.lv2/params.c index eadd144..56c39cd 100644 --- a/plugins/eg-params.lv2/params.c +++ b/plugins/eg-params.lv2/params.c @@ -37,106 +37,101 @@ #define MAX_STRING 1024 -#define EG_PARAMS_URI "http://lv2plug.in/plugins/eg-params" +#define EG_PARAMS_URI "http://lv2plug.in/plugins/eg-params" #define N_PROPS 9 typedef struct { - LV2_URID plugin; - LV2_URID atom_Path; - LV2_URID atom_Sequence; - LV2_URID atom_URID; - LV2_URID atom_eventTransfer; - LV2_URID eg_spring; - LV2_URID midi_Event; - LV2_URID patch_Get; - LV2_URID patch_Set; - LV2_URID patch_Put; - LV2_URID patch_body; - LV2_URID patch_subject; - LV2_URID patch_property; - LV2_URID patch_value; + LV2_URID plugin; + LV2_URID atom_Path; + LV2_URID atom_Sequence; + LV2_URID atom_URID; + LV2_URID atom_eventTransfer; + LV2_URID eg_spring; + LV2_URID midi_Event; + LV2_URID patch_Get; + LV2_URID patch_Set; + LV2_URID patch_Put; + LV2_URID patch_body; + LV2_URID patch_subject; + LV2_URID patch_property; + LV2_URID patch_value; } URIs; typedef struct { - LV2_Atom_Int aint; - LV2_Atom_Long along; - LV2_Atom_Float afloat; - LV2_Atom_Double adouble; - LV2_Atom_Bool abool; - LV2_Atom astring; - char string[MAX_STRING]; - LV2_Atom apath; - char path[MAX_STRING]; - LV2_Atom_Float lfo; - LV2_Atom_Float spring; + LV2_Atom_Int aint; + LV2_Atom_Long along; + LV2_Atom_Float afloat; + LV2_Atom_Double adouble; + LV2_Atom_Bool abool; + LV2_Atom astring; + char string[MAX_STRING]; + LV2_Atom apath; + char path[MAX_STRING]; + LV2_Atom_Float lfo; + LV2_Atom_Float spring; } State; static inline void map_uris(LV2_URID_Map* map, URIs* uris) { - uris->plugin = map->map(map->handle, EG_PARAMS_URI); - - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->eg_spring = map->map(map->handle, EG_PARAMS_URI "#spring"); - uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); - uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); - uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); - uris->patch_Put = map->map(map->handle, LV2_PATCH__Put); - uris->patch_body = map->map(map->handle, LV2_PATCH__body); - uris->patch_subject = map->map(map->handle, LV2_PATCH__subject); - uris->patch_property = map->map(map->handle, LV2_PATCH__property); - uris->patch_value = map->map(map->handle, LV2_PATCH__value); + uris->plugin = map->map(map->handle, EG_PARAMS_URI); + + uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); + uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); + uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); + uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); + uris->eg_spring = map->map(map->handle, EG_PARAMS_URI "#spring"); + uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); + uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); + uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); + uris->patch_Put = map->map(map->handle, LV2_PATCH__Put); + uris->patch_body = map->map(map->handle, LV2_PATCH__body); + uris->patch_subject = map->map(map->handle, LV2_PATCH__subject); + uris->patch_property = map->map(map->handle, LV2_PATCH__property); + uris->patch_value = map->map(map->handle, LV2_PATCH__value); } -enum { - PARAMS_IN = 0, - PARAMS_OUT = 1 -}; +enum { PARAMS_IN = 0, PARAMS_OUT = 1 }; typedef struct { - // Features - LV2_URID_Map* map; - LV2_URID_Unmap* unmap; - LV2_Log_Logger log; + // Features + LV2_URID_Map* map; + LV2_URID_Unmap* unmap; + LV2_Log_Logger log; - // Forge for creating atoms - LV2_Atom_Forge forge; + // Forge for creating atoms + LV2_Atom_Forge forge; - // Ports - const LV2_Atom_Sequence* in_port; - LV2_Atom_Sequence* out_port; + // Ports + const LV2_Atom_Sequence* in_port; + LV2_Atom_Sequence* out_port; - // URIs - URIs uris; + // URIs + URIs uris; - // Plugin state - StateMapItem props[N_PROPS]; - State state; + // Plugin state + StateMapItem props[N_PROPS]; + State state; - // Buffer for making strings from URIDs if unmap is not provided - char urid_buf[12]; + // Buffer for making strings from URIDs if unmap is not provided + char urid_buf[12]; } Params; static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) +connect_port(LV2_Handle instance, uint32_t port, void* data) { - Params* self = (Params*)instance; - switch (port) { - case PARAMS_IN: - self->in_port = (const LV2_Atom_Sequence*)data; - break; - case PARAMS_OUT: - self->out_port = (LV2_Atom_Sequence*)data; - break; - default: - break; - } + Params* self = (Params*)instance; + switch (port) { + case PARAMS_IN: + self->in_port = (const LV2_Atom_Sequence*)data; + break; + case PARAMS_OUT: + self->out_port = (LV2_Atom_Sequence*)data; + break; + default: + break; + } } static LV2_Handle @@ -145,86 +140,83 @@ instantiate(const LV2_Descriptor* descriptor, const char* path, const LV2_Feature* const* features) { - // Allocate instance - Params* self = (Params*)calloc(1, sizeof(Params)); - if (!self) { - return NULL; - } - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->log.log, false, - LV2_URID__map, &self->map, true, - LV2_URID__unmap, &self->unmap, false, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->log, self->map); - if (missing) { - lv2_log_error(&self->log, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIs and initialise forge - map_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - - // Initialise state dictionary - // clang-format off - State* state = &self->state; - state_map_init( - self->props, self->map, self->map->handle, - EG_PARAMS_URI "#int", STATE_MAP_INIT(Int, &state->aint), - EG_PARAMS_URI "#long", STATE_MAP_INIT(Long, &state->along), - EG_PARAMS_URI "#float", STATE_MAP_INIT(Float, &state->afloat), - EG_PARAMS_URI "#double", STATE_MAP_INIT(Double, &state->adouble), - EG_PARAMS_URI "#bool", STATE_MAP_INIT(Bool, &state->abool), - EG_PARAMS_URI "#string", STATE_MAP_INIT(String, &state->astring), - EG_PARAMS_URI "#path", STATE_MAP_INIT(Path, &state->apath), - EG_PARAMS_URI "#lfo", STATE_MAP_INIT(Float, &state->lfo), - EG_PARAMS_URI "#spring", STATE_MAP_INIT(Float, &state->spring), - NULL); - // clang-format on - - return (LV2_Handle)self; + // Allocate instance + Params* self = (Params*)calloc(1, sizeof(Params)); + if (!self) { + return NULL; + } + + // Get host features + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &self->log.log, false, + LV2_URID__map, &self->map, true, + LV2_URID__unmap, &self->unmap, false, + NULL); + // clang-format on + + lv2_log_logger_set_map(&self->log, self->map); + if (missing) { + lv2_log_error(&self->log, "Missing feature <%s>\n", missing); + free(self); + return NULL; + } + + // Map URIs and initialise forge + map_uris(self->map, &self->uris); + lv2_atom_forge_init(&self->forge, self->map); + + // Initialise state dictionary + // clang-format off + State* state = &self->state; + state_map_init( + self->props, self->map, self->map->handle, + EG_PARAMS_URI "#int", STATE_MAP_INIT(Int, &state->aint), + EG_PARAMS_URI "#long", STATE_MAP_INIT(Long, &state->along), + EG_PARAMS_URI "#float", STATE_MAP_INIT(Float, &state->afloat), + EG_PARAMS_URI "#double", STATE_MAP_INIT(Double, &state->adouble), + EG_PARAMS_URI "#bool", STATE_MAP_INIT(Bool, &state->abool), + EG_PARAMS_URI "#string", STATE_MAP_INIT(String, &state->astring), + EG_PARAMS_URI "#path", STATE_MAP_INIT(Path, &state->apath), + EG_PARAMS_URI "#lfo", STATE_MAP_INIT(Float, &state->lfo), + EG_PARAMS_URI "#spring", STATE_MAP_INIT(Float, &state->spring), + NULL); + // clang-format on + + return (LV2_Handle)self; } static void cleanup(LV2_Handle instance) { - free(instance); + free(instance); } /** Helper function to unmap a URID if possible. */ static const char* unmap(Params* self, LV2_URID urid) { - if (self->unmap) { - return self->unmap->unmap(self->unmap->handle, urid); - } else { - snprintf(self->urid_buf, sizeof(self->urid_buf), "%u", urid); - return self->urid_buf; - } + if (self->unmap) { + return self->unmap->unmap(self->unmap->handle, urid); + } else { + snprintf(self->urid_buf, sizeof(self->urid_buf), "%u", urid); + return self->urid_buf; + } } static LV2_State_Status -check_type(Params* self, - LV2_URID key, - LV2_URID type, - LV2_URID required_type) +check_type(Params* self, LV2_URID key, LV2_URID type, LV2_URID required_type) { - if (type != required_type) { - lv2_log_trace( - &self->log, "Bad type <%s> for <%s> (needs <%s>)\n", - unmap(self, type), - unmap(self, key), - unmap(self, required_type)); - return LV2_STATE_ERR_BAD_TYPE; - } - return LV2_STATE_SUCCESS; + if (type != required_type) { + lv2_log_trace(&self->log, + "Bad type <%s> for <%s> (needs <%s>)\n", + unmap(self, type), + unmap(self, key), + unmap(self, required_type)); + return LV2_STATE_ERR_BAD_TYPE; + } + return LV2_STATE_SUCCESS; } static LV2_State_Status @@ -235,36 +227,36 @@ set_parameter(Params* self, const void* body, bool from_state) { - // Look up property in state dictionary - const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); - if (!entry) { - lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); - return LV2_STATE_ERR_NO_PROPERTY; - } - - // Ensure given type matches property's type - if (check_type(self, key, type, entry->value->type)) { - return LV2_STATE_ERR_BAD_TYPE; - } - - // Set property value in state dictionary - lv2_log_trace(&self->log, "Set <%s>\n", entry->uri); - memcpy(entry->value + 1, body, size); - entry->value->size = size; - return LV2_STATE_SUCCESS; + // Look up property in state dictionary + const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); + if (!entry) { + lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); + return LV2_STATE_ERR_NO_PROPERTY; + } + + // Ensure given type matches property's type + if (check_type(self, key, type, entry->value->type)) { + return LV2_STATE_ERR_BAD_TYPE; + } + + // Set property value in state dictionary + lv2_log_trace(&self->log, "Set <%s>\n", entry->uri); + memcpy(entry->value + 1, body, size); + entry->value->size = size; + return LV2_STATE_SUCCESS; } static const LV2_Atom* get_parameter(Params* self, LV2_URID key) { - const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); - if (entry) { - lv2_log_trace(&self->log, "Get <%s>\n", entry->uri); - return entry->value; - } - - lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); - return NULL; + const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); + if (entry) { + lv2_log_trace(&self->log, "Get <%s>\n", entry->uri); + return entry->value; + } + + lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); + return NULL; } static LV2_State_Status @@ -275,15 +267,15 @@ write_param_to_forge(LV2_State_Handle handle, uint32_t type, uint32_t flags) { - LV2_Atom_Forge* forge = (LV2_Atom_Forge*)handle; + LV2_Atom_Forge* forge = (LV2_Atom_Forge*)handle; - if (!lv2_atom_forge_key(forge, key) || - !lv2_atom_forge_atom(forge, size, type) || - !lv2_atom_forge_write(forge, value, size)) { - return LV2_STATE_ERR_UNKNOWN; - } + if (!lv2_atom_forge_key(forge, key) || + !lv2_atom_forge_atom(forge, size, type) || + !lv2_atom_forge_write(forge, value, size)) { + return LV2_STATE_ERR_UNKNOWN; + } - return LV2_STATE_SUCCESS; + return LV2_STATE_SUCCESS; } static void @@ -295,31 +287,31 @@ store_prop(Params* self, LV2_URID key, const LV2_Atom* value) { - LV2_State_Status st = LV2_STATE_SUCCESS; - if (map_path && value->type == self->uris.atom_Path) { - // Map path to abstract path for portable storage - const char* path = (const char*)(value + 1); - char* apath = map_path->abstract_path(map_path->handle, path); - st = store(handle, - key, - apath, - strlen(apath) + 1, - self->uris.atom_Path, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - free(apath); - } else { - // Store simple property - st = store(handle, - key, - value + 1, - value->size, - value->type, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - } - - if (save_status && !*save_status) { - *save_status = st; - } + LV2_State_Status st = LV2_STATE_SUCCESS; + if (map_path && value->type == self->uris.atom_Path) { + // Map path to abstract path for portable storage + const char* path = (const char*)(value + 1); + char* apath = map_path->abstract_path(map_path->handle, path); + st = store(handle, + key, + apath, + strlen(apath) + 1, + self->uris.atom_Path, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + free(apath); + } else { + // Store simple property + st = store(handle, + key, + value + 1, + value->size, + value->type, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + } + + if (save_status && !*save_status) { + *save_status = st; + } } /** @@ -336,17 +328,17 @@ save(LV2_Handle instance, uint32_t flags, const LV2_Feature* const* features) { - Params* self = (Params*)instance; - LV2_State_Map_Path* map_path = (LV2_State_Map_Path*)lv2_features_data( - features, LV2_STATE__mapPath); + Params* self = (Params*)instance; + LV2_State_Map_Path* map_path = + (LV2_State_Map_Path*)lv2_features_data(features, LV2_STATE__mapPath); - LV2_State_Status st = LV2_STATE_SUCCESS; - for (unsigned i = 0; i < N_PROPS; ++i) { - StateMapItem* prop = &self->props[i]; - store_prop(self, map_path, &st, store, handle, prop->urid, prop->value); - } + LV2_State_Status st = LV2_STATE_SUCCESS; + for (unsigned i = 0; i < N_PROPS; ++i) { + StateMapItem* prop = &self->props[i]; + store_prop(self, map_path, &st, store, handle, prop->urid, prop->value); + } - return st; + return st; } static void @@ -356,20 +348,20 @@ retrieve_prop(Params* self, LV2_State_Handle handle, LV2_URID key) { - // Retrieve value from saved state - size_t vsize = 0; - uint32_t vtype = 0; - uint32_t vflags = 0; - const void* value = retrieve(handle, key, &vsize, &vtype, &vflags); - - // Set plugin instance state - const LV2_State_Status st = value - ? set_parameter(self, key, vsize, vtype, value, true) - : LV2_STATE_ERR_NO_PROPERTY; - - if (!*restore_status) { - *restore_status = st; // Set status if there has been no error yet - } + // Retrieve value from saved state + size_t vsize = 0; + uint32_t vtype = 0; + uint32_t vflags = 0; + const void* value = retrieve(handle, key, &vsize, &vtype, &vflags); + + // Set plugin instance state + const LV2_State_Status st = + value ? set_parameter(self, key, vsize, vtype, value, true) + : LV2_STATE_ERR_NO_PROPERTY; + + if (!*restore_status) { + *restore_status = st; // Set status if there has been no error yet + } } /** State restore method. */ @@ -380,160 +372,162 @@ restore(LV2_Handle instance, uint32_t flags, const LV2_Feature* const* features) { - Params* self = (Params*)instance; - LV2_State_Status st = LV2_STATE_SUCCESS; + Params* self = (Params*)instance; + LV2_State_Status st = LV2_STATE_SUCCESS; - for (unsigned i = 0; i < N_PROPS; ++i) { - retrieve_prop(self, &st, retrieve, handle, self->props[i].urid); - } + for (unsigned i = 0; i < N_PROPS; ++i) { + retrieve_prop(self, &st, retrieve, handle, self->props[i].urid); + } - return st; + return st; } static inline bool subject_is_plugin(Params* self, const LV2_Atom_URID* subject) { - // This simple plugin only supports one subject: itself - return (!subject || (subject->atom.type == self->uris.atom_URID && - subject->body == self->uris.plugin)); + // This simple plugin only supports one subject: itself + return (!subject || (subject->atom.type == self->uris.atom_URID && + subject->body == self->uris.plugin)); } static void run(LV2_Handle instance, uint32_t sample_count) { - Params* self = (Params*)instance; - URIs* uris = &self->uris; - - // Initially, self->out_port contains a Chunk with size set to capacity - // Set up forge to write directly to output port - const uint32_t out_capacity = self->out_port->atom.size; - lv2_atom_forge_set_buffer(&self->forge, - (uint8_t*)self->out_port, - out_capacity); - - // Start a sequence in the output port - LV2_Atom_Forge_Frame out_frame; - lv2_atom_forge_sequence_head(&self->forge, &out_frame, 0); - - // Read incoming events - LV2_ATOM_SEQUENCE_FOREACH(self->in_port, ev) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->patch_Set) { - // Get the property and value of the set message - const LV2_Atom_URID* subject = NULL; - const LV2_Atom_URID* property = NULL; - const LV2_Atom* value = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->patch_subject, (const LV2_Atom**)&subject, - uris->patch_property, (const LV2_Atom**)&property, - uris->patch_value, &value, - 0); - // clang-format on - - if (!subject_is_plugin(self, subject)) { - lv2_log_error(&self->log, "Set for unknown subject\n"); - } else if (!property) { - lv2_log_error(&self->log, "Set with no property\n"); - } else if (property->atom.type != uris->atom_URID) { - lv2_log_error(&self->log, "Set property is not a URID\n"); - } else { - // Set property to the given value - const LV2_URID key = property->body; - set_parameter(self, key, value->size, value->type, value + 1, false); - } - } else if (obj->body.otype == uris->patch_Get) { - // Get the property of the get message - const LV2_Atom_URID* subject = NULL; - const LV2_Atom_URID* property = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->patch_subject, (const LV2_Atom**)&subject, - uris->patch_property, (const LV2_Atom**)&property, - 0); - // clang-format on - - if (!subject_is_plugin(self, subject)) { - lv2_log_error(&self->log, "Get with unknown subject\n"); - } else if (!property) { - // Get with no property, emit complete state - lv2_atom_forge_frame_time(&self->forge, ev->time.frames); - LV2_Atom_Forge_Frame pframe; - lv2_atom_forge_object(&self->forge, &pframe, 0, uris->patch_Put); - lv2_atom_forge_key(&self->forge, uris->patch_body); - - LV2_Atom_Forge_Frame bframe; - lv2_atom_forge_object(&self->forge, &bframe, 0, 0); - save(self, write_param_to_forge, &self->forge, 0, NULL); - - lv2_atom_forge_pop(&self->forge, &bframe); - lv2_atom_forge_pop(&self->forge, &pframe); - } else if (property->atom.type != uris->atom_URID) { - lv2_log_error(&self->log, "Get property is not a URID\n"); - } else { - // Get for a specific property - const LV2_URID key = property->body; - const LV2_Atom* value = get_parameter(self, key); - if (value) { - lv2_atom_forge_frame_time(&self->forge, ev->time.frames); - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); - lv2_atom_forge_key(&self->forge, uris->patch_property); - lv2_atom_forge_urid(&self->forge, property->body); - store_prop(self, NULL, NULL, write_param_to_forge, &self->forge, - uris->patch_value, value); - lv2_atom_forge_pop(&self->forge, &frame); - } - } - } else { - lv2_log_trace(&self->log, "Unknown object type <%s>\n", - unmap(self, obj->body.otype)); - } - } - - if (self->state.spring.body > 0.0f) { - const float spring = self->state.spring.body; - self->state.spring.body = (spring >= 0.001) ? spring - 0.001f : 0.0f; - lv2_atom_forge_frame_time(&self->forge, 0); - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); - - lv2_atom_forge_key(&self->forge, uris->patch_property); - lv2_atom_forge_urid(&self->forge, uris->eg_spring); - lv2_atom_forge_key(&self->forge, uris->patch_value); - lv2_atom_forge_float(&self->forge, self->state.spring.body); - - lv2_atom_forge_pop(&self->forge, &frame); - } - - lv2_atom_forge_pop(&self->forge, &out_frame); + Params* self = (Params*)instance; + URIs* uris = &self->uris; + + // Initially, self->out_port contains a Chunk with size set to capacity + // Set up forge to write directly to output port + const uint32_t out_capacity = self->out_port->atom.size; + lv2_atom_forge_set_buffer( + &self->forge, (uint8_t*)self->out_port, out_capacity); + + // Start a sequence in the output port + LV2_Atom_Forge_Frame out_frame; + lv2_atom_forge_sequence_head(&self->forge, &out_frame, 0); + + // Read incoming events + LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; + if (obj->body.otype == uris->patch_Set) { + // Get the property and value of the set message + const LV2_Atom_URID* subject = NULL; + const LV2_Atom_URID* property = NULL; + const LV2_Atom* value = NULL; + + // clang-format off + lv2_atom_object_get(obj, + uris->patch_subject, (const LV2_Atom**)&subject, + uris->patch_property, (const LV2_Atom**)&property, + uris->patch_value, &value, + 0); + // clang-format on + + if (!subject_is_plugin(self, subject)) { + lv2_log_error(&self->log, "Set for unknown subject\n"); + } else if (!property) { + lv2_log_error(&self->log, "Set with no property\n"); + } else if (property->atom.type != uris->atom_URID) { + lv2_log_error(&self->log, "Set property is not a URID\n"); + } else { + // Set property to the given value + const LV2_URID key = property->body; + set_parameter(self, key, value->size, value->type, value + 1, false); + } + } else if (obj->body.otype == uris->patch_Get) { + // Get the property of the get message + const LV2_Atom_URID* subject = NULL; + const LV2_Atom_URID* property = NULL; + + // clang-format off + lv2_atom_object_get(obj, + uris->patch_subject, (const LV2_Atom**)&subject, + uris->patch_property, (const LV2_Atom**)&property, + 0); + // clang-format on + + if (!subject_is_plugin(self, subject)) { + lv2_log_error(&self->log, "Get with unknown subject\n"); + } else if (!property) { + // Get with no property, emit complete state + lv2_atom_forge_frame_time(&self->forge, ev->time.frames); + LV2_Atom_Forge_Frame pframe; + lv2_atom_forge_object(&self->forge, &pframe, 0, uris->patch_Put); + lv2_atom_forge_key(&self->forge, uris->patch_body); + + LV2_Atom_Forge_Frame bframe; + lv2_atom_forge_object(&self->forge, &bframe, 0, 0); + save(self, write_param_to_forge, &self->forge, 0, NULL); + + lv2_atom_forge_pop(&self->forge, &bframe); + lv2_atom_forge_pop(&self->forge, &pframe); + } else if (property->atom.type != uris->atom_URID) { + lv2_log_error(&self->log, "Get property is not a URID\n"); + } else { + // Get for a specific property + const LV2_URID key = property->body; + const LV2_Atom* value = get_parameter(self, key); + if (value) { + lv2_atom_forge_frame_time(&self->forge, ev->time.frames); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); + lv2_atom_forge_key(&self->forge, uris->patch_property); + lv2_atom_forge_urid(&self->forge, property->body); + store_prop(self, + NULL, + NULL, + write_param_to_forge, + &self->forge, + uris->patch_value, + value); + lv2_atom_forge_pop(&self->forge, &frame); + } + } + } else { + lv2_log_trace( + &self->log, "Unknown object type <%s>\n", unmap(self, obj->body.otype)); + } + } + + if (self->state.spring.body > 0.0f) { + const float spring = self->state.spring.body; + self->state.spring.body = (spring >= 0.001) ? spring - 0.001f : 0.0f; + lv2_atom_forge_frame_time(&self->forge, 0); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); + + lv2_atom_forge_key(&self->forge, uris->patch_property); + lv2_atom_forge_urid(&self->forge, uris->eg_spring); + lv2_atom_forge_key(&self->forge, uris->patch_value); + lv2_atom_forge_float(&self->forge, self->state.spring.body); + + lv2_atom_forge_pop(&self->forge, &frame); + } + + lv2_atom_forge_pop(&self->forge, &out_frame); } static const void* extension_data(const char* uri) { - static const LV2_State_Interface state = { save, restore }; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - return NULL; + static const LV2_State_Interface state = {save, restore}; + if (!strcmp(uri, LV2_STATE__interface)) { + return &state; + } + return NULL; } -static const LV2_Descriptor descriptor = { - EG_PARAMS_URI, - instantiate, - connect_port, - NULL, // activate, - run, - NULL, // deactivate, - cleanup, - extension_data -}; +static const LV2_Descriptor descriptor = {EG_PARAMS_URI, + instantiate, + connect_port, + NULL, // activate, + run, + NULL, // deactivate, + cleanup, + extension_data}; LV2_SYMBOL_EXPORT const LV2_Descriptor* -lv2_descriptor(uint32_t index) + lv2_descriptor(uint32_t index) { - return (index == 0) ? &descriptor : NULL; + return (index == 0) ? &descriptor : NULL; } diff --git a/plugins/eg-params.lv2/state_map.h b/plugins/eg-params.lv2/state_map.h index 85358f8..f534f60 100644 --- a/plugins/eg-params.lv2/state_map.h +++ b/plugins/eg-params.lv2/state_map.h @@ -24,33 +24,31 @@ /** Entry in an array that serves as a dictionary of properties. */ typedef struct { - const char* uri; - LV2_URID urid; - LV2_Atom* value; + const char* uri; + LV2_URID urid; + LV2_Atom* value; } StateMapItem; /** Comparator for StateMapItems sorted by URID. */ static int state_map_cmp(const void* a, const void* b) { - const StateMapItem* ka = (const StateMapItem*)a; - const StateMapItem* kb = (const StateMapItem*)b; - if (ka->urid < kb->urid) { - return -1; - } + const StateMapItem* ka = (const StateMapItem*)a; + const StateMapItem* kb = (const StateMapItem*)b; + if (ka->urid < kb->urid) { + return -1; + } - if (kb->urid < ka->urid) { - return 1; - } + if (kb->urid < ka->urid) { + return 1; + } - return 0; + return 0; } /** Helper macro for terse state map initialisation. */ #define STATE_MAP_INIT(type, ptr) \ - (LV2_ATOM__ ## type), \ - (sizeof(*ptr) - sizeof(LV2_Atom)), \ - (ptr) + (LV2_ATOM__##type), (sizeof(*ptr) - sizeof(LV2_Atom)), (ptr) /** Initialise a state map. @@ -76,29 +74,30 @@ state_map_cmp(const void* a, const void* b) NULL); */ static void -state_map_init(StateMapItem dict[], - LV2_URID_Map* map, - LV2_URID_Map_Handle handle, - /* const char* uri, const char* type, uint32_t size, LV2_Atom* value */ ...) +state_map_init( + StateMapItem dict[], + LV2_URID_Map* map, + LV2_URID_Map_Handle handle, + /* const char* uri, const char* type, uint32_t size, LV2_Atom* value */...) { - // Set dict entries from parameters - unsigned i = 0; - va_list args; - va_start(args, handle); - for (const char* uri = NULL; (uri = va_arg(args, const char*)); ++i) { - const char* type = va_arg(args, const char*); - const uint32_t size = va_arg(args, uint32_t); - LV2_Atom* const value = va_arg(args, LV2_Atom*); - dict[i].uri = uri; - dict[i].urid = map->map(map->handle, uri); - dict[i].value = value; - dict[i].value->size = size; - dict[i].value->type = map->map(map->handle, type); - } - va_end(args); - - // Sort for fast lookup by URID by state_map_find() - qsort(dict, i, sizeof(StateMapItem), state_map_cmp); + // Set dict entries from parameters + unsigned i = 0; + va_list args; + va_start(args, handle); + for (const char* uri = NULL; (uri = va_arg(args, const char*)); ++i) { + const char* type = va_arg(args, const char*); + const uint32_t size = va_arg(args, uint32_t); + LV2_Atom* const value = va_arg(args, LV2_Atom*); + dict[i].uri = uri; + dict[i].urid = map->map(map->handle, uri); + dict[i].value = value; + dict[i].value->size = size; + dict[i].value->type = map->map(map->handle, type); + } + va_end(args); + + // Sort for fast lookup by URID by state_map_find() + qsort(dict, i, sizeof(StateMapItem), state_map_cmp); } /** @@ -111,8 +110,7 @@ state_map_init(StateMapItem dict[], static StateMapItem* state_map_find(StateMapItem dict[], uint32_t n_entries, LV2_URID urid) { - const StateMapItem key = { NULL, urid, NULL }; - return (StateMapItem*)bsearch( - &key, dict, n_entries, sizeof(StateMapItem), state_map_cmp); + const StateMapItem key = {NULL, urid, NULL}; + return (StateMapItem*)bsearch( + &key, dict, n_entries, sizeof(StateMapItem), state_map_cmp); } - diff --git a/plugins/eg-sampler.lv2/atom_sink.h b/plugins/eg-sampler.lv2/atom_sink.h index 659b767..57035e1 100644 --- a/plugins/eg-sampler.lv2/atom_sink.h +++ b/plugins/eg-sampler.lv2/atom_sink.h @@ -30,11 +30,11 @@ static LV2_Atom_Forge_Ref atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void* buf, uint32_t size) { - LV2_Atom* atom = (LV2_Atom*)handle; - const uint32_t offset = lv2_atom_total_size(atom); - memcpy((char*)atom + offset, buf, size); - atom->size += size; - return offset; + LV2_Atom* atom = (LV2_Atom*)handle; + const uint32_t offset = lv2_atom_total_size(atom); + memcpy((char*)atom + offset, buf, size); + atom->size += size; + return offset; } /** @@ -43,5 +43,5 @@ atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void* buf, uint32_t size) static LV2_Atom* atom_sink_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref) { - return (LV2_Atom*)((char*)handle + ref); + return (LV2_Atom*)((char*)handle + ref); } diff --git a/plugins/eg-sampler.lv2/peaks.h b/plugins/eg-sampler.lv2/peaks.h index 2bf1023..ca5328d 100644 --- a/plugins/eg-sampler.lv2/peaks.h +++ b/plugins/eg-sampler.lv2/peaks.h @@ -39,42 +39,42 @@ #include <stdlib.h> #include <string.h> -#define PEAKS_URI "http://lv2plug.in/ns/peaks#" -#define PEAKS__PeakUpdate PEAKS_URI "PeakUpdate" -#define PEAKS__magnitudes PEAKS_URI "magnitudes" -#define PEAKS__offset PEAKS_URI "offset" -#define PEAKS__total PEAKS_URI "total" +#define PEAKS_URI "http://lv2plug.in/ns/peaks#" +#define PEAKS__PeakUpdate PEAKS_URI "PeakUpdate" +#define PEAKS__magnitudes PEAKS_URI "magnitudes" +#define PEAKS__offset PEAKS_URI "offset" +#define PEAKS__total PEAKS_URI "total" #ifndef MIN -# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX -# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif typedef struct { - LV2_URID atom_Float; - LV2_URID atom_Int; - LV2_URID atom_Vector; - LV2_URID peaks_PeakUpdate; - LV2_URID peaks_magnitudes; - LV2_URID peaks_offset; - LV2_URID peaks_total; + LV2_URID atom_Float; + LV2_URID atom_Int; + LV2_URID atom_Vector; + LV2_URID peaks_PeakUpdate; + LV2_URID peaks_magnitudes; + LV2_URID peaks_offset; + LV2_URID peaks_total; } PeaksURIs; typedef struct { - PeaksURIs uris; ///< URIDs used in protocol - const float* samples; ///< Sample data - uint32_t n_samples; ///< Total number of samples - uint32_t n_peaks; ///< Total number of peaks - uint32_t current_offset; ///< Current peak offset - bool sending; ///< True iff currently sending + PeaksURIs uris; ///< URIDs used in protocol + const float* samples; ///< Sample data + uint32_t n_samples; ///< Total number of samples + uint32_t n_peaks; ///< Total number of peaks + uint32_t current_offset; ///< Current peak offset + bool sending; ///< True iff currently sending } PeaksSender; typedef struct { - PeaksURIs uris; ///< URIDs used in protocol - float* peaks; ///< Received peaks, or zeroes - uint32_t n_peaks; ///< Total number of peaks + PeaksURIs uris; ///< URIDs used in protocol + float* peaks; ///< Received peaks, or zeroes + uint32_t n_peaks; ///< Total number of peaks } PeaksReceiver; /** @@ -83,13 +83,13 @@ typedef struct { static inline void peaks_map_uris(PeaksURIs* uris, LV2_URID_Map* map) { - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); - uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); - uris->peaks_PeakUpdate = map->map(map->handle, PEAKS__PeakUpdate); - uris->peaks_magnitudes = map->map(map->handle, PEAKS__magnitudes); - uris->peaks_offset = map->map(map->handle, PEAKS__offset); - uris->peaks_total = map->map(map->handle, PEAKS__total); + uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); + uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); + uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); + uris->peaks_PeakUpdate = map->map(map->handle, PEAKS__PeakUpdate); + uris->peaks_magnitudes = map->map(map->handle, PEAKS__magnitudes); + uris->peaks_offset = map->map(map->handle, PEAKS__offset); + uris->peaks_total = map->map(map->handle, PEAKS__total); } /** @@ -100,9 +100,9 @@ peaks_map_uris(PeaksURIs* uris, LV2_URID_Map* map) static inline PeaksSender* peaks_sender_init(PeaksSender* sender, LV2_URID_Map* map) { - memset(sender, 0, sizeof(*sender)); - peaks_map_uris(&sender->uris, map); - return sender; + memset(sender, 0, sizeof(*sender)); + peaks_map_uris(&sender->uris, map); + return sender; } /** @@ -115,11 +115,11 @@ peaks_sender_start(PeaksSender* sender, uint32_t n_samples, uint32_t n_peaks) { - sender->samples = samples; - sender->n_samples = n_samples; - sender->n_peaks = n_peaks; - sender->current_offset = 0; - sender->sending = true; + sender->samples = samples; + sender->n_samples = n_samples; + sender->n_peaks = n_peaks; + sender->current_offset = 0; + sender->sending = true; } /** @@ -129,10 +129,10 @@ peaks_sender_start(PeaksSender* sender, [source,turtle] ---- [] - a peaks:PeakUpdate ; - peaks:offset 256 ; - peaks:total 1024 ; - peaks:magnitudes [ 0.2f, 0.3f, ... ] . + a peaks:PeakUpdate ; + peaks:offset 256 ; + peaks:total 1024 ; + peaks:magnitudes [ 0.2f, 0.3f, ... ] . ---- */ static inline bool @@ -141,53 +141,53 @@ peaks_sender_send(PeaksSender* sender, uint32_t n_frames, uint32_t offset) { - const PeaksURIs* uris = &sender->uris; - if (!sender->sending || sender->current_offset >= sender->n_peaks) { - return sender->sending = false; - } - - // Start PeakUpdate object - lv2_atom_forge_frame_time(forge, offset); - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(forge, &frame, 0, uris->peaks_PeakUpdate); - - // eg:offset = OFFSET - lv2_atom_forge_key(forge, uris->peaks_offset); - lv2_atom_forge_int(forge, sender->current_offset); - - // eg:total = TOTAL - lv2_atom_forge_key(forge, uris->peaks_total); - lv2_atom_forge_int(forge, sender->n_peaks); - - // eg:magnitudes = Vector<Float>(PEAK, PEAK, ...) - lv2_atom_forge_key(forge, uris->peaks_magnitudes); - LV2_Atom_Forge_Frame vec_frame; - lv2_atom_forge_vector_head( - forge, &vec_frame, sizeof(float), uris->atom_Float); - - // Calculate how many peaks to send this update - const uint32_t chunk_size = MAX(1u, sender->n_samples / sender->n_peaks); - const uint32_t space = forge->size - forge->offset; - const uint32_t remaining = sender->n_peaks - sender->current_offset; - const uint32_t n_update = MIN(remaining, - MIN(n_frames / 4u, space / sizeof(float))); - - // Calculate peak (maximum magnitude) for each chunk - for (uint32_t i = 0; i < n_update; ++i) { - const uint32_t start = (sender->current_offset + i) * chunk_size; - float peak = 0.0f; - for (uint32_t j = 0; j < chunk_size; ++j) { - peak = fmaxf(peak, fabsf(sender->samples[start + j])); - } - lv2_atom_forge_float(forge, peak); - } - - // Finish message - lv2_atom_forge_pop(forge, &vec_frame); - lv2_atom_forge_pop(forge, &frame); - - sender->current_offset += n_update; - return true; + const PeaksURIs* uris = &sender->uris; + if (!sender->sending || sender->current_offset >= sender->n_peaks) { + return sender->sending = false; + } + + // Start PeakUpdate object + lv2_atom_forge_frame_time(forge, offset); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(forge, &frame, 0, uris->peaks_PeakUpdate); + + // eg:offset = OFFSET + lv2_atom_forge_key(forge, uris->peaks_offset); + lv2_atom_forge_int(forge, sender->current_offset); + + // eg:total = TOTAL + lv2_atom_forge_key(forge, uris->peaks_total); + lv2_atom_forge_int(forge, sender->n_peaks); + + // eg:magnitudes = Vector<Float>(PEAK, PEAK, ...) + lv2_atom_forge_key(forge, uris->peaks_magnitudes); + LV2_Atom_Forge_Frame vec_frame; + lv2_atom_forge_vector_head( + forge, &vec_frame, sizeof(float), uris->atom_Float); + + // Calculate how many peaks to send this update + const uint32_t chunk_size = MAX(1u, sender->n_samples / sender->n_peaks); + const uint32_t space = forge->size - forge->offset; + const uint32_t remaining = sender->n_peaks - sender->current_offset; + const uint32_t n_update = + MIN(remaining, MIN(n_frames / 4u, space / sizeof(float))); + + // Calculate peak (maximum magnitude) for each chunk + for (uint32_t i = 0; i < n_update; ++i) { + const uint32_t start = (sender->current_offset + i) * chunk_size; + float peak = 0.0f; + for (uint32_t j = 0; j < chunk_size; ++j) { + peak = fmaxf(peak, fabsf(sender->samples[start + j])); + } + lv2_atom_forge_float(forge, peak); + } + + // Finish message + lv2_atom_forge_pop(forge, &vec_frame); + lv2_atom_forge_pop(forge, &frame); + + sender->current_offset += n_update; + return true; } /** @@ -197,9 +197,9 @@ peaks_sender_send(PeaksSender* sender, static inline PeaksReceiver* peaks_receiver_init(PeaksReceiver* receiver, LV2_URID_Map* map) { - memset(receiver, 0, sizeof(*receiver)); - peaks_map_uris(&receiver->uris, map); - return receiver; + memset(receiver, 0, sizeof(*receiver)); + peaks_map_uris(&receiver->uris, map); + return receiver; } /** @@ -209,9 +209,9 @@ peaks_receiver_init(PeaksReceiver* receiver, LV2_URID_Map* map) static inline void peaks_receiver_clear(PeaksReceiver* receiver) { - free(receiver->peaks); - receiver->peaks = NULL; - receiver->n_peaks = 0; + free(receiver->peaks); + receiver->peaks = NULL; + receiver->n_peaks = 0; } /** @@ -225,56 +225,56 @@ peaks_receiver_clear(PeaksReceiver* receiver) static inline int peaks_receiver_receive(PeaksReceiver* receiver, const LV2_Atom_Object* update) { - const PeaksURIs* uris = &receiver->uris; - - // Get properties of interest from update - const LV2_Atom_Int* offset = NULL; - const LV2_Atom_Int* total = NULL; - const LV2_Atom_Vector* peaks = NULL; - - // clang-format off - lv2_atom_object_get_typed(update, - uris->peaks_offset, &offset, uris->atom_Int, - uris->peaks_total, &total, uris->atom_Int, - uris->peaks_magnitudes, &peaks, uris->atom_Vector, - 0); - // clang-format on - - if (!offset || !total || !peaks || - peaks->body.child_type != uris->atom_Float) { - return -1; // Invalid update - } - - const uint32_t n = (uint32_t)total->body; - if (receiver->n_peaks != n) { - // Update is for a different total number of peaks, resize - receiver->peaks = (float*)realloc(receiver->peaks, n * sizeof(float)); - if (receiver->n_peaks > 0 && receiver->n_peaks < n) { - /* The peaks array is being expanded. Copy the old peaks, - duplicating each as necessary to fill the new peaks buffer. - This preserves the current peaks so that the peaks array can be - reasonably drawn at any time, but the resolution will increase - as new updates arrive. */ - const int64_t n_per = n / receiver->n_peaks; - for (int64_t i = n - 1; i >= 0; --i) { - receiver->peaks[i] = receiver->peaks[i / n_per]; - } - } else if (receiver->n_peaks > 0) { - /* The peak array is being shrunk. Similar to the above. */ - const int64_t n_per = receiver->n_peaks / n; - for (int64_t i = n - 1; i >= 0; --i) { - receiver->peaks[i] = receiver->peaks[i * n_per]; - } - } - receiver->n_peaks = n; - } - - // Copy vector contents to corresponding range in peaks array - memcpy(receiver->peaks + offset->body, - peaks + 1, - peaks->atom.size - sizeof(LV2_Atom_Vector_Body)); - - return 0; + const PeaksURIs* uris = &receiver->uris; + + // Get properties of interest from update + const LV2_Atom_Int* offset = NULL; + const LV2_Atom_Int* total = NULL; + const LV2_Atom_Vector* peaks = NULL; + + // clang-format off + lv2_atom_object_get_typed(update, + uris->peaks_offset, &offset, uris->atom_Int, + uris->peaks_total, &total, uris->atom_Int, + uris->peaks_magnitudes, &peaks, uris->atom_Vector, + 0); + // clang-format on + + if (!offset || !total || !peaks || + peaks->body.child_type != uris->atom_Float) { + return -1; // Invalid update + } + + const uint32_t n = (uint32_t)total->body; + if (receiver->n_peaks != n) { + // Update is for a different total number of peaks, resize + receiver->peaks = (float*)realloc(receiver->peaks, n * sizeof(float)); + if (receiver->n_peaks > 0 && receiver->n_peaks < n) { + /* The peaks array is being expanded. Copy the old peaks, + duplicating each as necessary to fill the new peaks buffer. + This preserves the current peaks so that the peaks array can be + reasonably drawn at any time, but the resolution will increase + as new updates arrive. */ + const int64_t n_per = n / receiver->n_peaks; + for (int64_t i = n - 1; i >= 0; --i) { + receiver->peaks[i] = receiver->peaks[i / n_per]; + } + } else if (receiver->n_peaks > 0) { + /* The peak array is being shrunk. Similar to the above. */ + const int64_t n_per = receiver->n_peaks / n; + for (int64_t i = n - 1; i >= 0; --i) { + receiver->peaks[i] = receiver->peaks[i * n_per]; + } + } + receiver->n_peaks = n; + } + + // Copy vector contents to corresponding range in peaks array + memcpy(receiver->peaks + offset->body, + peaks + 1, + peaks->atom.size - sizeof(LV2_Atom_Vector_Body)); + + return 0; } #endif // PEAKS_H_INCLUDED diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index cd410d1..6c64df5 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -42,48 +42,44 @@ #include <stdlib.h> #include <string.h> -enum { - SAMPLER_CONTROL = 0, - SAMPLER_NOTIFY = 1, - SAMPLER_OUT = 2 -}; +enum { SAMPLER_CONTROL = 0, SAMPLER_NOTIFY = 1, SAMPLER_OUT = 2 }; typedef struct { - SF_INFO info; // Info about sample from sndfile - float* data; // Sample data in float - char* path; // Path of file - uint32_t path_len; // Length of path + SF_INFO info; // Info about sample from sndfile + float* data; // Sample data in float + char* path; // Path of file + uint32_t path_len; // Length of path } Sample; typedef struct { - // Features - LV2_URID_Map* map; - LV2_Worker_Schedule* schedule; - LV2_Log_Logger logger; - - // Ports - const LV2_Atom_Sequence* control_port; - LV2_Atom_Sequence* notify_port; - float* output_port; - - // Communication utilities - LV2_Atom_Forge_Frame notify_frame; ///< Cached for worker replies - LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread - PeaksSender psend; ///< Audio peaks sender - - // URIs - SamplerURIs uris; - - // Playback state - Sample* sample; - uint32_t frame_offset; - float gain; - float gain_dB; - sf_count_t frame; - bool play; - bool activated; - bool gain_changed; - bool sample_changed; + // Features + LV2_URID_Map* map; + LV2_Worker_Schedule* schedule; + LV2_Log_Logger logger; + + // Ports + const LV2_Atom_Sequence* control_port; + LV2_Atom_Sequence* notify_port; + float* output_port; + + // Communication utilities + LV2_Atom_Forge_Frame notify_frame; ///< Cached for worker replies + LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread + PeaksSender psend; ///< Audio peaks sender + + // URIs + SamplerURIs uris; + + // Playback state + Sample* sample; + uint32_t frame_offset; + float gain; + float gain_dB; + sf_count_t frame; + bool play; + bool activated; + bool gain_changed; + bool sample_changed; } Sampler; /** @@ -95,8 +91,8 @@ typedef struct { ringbuffer. */ typedef struct { - LV2_Atom atom; - Sample* sample; + LV2_Atom atom; + Sample* sample; } SampleMessage; /** @@ -109,53 +105,53 @@ typedef struct { static Sample* load_sample(LV2_Log_Logger* logger, const char* path) { - lv2_log_trace(logger, "Loading %s\n", path); - - const size_t path_len = strlen(path); - Sample* const sample = (Sample*)calloc(1, sizeof(Sample)); - SF_INFO* const info = &sample->info; - SNDFILE* const sndfile = sf_open(path, SFM_READ, info); - float* data = NULL; - bool error = true; - if (!sndfile || !info->frames) { - lv2_log_error(logger, "Failed to open %s\n", path); - } else if (info->channels != 1) { - lv2_log_error(logger, "%s has %d channels\n", path, info->channels); - } else if (!(data = (float*)malloc(sizeof(float) * info->frames))) { - lv2_log_error(logger, "Failed to allocate memory for sample\n"); - } else { - error = false; - } - - if (error) { - free(sample); - free(data); - sf_close(sndfile); - return NULL; - } - - sf_seek(sndfile, 0ul, SEEK_SET); - sf_read_float(sndfile, data, info->frames); - sf_close(sndfile); - - // Fill sample struct and return it - sample->data = data; - sample->path = (char*)malloc(path_len + 1); - sample->path_len = (uint32_t)path_len; - memcpy(sample->path, path, path_len + 1); - - return sample; + lv2_log_trace(logger, "Loading %s\n", path); + + const size_t path_len = strlen(path); + Sample* const sample = (Sample*)calloc(1, sizeof(Sample)); + SF_INFO* const info = &sample->info; + SNDFILE* const sndfile = sf_open(path, SFM_READ, info); + float* data = NULL; + bool error = true; + if (!sndfile || !info->frames) { + lv2_log_error(logger, "Failed to open %s\n", path); + } else if (info->channels != 1) { + lv2_log_error(logger, "%s has %d channels\n", path, info->channels); + } else if (!(data = (float*)malloc(sizeof(float) * info->frames))) { + lv2_log_error(logger, "Failed to allocate memory for sample\n"); + } else { + error = false; + } + + if (error) { + free(sample); + free(data); + sf_close(sndfile); + return NULL; + } + + sf_seek(sndfile, 0ul, SEEK_SET); + sf_read_float(sndfile, data, info->frames); + sf_close(sndfile); + + // Fill sample struct and return it + sample->data = data; + sample->path = (char*)malloc(path_len + 1); + sample->path_len = (uint32_t)path_len; + memcpy(sample->path, path, path_len + 1); + + return sample; } static void free_sample(Sampler* self, Sample* sample) { - if (sample) { - lv2_log_trace(&self->logger, "Freeing %s\n", sample->path); - free(sample->path); - free(sample->data); - free(sample); - } + if (sample) { + lv2_log_trace(&self->logger, "Freeing %s\n", sample->path); + free(sample->path); + free(sample->data); + free(sample); + } } /** @@ -172,30 +168,30 @@ work(LV2_Handle instance, uint32_t size, const void* data) { - Sampler* self = (Sampler*)instance; - const LV2_Atom* atom = (const LV2_Atom*)data; - if (atom->type == self->uris.eg_freeSample) { - // Free old sample - const SampleMessage* msg = (const SampleMessage*)data; - free_sample(self, msg->sample); - } else if (atom->type == self->forge.Object) { - // Handle set message (load sample). - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data; - const char* path = read_set_file(&self->uris, obj); - if (!path) { - lv2_log_error(&self->logger, "Malformed set file request\n"); - return LV2_WORKER_ERR_UNKNOWN; - } - - // Load sample. - Sample* sample = load_sample(&self->logger, path); - if (sample) { - // Send new sample to run() to be applied - respond(handle, sizeof(Sample*), &sample); - } - } - - return LV2_WORKER_SUCCESS; + Sampler* self = (Sampler*)instance; + const LV2_Atom* atom = (const LV2_Atom*)data; + if (atom->type == self->uris.eg_freeSample) { + // Free old sample + const SampleMessage* msg = (const SampleMessage*)data; + free_sample(self, msg->sample); + } else if (atom->type == self->forge.Object) { + // Handle set message (load sample). + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data; + const char* path = read_set_file(&self->uris, obj); + if (!path) { + lv2_log_error(&self->logger, "Malformed set file request\n"); + return LV2_WORKER_ERR_UNKNOWN; + } + + // Load sample. + Sample* sample = load_sample(&self->logger, path); + if (sample) { + // Send new sample to run() to be applied + respond(handle, sizeof(Sample*), &sample); + } + } + + return LV2_WORKER_SUCCESS; } /** @@ -206,50 +202,44 @@ work(LV2_Handle instance, scheduled. */ static LV2_Worker_Status -work_response(LV2_Handle instance, - uint32_t size, - const void* data) +work_response(LV2_Handle instance, uint32_t size, const void* data) { - Sampler* self = (Sampler*)instance; - Sample* old_sample = self->sample; - Sample* new_sample = *(Sample*const*)data; + Sampler* self = (Sampler*)instance; + Sample* old_sample = self->sample; + Sample* new_sample = *(Sample* const*)data; - // Install the new sample - self->sample = *(Sample*const*)data; + // Install the new sample + self->sample = *(Sample* const*)data; - // Schedule work to free the old sample - SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample }, - old_sample }; - self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); + // Schedule work to free the old sample + SampleMessage msg = {{sizeof(Sample*), self->uris.eg_freeSample}, old_sample}; + self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); - // Send a notification that we're using a new sample - lv2_atom_forge_frame_time(&self->forge, self->frame_offset); - write_set_file(&self->forge, &self->uris, - new_sample->path, - new_sample->path_len); + // Send a notification that we're using a new sample + lv2_atom_forge_frame_time(&self->forge, self->frame_offset); + write_set_file( + &self->forge, &self->uris, new_sample->path, new_sample->path_len); - return LV2_WORKER_SUCCESS; + return LV2_WORKER_SUCCESS; } static void -connect_port(LV2_Handle instance, - uint32_t port, - void* data) +connect_port(LV2_Handle instance, uint32_t port, void* data) { - Sampler* self = (Sampler*)instance; - switch (port) { - case SAMPLER_CONTROL: - self->control_port = (const LV2_Atom_Sequence*)data; - break; - case SAMPLER_NOTIFY: - self->notify_port = (LV2_Atom_Sequence*)data; - break; - case SAMPLER_OUT: - self->output_port = (float*)data; - break; - default: - break; - } + Sampler* self = (Sampler*)instance; + switch (port) { + case SAMPLER_CONTROL: + self->control_port = (const LV2_Atom_Sequence*)data; + break; + case SAMPLER_NOTIFY: + self->notify_port = (LV2_Atom_Sequence*)data; + break; + case SAMPLER_OUT: + self->output_port = (float*)data; + break; + default: + break; + } } static LV2_Handle @@ -258,62 +248,62 @@ instantiate(const LV2_Descriptor* descriptor, const char* path, const LV2_Feature* const* features) { - // Allocate and initialise instance structure. - Sampler* self = (Sampler*)calloc(1, sizeof(Sampler)); - if (!self) { - return NULL; - } - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - LV2_WORKER__schedule, &self->schedule, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Map URIs and initialise forge - map_sampler_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - peaks_sender_init(&self->psend, self->map); - - self->gain = 1.0f; - self->gain_dB = 0.0f; - - return (LV2_Handle)self; + // Allocate and initialise instance structure. + Sampler* self = (Sampler*)calloc(1, sizeof(Sampler)); + if (!self) { + return NULL; + } + + // Get host features + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &self->logger.log, false, + LV2_URID__map, &self->map, true, + LV2_WORKER__schedule, &self->schedule, true, + NULL); + // clang-format on + + lv2_log_logger_set_map(&self->logger, self->map); + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + free(self); + return NULL; + } + + // Map URIs and initialise forge + map_sampler_uris(self->map, &self->uris); + lv2_atom_forge_init(&self->forge, self->map); + peaks_sender_init(&self->psend, self->map); + + self->gain = 1.0f; + self->gain_dB = 0.0f; + + return (LV2_Handle)self; } static void cleanup(LV2_Handle instance) { - Sampler* self = (Sampler*)instance; - free_sample(self, self->sample); - free(self); + Sampler* self = (Sampler*)instance; + free_sample(self, self->sample); + free(self); } static void activate(LV2_Handle instance) { - ((Sampler*)instance)->activated = true; + ((Sampler*)instance)->activated = true; } static void deactivate(LV2_Handle instance) { - ((Sampler*)instance)->activated = false; + ((Sampler*)instance)->activated = false; } /** Define a macro for converting a gain in dB to a coefficient. */ -#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) +#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g)*0.05f) : 0.0f) /** Handle an incoming event in the audio thread. @@ -324,88 +314,86 @@ deactivate(LV2_Handle instance) static void handle_event(Sampler* self, LV2_Atom_Event* ev) { - SamplerURIs* uris = &self->uris; - PeaksURIs* peaks_uris = &self->psend.uris; - - if (ev->body.type == uris->midi_Event) { - const uint8_t* const msg = (const uint8_t*)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - self->frame = 0; - self->play = true; - break; - default: - break; - } - } else if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris->patch_Set) { - // Get the property and value of the set message - const LV2_Atom* property = NULL; - const LV2_Atom* value = NULL; - - // clang-format off - lv2_atom_object_get(obj, - uris->patch_property, &property, - uris->patch_value, &value, - 0); - // clang-format on - - if (!property) { - lv2_log_error(&self->logger, "Set message with no property\n"); - return; - } else if (property->type != uris->atom_URID) { - lv2_log_error(&self->logger, "Set property is not a URID\n"); - return; - } - - const uint32_t key = ((const LV2_Atom_URID*)property)->body; - if (key == uris->eg_sample) { - // Sample change, send it to the worker. - lv2_log_trace(&self->logger, "Scheduling sample change\n"); - self->schedule->schedule_work(self->schedule->handle, - lv2_atom_total_size(&ev->body), - &ev->body); - } else if (key == uris->param_gain) { - // Gain change - if (value->type == uris->atom_Float) { - self->gain_dB = ((LV2_Atom_Float*)value)->body; - self->gain = DB_CO(self->gain_dB); - } - } - } else if (obj->body.otype == uris->patch_Get && self->sample) { - const LV2_Atom_URID* accept = NULL; - const LV2_Atom_Int* n_peaks = NULL; - - // clang-format off - lv2_atom_object_get_typed( - obj, - uris->patch_accept, &accept, uris->atom_URID, - peaks_uris->peaks_total, &n_peaks, peaks_uris->atom_Int, 0); - // clang-format on - - if (accept && accept->body == peaks_uris->peaks_PeakUpdate) { - // Received a request for peaks, prepare for transmission - peaks_sender_start(&self->psend, - self->sample->data, - self->sample->info.frames, - n_peaks->body); - } else { - // Received a get message, emit our state (probably to UI) - lv2_atom_forge_frame_time(&self->forge, self->frame_offset); - write_set_file(&self->forge, &self->uris, - self->sample->path, - self->sample->path_len); - } - } else { - lv2_log_trace(&self->logger, - "Unknown object type %u\n", obj->body.otype); - } - } else { - lv2_log_trace(&self->logger, - "Unknown event type %u\n", ev->body.type); - } - + SamplerURIs* uris = &self->uris; + PeaksURIs* peaks_uris = &self->psend.uris; + + if (ev->body.type == uris->midi_Event) { + const uint8_t* const msg = (const uint8_t*)(ev + 1); + switch (lv2_midi_message_type(msg)) { + case LV2_MIDI_MSG_NOTE_ON: + self->frame = 0; + self->play = true; + break; + default: + break; + } + } else if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; + if (obj->body.otype == uris->patch_Set) { + // Get the property and value of the set message + const LV2_Atom* property = NULL; + const LV2_Atom* value = NULL; + + // clang-format off + lv2_atom_object_get(obj, + uris->patch_property, &property, + uris->patch_value, &value, + 0); + // clang-format on + + if (!property) { + lv2_log_error(&self->logger, "Set message with no property\n"); + return; + } else if (property->type != uris->atom_URID) { + lv2_log_error(&self->logger, "Set property is not a URID\n"); + return; + } + + const uint32_t key = ((const LV2_Atom_URID*)property)->body; + if (key == uris->eg_sample) { + // Sample change, send it to the worker. + lv2_log_trace(&self->logger, "Scheduling sample change\n"); + self->schedule->schedule_work( + self->schedule->handle, lv2_atom_total_size(&ev->body), &ev->body); + } else if (key == uris->param_gain) { + // Gain change + if (value->type == uris->atom_Float) { + self->gain_dB = ((LV2_Atom_Float*)value)->body; + self->gain = DB_CO(self->gain_dB); + } + } + } else if (obj->body.otype == uris->patch_Get && self->sample) { + const LV2_Atom_URID* accept = NULL; + const LV2_Atom_Int* n_peaks = NULL; + + // clang-format off + lv2_atom_object_get_typed( + obj, + uris->patch_accept, &accept, uris->atom_URID, + peaks_uris->peaks_total, &n_peaks, peaks_uris->atom_Int, + 0); + // clang-format on + + if (accept && accept->body == peaks_uris->peaks_PeakUpdate) { + // Received a request for peaks, prepare for transmission + peaks_sender_start(&self->psend, + self->sample->data, + self->sample->info.frames, + n_peaks->body); + } else { + // Received a get message, emit our state (probably to UI) + lv2_atom_forge_frame_time(&self->forge, self->frame_offset); + write_set_file(&self->forge, + &self->uris, + self->sample->path, + self->sample->path_len); + } + } else { + lv2_log_trace(&self->logger, "Unknown object type %u\n", obj->body.otype); + } + } else { + lv2_log_trace(&self->logger, "Unknown event type %u\n", ev->body.type); + } } /** @@ -414,78 +402,77 @@ handle_event(Sampler* self, LV2_Atom_Event* ev) static void render(Sampler* self, uint32_t start, uint32_t end) { - float* output = self->output_port; - - if (self->play && self->sample) { - // Start/continue writing sample to output - for (; start < end; ++start) { - output[start] = self->sample->data[self->frame] * self->gain; - if (++self->frame == self->sample->info.frames) { - self->play = false; // Reached end of sample - break; - } - } - } - - // Write silence to remaining buffer - for (; start < end; ++start) { - output[start] = 0.0f; - } + float* output = self->output_port; + + if (self->play && self->sample) { + // Start/continue writing sample to output + for (; start < end; ++start) { + output[start] = self->sample->data[self->frame] * self->gain; + if (++self->frame == self->sample->info.frames) { + self->play = false; // Reached end of sample + break; + } + } + } + + // Write silence to remaining buffer + for (; start < end; ++start) { + output[start] = 0.0f; + } } static void run(LV2_Handle instance, uint32_t sample_count) { - Sampler* self = (Sampler*)instance; - - // Set up forge to write directly to notify output port. - const uint32_t notify_capacity = self->notify_port->atom.size; - lv2_atom_forge_set_buffer(&self->forge, - (uint8_t*)self->notify_port, - notify_capacity); - - // Start a sequence in the notify output port. - lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0); - - // Send update to UI if gain or sample has changed due to state restore - if (self->gain_changed || self->sample_changed) { - lv2_atom_forge_frame_time(&self->forge, 0); - - if (self->gain_changed) { - write_set_gain(&self->forge, &self->uris, self->gain_dB); - self->gain_changed = false; - } - - if (self->sample_changed) { - write_set_file(&self->forge, &self->uris, - self->sample->path, - self->sample->path_len); - self->sample_changed = false; - } - } - - // Iterate over incoming events, emitting audio along the way - self->frame_offset = 0; - LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev) { - // Render output up to the time of this event - render(self, self->frame_offset, ev->time.frames); - - /* Update current frame offset to this event's time. This is stored in - the instance because it is used for sychronous worker event - execution. This allows a sample load event to be executed with - sample accuracy when running in a non-realtime context (such as - exporting a session). */ - self->frame_offset = ev->time.frames; - - // Process this event - handle_event(self, ev); - } - - // Use available space after any emitted events to send peaks - peaks_sender_send(&self->psend, &self->forge, sample_count, self->frame_offset); - - // Render output for the rest of the cycle past the last event - render(self, self->frame_offset, sample_count); + Sampler* self = (Sampler*)instance; + + // Set up forge to write directly to notify output port. + const uint32_t notify_capacity = self->notify_port->atom.size; + lv2_atom_forge_set_buffer( + &self->forge, (uint8_t*)self->notify_port, notify_capacity); + + // Start a sequence in the notify output port. + lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0); + + // Send update to UI if gain or sample has changed due to state restore + if (self->gain_changed || self->sample_changed) { + lv2_atom_forge_frame_time(&self->forge, 0); + + if (self->gain_changed) { + write_set_gain(&self->forge, &self->uris, self->gain_dB); + self->gain_changed = false; + } + + if (self->sample_changed) { + write_set_file( + &self->forge, &self->uris, self->sample->path, self->sample->path_len); + self->sample_changed = false; + } + } + + // Iterate over incoming events, emitting audio along the way + self->frame_offset = 0; + LV2_ATOM_SEQUENCE_FOREACH (self->control_port, ev) { + // Render output up to the time of this event + render(self, self->frame_offset, ev->time.frames); + + /* Update current frame offset to this event's time. This is stored in + the instance because it is used for sychronous worker event + execution. This allows a sample load event to be executed with + sample accuracy when running in a non-realtime context (such as + exporting a session). */ + self->frame_offset = ev->time.frames; + + // Process this event + handle_event(self, ev); + } + + // Use available space after any emitted events to send peaks + peaks_sender_send( + &self->psend, &self->forge, sample_count, self->frame_offset); + + // Render output for the rest of the cycle past the last event + render(self, self->frame_offset, sample_count); } static LV2_State_Status @@ -495,39 +482,39 @@ save(LV2_Handle instance, uint32_t flags, const LV2_Feature* const* features) { - Sampler* self = (Sampler*)instance; - if (!self->sample) { - return LV2_STATE_SUCCESS; - } - - LV2_State_Map_Path* map_path = (LV2_State_Map_Path*)lv2_features_data( - features, LV2_STATE__mapPath); - if (!map_path) { - return LV2_STATE_ERR_NO_FEATURE; - } - - // Map absolute sample path to an abstract state path - char* apath = map_path->abstract_path(map_path->handle, self->sample->path); - - // Store eg:sample = abstract path - store(handle, - self->uris.eg_sample, - apath, - strlen(apath) + 1, - self->uris.atom_Path, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - free(apath); - - // Store the gain value - store(handle, - self->uris.param_gain, - &self->gain_dB, - sizeof(self->gain_dB), - self->uris.atom_Float, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - return LV2_STATE_SUCCESS; + Sampler* self = (Sampler*)instance; + if (!self->sample) { + return LV2_STATE_SUCCESS; + } + + LV2_State_Map_Path* map_path = + (LV2_State_Map_Path*)lv2_features_data(features, LV2_STATE__mapPath); + if (!map_path) { + return LV2_STATE_ERR_NO_FEATURE; + } + + // Map absolute sample path to an abstract state path + char* apath = map_path->abstract_path(map_path->handle, self->sample->path); + + // Store eg:sample = abstract path + store(handle, + self->uris.eg_sample, + apath, + strlen(apath) + 1, + self->uris.atom_Path, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + free(apath); + + // Store the gain value + store(handle, + self->uris.param_gain, + &self->gain_dB, + sizeof(self->gain_dB), + self->uris.atom_Float, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + return LV2_STATE_SUCCESS; } static LV2_State_Status @@ -537,113 +524,112 @@ restore(LV2_Handle instance, uint32_t flags, const LV2_Feature* const* features) { - Sampler* self = (Sampler*)instance; - - // Get host features - LV2_Worker_Schedule* schedule = NULL; - LV2_State_Map_Path* paths = NULL; - - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_STATE__mapPath, &paths, true, - LV2_WORKER__schedule, &schedule, false, - NULL); - // clang-format on - - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - return LV2_STATE_ERR_NO_FEATURE; - } - - // Get eg:sample from state - size_t size = 0; - uint32_t type = 0; - uint32_t valflags = 0; - const void* value = retrieve(handle, self->uris.eg_sample, - &size, &type, &valflags); - if (!value) { - lv2_log_error(&self->logger, "Missing eg:sample\n"); - return LV2_STATE_ERR_NO_PROPERTY; - } else if (type != self->uris.atom_Path) { - lv2_log_error(&self->logger, "Non-path eg:sample\n"); - return LV2_STATE_ERR_BAD_TYPE; - } - - // Map abstract state path to absolute path - const char* apath = (const char*)value; - char* path = paths->absolute_path(paths->handle, apath); - - // Replace current sample with the new one - if (!self->activated || !schedule) { - // No scheduling available, load sample immediately - lv2_log_trace(&self->logger, "Synchronous restore\n"); - Sample* sample = load_sample(&self->logger, path); - if (sample) { - free_sample(self, self->sample); - self->sample = sample; - self->sample_changed = true; - } - } else { - // Schedule sample to be loaded by the provided worker - lv2_log_trace(&self->logger, "Scheduling restore\n"); - LV2_Atom_Forge forge; - LV2_Atom* buf = (LV2_Atom*)calloc(1, strlen(path) + 128); - lv2_atom_forge_init(&forge, self->map); - lv2_atom_forge_set_sink(&forge, atom_sink, atom_sink_deref, buf); - write_set_file(&forge, &self->uris, path, strlen(path)); - - const uint32_t msg_size = lv2_atom_pad_size(buf->size); - schedule->schedule_work(self->schedule->handle, msg_size, buf + 1); - free(buf); - } - - free(path); - - // Get param:gain from state - value = retrieve(handle, self->uris.param_gain, &size, &type, &valflags); - if (!value) { - // Not an error, since older versions did not save this property - lv2_log_note(&self->logger, "Missing param:gain\n"); - return LV2_STATE_SUCCESS; - } else if (type != self->uris.atom_Float) { - lv2_log_error(&self->logger, "Non-float param:gain\n"); - return LV2_STATE_ERR_BAD_TYPE; - } - - self->gain_dB = *(const float*)value; - self->gain = DB_CO(self->gain_dB); - self->gain_changed = true; - - return LV2_STATE_SUCCESS; + Sampler* self = (Sampler*)instance; + + // Get host features + LV2_Worker_Schedule* schedule = NULL; + LV2_State_Map_Path* paths = NULL; + + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_STATE__mapPath, &paths, true, + LV2_WORKER__schedule, &schedule, false, + NULL); + // clang-format on + + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + return LV2_STATE_ERR_NO_FEATURE; + } + + // Get eg:sample from state + size_t size = 0; + uint32_t type = 0; + uint32_t valflags = 0; + const void* value = + retrieve(handle, self->uris.eg_sample, &size, &type, &valflags); + if (!value) { + lv2_log_error(&self->logger, "Missing eg:sample\n"); + return LV2_STATE_ERR_NO_PROPERTY; + } else if (type != self->uris.atom_Path) { + lv2_log_error(&self->logger, "Non-path eg:sample\n"); + return LV2_STATE_ERR_BAD_TYPE; + } + + // Map abstract state path to absolute path + const char* apath = (const char*)value; + char* path = paths->absolute_path(paths->handle, apath); + + // Replace current sample with the new one + if (!self->activated || !schedule) { + // No scheduling available, load sample immediately + lv2_log_trace(&self->logger, "Synchronous restore\n"); + Sample* sample = load_sample(&self->logger, path); + if (sample) { + free_sample(self, self->sample); + self->sample = sample; + self->sample_changed = true; + } + } else { + // Schedule sample to be loaded by the provided worker + lv2_log_trace(&self->logger, "Scheduling restore\n"); + LV2_Atom_Forge forge; + LV2_Atom* buf = (LV2_Atom*)calloc(1, strlen(path) + 128); + lv2_atom_forge_init(&forge, self->map); + lv2_atom_forge_set_sink(&forge, atom_sink, atom_sink_deref, buf); + write_set_file(&forge, &self->uris, path, strlen(path)); + + const uint32_t msg_size = lv2_atom_pad_size(buf->size); + schedule->schedule_work(self->schedule->handle, msg_size, buf + 1); + free(buf); + } + + free(path); + + // Get param:gain from state + value = retrieve(handle, self->uris.param_gain, &size, &type, &valflags); + if (!value) { + // Not an error, since older versions did not save this property + lv2_log_note(&self->logger, "Missing param:gain\n"); + return LV2_STATE_SUCCESS; + } else if (type != self->uris.atom_Float) { + lv2_log_error(&self->logger, "Non-float param:gain\n"); + return LV2_STATE_ERR_BAD_TYPE; + } + + self->gain_dB = *(const float*)value; + self->gain = DB_CO(self->gain_dB); + self->gain_changed = true; + + return LV2_STATE_SUCCESS; } static const void* extension_data(const char* uri) { - static const LV2_State_Interface state = { save, restore }; - static const LV2_Worker_Interface worker = { work, work_response, NULL }; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } else if (!strcmp(uri, LV2_WORKER__interface)) { - return &worker; - } - return NULL; + static const LV2_State_Interface state = {save, restore}; + static const LV2_Worker_Interface worker = {work, work_response, NULL}; + if (!strcmp(uri, LV2_STATE__interface)) { + return &state; + } else if (!strcmp(uri, LV2_WORKER__interface)) { + return &worker; + } + return NULL; } -static const LV2_Descriptor descriptor = { - EG_SAMPLER_URI, - instantiate, - connect_port, - activate, - run, - deactivate, - cleanup, - extension_data -}; +static const LV2_Descriptor descriptor = {EG_SAMPLER_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data}; LV2_SYMBOL_EXPORT -const LV2_Descriptor* lv2_descriptor(uint32_t index) +const LV2_Descriptor* +lv2_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c index f733216..533e720 100644 --- a/plugins/eg-sampler.lv2/sampler_ui.c +++ b/plugins/eg-sampler.lv2/sampler_ui.c @@ -48,195 +48,198 @@ #define MIN_CANVAS_H 80 typedef struct { - LV2_Atom_Forge forge; - LV2_URID_Map* map; - LV2UI_Request_Value* request_value; - LV2_Log_Logger logger; - SamplerURIs uris; - PeaksReceiver precv; - - LV2UI_Write_Function write; - LV2UI_Controller controller; - - GtkWidget* box; - GtkWidget* play_button; - GtkWidget* file_button; - GtkWidget* request_file_button; - GtkWidget* button_box; - GtkWidget* canvas; - - uint32_t width; - uint32_t requested_n_peaks; - char* filename; - - uint8_t forge_buf[1024]; - - // Optional show/hide interface - GtkWidget* window; - bool did_init; + LV2_Atom_Forge forge; + LV2_URID_Map* map; + LV2UI_Request_Value* request_value; + LV2_Log_Logger logger; + SamplerURIs uris; + PeaksReceiver precv; + + LV2UI_Write_Function write; + LV2UI_Controller controller; + + GtkWidget* box; + GtkWidget* play_button; + GtkWidget* file_button; + GtkWidget* request_file_button; + GtkWidget* button_box; + GtkWidget* canvas; + + uint32_t width; + uint32_t requested_n_peaks; + char* filename; + + uint8_t forge_buf[1024]; + + // Optional show/hide interface + GtkWidget* window; + bool did_init; } SamplerUI; static void on_file_set(GtkFileChooserButton* widget, void* handle) { - SamplerUI* ui = (SamplerUI*)handle; + SamplerUI* ui = (SamplerUI*)handle; - // Get the filename from the file chooser - char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); + // Get the filename from the file chooser + char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); - // Write a set message to the plugin to load new file - lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - LV2_Atom* msg = (LV2_Atom*)write_set_file(&ui->forge, &ui->uris, - filename, strlen(filename)); + // Write a set message to the plugin to load new file + lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); + LV2_Atom* msg = (LV2_Atom*)write_set_file( + &ui->forge, &ui->uris, filename, strlen(filename)); - assert(msg); + assert(msg); - ui->write(ui->controller, 0, lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); + ui->write(ui->controller, + 0, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); - g_free(filename); + g_free(filename); } static void on_request_file(GtkButton* widget, void* handle) { - SamplerUI* ui = (SamplerUI*)handle; + SamplerUI* ui = (SamplerUI*)handle; - ui->request_value->request(ui->request_value->handle, - ui->uris.eg_sample, - 0, - NULL); + ui->request_value->request( + ui->request_value->handle, ui->uris.eg_sample, 0, NULL); } static void on_play_clicked(GtkFileChooserButton* widget, void* handle) { - SamplerUI* ui = (SamplerUI*)handle; - struct { - LV2_Atom atom; - uint8_t msg[3]; - } note_on; - - note_on.atom.type = ui->uris.midi_Event; - note_on.atom.size = 3; - note_on.msg[0] = LV2_MIDI_MSG_NOTE_ON; - note_on.msg[1] = 60; - note_on.msg[2] = 60; - ui->write(ui->controller, 0, sizeof(LV2_Atom) + 3, - ui->uris.atom_eventTransfer, - ¬e_on); + SamplerUI* ui = (SamplerUI*)handle; + struct { + LV2_Atom atom; + uint8_t msg[3]; + } note_on; + + note_on.atom.type = ui->uris.midi_Event; + note_on.atom.size = 3; + note_on.msg[0] = LV2_MIDI_MSG_NOTE_ON; + note_on.msg[1] = 60; + note_on.msg[2] = 60; + ui->write(ui->controller, + 0, + sizeof(LV2_Atom) + 3, + ui->uris.atom_eventTransfer, + ¬e_on); } static void request_peaks(SamplerUI* ui, uint32_t n_peaks) { - if (n_peaks == ui->requested_n_peaks) { - return; - } - - lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.patch_Get); - lv2_atom_forge_key(&ui->forge, ui->uris.patch_accept); - lv2_atom_forge_urid(&ui->forge, ui->precv.uris.peaks_PeakUpdate); - lv2_atom_forge_key(&ui->forge, ui->precv.uris.peaks_total); - lv2_atom_forge_int(&ui->forge, n_peaks); - lv2_atom_forge_pop(&ui->forge, &frame); - - LV2_Atom* msg = lv2_atom_forge_deref(&ui->forge, frame.ref); - ui->write(ui->controller, 0, lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); - - ui->requested_n_peaks = n_peaks; + if (n_peaks == ui->requested_n_peaks) { + return; + } + + lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); + + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.patch_Get); + lv2_atom_forge_key(&ui->forge, ui->uris.patch_accept); + lv2_atom_forge_urid(&ui->forge, ui->precv.uris.peaks_PeakUpdate); + lv2_atom_forge_key(&ui->forge, ui->precv.uris.peaks_total); + lv2_atom_forge_int(&ui->forge, n_peaks); + lv2_atom_forge_pop(&ui->forge, &frame); + + LV2_Atom* msg = lv2_atom_forge_deref(&ui->forge, frame.ref); + ui->write(ui->controller, + 0, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); + + ui->requested_n_peaks = n_peaks; } /** Set Cairo color to a GDK color (to follow Gtk theme). */ static void cairo_set_source_gdk(cairo_t* cr, const GdkColor* color) { - cairo_set_source_rgb( - cr, color->red / 65535.0, color->green / 65535.0, color->blue / 65535.0); - + cairo_set_source_rgb( + cr, color->red / 65535.0, color->green / 65535.0, color->blue / 65535.0); } static gboolean on_canvas_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data) { - SamplerUI* ui = (SamplerUI*)data; + SamplerUI* ui = (SamplerUI*)data; - GtkAllocation size; - gtk_widget_get_allocation(widget, &size); + GtkAllocation size; + gtk_widget_get_allocation(widget, &size); - ui->width = size.width; - if (ui->width > 2 * ui->requested_n_peaks) { - request_peaks(ui, 2 * ui->requested_n_peaks); - } + ui->width = size.width; + if (ui->width > 2 * ui->requested_n_peaks) { + request_peaks(ui, 2 * ui->requested_n_peaks); + } - cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); + cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); - cairo_set_line_width(cr, 1.0); - cairo_translate(cr, 0.5, 0.5); + cairo_set_line_width(cr, 1.0); + cairo_translate(cr, 0.5, 0.5); - const double mid_y = size.height / 2.0; + const double mid_y = size.height / 2.0; - const float* const peaks = ui->precv.peaks; - const int32_t n_peaks = ui->precv.n_peaks; - if (peaks) { - // Draw waveform - const double scale = size.width / ((double)n_peaks - 1.0f); + const float* const peaks = ui->precv.peaks; + const int32_t n_peaks = ui->precv.n_peaks; + if (peaks) { + // Draw waveform + const double scale = size.width / ((double)n_peaks - 1.0f); - // Start at left origin - cairo_move_to(cr, 0, mid_y); + // Start at left origin + cairo_move_to(cr, 0, mid_y); - // Draw line through top peaks - for (int i = 0; i < n_peaks; ++i) { - const float peak = peaks[i]; - cairo_line_to(cr, i * scale, mid_y + (peak / 2.0f) * size.height); - } + // Draw line through top peaks + for (int i = 0; i < n_peaks; ++i) { + const float peak = peaks[i]; + cairo_line_to(cr, i * scale, mid_y + (peak / 2.0f) * size.height); + } - // Continue through bottom peaks - for (int i = n_peaks - 1; i >= 0; --i) { - const float peak = peaks[i]; - cairo_line_to(cr, i * scale, mid_y - (peak / 2.0f) * size.height); - } + // Continue through bottom peaks + for (int i = n_peaks - 1; i >= 0; --i) { + const float peak = peaks[i]; + cairo_line_to(cr, i * scale, mid_y - (peak / 2.0f) * size.height); + } - // Close shape - cairo_line_to(cr, 0, mid_y); + // Close shape + cairo_line_to(cr, 0, mid_y); - cairo_set_source_gdk(cr, widget->style->mid); - cairo_fill_preserve(cr); + cairo_set_source_gdk(cr, widget->style->mid); + cairo_fill_preserve(cr); - cairo_set_source_gdk(cr, widget->style->fg); - cairo_stroke(cr); - } + cairo_set_source_gdk(cr, widget->style->fg); + cairo_stroke(cr); + } - cairo_destroy(cr); - return TRUE; + cairo_destroy(cr); + return TRUE; } static void destroy_window(SamplerUI* ui) { - if (ui->window) { - gtk_container_remove(GTK_CONTAINER(ui->window), ui->box); - gtk_widget_destroy(ui->window); - ui->window = NULL; - } + if (ui->window) { + gtk_container_remove(GTK_CONTAINER(ui->window), ui->box); + gtk_widget_destroy(ui->window); + ui->window = NULL; + } } static gboolean on_window_closed(GtkWidget* widget, GdkEvent* event, gpointer data) { - SamplerUI* ui = (SamplerUI*)data; + SamplerUI* ui = (SamplerUI*)data; - // Remove widget so Gtk doesn't delete it when the window is closed - gtk_container_remove(GTK_CONTAINER(ui->window), ui->box); - ui->window = NULL; + // Remove widget so Gtk doesn't delete it when the window is closed + gtk_container_remove(GTK_CONTAINER(ui->window), ui->box); + ui->window = NULL; - return FALSE; + return FALSE; } static LV2UI_Handle @@ -248,103 +251,104 @@ instantiate(const LV2UI_Descriptor* descriptor, LV2UI_Widget* widget, const LV2_Feature* const* features) { - SamplerUI* ui = (SamplerUI*)calloc(1, sizeof(SamplerUI)); - if (!ui) { - return NULL; - } - - ui->write = write_function; - ui->controller = controller; - ui->width = MIN_CANVAS_W; - *widget = NULL; - ui->window = NULL; - ui->did_init = false; - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &ui->logger.log , false, - LV2_URID__map, &ui->map, true, - LV2_UI__requestValue, &ui->request_value, false, - NULL); - // clang-format on - - lv2_log_logger_set_map(&ui->logger, ui->map); - if (missing) { - lv2_log_error(&ui->logger, "Missing feature <%s>\n", missing); - free(ui); - return NULL; - } - - // Map URIs and initialise forge - map_sampler_uris(ui->map, &ui->uris); - lv2_atom_forge_init(&ui->forge, ui->map); - peaks_receiver_init(&ui->precv, ui->map); - - // Construct Gtk UI - ui->box = gtk_vbox_new(FALSE, 4); - ui->play_button = gtk_button_new_with_label("â–¶"); - ui->canvas = gtk_drawing_area_new(); - ui->button_box = gtk_hbox_new(FALSE, 4); - ui->file_button = gtk_file_chooser_button_new( - "Load Sample", GTK_FILE_CHOOSER_ACTION_OPEN); - ui->request_file_button = gtk_button_new_with_label("Request Sample"); - gtk_widget_set_size_request(ui->canvas, MIN_CANVAS_W, MIN_CANVAS_H); - gtk_container_set_border_width(GTK_CONTAINER(ui->box), 4); - gtk_box_pack_start(GTK_BOX(ui->box), ui->canvas, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(ui->box), ui->button_box, FALSE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(ui->button_box), ui->play_button, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(ui->button_box), ui->request_file_button, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(ui->button_box), ui->file_button, TRUE, TRUE, 0); - - g_signal_connect(ui->file_button, "file-set", - G_CALLBACK(on_file_set), ui); - - g_signal_connect(ui->request_file_button, "clicked", - G_CALLBACK(on_request_file), ui); - - g_signal_connect(ui->play_button, "clicked", - G_CALLBACK(on_play_clicked), ui); - - g_signal_connect(G_OBJECT(ui->canvas), "expose_event", - G_CALLBACK(on_canvas_expose), ui); - - // Request state (filename) from plugin - lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.patch_Get); - - assert(msg); - - lv2_atom_forge_pop(&ui->forge, &frame); - - ui->write(ui->controller, 0, lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); - - *widget = ui->box; - - return ui; + SamplerUI* ui = (SamplerUI*)calloc(1, sizeof(SamplerUI)); + if (!ui) { + return NULL; + } + + ui->write = write_function; + ui->controller = controller; + ui->width = MIN_CANVAS_W; + *widget = NULL; + ui->window = NULL; + ui->did_init = false; + + // Get host features + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &ui->logger.log, false, + LV2_URID__map, &ui->map, true, + LV2_UI__requestValue, &ui->request_value, false, + NULL); + // clang-format on + + lv2_log_logger_set_map(&ui->logger, ui->map); + if (missing) { + lv2_log_error(&ui->logger, "Missing feature <%s>\n", missing); + free(ui); + return NULL; + } + + // Map URIs and initialise forge + map_sampler_uris(ui->map, &ui->uris); + lv2_atom_forge_init(&ui->forge, ui->map); + peaks_receiver_init(&ui->precv, ui->map); + + // Construct Gtk UI + ui->box = gtk_vbox_new(FALSE, 4); + ui->play_button = gtk_button_new_with_label("â–¶"); + ui->canvas = gtk_drawing_area_new(); + ui->button_box = gtk_hbox_new(FALSE, 4); + ui->file_button = + gtk_file_chooser_button_new("Load Sample", GTK_FILE_CHOOSER_ACTION_OPEN); + ui->request_file_button = gtk_button_new_with_label("Request Sample"); + gtk_widget_set_size_request(ui->canvas, MIN_CANVAS_W, MIN_CANVAS_H); + gtk_container_set_border_width(GTK_CONTAINER(ui->box), 4); + gtk_box_pack_start(GTK_BOX(ui->box), ui->canvas, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(ui->box), ui->button_box, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(ui->button_box), ui->play_button, FALSE, FALSE, 0); + gtk_box_pack_start( + GTK_BOX(ui->button_box), ui->request_file_button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(ui->button_box), ui->file_button, TRUE, TRUE, 0); + + g_signal_connect(ui->file_button, "file-set", G_CALLBACK(on_file_set), ui); + + g_signal_connect( + ui->request_file_button, "clicked", G_CALLBACK(on_request_file), ui); + + g_signal_connect(ui->play_button, "clicked", G_CALLBACK(on_play_clicked), ui); + + g_signal_connect( + G_OBJECT(ui->canvas), "expose_event", G_CALLBACK(on_canvas_expose), ui); + + // Request state (filename) from plugin + lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); + LV2_Atom_Forge_Frame frame; + LV2_Atom* msg = + (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.patch_Get); + + assert(msg); + + lv2_atom_forge_pop(&ui->forge, &frame); + + ui->write(ui->controller, + 0, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); + + *widget = ui->box; + + return ui; } static void cleanup(LV2UI_Handle handle) { - SamplerUI* ui = (SamplerUI*)handle; - - if (ui->window) { - destroy_window(ui); - } - - gtk_widget_destroy(ui->canvas); - gtk_widget_destroy(ui->play_button); - gtk_widget_destroy(ui->file_button); - gtk_widget_destroy(ui->request_file_button); - gtk_widget_destroy(ui->button_box); - gtk_widget_destroy(ui->box); - free(ui); + SamplerUI* ui = (SamplerUI*)handle; + + if (ui->window) { + destroy_window(ui); + } + + gtk_widget_destroy(ui->canvas); + gtk_widget_destroy(ui->play_button); + gtk_widget_destroy(ui->file_button); + gtk_widget_destroy(ui->request_file_button); + gtk_widget_destroy(ui->button_box); + gtk_widget_destroy(ui->box); + free(ui); } static void @@ -354,116 +358,112 @@ port_event(LV2UI_Handle handle, uint32_t format, const void* buffer) { - SamplerUI* ui = (SamplerUI*)handle; - if (format == ui->uris.atom_eventTransfer) { - const LV2_Atom* atom = (const LV2_Atom*)buffer; - if (lv2_atom_forge_is_object_type(&ui->forge, atom->type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; - if (obj->body.otype == ui->uris.patch_Set) { - const char* path = read_set_file(&ui->uris, obj); - if (path && (!ui->filename || strcmp(path, ui->filename))) { - g_free(ui->filename); - ui->filename = g_strdup(path); - gtk_file_chooser_set_filename( - GTK_FILE_CHOOSER(ui->file_button), path); - peaks_receiver_clear(&ui->precv); - ui->requested_n_peaks = 0; - request_peaks(ui, ui->width / 2 * 2); - } else if (!path) { - lv2_log_warning(&ui->logger, "Set message has no path\n"); - } - } else if (obj->body.otype == ui->precv.uris.peaks_PeakUpdate) { - if (!peaks_receiver_receive(&ui->precv, obj)) { - gtk_widget_queue_draw(ui->canvas); - } - } - } else { - lv2_log_error(&ui->logger, "Unknown message type\n"); - } - } else { - lv2_log_warning(&ui->logger, "Unknown port event format\n"); - } + SamplerUI* ui = (SamplerUI*)handle; + if (format == ui->uris.atom_eventTransfer) { + const LV2_Atom* atom = (const LV2_Atom*)buffer; + if (lv2_atom_forge_is_object_type(&ui->forge, atom->type)) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; + if (obj->body.otype == ui->uris.patch_Set) { + const char* path = read_set_file(&ui->uris, obj); + if (path && (!ui->filename || strcmp(path, ui->filename))) { + g_free(ui->filename); + ui->filename = g_strdup(path); + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(ui->file_button), + path); + peaks_receiver_clear(&ui->precv); + ui->requested_n_peaks = 0; + request_peaks(ui, ui->width / 2 * 2); + } else if (!path) { + lv2_log_warning(&ui->logger, "Set message has no path\n"); + } + } else if (obj->body.otype == ui->precv.uris.peaks_PeakUpdate) { + if (!peaks_receiver_receive(&ui->precv, obj)) { + gtk_widget_queue_draw(ui->canvas); + } + } + } else { + lv2_log_error(&ui->logger, "Unknown message type\n"); + } + } else { + lv2_log_warning(&ui->logger, "Unknown port event format\n"); + } } /* Optional non-embedded UI show interface. */ static int ui_show(LV2UI_Handle handle) { - SamplerUI* ui = (SamplerUI*)handle; + SamplerUI* ui = (SamplerUI*)handle; - if (ui->window) { - return 0; - } + if (ui->window) { + return 0; + } - if (!ui->did_init) { - int argc = 0; - gtk_init_check(&argc, NULL); - g_object_ref(ui->box); - ui->did_init = true; - } + if (!ui->did_init) { + int argc = 0; + gtk_init_check(&argc, NULL); + g_object_ref(ui->box); + ui->did_init = true; + } - ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_container_add(GTK_CONTAINER(ui->window), ui->box); + ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_add(GTK_CONTAINER(ui->window), ui->box); - g_signal_connect(G_OBJECT(ui->window), - "delete-event", - G_CALLBACK(on_window_closed), - handle); + g_signal_connect( + G_OBJECT(ui->window), "delete-event", G_CALLBACK(on_window_closed), handle); - gtk_widget_show_all(ui->window); - gtk_window_present(GTK_WINDOW(ui->window)); + gtk_widget_show_all(ui->window); + gtk_window_present(GTK_WINDOW(ui->window)); - return 0; + return 0; } /* Optional non-embedded UI hide interface. */ static int ui_hide(LV2UI_Handle handle) { - SamplerUI* ui = (SamplerUI*)handle; + SamplerUI* ui = (SamplerUI*)handle; - if (ui->window) { - destroy_window(ui); - } + if (ui->window) { + destroy_window(ui); + } - return 0; + return 0; } /* Idle interface for optional non-embedded UI. */ static int ui_idle(LV2UI_Handle handle) { - SamplerUI* ui = (SamplerUI*)handle; - if (ui->window) { - gtk_main_iteration_do(false); - } - return 0; + SamplerUI* ui = (SamplerUI*)handle; + if (ui->window) { + gtk_main_iteration_do(false); + } + return 0; } static const void* extension_data(const char* uri) { - static const LV2UI_Show_Interface show = { ui_show, ui_hide }; - static const LV2UI_Idle_Interface idle = { ui_idle }; - if (!strcmp(uri, LV2_UI__showInterface)) { - return &show; - } else if (!strcmp(uri, LV2_UI__idleInterface)) { - return &idle; - } - return NULL; + static const LV2UI_Show_Interface show = {ui_show, ui_hide}; + static const LV2UI_Idle_Interface idle = {ui_idle}; + if (!strcmp(uri, LV2_UI__showInterface)) { + return &show; + } else if (!strcmp(uri, LV2_UI__idleInterface)) { + return &idle; + } + return NULL; } -static const LV2UI_Descriptor descriptor = { - SAMPLER_UI_URI, - instantiate, - cleanup, - port_event, - extension_data -}; +static const LV2UI_Descriptor descriptor = {SAMPLER_UI_URI, + instantiate, + cleanup, + port_event, + extension_data}; LV2_SYMBOL_EXPORT const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h index 8a0eb6f..d7201fa 100644 --- a/plugins/eg-sampler.lv2/uris.h +++ b/plugins/eg-sampler.lv2/uris.h @@ -29,49 +29,49 @@ #include <stdint.h> #include <stdio.h> -#define EG_SAMPLER_URI "http://lv2plug.in/plugins/eg-sampler" +#define EG_SAMPLER_URI "http://lv2plug.in/plugins/eg-sampler" #define EG_SAMPLER__applySample EG_SAMPLER_URI "#applySample" -#define EG_SAMPLER__freeSample EG_SAMPLER_URI "#freeSample" -#define EG_SAMPLER__sample EG_SAMPLER_URI "#sample" +#define EG_SAMPLER__freeSample EG_SAMPLER_URI "#freeSample" +#define EG_SAMPLER__sample EG_SAMPLER_URI "#sample" typedef struct { - LV2_URID atom_Float; - LV2_URID atom_Path; - LV2_URID atom_Resource; - LV2_URID atom_Sequence; - LV2_URID atom_URID; - LV2_URID atom_eventTransfer; - LV2_URID eg_applySample; - LV2_URID eg_freeSample; - LV2_URID eg_sample; - LV2_URID midi_Event; - LV2_URID param_gain; - LV2_URID patch_Get; - LV2_URID patch_Set; - LV2_URID patch_accept; - LV2_URID patch_property; - LV2_URID patch_value; + LV2_URID atom_Float; + LV2_URID atom_Path; + LV2_URID atom_Resource; + LV2_URID atom_Sequence; + LV2_URID atom_URID; + LV2_URID atom_eventTransfer; + LV2_URID eg_applySample; + LV2_URID eg_freeSample; + LV2_URID eg_sample; + LV2_URID midi_Event; + LV2_URID param_gain; + LV2_URID patch_Get; + LV2_URID patch_Set; + LV2_URID patch_accept; + LV2_URID patch_property; + LV2_URID patch_value; } SamplerURIs; static inline void map_sampler_uris(LV2_URID_Map* map, SamplerURIs* uris) { - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); - uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); - uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); - uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->eg_applySample = map->map(map->handle, EG_SAMPLER__applySample); - uris->eg_freeSample = map->map(map->handle, EG_SAMPLER__freeSample); - uris->eg_sample = map->map(map->handle, EG_SAMPLER__sample); - uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); - uris->param_gain = map->map(map->handle, LV2_PARAMETERS__gain); - uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); - uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); - uris->patch_accept = map->map(map->handle, LV2_PATCH__accept); - uris->patch_property = map->map(map->handle, LV2_PATCH__property); - uris->patch_value = map->map(map->handle, LV2_PATCH__value); + uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); + uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); + uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); + uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); + uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); + uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); + uris->eg_applySample = map->map(map->handle, EG_SAMPLER__applySample); + uris->eg_freeSample = map->map(map->handle, EG_SAMPLER__freeSample); + uris->eg_sample = map->map(map->handle, EG_SAMPLER__sample); + uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); + uris->param_gain = map->map(map->handle, LV2_PARAMETERS__gain); + uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); + uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); + uris->patch_accept = map->map(map->handle, LV2_PATCH__accept); + uris->patch_property = map->map(map->handle, LV2_PATCH__property); + uris->patch_value = map->map(map->handle, LV2_PATCH__value); } /** @@ -85,21 +85,19 @@ map_sampler_uris(LV2_URID_Map* map, SamplerURIs* uris) ---- */ static inline LV2_Atom_Forge_Ref -write_set_gain(LV2_Atom_Forge* forge, - const SamplerURIs* uris, - const float gain) +write_set_gain(LV2_Atom_Forge* forge, const SamplerURIs* uris, const float gain) { - LV2_Atom_Forge_Frame frame; - LV2_Atom_Forge_Ref set = lv2_atom_forge_object( - forge, &frame, 0, uris->patch_Set); + LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge_Ref set = + lv2_atom_forge_object(forge, &frame, 0, uris->patch_Set); - lv2_atom_forge_key(forge, uris->patch_property); - lv2_atom_forge_urid(forge, uris->param_gain); - lv2_atom_forge_key(forge, uris->patch_value); - lv2_atom_forge_float(forge, gain); + lv2_atom_forge_key(forge, uris->patch_property); + lv2_atom_forge_urid(forge, uris->param_gain); + lv2_atom_forge_key(forge, uris->patch_value); + lv2_atom_forge_float(forge, gain); - lv2_atom_forge_pop(forge, &frame); - return set; + lv2_atom_forge_pop(forge, &frame); + return set; } /** @@ -118,17 +116,17 @@ write_set_file(LV2_Atom_Forge* forge, const char* filename, const uint32_t filename_len) { - LV2_Atom_Forge_Frame frame; - LV2_Atom_Forge_Ref set = lv2_atom_forge_object( - forge, &frame, 0, uris->patch_Set); + LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge_Ref set = + lv2_atom_forge_object(forge, &frame, 0, uris->patch_Set); - lv2_atom_forge_key(forge, uris->patch_property); - lv2_atom_forge_urid(forge, uris->eg_sample); - lv2_atom_forge_key(forge, uris->patch_value); - lv2_atom_forge_path(forge, filename, filename_len); + lv2_atom_forge_key(forge, uris->patch_property); + lv2_atom_forge_urid(forge, uris->eg_sample); + lv2_atom_forge_key(forge, uris->patch_value); + lv2_atom_forge_path(forge, filename, filename_len); - lv2_atom_forge_pop(forge, &frame); - return set; + lv2_atom_forge_pop(forge, &frame); + return set; } /** @@ -142,46 +140,45 @@ write_set_file(LV2_Atom_Forge* forge, ---- */ static inline const char* -read_set_file(const SamplerURIs* uris, - const LV2_Atom_Object* obj) +read_set_file(const SamplerURIs* uris, const LV2_Atom_Object* obj) { - if (obj->body.otype != uris->patch_Set) { - fprintf(stderr, "Ignoring unknown message type %u\n", obj->body.otype); - return NULL; - } - - /* Get property URI. */ - const LV2_Atom* property = NULL; - lv2_atom_object_get(obj, uris->patch_property, &property, 0); - if (!property) { - fprintf(stderr, "Malformed set message has no body.\n"); - return NULL; - } - - if (property->type != uris->atom_URID) { - fprintf(stderr, "Malformed set message has non-URID property.\n"); - return NULL; - } - - if (((const LV2_Atom_URID*)property)->body != uris->eg_sample) { - fprintf(stderr, "Set message for unknown property.\n"); - return NULL; - } - - /* Get value. */ - const LV2_Atom* value = NULL; - lv2_atom_object_get(obj, uris->patch_value, &value, 0); - if (!value) { - fprintf(stderr, "Malformed set message has no value.\n"); - return NULL; - } - - if (value->type != uris->atom_Path) { - fprintf(stderr, "Set message value is not a Path.\n"); - return NULL; - } - - return (const char*)LV2_ATOM_BODY_CONST(value); + if (obj->body.otype != uris->patch_Set) { + fprintf(stderr, "Ignoring unknown message type %u\n", obj->body.otype); + return NULL; + } + + /* Get property URI. */ + const LV2_Atom* property = NULL; + lv2_atom_object_get(obj, uris->patch_property, &property, 0); + if (!property) { + fprintf(stderr, "Malformed set message has no body.\n"); + return NULL; + } + + if (property->type != uris->atom_URID) { + fprintf(stderr, "Malformed set message has non-URID property.\n"); + return NULL; + } + + if (((const LV2_Atom_URID*)property)->body != uris->eg_sample) { + fprintf(stderr, "Set message for unknown property.\n"); + return NULL; + } + + /* Get value. */ + const LV2_Atom* value = NULL; + lv2_atom_object_get(obj, uris->patch_value, &value, 0); + if (!value) { + fprintf(stderr, "Malformed set message has no value.\n"); + return NULL; + } + + if (value->type != uris->atom_Path) { + fprintf(stderr, "Set message value is not a Path.\n"); + return NULL; + } + + return (const char*)LV2_ATOM_BODY_CONST(value); } -#endif /* SAMPLER_URIS_H */ +#endif /* SAMPLER_URIS_H */ diff --git a/plugins/eg-scope.lv2/examploscope.c b/plugins/eg-scope.lv2/examploscope.c index b767508..ec013a1 100644 --- a/plugins/eg-scope.lv2/examploscope.c +++ b/plugins/eg-scope.lv2/examploscope.c @@ -42,40 +42,40 @@ UI using atom messages via a sequence port, similarly to MIDI I/O. */ typedef struct { - // Port buffers - float* input[2]; - float* output[2]; - const LV2_Atom_Sequence* control; - LV2_Atom_Sequence* notify; - - // Atom forge and URI mapping - LV2_URID_Map* map; - ScoLV2URIs uris; - LV2_Atom_Forge forge; - LV2_Atom_Forge_Frame frame; - - // Log feature and convenience API - LV2_Log_Logger logger; - - // Instantiation settings - uint32_t n_channels; - double rate; - - // UI state - bool ui_active; - bool send_settings_to_ui; - float ui_amp; - uint32_t ui_spp; + // Port buffers + float* input[2]; + float* output[2]; + const LV2_Atom_Sequence* control; + LV2_Atom_Sequence* notify; + + // Atom forge and URI mapping + LV2_URID_Map* map; + ScoLV2URIs uris; + LV2_Atom_Forge forge; + LV2_Atom_Forge_Frame frame; + + // Log feature and convenience API + LV2_Log_Logger logger; + + // Instantiation settings + uint32_t n_channels; + double rate; + + // UI state + bool ui_active; + bool send_settings_to_ui; + float ui_amp; + uint32_t ui_spp; } EgScope; /** ==== Port Indices ==== */ typedef enum { - SCO_CONTROL = 0, // Event input - SCO_NOTIFY = 1, // Event output - SCO_INPUT0 = 2, // Audio input 0 - SCO_OUTPUT0 = 3, // Audio output 0 - SCO_INPUT1 = 4, // Audio input 1 (stereo variant) - SCO_OUTPUT1 = 5, // Audio input 2 (stereo variant) + SCO_CONTROL = 0, // Event input + SCO_NOTIFY = 1, // Event output + SCO_INPUT0 = 2, // Audio input 0 + SCO_OUTPUT0 = 3, // Audio output 0 + SCO_INPUT1 = 4, // Audio input 1 (stereo variant) + SCO_OUTPUT1 = 5, // Audio input 2 (stereo variant) } PortIndex; /** ==== Instantiate Method ==== */ @@ -85,85 +85,83 @@ instantiate(const LV2_Descriptor* descriptor, const char* bundle_path, const LV2_Feature* const* features) { - (void)descriptor; // Unused variable - (void)bundle_path; // Unused variable - - // Allocate and initialise instance structure. - EgScope* self = (EgScope*)calloc(1, sizeof(EgScope)); - if (!self) { - return NULL; - } - - // Get host features - // clang-format off - const char* missing = lv2_features_query( - features, - LV2_LOG__log, &self->logger.log, false, - LV2_URID__map, &self->map, true, - NULL); - // clang-format on - - lv2_log_logger_set_map(&self->logger, self->map); - if (missing) { - lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); - free(self); - return NULL; - } - - // Decide which variant to use depending on the plugin URI - if (!strcmp(descriptor->URI, SCO_URI "#Stereo")) { - self->n_channels = 2; - } else if (!strcmp(descriptor->URI, SCO_URI "#Mono")) { - self->n_channels = 1; - } else { - free(self); - return NULL; - } - - // Initialise local variables - self->ui_active = false; - self->send_settings_to_ui = false; - self->rate = rate; - - // Set default UI settings - self->ui_spp = 50; - self->ui_amp = 1.0f; - - // Map URIs and initialise forge/logger - map_sco_uris(self->map, &self->uris); - lv2_atom_forge_init(&self->forge, self->map); - - return (LV2_Handle)self; + (void)descriptor; // Unused variable + (void)bundle_path; // Unused variable + + // Allocate and initialise instance structure. + EgScope* self = (EgScope*)calloc(1, sizeof(EgScope)); + if (!self) { + return NULL; + } + + // Get host features + // clang-format off + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &self->logger.log, false, + LV2_URID__map, &self->map, true, + NULL); + // clang-format on + + lv2_log_logger_set_map(&self->logger, self->map); + if (missing) { + lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); + free(self); + return NULL; + } + + // Decide which variant to use depending on the plugin URI + if (!strcmp(descriptor->URI, SCO_URI "#Stereo")) { + self->n_channels = 2; + } else if (!strcmp(descriptor->URI, SCO_URI "#Mono")) { + self->n_channels = 1; + } else { + free(self); + return NULL; + } + + // Initialise local variables + self->ui_active = false; + self->send_settings_to_ui = false; + self->rate = rate; + + // Set default UI settings + self->ui_spp = 50; + self->ui_amp = 1.0f; + + // Map URIs and initialise forge/logger + map_sco_uris(self->map, &self->uris); + lv2_atom_forge_init(&self->forge, self->map); + + return (LV2_Handle)self; } /** ==== Connect Port Method ==== */ static void -connect_port(LV2_Handle handle, - uint32_t port, - void* data) +connect_port(LV2_Handle handle, uint32_t port, void* data) { - EgScope* self = (EgScope*)handle; - - switch ((PortIndex)port) { - case SCO_CONTROL: - self->control = (const LV2_Atom_Sequence*)data; - break; - case SCO_NOTIFY: - self->notify = (LV2_Atom_Sequence*)data; - break; - case SCO_INPUT0: - self->input[0] = (float*)data; - break; - case SCO_OUTPUT0: - self->output[0] = (float*)data; - break; - case SCO_INPUT1: - self->input[1] = (float*)data; - break; - case SCO_OUTPUT1: - self->output[1] = (float*)data; - break; - } + EgScope* self = (EgScope*)handle; + + switch ((PortIndex)port) { + case SCO_CONTROL: + self->control = (const LV2_Atom_Sequence*)data; + break; + case SCO_NOTIFY: + self->notify = (LV2_Atom_Sequence*)data; + break; + case SCO_INPUT0: + self->input[0] = (float*)data; + break; + case SCO_OUTPUT0: + self->output[0] = (float*)data; + break; + case SCO_INPUT1: + self->input[1] = (float*)data; + break; + case SCO_OUTPUT1: + self->output[1] = (float*)data; + break; + } } /** @@ -174,9 +172,9 @@ connect_port(LV2_Handle handle, [source,turtle] -------- [] - a sco:RawAudio ; - sco:channelID 0 ; - sco:audioData [ 0.0, 0.0, ... ] . + a sco:RawAudio ; + sco:channelID 0 ; + sco:audioData [ 0.0, 0.0, ... ] . -------- where the value of the `sco:audioData` property, `[ 0.0, 0.0, ... ]`, is a @@ -190,134 +188,131 @@ tx_rawaudio(LV2_Atom_Forge* forge, const size_t n_samples, const float* data) { - LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge_Frame frame; - // Forge container object of type 'RawAudio' - lv2_atom_forge_frame_time(forge, 0); - lv2_atom_forge_object(forge, &frame, 0, uris->RawAudio); + // Forge container object of type 'RawAudio' + lv2_atom_forge_frame_time(forge, 0); + lv2_atom_forge_object(forge, &frame, 0, uris->RawAudio); - // Add integer 'channelID' property - lv2_atom_forge_key(forge, uris->channelID); - lv2_atom_forge_int(forge, channel); + // Add integer 'channelID' property + lv2_atom_forge_key(forge, uris->channelID); + lv2_atom_forge_int(forge, channel); - // Add vector of floats 'audioData' property - lv2_atom_forge_key(forge, uris->audioData); - lv2_atom_forge_vector( - forge, sizeof(float), uris->atom_Float, n_samples, data); + // Add vector of floats 'audioData' property + lv2_atom_forge_key(forge, uris->audioData); + lv2_atom_forge_vector( + forge, sizeof(float), uris->atom_Float, n_samples, data); - // Close off object - lv2_atom_forge_pop(forge, &frame); + // Close off object + lv2_atom_forge_pop(forge, &frame); } /** ==== Run Method ==== */ static void run(LV2_Handle handle, uint32_t n_samples) { - EgScope* self = (EgScope*)handle; - - /* Ensure notify port buffer is large enough to hold all audio-samples and - configuration settings. A minimum size was requested in the .ttl file, - but check here just to be sure. - - TODO: Explain these magic numbers. - */ - const size_t size = (sizeof(float) * n_samples + 64) * self->n_channels; - const uint32_t space = self->notify->atom.size; - if (space < size + 128) { - /* Insufficient space, report error and do nothing. Note that a - real-time production plugin mustn't call log functions in run(), but - this can be useful for debugging and example purposes. - */ - lv2_log_error(&self->logger, "Buffer size is insufficient\n"); - return; - } - - // Prepare forge buffer and initialize atom-sequence - lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, space); - lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); - - /* Send settings to UI - - The plugin can continue to run while the UI is closed and re-opened. - The state and settings of the UI are kept here and transmitted to the UI - every time it asks for them or if the user initializes a 'load preset'. - */ - if (self->send_settings_to_ui && self->ui_active) { - self->send_settings_to_ui = false; - // Forge container object of type 'ui_state' - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_frame_time(&self->forge, 0); - lv2_atom_forge_object(&self->forge, &frame, 0, self->uris.ui_State); - - // Add UI state as properties - lv2_atom_forge_key(&self->forge, self->uris.ui_spp); - lv2_atom_forge_int(&self->forge, self->ui_spp); - lv2_atom_forge_key(&self->forge, self->uris.ui_amp); - lv2_atom_forge_float(&self->forge, self->ui_amp); - lv2_atom_forge_key(&self->forge, self->uris.param_sampleRate); - lv2_atom_forge_float(&self->forge, (float)self->rate); - lv2_atom_forge_pop(&self->forge, &frame); - } - - // Process incoming events from GUI - if (self->control) { - const LV2_Atom_Event* ev = lv2_atom_sequence_begin( - &(self->control)->body); - // For each incoming message... - while (!lv2_atom_sequence_is_end( - &self->control->body, self->control->atom.size, ev)) { - // If the event is an atom:Blank object - if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; - if (obj->body.otype == self->uris.ui_On) { - // If the object is a ui-on, the UI was activated - self->ui_active = true; - self->send_settings_to_ui = true; - } else if (obj->body.otype == self->uris.ui_Off) { - // If the object is a ui-off, the UI was closed - self->ui_active = false; - } else if (obj->body.otype == self->uris.ui_State) { - // If the object is a ui-state, it's the current UI settings - const LV2_Atom* spp = NULL; - const LV2_Atom* amp = NULL; - lv2_atom_object_get(obj, self->uris.ui_spp, &spp, - self->uris.ui_amp, &, - 0); - if (spp) { - self->ui_spp = ((const LV2_Atom_Int*)spp)->body; - } - if (amp) { - self->ui_amp = ((const LV2_Atom_Float*)amp)->body; - } - } - } - ev = lv2_atom_sequence_next(ev); - } - } - - // Process audio data - for (uint32_t c = 0; c < self->n_channels; ++c) { - if (self->ui_active) { - // If UI is active, send raw audio data to UI - tx_rawaudio(&self->forge, &self->uris, c, n_samples, self->input[c]); - } - // If not processing audio in-place, forward audio - if (self->input[c] != self->output[c]) { - memcpy(self->output[c], self->input[c], sizeof(float) * n_samples); - } - } - - // Close off sequence - lv2_atom_forge_pop(&self->forge, &self->frame); + EgScope* self = (EgScope*)handle; + + /* Ensure notify port buffer is large enough to hold all audio-samples and + configuration settings. A minimum size was requested in the .ttl file, + but check here just to be sure. + + TODO: Explain these magic numbers. + */ + const size_t size = (sizeof(float) * n_samples + 64) * self->n_channels; + const uint32_t space = self->notify->atom.size; + if (space < size + 128) { + /* Insufficient space, report error and do nothing. Note that a + real-time production plugin mustn't call log functions in run(), but + this can be useful for debugging and example purposes. + */ + lv2_log_error(&self->logger, "Buffer size is insufficient\n"); + return; + } + + // Prepare forge buffer and initialize atom-sequence + lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, space); + lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); + + /* Send settings to UI + + The plugin can continue to run while the UI is closed and re-opened. + The state and settings of the UI are kept here and transmitted to the UI + every time it asks for them or if the user initializes a 'load preset'. + */ + if (self->send_settings_to_ui && self->ui_active) { + self->send_settings_to_ui = false; + // Forge container object of type 'ui_state' + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_frame_time(&self->forge, 0); + lv2_atom_forge_object(&self->forge, &frame, 0, self->uris.ui_State); + + // Add UI state as properties + lv2_atom_forge_key(&self->forge, self->uris.ui_spp); + lv2_atom_forge_int(&self->forge, self->ui_spp); + lv2_atom_forge_key(&self->forge, self->uris.ui_amp); + lv2_atom_forge_float(&self->forge, self->ui_amp); + lv2_atom_forge_key(&self->forge, self->uris.param_sampleRate); + lv2_atom_forge_float(&self->forge, (float)self->rate); + lv2_atom_forge_pop(&self->forge, &frame); + } + + // Process incoming events from GUI + if (self->control) { + const LV2_Atom_Event* ev = lv2_atom_sequence_begin(&(self->control)->body); + // For each incoming message... + while (!lv2_atom_sequence_is_end( + &self->control->body, self->control->atom.size, ev)) { + // If the event is an atom:Blank object + if (lv2_atom_forge_is_object_type(&self->forge, ev->body.type)) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; + if (obj->body.otype == self->uris.ui_On) { + // If the object is a ui-on, the UI was activated + self->ui_active = true; + self->send_settings_to_ui = true; + } else if (obj->body.otype == self->uris.ui_Off) { + // If the object is a ui-off, the UI was closed + self->ui_active = false; + } else if (obj->body.otype == self->uris.ui_State) { + // If the object is a ui-state, it's the current UI settings + const LV2_Atom* spp = NULL; + const LV2_Atom* amp = NULL; + lv2_atom_object_get( + obj, self->uris.ui_spp, &spp, self->uris.ui_amp, &, 0); + if (spp) { + self->ui_spp = ((const LV2_Atom_Int*)spp)->body; + } + if (amp) { + self->ui_amp = ((const LV2_Atom_Float*)amp)->body; + } + } + } + ev = lv2_atom_sequence_next(ev); + } + } + + // Process audio data + for (uint32_t c = 0; c < self->n_channels; ++c) { + if (self->ui_active) { + // If UI is active, send raw audio data to UI + tx_rawaudio(&self->forge, &self->uris, c, n_samples, self->input[c]); + } + // If not processing audio in-place, forward audio + if (self->input[c] != self->output[c]) { + memcpy(self->output[c], self->input[c], sizeof(float) * n_samples); + } + } + + // Close off sequence + lv2_atom_forge_pop(&self->forge, &self->frame); } static void cleanup(LV2_Handle handle) { - free(handle); + free(handle); } - /** ==== State Methods ==== @@ -334,22 +329,26 @@ state_save(LV2_Handle instance, uint32_t flags, const LV2_Feature* const* features) { - EgScope* self = (EgScope*)instance; - if (!self) { - return LV2_STATE_SUCCESS; - } - - store(handle, self->uris.ui_spp, - (void*)&self->ui_spp, sizeof(uint32_t), - self->uris.atom_Int, - LV2_STATE_IS_POD); - - store(handle, self->uris.ui_amp, - (void*)&self->ui_amp, sizeof(float), - self->uris.atom_Float, - LV2_STATE_IS_POD); - - return LV2_STATE_SUCCESS; + EgScope* self = (EgScope*)instance; + if (!self) { + return LV2_STATE_SUCCESS; + } + + store(handle, + self->uris.ui_spp, + (void*)&self->ui_spp, + sizeof(uint32_t), + self->uris.atom_Int, + LV2_STATE_IS_POD); + + store(handle, + self->uris.ui_amp, + (void*)&self->ui_amp, + sizeof(float), + self->uris.atom_Float, + LV2_STATE_IS_POD); + + return LV2_STATE_SUCCESS; } static LV2_State_Status @@ -359,72 +358,68 @@ state_restore(LV2_Handle instance, uint32_t flags, const LV2_Feature* const* features) { - EgScope* self = (EgScope*)instance; - - size_t size = 0; - uint32_t type = 0; - uint32_t valflags = 0; - - const void* spp = retrieve( - handle, self->uris.ui_spp, &size, &type, &valflags); - if (spp && size == sizeof(uint32_t) && type == self->uris.atom_Int) { - self->ui_spp = *((const uint32_t*)spp); - self->send_settings_to_ui = true; - } - - const void* amp = retrieve( - handle, self->uris.ui_amp, &size, &type, &valflags); - if (amp && size == sizeof(float) && type == self->uris.atom_Float) { - self->ui_amp = *((const float*)amp); - self->send_settings_to_ui = true; - } - - return LV2_STATE_SUCCESS; + EgScope* self = (EgScope*)instance; + + size_t size = 0; + uint32_t type = 0; + uint32_t valflags = 0; + + const void* spp = + retrieve(handle, self->uris.ui_spp, &size, &type, &valflags); + if (spp && size == sizeof(uint32_t) && type == self->uris.atom_Int) { + self->ui_spp = *((const uint32_t*)spp); + self->send_settings_to_ui = true; + } + + const void* amp = + retrieve(handle, self->uris.ui_amp, &size, &type, &valflags); + if (amp && size == sizeof(float) && type == self->uris.atom_Float) { + self->ui_amp = *((const float*)amp); + self->send_settings_to_ui = true; + } + + return LV2_STATE_SUCCESS; } static const void* extension_data(const char* uri) { - static const LV2_State_Interface state = { state_save, state_restore }; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - return NULL; + static const LV2_State_Interface state = {state_save, state_restore}; + if (!strcmp(uri, LV2_STATE__interface)) { + return &state; + } + return NULL; } /** ==== Plugin Descriptors ==== */ -static const LV2_Descriptor descriptor_mono = { - SCO_URI "#Mono", - instantiate, - connect_port, - NULL, - run, - NULL, - cleanup, - extension_data -}; - -static const LV2_Descriptor descriptor_stereo = { - SCO_URI "#Stereo", - instantiate, - connect_port, - NULL, - run, - NULL, - cleanup, - extension_data -}; +static const LV2_Descriptor descriptor_mono = {SCO_URI "#Mono", + instantiate, + connect_port, + NULL, + run, + NULL, + cleanup, + extension_data}; + +static const LV2_Descriptor descriptor_stereo = {SCO_URI "#Stereo", + instantiate, + connect_port, + NULL, + run, + NULL, + cleanup, + extension_data}; LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index) { - switch (index) { - case 0: - return &descriptor_mono; - case 1: - return &descriptor_stereo; - default: - return NULL; - } + switch (index) { + case 0: + return &descriptor_mono; + case 1: + return &descriptor_stereo; + default: + return NULL; + } } diff --git a/plugins/eg-scope.lv2/examploscope_ui.c b/plugins/eg-scope.lv2/examploscope_ui.c index e9e0c6f..e601843 100644 --- a/plugins/eg-scope.lv2/examploscope_ui.c +++ b/plugins/eg-scope.lv2/examploscope_ui.c @@ -38,7 +38,7 @@ #include <string.h> // Drawing area size -#define DAWIDTH (640) +#define DAWIDTH (640) #define DAHEIGHT (200) /** @@ -53,101 +53,100 @@ given 'index' position. */ typedef struct { - float data_min[DAWIDTH]; - float data_max[DAWIDTH]; + float data_min[DAWIDTH]; + float data_max[DAWIDTH]; - uint32_t idx; - uint32_t sub; + uint32_t idx; + uint32_t sub; } ScoChan; typedef struct { - LV2_Atom_Forge forge; - LV2_URID_Map* map; - ScoLV2URIs uris; - - LV2UI_Write_Function write; - LV2UI_Controller controller; - - GtkWidget* hbox; - GtkWidget* vbox; - GtkWidget* sep[2]; - GtkWidget* darea; - GtkWidget* btn_pause; - GtkWidget* lbl_speed; - GtkWidget* lbl_amp; - GtkWidget* spb_speed; - GtkWidget* spb_amp; - GtkAdjustment* spb_speed_adj; - GtkAdjustment* spb_amp_adj; - - ScoChan chn[2]; - uint32_t stride; - uint32_t n_channels; - bool paused; - float rate; - bool updating; + LV2_Atom_Forge forge; + LV2_URID_Map* map; + ScoLV2URIs uris; + + LV2UI_Write_Function write; + LV2UI_Controller controller; + + GtkWidget* hbox; + GtkWidget* vbox; + GtkWidget* sep[2]; + GtkWidget* darea; + GtkWidget* btn_pause; + GtkWidget* lbl_speed; + GtkWidget* lbl_amp; + GtkWidget* spb_speed; + GtkWidget* spb_amp; + GtkAdjustment* spb_speed_adj; + GtkAdjustment* spb_amp_adj; + + ScoChan chn[2]; + uint32_t stride; + uint32_t n_channels; + bool paused; + float rate; + bool updating; } EgScopeUI; - /** Send current UI settings to backend. */ static void send_ui_state(LV2UI_Handle handle) { - EgScopeUI* ui = (EgScopeUI*)handle; - const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); + EgScopeUI* ui = (EgScopeUI*)handle; + const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); - // Use local buffer on the stack to build atom - uint8_t obj_buf[1024]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); + // Use local buffer on the stack to build atom + uint8_t obj_buf[1024]; + lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - // Start a ui:State object - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.ui_State); + // Start a ui:State object + LV2_Atom_Forge_Frame frame; + LV2_Atom* msg = + (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_State); - assert(msg); + assert(msg); - // msg[samples-per-pixel] = integer - lv2_atom_forge_key(&ui->forge, ui->uris.ui_spp); - lv2_atom_forge_int(&ui->forge, ui->stride); + // msg[samples-per-pixel] = integer + lv2_atom_forge_key(&ui->forge, ui->uris.ui_spp); + lv2_atom_forge_int(&ui->forge, ui->stride); - // msg[amplitude] = float - lv2_atom_forge_key(&ui->forge, ui->uris.ui_amp); - lv2_atom_forge_float(&ui->forge, gain); + // msg[amplitude] = float + lv2_atom_forge_key(&ui->forge, ui->uris.ui_amp); + lv2_atom_forge_float(&ui->forge, gain); - // Finish ui:State object - lv2_atom_forge_pop(&ui->forge, &frame); + // Finish ui:State object + lv2_atom_forge_pop(&ui->forge, &frame); - // Send message to plugin port '0' - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); + // Send message to plugin port '0' + ui->write(ui->controller, + 0, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); } /** Notify backend that UI is closed. */ static void send_ui_disable(LV2UI_Handle handle) { - EgScopeUI* ui = (EgScopeUI*)handle; - send_ui_state(handle); + EgScopeUI* ui = (EgScopeUI*)handle; + send_ui_state(handle); - uint8_t obj_buf[64]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); + uint8_t obj_buf[64]; + lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.ui_Off); + LV2_Atom_Forge_Frame frame; + LV2_Atom* msg = + (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_Off); - assert(msg); + assert(msg); - lv2_atom_forge_pop(&ui->forge, &frame); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); + lv2_atom_forge_pop(&ui->forge, &frame); + ui->write(ui->controller, + 0, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); } /** @@ -158,35 +157,35 @@ send_ui_disable(LV2UI_Handle handle) static void send_ui_enable(LV2UI_Handle handle) { - EgScopeUI* ui = (EgScopeUI*)handle; + EgScopeUI* ui = (EgScopeUI*)handle; - uint8_t obj_buf[64]; - lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); + uint8_t obj_buf[64]; + lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - LV2_Atom_Forge_Frame frame; - LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object( - &ui->forge, &frame, 0, ui->uris.ui_On); + LV2_Atom_Forge_Frame frame; + LV2_Atom* msg = + (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_On); - assert(msg); + assert(msg); - lv2_atom_forge_pop(&ui->forge, &frame); - ui->write(ui->controller, - 0, - lv2_atom_total_size(msg), - ui->uris.atom_eventTransfer, - msg); + lv2_atom_forge_pop(&ui->forge, &frame); + ui->write(ui->controller, + 0, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); } /** Gtk widget callback. */ static gboolean on_cfg_changed(GtkWidget* widget, gpointer data) { - EgScopeUI* ui = (EgScopeUI*)data; - if (!ui->updating) { - // Only send UI state if the change is from user interaction - send_ui_state(data); - } - return TRUE; + EgScopeUI* ui = (EgScopeUI*)data; + if (!ui->updating) { + // Only send UI state if the change is from user interaction + send_ui_state(data); + } + return TRUE; } /** @@ -197,127 +196,127 @@ on_cfg_changed(GtkWidget* widget, gpointer data) static gboolean on_expose_event(GtkWidget* widget, GdkEventExpose* ev, gpointer data) { - EgScopeUI* ui = (EgScopeUI*)data; - const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); - - // Get cairo type for the gtk window - cairo_t* cr = gdk_cairo_create(ui->darea->window); - - // Limit cairo-drawing to exposed area - cairo_rectangle(cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); - cairo_clip(cr); - - // Clear background - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - cairo_rectangle(cr, 0, 0, DAWIDTH, DAHEIGHT * ui->n_channels); - cairo_fill(cr); - - cairo_set_line_width(cr, 1.0); - - const uint32_t start = ev->area.x; - const uint32_t end = ev->area.x + ev->area.width; - - assert(start < DAWIDTH); - assert(end <= DAWIDTH); - assert(start < end); - - for (uint32_t c = 0; c < ui->n_channels; ++c) { - ScoChan* chn = &ui->chn[c]; - - /* Drawing area Y-position of given sample-value. - * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5) - * also the cairo Y-axis points upwards (hence 'minus value') - * - * == ( DAHEIGHT * (CHN) // channel offset - * + (DAHEIGHT / 2) - 0.5 // vertical center -- '0' - * - (DAHEIGHT / 2) * (VAL) * (GAIN) - * ) - */ - const float chn_y_offset = DAHEIGHT * c + DAHEIGHT * 0.5f - 0.5f; - const float chn_y_scale = DAHEIGHT * 0.5f * gain; - -#define CYPOS(VAL) (chn_y_offset - (VAL) * chn_y_scale) - - cairo_save(cr); - - /* Restrict drawing to current channel area, don't bleed drawing into - neighboring channels. */ - cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT); - cairo_clip(cr); - - // Set color of wave-form - cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); - - /* This is a somewhat 'smart' mechanism to plot audio data using - alternating up/down line-directions. It works well for both cases: - 1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but - is not ideal for either. */ - if (start == chn->idx) { - cairo_move_to(cr, start - 0.5, CYPOS(0)); - } else { - cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start])); - } - - uint32_t pathlength = 0; - for (uint32_t i = start; i < end; ++i) { - if (i == chn->idx) { - continue; - } else if (i % 2) { - cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); - cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); - ++pathlength; - } else { - cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); - cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); - ++pathlength; - } - - /** Limit the max cairo path length. This is an optimization trade - off: too short path: high load CPU/GPU load. too-long path: - bad anti-aliasing, or possibly lost points */ - if (pathlength > MAX_CAIRO_PATH) { - pathlength = 0; - cairo_stroke(cr); - if (i % 2) { - cairo_move_to(cr, i - .5, CYPOS(chn->data_max[i])); - } else { - cairo_move_to(cr, i - .5, CYPOS(chn->data_min[i])); - } - } - } - - if (pathlength > 0) { - cairo_stroke(cr); - } - - // Draw current position vertical line if display is slow - if (ui->stride >= ui->rate / 4800.0f || ui->paused) { - cairo_set_source_rgba(cr, .9, .2, .2, .6); - cairo_move_to(cr, chn->idx - .5, DAHEIGHT * c); - cairo_line_to(cr, chn->idx - .5, DAHEIGHT * (c + 1)); - cairo_stroke(cr); - } - - // Undo the 'clipping' restriction - cairo_restore(cr); - - // Channel separator - if (c > 0) { - cairo_set_source_rgba(cr, .5, .5, .5, 1.0); - cairo_move_to(cr, 0, DAHEIGHT * c - .5); - cairo_line_to(cr, DAWIDTH, DAHEIGHT * c - .5); - cairo_stroke(cr); - } - - // Zero scale line - cairo_set_source_rgba(cr, .3, .3, .7, .5); - cairo_move_to(cr, 0, DAHEIGHT * (c + .5) - .5); - cairo_line_to(cr, DAWIDTH, DAHEIGHT * (c + .5) - .5); - cairo_stroke(cr); - } - - cairo_destroy(cr); - return TRUE; + EgScopeUI* ui = (EgScopeUI*)data; + const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp)); + + // Get cairo type for the gtk window + cairo_t* cr = gdk_cairo_create(ui->darea->window); + + // Limit cairo-drawing to exposed area + cairo_rectangle(cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); + cairo_clip(cr); + + // Clear background + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + cairo_rectangle(cr, 0, 0, DAWIDTH, DAHEIGHT * ui->n_channels); + cairo_fill(cr); + + cairo_set_line_width(cr, 1.0); + + const uint32_t start = ev->area.x; + const uint32_t end = ev->area.x + ev->area.width; + + assert(start < DAWIDTH); + assert(end <= DAWIDTH); + assert(start < end); + + for (uint32_t c = 0; c < ui->n_channels; ++c) { + ScoChan* chn = &ui->chn[c]; + + /* Drawing area Y-position of given sample-value. + * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5) + * also the cairo Y-axis points upwards (hence 'minus value') + * + * == ( DAHEIGHT * (CHN) // channel offset + * + (DAHEIGHT / 2) - 0.5 // vertical center -- '0' + * - (DAHEIGHT / 2) * (VAL) * (GAIN) + * ) + */ + const float chn_y_offset = DAHEIGHT * c + DAHEIGHT * 0.5f - 0.5f; + const float chn_y_scale = DAHEIGHT * 0.5f * gain; + +#define CYPOS(VAL) (chn_y_offset - (VAL)*chn_y_scale) + + cairo_save(cr); + + /* Restrict drawing to current channel area, don't bleed drawing into + neighboring channels. */ + cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT); + cairo_clip(cr); + + // Set color of wave-form + cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); + + /* This is a somewhat 'smart' mechanism to plot audio data using + alternating up/down line-directions. It works well for both cases: + 1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but + is not ideal for either. */ + if (start == chn->idx) { + cairo_move_to(cr, start - 0.5, CYPOS(0)); + } else { + cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start])); + } + + uint32_t pathlength = 0; + for (uint32_t i = start; i < end; ++i) { + if (i == chn->idx) { + continue; + } else if (i % 2) { + cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); + cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); + ++pathlength; + } else { + cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); + cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); + ++pathlength; + } + + /** Limit the max cairo path length. This is an optimization trade + off: too short path: high load CPU/GPU load. too-long path: + bad anti-aliasing, or possibly lost points */ + if (pathlength > MAX_CAIRO_PATH) { + pathlength = 0; + cairo_stroke(cr); + if (i % 2) { + cairo_move_to(cr, i - .5, CYPOS(chn->data_max[i])); + } else { + cairo_move_to(cr, i - .5, CYPOS(chn->data_min[i])); + } + } + } + + if (pathlength > 0) { + cairo_stroke(cr); + } + + // Draw current position vertical line if display is slow + if (ui->stride >= ui->rate / 4800.0f || ui->paused) { + cairo_set_source_rgba(cr, .9, .2, .2, .6); + cairo_move_to(cr, chn->idx - .5, DAHEIGHT * c); + cairo_line_to(cr, chn->idx - .5, DAHEIGHT * (c + 1)); + cairo_stroke(cr); + } + + // Undo the 'clipping' restriction + cairo_restore(cr); + + // Channel separator + if (c > 0) { + cairo_set_source_rgba(cr, .5, .5, .5, 1.0); + cairo_move_to(cr, 0, DAHEIGHT * c - .5); + cairo_line_to(cr, DAWIDTH, DAHEIGHT * c - .5); + cairo_stroke(cr); + } + + // Zero scale line + cairo_set_source_rgba(cr, .3, .3, .7, .5); + cairo_move_to(cr, 0, DAHEIGHT * (c + .5) - .5); + cairo_line_to(cr, DAWIDTH, DAHEIGHT * (c + .5) - .5); + cairo_stroke(cr); + } + + cairo_destroy(cr); + return TRUE; } /** @@ -346,27 +345,27 @@ process_channel(EgScopeUI* ui, uint32_t* idx_start, uint32_t* idx_end) { - int overflow = 0; - *idx_start = chn->idx; - for (size_t i = 0; i < n_elem; ++i) { - if (data[i] < chn->data_min[chn->idx]) { - chn->data_min[chn->idx] = data[i]; - } - if (data[i] > chn->data_max[chn->idx]) { - chn->data_max[chn->idx] = data[i]; - } - if (++chn->sub >= ui->stride) { - chn->sub = 0; - chn->idx = (chn->idx + 1) % DAWIDTH; - if (chn->idx == 0) { - ++overflow; - } - chn->data_min[chn->idx] = 1.0f; - chn->data_max[chn->idx] = -1.0f; - } - } - *idx_end = chn->idx; - return overflow; + int overflow = 0; + *idx_start = chn->idx; + for (size_t i = 0; i < n_elem; ++i) { + if (data[i] < chn->data_min[chn->idx]) { + chn->data_min[chn->idx] = data[i]; + } + if (data[i] > chn->data_max[chn->idx]) { + chn->data_max[chn->idx] = data[i]; + } + if (++chn->sub >= ui->stride) { + chn->sub = 0; + chn->idx = (chn->idx + 1) % DAWIDTH; + if (chn->idx == 0) { + ++overflow; + } + chn->data_min[chn->idx] = 1.0f; + chn->data_max[chn->idx] = -1.0f; + } + } + *idx_end = chn->idx; + return overflow; } /** @@ -379,56 +378,57 @@ update_scope(EgScopeUI* ui, const size_t n_elem, float const* data) { - // Never trust input data which could lead to application failure. - if (channel < 0 || (uint32_t)channel > ui->n_channels) { - return; - } - - // Update state in sync with 1st channel - if (channel == 0) { - ui->stride = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_speed)); - const bool paused = gtk_toggle_button_get_active( - GTK_TOGGLE_BUTTON(ui->btn_pause)); - - if (paused != ui->paused) { - ui->paused = paused; - gtk_widget_queue_draw(ui->darea); - } - } - if (ui->paused) { - return; - } - - uint32_t idx_start = 0; // Display pixel start - uint32_t idx_end = 0; // Display pixel end - int overflow = 0; // Received more audio-data than display-pixel - - // Process this channel's audio-data for display - ScoChan* chn = &ui->chn[channel]; - overflow = process_channel(ui, chn, n_elem, data, &idx_start, &idx_end); - - // Signal gtk's main thread to redraw the widget after the last channel - if ((uint32_t)channel + 1 == ui->n_channels) { - if (overflow > 1) { - // Redraw complete widget - gtk_widget_queue_draw(ui->darea); - } else if (idx_end > idx_start) { - // Redraw area between start -> end pixel - gtk_widget_queue_draw_area(ui->darea, idx_start - 2, 0, 3 - + idx_end - idx_start, - DAHEIGHT * ui->n_channels); - } else if (idx_end < idx_start) { - // Wrap-around: redraw area between 0->start AND end->right-end - gtk_widget_queue_draw_area( - ui->darea, - idx_start - 2, 0, - 3 + DAWIDTH - idx_start, DAHEIGHT * ui->n_channels); - gtk_widget_queue_draw_area( - ui->darea, - 0, 0, - idx_end + 1, DAHEIGHT * ui->n_channels); - } - } + // Never trust input data which could lead to application failure. + if (channel < 0 || (uint32_t)channel > ui->n_channels) { + return; + } + + // Update state in sync with 1st channel + if (channel == 0) { + ui->stride = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_speed)); + const bool paused = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ui->btn_pause)); + + if (paused != ui->paused) { + ui->paused = paused; + gtk_widget_queue_draw(ui->darea); + } + } + if (ui->paused) { + return; + } + + uint32_t idx_start = 0; // Display pixel start + uint32_t idx_end = 0; // Display pixel end + int overflow = 0; // Received more audio-data than display-pixel + + // Process this channel's audio-data for display + ScoChan* chn = &ui->chn[channel]; + overflow = process_channel(ui, chn, n_elem, data, &idx_start, &idx_end); + + // Signal gtk's main thread to redraw the widget after the last channel + if ((uint32_t)channel + 1 == ui->n_channels) { + if (overflow > 1) { + // Redraw complete widget + gtk_widget_queue_draw(ui->darea); + } else if (idx_end > idx_start) { + // Redraw area between start -> end pixel + gtk_widget_queue_draw_area(ui->darea, + idx_start - 2, + 0, + 3 + idx_end - idx_start, + DAHEIGHT * ui->n_channels); + } else if (idx_end < idx_start) { + // Wrap-around: redraw area between 0->start AND end->right-end + gtk_widget_queue_draw_area(ui->darea, + idx_start - 2, + 0, + 3 + DAWIDTH - idx_start, + DAHEIGHT * ui->n_channels); + gtk_widget_queue_draw_area( + ui->darea, 0, 0, idx_end + 1, DAHEIGHT * ui->n_channels); + } + } } static LV2UI_Handle @@ -440,199 +440,191 @@ instantiate(const LV2UI_Descriptor* descriptor, LV2UI_Widget* widget, const LV2_Feature* const* features) { - EgScopeUI* ui = (EgScopeUI*)calloc(1, sizeof(EgScopeUI)); - - if (!ui) { - fprintf(stderr, "EgScope.lv2 UI: out of memory\n"); - return NULL; - } - - ui->map = NULL; - *widget = NULL; - - if (!strcmp(plugin_uri, SCO_URI "#Mono")) { - ui->n_channels = 1; - } else if (!strcmp(plugin_uri, SCO_URI "#Stereo")) { - ui->n_channels = 2; - } else { - free(ui); - return NULL; - } - - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { - ui->map = (LV2_URID_Map*)features[i]->data; - } - } - - if (!ui->map) { - fprintf(stderr, "EgScope.lv2 UI: Host does not support urid:map\n"); - free(ui); - return NULL; - } - - // Initialize private data structure - ui->write = write_function; - ui->controller = controller; - - ui->vbox = NULL; - ui->hbox = NULL; - ui->darea = NULL; - ui->stride = 25; - ui->paused = false; - ui->rate = 48000; - - ui->chn[0].idx = 0; - ui->chn[0].sub = 0; - ui->chn[1].idx = 0; - ui->chn[1].sub = 0; - memset(ui->chn[0].data_min, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[0].data_max, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[1].data_min, 0, sizeof(float) * DAWIDTH); - memset(ui->chn[1].data_max, 0, sizeof(float) * DAWIDTH); - - map_sco_uris(ui->map, &ui->uris); - lv2_atom_forge_init(&ui->forge, ui->map); - - // Setup UI - ui->hbox = gtk_hbox_new(FALSE, 0); - ui->vbox = gtk_vbox_new(FALSE, 0); - - ui->darea = gtk_drawing_area_new(); - gtk_widget_set_size_request(ui->darea, DAWIDTH, DAHEIGHT * ui->n_channels); - - ui->lbl_speed = gtk_label_new("Samples/Pixel"); - ui->lbl_amp = gtk_label_new("Amplitude"); - - ui->sep[0] = gtk_hseparator_new(); - ui->sep[1] = gtk_label_new(""); - ui->btn_pause = gtk_toggle_button_new_with_label("Pause"); - - ui->spb_speed_adj = (GtkAdjustment*)gtk_adjustment_new( - 25.0, 1.0, 1000.0, 1.0, 5.0, 0.0); - ui->spb_speed = gtk_spin_button_new(ui->spb_speed_adj, 1.0, 0); - - ui->spb_amp_adj = (GtkAdjustment*)gtk_adjustment_new( - 1.0, 0.1, 6.0, 0.1, 1.0, 0.0); - ui->spb_amp = gtk_spin_button_new(ui->spb_amp_adj, 0.1, 1); - - gtk_box_pack_start(GTK_BOX(ui->hbox), ui->darea, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(ui->hbox), ui->vbox, FALSE, FALSE, 4); - - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_speed, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_speed, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[0], FALSE, FALSE, 8); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_amp, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_amp, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[1], TRUE, FALSE, 8); - gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_pause, FALSE, FALSE, 2); - - g_signal_connect(G_OBJECT(ui->darea), "expose_event", - G_CALLBACK(on_expose_event), ui); - g_signal_connect(G_OBJECT(ui->spb_amp), "value-changed", - G_CALLBACK(on_cfg_changed), ui); - g_signal_connect(G_OBJECT(ui->spb_speed), "value-changed", - G_CALLBACK(on_cfg_changed), ui); - - *widget = ui->hbox; - - /* Send UIOn message to plugin, which will request state and enable message - transmission. */ - send_ui_enable(ui); - - return ui; + EgScopeUI* ui = (EgScopeUI*)calloc(1, sizeof(EgScopeUI)); + + if (!ui) { + fprintf(stderr, "EgScope.lv2 UI: out of memory\n"); + return NULL; + } + + ui->map = NULL; + *widget = NULL; + + if (!strcmp(plugin_uri, SCO_URI "#Mono")) { + ui->n_channels = 1; + } else if (!strcmp(plugin_uri, SCO_URI "#Stereo")) { + ui->n_channels = 2; + } else { + free(ui); + return NULL; + } + + for (int i = 0; features[i]; ++i) { + if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { + ui->map = (LV2_URID_Map*)features[i]->data; + } + } + + if (!ui->map) { + fprintf(stderr, "EgScope.lv2 UI: Host does not support urid:map\n"); + free(ui); + return NULL; + } + + // Initialize private data structure + ui->write = write_function; + ui->controller = controller; + + ui->vbox = NULL; + ui->hbox = NULL; + ui->darea = NULL; + ui->stride = 25; + ui->paused = false; + ui->rate = 48000; + + ui->chn[0].idx = 0; + ui->chn[0].sub = 0; + ui->chn[1].idx = 0; + ui->chn[1].sub = 0; + memset(ui->chn[0].data_min, 0, sizeof(float) * DAWIDTH); + memset(ui->chn[0].data_max, 0, sizeof(float) * DAWIDTH); + memset(ui->chn[1].data_min, 0, sizeof(float) * DAWIDTH); + memset(ui->chn[1].data_max, 0, sizeof(float) * DAWIDTH); + + map_sco_uris(ui->map, &ui->uris); + lv2_atom_forge_init(&ui->forge, ui->map); + + // Setup UI + ui->hbox = gtk_hbox_new(FALSE, 0); + ui->vbox = gtk_vbox_new(FALSE, 0); + + ui->darea = gtk_drawing_area_new(); + gtk_widget_set_size_request(ui->darea, DAWIDTH, DAHEIGHT * ui->n_channels); + + ui->lbl_speed = gtk_label_new("Samples/Pixel"); + ui->lbl_amp = gtk_label_new("Amplitude"); + + ui->sep[0] = gtk_hseparator_new(); + ui->sep[1] = gtk_label_new(""); + ui->btn_pause = gtk_toggle_button_new_with_label("Pause"); + + ui->spb_speed_adj = + (GtkAdjustment*)gtk_adjustment_new(25.0, 1.0, 1000.0, 1.0, 5.0, 0.0); + ui->spb_speed = gtk_spin_button_new(ui->spb_speed_adj, 1.0, 0); + + ui->spb_amp_adj = + (GtkAdjustment*)gtk_adjustment_new(1.0, 0.1, 6.0, 0.1, 1.0, 0.0); + ui->spb_amp = gtk_spin_button_new(ui->spb_amp_adj, 0.1, 1); + + gtk_box_pack_start(GTK_BOX(ui->hbox), ui->darea, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(ui->hbox), ui->vbox, FALSE, FALSE, 4); + + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_speed, FALSE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_speed, FALSE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[0], FALSE, FALSE, 8); + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_amp, FALSE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->spb_amp, FALSE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->sep[1], TRUE, FALSE, 8); + gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_pause, FALSE, FALSE, 2); + + g_signal_connect( + G_OBJECT(ui->darea), "expose_event", G_CALLBACK(on_expose_event), ui); + g_signal_connect( + G_OBJECT(ui->spb_amp), "value-changed", G_CALLBACK(on_cfg_changed), ui); + g_signal_connect( + G_OBJECT(ui->spb_speed), "value-changed", G_CALLBACK(on_cfg_changed), ui); + + *widget = ui->hbox; + + /* Send UIOn message to plugin, which will request state and enable message + transmission. */ + send_ui_enable(ui); + + return ui; } static void cleanup(LV2UI_Handle handle) { - EgScopeUI* ui = (EgScopeUI*)handle; - /* Send UIOff message to plugin, which will save state and disable message - * transmission. */ - send_ui_disable(ui); - gtk_widget_destroy(ui->darea); - free(ui); + EgScopeUI* ui = (EgScopeUI*)handle; + /* Send UIOff message to plugin, which will save state and disable message + * transmission. */ + send_ui_disable(ui); + gtk_widget_destroy(ui->darea); + free(ui); } static int recv_raw_audio(EgScopeUI* ui, const LV2_Atom_Object* obj) { - const LV2_Atom* chan_val = NULL; - const LV2_Atom* data_val = NULL; - - // clang-format off - const int n_props = lv2_atom_object_get( - obj, - ui->uris.channelID, &chan_val, - ui->uris.audioData, &data_val, - NULL); - // clang-format on - - if (n_props != 2 || - chan_val->type != ui->uris.atom_Int || - data_val->type != ui->uris.atom_Vector) { - // Object does not have the required properties with correct types - fprintf(stderr, "eg-scope.lv2 UI error: Corrupt audio message\n"); - return 1; - } - - // Get the values we need from the body of the property value atoms - const int32_t chn = ((const LV2_Atom_Int*)chan_val)->body; - const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*)data_val; - if (vec->body.child_type != ui->uris.atom_Float) { - return 1; // Vector has incorrect element type - } - - // Number of elements = (total size - header size) / element size - const size_t n_elem = ((data_val->size - sizeof(LV2_Atom_Vector_Body)) - / sizeof(float)); - - // Float elements immediately follow the vector body header - const float* data = (const float*)(&vec->body + 1); - - // Update display - update_scope(ui, chn, n_elem, data); - return 0; + const LV2_Atom* chan_val = NULL; + const LV2_Atom* data_val = NULL; + const int n_props = lv2_atom_object_get( + obj, ui->uris.channelID, &chan_val, ui->uris.audioData, &data_val, NULL); + + if (n_props != 2 || chan_val->type != ui->uris.atom_Int || + data_val->type != ui->uris.atom_Vector) { + // Object does not have the required properties with correct types + fprintf(stderr, "eg-scope.lv2 UI error: Corrupt audio message\n"); + return 1; + } + + // Get the values we need from the body of the property value atoms + const int32_t chn = ((const LV2_Atom_Int*)chan_val)->body; + const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*)data_val; + if (vec->body.child_type != ui->uris.atom_Float) { + return 1; // Vector has incorrect element type + } + + // Number of elements = (total size - header size) / element size + const size_t n_elem = + ((data_val->size - sizeof(LV2_Atom_Vector_Body)) / sizeof(float)); + + // Float elements immediately follow the vector body header + const float* data = (const float*)(&vec->body + 1); + + // Update display + update_scope(ui, chn, n_elem, data); + return 0; } static int recv_ui_state(EgScopeUI* ui, const LV2_Atom_Object* obj) { - const LV2_Atom* spp_val = NULL; - const LV2_Atom* amp_val = NULL; - const LV2_Atom* rate_val = NULL; - - // clang-format off - const int n_props = lv2_atom_object_get( - obj, - ui->uris.ui_spp, &spp_val, - ui->uris.ui_amp, &_val, - ui->uris.param_sampleRate, &rate_val, - NULL); - // clang-format on - - if (n_props != 3 || - spp_val->type != ui->uris.atom_Int || - amp_val->type != ui->uris.atom_Float || - rate_val->type != ui->uris.atom_Float) { - // Object does not have the required properties with correct types - fprintf(stderr, "eg-scope.lv2 UI error: Corrupt state message\n"); - return 1; - } - - // Get the values we need from the body of the property value atoms - const int32_t spp = ((const LV2_Atom_Int*)spp_val)->body; - const float amp = ((const LV2_Atom_Float*)amp_val)->body; - const float rate = ((const LV2_Atom_Float*)rate_val)->body; - - // Disable transmission and update UI - ui->updating = true; - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_speed), spp); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_amp), amp); - ui->updating = false; - ui->rate = rate; - - return 0; + const LV2_Atom* spp_val = NULL; + const LV2_Atom* amp_val = NULL; + const LV2_Atom* rate_val = NULL; + + const int n_props = lv2_atom_object_get(obj, + ui->uris.ui_spp, + &spp_val, + ui->uris.ui_amp, + &_val, + ui->uris.param_sampleRate, + &rate_val, + NULL); + + if (n_props != 3 || spp_val->type != ui->uris.atom_Int || + amp_val->type != ui->uris.atom_Float || + rate_val->type != ui->uris.atom_Float) { + // Object does not have the required properties with correct types + fprintf(stderr, "eg-scope.lv2 UI error: Corrupt state message\n"); + return 1; + } + + // Get the values we need from the body of the property value atoms + const int32_t spp = ((const LV2_Atom_Int*)spp_val)->body; + const float amp = ((const LV2_Atom_Float*)amp_val)->body; + const float rate = ((const LV2_Atom_Float*)rate_val)->body; + + // Disable transmission and update UI + ui->updating = true; + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_speed), spp); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ui->spb_amp), amp); + ui->updating = false; + ui->rate = rate; + + return 0; } /** @@ -650,35 +642,33 @@ port_event(LV2UI_Handle handle, uint32_t format, const void* buffer) { - EgScopeUI* ui = (EgScopeUI*)handle; - const LV2_Atom* atom = (const LV2_Atom*)buffer; - - /* Check type of data received - * - format == 0: Control port event (float) - * - format > 0: Message (atom) - */ - if (format == ui->uris.atom_eventTransfer && - lv2_atom_forge_is_object_type(&ui->forge, atom->type)) { - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; - if (obj->body.otype == ui->uris.RawAudio) { - recv_raw_audio(ui, obj); - } else if (obj->body.otype == ui->uris.ui_State) { - recv_ui_state(ui, obj); - } - } + EgScopeUI* ui = (EgScopeUI*)handle; + const LV2_Atom* atom = (const LV2_Atom*)buffer; + + /* Check type of data received + * - format == 0: Control port event (float) + * - format > 0: Message (atom) + */ + if (format == ui->uris.atom_eventTransfer && + lv2_atom_forge_is_object_type(&ui->forge, atom->type)) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; + if (obj->body.otype == ui->uris.RawAudio) { + recv_raw_audio(ui, obj); + } else if (obj->body.otype == ui->uris.ui_State) { + recv_ui_state(ui, obj); + } + } } -static const LV2UI_Descriptor descriptor = { - SCO_URI "#ui", - instantiate, - cleanup, - port_event, - NULL -}; +static const LV2UI_Descriptor descriptor = {SCO_URI "#ui", + instantiate, + cleanup, + port_event, + NULL}; LV2_SYMBOL_EXPORT const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) { - return index == 0 ? &descriptor : NULL; + return index == 0 ? &descriptor : NULL; } diff --git a/plugins/eg-scope.lv2/uris.h b/plugins/eg-scope.lv2/uris.h index cba978f..8873786 100644 --- a/plugins/eg-scope.lv2/uris.h +++ b/plugins/eg-scope.lv2/uris.h @@ -24,47 +24,47 @@ #define SCO_URI "http://lv2plug.in/plugins/eg-scope" typedef struct { - // URIs defined in LV2 specifications - LV2_URID atom_Vector; - LV2_URID atom_Float; - LV2_URID atom_Int; - LV2_URID atom_eventTransfer; - LV2_URID param_sampleRate; + // URIs defined in LV2 specifications + LV2_URID atom_Vector; + LV2_URID atom_Float; + LV2_URID atom_Int; + LV2_URID atom_eventTransfer; + LV2_URID param_sampleRate; - /* URIs defined for this plugin. It is best to re-use existing URIs as - much as possible, but plugins may need more vocabulary specific to their - needs. These are used as types and properties for plugin:UI - communication, as well as for saving state. */ - LV2_URID RawAudio; - LV2_URID channelID; - LV2_URID audioData; - LV2_URID ui_On; - LV2_URID ui_Off; - LV2_URID ui_State; - LV2_URID ui_spp; - LV2_URID ui_amp; + /* URIs defined for this plugin. It is best to re-use existing URIs as + much as possible, but plugins may need more vocabulary specific to their + needs. These are used as types and properties for plugin:UI + communication, as well as for saving state. */ + LV2_URID RawAudio; + LV2_URID channelID; + LV2_URID audioData; + LV2_URID ui_On; + LV2_URID ui_Off; + LV2_URID ui_State; + LV2_URID ui_spp; + LV2_URID ui_amp; } ScoLV2URIs; static inline void map_sco_uris(LV2_URID_Map* map, ScoLV2URIs* uris) { - uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); - uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); - uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); - uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->param_sampleRate = map->map(map->handle, LV2_PARAMETERS__sampleRate); + uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector); + uris->atom_Float = map->map(map->handle, LV2_ATOM__Float); + uris->atom_Int = map->map(map->handle, LV2_ATOM__Int); + uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); + uris->param_sampleRate = map->map(map->handle, LV2_PARAMETERS__sampleRate); - /* Note the convention that URIs for types are capitalized, and URIs for - everything else (mainly properties) are not, just as in LV2 - specifications. */ - uris->RawAudio = map->map(map->handle, SCO_URI "#RawAudio"); - uris->audioData = map->map(map->handle, SCO_URI "#audioData"); - uris->channelID = map->map(map->handle, SCO_URI "#channelID"); - uris->ui_On = map->map(map->handle, SCO_URI "#UIOn"); - uris->ui_Off = map->map(map->handle, SCO_URI "#UIOff"); - uris->ui_State = map->map(map->handle, SCO_URI "#UIState"); - uris->ui_spp = map->map(map->handle, SCO_URI "#ui-spp"); - uris->ui_amp = map->map(map->handle, SCO_URI "#ui-amp"); + /* Note the convention that URIs for types are capitalized, and URIs for + everything else (mainly properties) are not, just as in LV2 + specifications. */ + uris->RawAudio = map->map(map->handle, SCO_URI "#RawAudio"); + uris->audioData = map->map(map->handle, SCO_URI "#audioData"); + uris->channelID = map->map(map->handle, SCO_URI "#channelID"); + uris->ui_On = map->map(map->handle, SCO_URI "#UIOn"); + uris->ui_Off = map->map(map->handle, SCO_URI "#UIOff"); + uris->ui_State = map->map(map->handle, SCO_URI "#UIState"); + uris->ui_spp = map->map(map->handle, SCO_URI "#ui-spp"); + uris->ui_amp = map->map(map->handle, SCO_URI "#ui-amp"); } -#endif /* SCO_URIS_H */ +#endif /* SCO_URIS_H */ |