From 982c3cb2aa4a15b8ba3b9cbfb1b9fa60b925f967 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 28 Jul 2020 20:06:22 +0200 Subject: [PATCH 1/9] Slight C conformance --- source/alo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/alo.c b/source/alo.c index 2e0b357..0e702a5 100644 --- a/source/alo.c +++ b/source/alo.c @@ -154,7 +154,7 @@ typedef struct { State state[NUM_LOOPS]; // we're recording, playing or not playing bool button_state[NUM_LOOPS]; - bool midi_control = false; + bool midi_control; uint32_t button_time[NUM_LOOPS]; // last time button was pressed float* loops[NUM_LOOPS]; // pointers to memory for playing loops @@ -198,6 +198,8 @@ instantiate(const LV2_Descriptor* descriptor, self->loop_beats = 0; self->current_bb = 0; self->current_lb = 0; + + self->midi_control = false; self->recording = (float *)calloc(STORAGE_MEMORY, sizeof(float)); From e8e3cb6a51ea7a61d032b80d6c64d23e91db291d Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 28 Jul 2020 20:15:39 +0200 Subject: [PATCH 2/9] Add sensible defaults to avoid arithmetic errors for some hosts --- source/alo.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/alo.c b/source/alo.c index 0e702a5..8bdad5b 100644 --- a/source/alo.c +++ b/source/alo.c @@ -85,6 +85,11 @@ static const size_t STORAGE_MEMORY = 2880000; static const int NUM_LOOPS = 6; static const bool LOG_ENABLED = false; +#define DEFAULT_BEATS_PER_BAR 4 +#define DEFAULT_NUM_BARS 4 +#define DEFAULT_BPM 120 +#define DEFAULT_PER_BEAT_LOOPS 0 + void log(const char *message, ...) { if (!LOG_ENABLED) { @@ -194,10 +199,13 @@ instantiate(const LV2_Descriptor* descriptor, { Alo* self = (Alo*)calloc(1, sizeof(Alo)); self->rate = rate; - self->bpb = 4; - self->loop_beats = 0; + self->bpb = DEFAULT_BEATS_PER_BAR; + self->loop_beats = DEFAULT_BEATS_PER_BAR * DEFAULT_NUM_BARS; + self->bpm = DEFAULT_BPM; + self->loop_samples = self->loop_beats * self->rate * 60.0f / self->bpm; self->current_bb = 0; self->current_lb = 0; + self->pb_loops = DEFAULT_PER_BEAT_LOOPS; self->midi_control = false; From e8f4d5d62d13f6e4898258c067b3d2a17372a02d Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 28 Jul 2020 23:28:41 +0200 Subject: [PATCH 3/9] The metronome click was leaked --- source/alo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/alo.c b/source/alo.c index 8bdad5b..09e9245 100644 --- a/source/alo.c +++ b/source/alo.c @@ -679,6 +679,7 @@ cleanup(LV2_Handle instance) for (int i = 0; i < NUM_LOOPS; i++) { free(self->loops[i]); } + free(self->wave); free(self->recording); free(self); } From 9cd9bbddb55592b0719301a4fd8ccd64defb8a13 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 29 Jul 2020 01:19:50 +0200 Subject: [PATCH 4/9] Smoother metronome --- source/alo.c | 137 +++++++++++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 60 deletions(-) diff --git a/source/alo.c b/source/alo.c index 09e9245..b77aa0a 100644 --- a/source/alo.c +++ b/source/alo.c @@ -90,6 +90,9 @@ static const bool LOG_ENABLED = false; #define DEFAULT_BPM 120 #define DEFAULT_PER_BEAT_LOOPS 0 +#define HIGH_BEAT_FREQ 880 +#define LOW_BEAT_FREQ 440 + void log(const char *message, ...) { if (!LOG_ENABLED) { @@ -153,6 +156,7 @@ typedef struct { uint32_t loop_samples; // loop length in samples uint32_t current_bb; // which beat of the bar we are on (1, 2, 3, 0) uint32_t current_lb; // which beat of the loop we are on (1, 2, ...) + float current_position; uint32_t pb_loops; // number of loops in per-beat mode @@ -172,15 +176,35 @@ typedef struct { uint32_t elapsed_len; // Frames since the start of the last click uint32_t wave_offset; // Current play offset in the wave - // One cycle of a sine wave - float* wave; - uint32_t wave_len; - - // Envelope parameters - uint32_t attack_len; - uint32_t decay_len; + // Click beats + float* high_beat; + float* low_beat; + uint32_t beat_len; + uint32_t high_beat_offset; + uint32_t low_beat_offset; } Alo; +void +sine_pulse(float* target, double frequency, double sample_rate, uint32_t num_samples) +{ + const uint32_t half_length = (uint32_t)(num_samples * 0.5f); + const float amplitude_step = 1.0f / (float)half_length; + const double sample_sin_step = 2 * M_PI * frequency / sample_rate; + float amplitude = 0.0f; + + for (uint32_t i = 0; i < half_length; ++i) { + amplitude = fmin(amplitude + amplitude_step, 1.0f); + printf("%f\n", amplitude); + target[i] = 0.5f * amplitude * sin(i * sample_sin_step); + } + + for (uint32_t i = half_length; i < num_samples; ++i) { + amplitude = fmax(amplitude - amplitude_step, 0.0f); + printf("%f\n", amplitude); + target[i] = 0.5f * amplitude * sin(i * sample_sin_step); + } +} + /** The `instantiate()` function is called by the host to create a new plugin instance. The host passes the plugin descriptor, sample rate, and bundle @@ -205,6 +229,7 @@ instantiate(const LV2_Descriptor* descriptor, self->loop_samples = self->loop_beats * self->rate * 60.0f / self->bpm; self->current_bb = 0; self->current_lb = 0; + self->current_position = 0.0f; self->pb_loops = DEFAULT_PER_BEAT_LOOPS; self->midi_control = false; @@ -247,14 +272,14 @@ instantiate(const LV2_Descriptor* descriptor, uris->time_beatsPerBar = map->map(map->handle, LV2_TIME__beatsPerBar); uris->midi_MidiEvent = map->map (map->handle, LV2_MIDI__MidiEvent); - // 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); - } + // Generate pulses for the metronome + self->beat_len = (uint32_t)(0.02f * self->rate); + self->high_beat = (float*)malloc(self->beat_len * sizeof(float)); + self->low_beat = (float*)malloc(self->beat_len * sizeof(float)); + sine_pulse(self->high_beat, HIGH_BEAT_FREQ, self->rate, self->beat_len); + sine_pulse(self->low_beat, LOW_BEAT_FREQ, self->rate, self->beat_len); + self->high_beat_offset = self->beat_len; + self->low_beat_offset = self->beat_len; return (LV2_Handle)self; } @@ -406,15 +431,15 @@ update_position(Alo* self, const LV2_Atom_Object* obj) if (beat && beat->type == uris->atom_Float) { // Received a beat position, synchronise // const float frames_per_beat = 60.0f / self->bpm * self->rate; - const float bar_beat = ((LV2_Atom_Float*)beat)->body; + self->current_position = ((LV2_Atom_Float*)beat)->body; // const float beat_beats = bar_beats - floorf(bar_beats); - if (self->current_bb != (uint32_t)bar_beat) { + if (self->current_bb != (uint32_t)self->current_position) { // we are onto the next beat - self->current_bb = (uint32_t)bar_beat; + self->current_bb = (uint32_t)self->current_position; if (self->current_lb == self->loop_beats) { self->current_lb = 0; } - log("Beat:[%d][%d] index[%d] beat[%G]", self->current_bb, self->current_lb, self->loop_index, bar_beat); + log("Beat:[%d][%d] index[%d] beat[%G]\n", self->current_bb, self->current_lb, self->loop_index, self->current_position); self->current_lb += 1; } } @@ -460,45 +485,16 @@ static void click(Alo* 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->clickstate) { - 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->clickstate = 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 - ((self->elapsed_len - self->attack_len) / - (float)self->decay_len)); - if (self->elapsed_len >= self->attack_len + self->decay_len) { - self->clickstate = STATE_SILENT; - } - break; - case STATE_SILENT: - case STATE_OFF: - output[i] = 0.0f; + for (uint32_t idx = begin; idx < end; idx++) { + if (self->high_beat_offset < self->beat_len) { + output[idx] += self->high_beat[self->high_beat_offset]; + self->high_beat_offset++; } - // 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->clickstate = STATE_ATTACK; - self->elapsed_len = 0; + if (self->low_beat_offset < self->beat_len) { + output[idx] += self->low_beat[self->low_beat_offset]; + self->low_beat_offset++; } } } @@ -589,7 +585,7 @@ run(LV2_Handle instance, uint32_t n_samples) // Play the click for the time slice from last_t until now if (self->clickstate != STATE_OFF) { if (self->clickstate != STATE_SILENT) { - click(self, last_t, ev->time.frames); + // click(self, last_t, ev->time.frames); } // Update time for next iteration and move to next event last_t = ev->time.frames; @@ -610,10 +606,30 @@ run(LV2_Handle instance, uint32_t n_samples) } } + const float previous_beat = floorf(self->current_position); + self->current_position += n_samples / self->rate / 60.0f * self->bpm; + self->current_position = fmodf(self->current_position, self->bpb); + const float new_beat = floorf(self->current_position); + if (self->clickstate != STATE_OFF) { - // Play for remainder of cycle - click(self, last_t, n_samples); - } + if (new_beat != previous_beat) { + const uint32_t sample_offset = + (uint32_t)((self->current_position - new_beat) * self->rate); + + click(self, self->ports.output, 0, sample_offset); + + if (new_beat == 0.0f) { + self->high_beat_offset = 0; + } else { + self->low_beat_offset = 0; + } + + click(self, self->ports.output, sample_offset, n_samples); + } + else { + click(self, self->ports.output, 0, n_samples); + } + } if (self->midi_control == false) { for (int i = 0; i < NUM_LOOPS; i++) { @@ -679,7 +695,8 @@ cleanup(LV2_Handle instance) for (int i = 0; i < NUM_LOOPS; i++) { free(self->loops[i]); } - free(self->wave); + free(self->low_beat); + free(self->high_beat); free(self->recording); free(self); } From 07e13555ba50bfbc28a3f9c95a797f1248274f68 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 29 Jul 2020 01:25:19 +0200 Subject: [PATCH 5/9] Respect the transport and the click port --- source/alo.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/source/alo.c b/source/alo.c index b77aa0a..bda58f9 100644 --- a/source/alo.c +++ b/source/alo.c @@ -194,13 +194,11 @@ sine_pulse(float* target, double frequency, double sample_rate, uint32_t num_sam for (uint32_t i = 0; i < half_length; ++i) { amplitude = fmin(amplitude + amplitude_step, 1.0f); - printf("%f\n", amplitude); target[i] = 0.5f * amplitude * sin(i * sample_sin_step); } for (uint32_t i = half_length; i < num_samples; ++i) { amplitude = fmax(amplitude - amplitude_step, 0.0f); - printf("%f\n", amplitude); target[i] = 0.5f * amplitude * sin(i * sample_sin_step); } } @@ -362,13 +360,6 @@ reset(Alo* self) self->phrase_start[i] = 0; log("STATE: RECORDING (reset) [%d]", i); } - - self->clickstate = STATE_OFF; - uint32_t click = (uint32_t)floorf(*(self->ports.click)); - - if (click != 0) { - self->clickstate = STATE_SILENT; - } } /** @@ -611,12 +602,12 @@ run(LV2_Handle instance, uint32_t n_samples) self->current_position = fmodf(self->current_position, self->bpb); const float new_beat = floorf(self->current_position); - if (self->clickstate != STATE_OFF) { + if (*self->ports.click && self->speed) { if (new_beat != previous_beat) { const uint32_t sample_offset = (uint32_t)((self->current_position - new_beat) * self->rate); - click(self, self->ports.output, 0, sample_offset); + click(self, 0, sample_offset); if (new_beat == 0.0f) { self->high_beat_offset = 0; @@ -624,10 +615,10 @@ run(LV2_Handle instance, uint32_t n_samples) self->low_beat_offset = 0; } - click(self, self->ports.output, sample_offset, n_samples); + click(self, sample_offset, n_samples); } else { - click(self, self->ports.output, 0, n_samples); + click(self, 0, n_samples); } } From 96fb831db5613dcf044b691b648812fddbec4baa Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 29 Jul 2020 01:28:15 +0200 Subject: [PATCH 6/9] Remove warnings and dead code --- source/alo.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/source/alo.c b/source/alo.c index bda58f9..37f8660 100644 --- a/source/alo.c +++ b/source/alo.c @@ -507,15 +507,13 @@ run(LV2_Handle instance, uint32_t n_samples) float* const recording = self->recording; self->threshold = dbToFloat(*self->ports.threshold); - uint32_t last_t = 0; - for (uint32_t pos = 0; pos < n_samples; pos++) { // recording always happens sample = input[pos]; output[pos] = 0; // log("Sample: %.9f", sample); recording[self->loop_index] = sample; - for (int i = 0; i < NUM_LOOPS; i++) { + for (uint32_t i = 0; i < NUM_LOOPS; i++) { if (self->phrase_start[i] && self->phrase_start[i] == self->loop_index) { if (self->button_state[i]) { @@ -573,15 +571,6 @@ run(LV2_Handle instance, uint32_t n_samples) ev = lv2_atom_sequence_next(ev)) { - // Play the click for the time slice from last_t until now - if (self->clickstate != STATE_OFF) { - if (self->clickstate != STATE_SILENT) { - // click(self, last_t, ev->time.frames); - } - // Update time for next iteration and move to next event - last_t = ev->time.frames; - } - if (ev->body.type == self->uris.midi_MidiEvent) { const uint8_t* const msg = (const uint8_t*)(ev + 1); int i = msg[1] - (uint32_t)floorf(*(self->ports.midi_base)); From a503024d4cc049e7e9419d8204e1d7596bb667db Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 29 Jul 2020 11:27:28 +0200 Subject: [PATCH 7/9] Add a level output --- source/alo.c | 88 ++++++++++++++++++++++++++++++++++++++++++ source/alo.lv2/alo.ttl | 9 +++++ 2 files changed, 97 insertions(+) diff --git a/source/alo.c b/source/alo.c index 37f8660..304ac80 100644 --- a/source/alo.c +++ b/source/alo.c @@ -65,6 +65,7 @@ typedef enum { ALO_MIDI_BASE = 12, ALO_PER_BEAT_LOOPS = 13, ALO_CLICK = 14, + ALO_LEVEL = 15, } PortIndex; typedef enum { @@ -93,6 +94,12 @@ static const bool LOG_ENABLED = false; #define HIGH_BEAT_FREQ 880 #define LOW_BEAT_FREQ 440 +#define LEVEL_TAU 0.1f +#define LEVEL_MIN -90 +#define LEVEL_MAX 24 + + + void log(const char *message, ...) { if (!LOG_ENABLED) { @@ -122,6 +129,53 @@ static float dbToFloat(float db) return powf(10.0f, db * 0.05f); } +/// +/// Convert an input floating point power to a dB value (pow2db) +/// +static float pow2db(float power) +{ + return 10 * log10f(power); +} + +/// +/// Convert an input floating point power to a dB value (pow2db) +/// +static float db2pow(float db) +{ + return powf(10.0f, db * 0.1f); +} + +static const float level_min = db2pow(LEVEL_MIN); + +/** + * One-pole filter helper structure. + * To reset the filter, put the state to 0. + * To set the gain at a specific value of the time constant tau, use + * gain = tan( 1 / (2 * tau * samplerate) ). + * + */ +typedef struct { + float state; + float gain; +} one_pole_filter_t; + +/** + * @brief One tick of the one pole filter lowpass output + * + * @param filter + * @param input + * @return float + */ +static float +one_pole_lowpass(one_pole_filter_t* filter, float input) +{ + const float intermediate = filter->gain * (input - filter->state); + const float output = intermediate + filter->state; + filter->state = output + intermediate; + return output; +} + + /** Every plugin defines a private structure for the plugin instance. All data associated with a plugin instance is stored here, and is available to @@ -143,6 +197,7 @@ typedef struct { float* midi_base; // start note for midi control of loops float* pb_loops; // number of loops in per-beat mode float* click; // click mode on/off + float* level; // input level LV2_Atom_Sequence* midiin; // midi input } ports; @@ -182,6 +237,9 @@ typedef struct { uint32_t beat_len; uint32_t high_beat_offset; uint32_t low_beat_offset; + + // Level filter + one_pole_filter_t level_filter; } Alo; void @@ -279,6 +337,12 @@ instantiate(const LV2_Descriptor* descriptor, self->high_beat_offset = self->beat_len; self->low_beat_offset = self->beat_len; + // Initialize the level filter + self->level_filter = { + .state = 0.0f, + .gain = tanf(0.5f / (self->rate * LEVEL_TAU)) + }; + return (LV2_Handle)self; } @@ -334,6 +398,10 @@ connect_port(LV2_Handle instance, self->ports.click = (float*)data; log("Connect ALO_CLICK %d %d", port); break; + case ALO_LEVEL: + self->ports.level = (float*)data; + log("Connect ALO_LEVEL %d %d", port); + break; default: int loop = port - 4; self->ports.loops[loop] = (float*)data; @@ -490,6 +558,24 @@ click(Alo* self, uint32_t begin, uint32_t end) } } +static void +update_level(Alo* self, uint32_t n_samples) +{ + const float* const input = self->ports.input; + float* const level_output = self->ports.level; + + float output = level_min; + for (unsigned i = 0; i < n_samples; ++i) { + output = one_pole_lowpass(&self->level_filter, input[i] * input[i]); + } + + float output_db = pow2db(output); + output_db = fmax(LEVEL_MIN, output_db); + output_db = fmin(LEVEL_MAX, output_db); + + printf("Level %f dB\n", output_db); + *level_output = output_db; +} /** The `run()` method is the main process function of the plugin. It processes @@ -507,6 +593,8 @@ run(LV2_Handle instance, uint32_t n_samples) float* const recording = self->recording; self->threshold = dbToFloat(*self->ports.threshold); + update_level(self, n_samples); + for (uint32_t pos = 0; pos < n_samples; pos++) { // recording always happens sample = input[pos]; diff --git a/source/alo.lv2/alo.ttl b/source/alo.lv2/alo.ttl index 62dc01a..615a904 100644 --- a/source/alo.lv2/alo.ttl +++ b/source/alo.lv2/alo.ttl @@ -181,4 +181,13 @@ lv2:port lv2:minimum 0; lv2:maximum 1; lv2:portProperty lv2:integer, lv2:toggled; +], +[ + a lv2:ControlPort, lv2:OutputPort; + lv2:index 15; + lv2:symbol "level"; + lv2:name "Level"; + lv2:default -40; + lv2:minimum -90; + lv2:maximum 24; ]. From e02228589f6c668c74cd958f47c12282d6bf6aba Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 29 Jul 2020 14:54:48 +0100 Subject: [PATCH 8/9] Use the output level port for the threshold --- source/alo.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/alo.c b/source/alo.c index 304ac80..f77a849 100644 --- a/source/alo.c +++ b/source/alo.c @@ -573,7 +573,6 @@ update_level(Alo* self, uint32_t n_samples) output_db = fmax(LEVEL_MIN, output_db); output_db = fmin(LEVEL_MAX, output_db); - printf("Level %f dB\n", output_db); *level_output = output_db; } @@ -591,7 +590,6 @@ run(LV2_Handle instance, uint32_t n_samples) float sample = 0.0; float* const output = self->ports.output; float* const recording = self->recording; - self->threshold = dbToFloat(*self->ports.threshold); update_level(self, n_samples); @@ -635,7 +633,7 @@ run(LV2_Handle instance, uint32_t n_samples) if (self->state[i] == STATE_RECORDING && self->button_state[i]) { loop[self->loop_index] = sample; if (self->phrase_start[i] == 0 && self->speed != 0) { - if (fabs(sample) > self->threshold) { + if (*self->ports.level > *self->ports.threshold) { self->phrase_start[i] = self->loop_index; log("[%d]>>> DETECTED PHRASE START [%d]<<<", i, self->loop_index); } From c4120d68b35af4e66d74c4f14ddd35331d350f96 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 29 Jul 2020 14:55:34 +0100 Subject: [PATCH 9/9] Monitor the input level on the mod UI --- source/alo.lv2/modgui.ttl | 6 ++- source/alo.lv2/modgui/icon-alo.html | 19 ++++---- source/alo.lv2/modgui/script-alo.js | 15 ++++++ source/alo.lv2/modgui/stylesheet-alo.css | 62 +++++++++++++++++++++--- 4 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 source/alo.lv2/modgui/script-alo.js diff --git a/source/alo.lv2/modgui.ttl b/source/alo.lv2/modgui.ttl index 0302e27..8358562 100644 --- a/source/alo.lv2/modgui.ttl +++ b/source/alo.lv2/modgui.ttl @@ -8,6 +8,7 @@ modgui:stylesheet ; modgui:screenshot ; modgui:thumbnail ; + modgui:javascript ; modgui:brand "devcurmudgeon" ; modgui:label "ALO" ; modgui:model "combo-model-001" ; @@ -49,4 +50,7 @@ lv2:symbol "bars" ; lv2:name "Bars" ; ] ; -] . \ No newline at end of file + modgui:monitoredOutputs [ + lv2:symbol "level" ; + ] ; +] . diff --git a/source/alo.lv2/modgui/icon-alo.html b/source/alo.lv2/modgui/icon-alo.html index 8a35706..b83957f 100644 --- a/source/alo.lv2/modgui/icon-alo.html +++ b/source/alo.lv2/modgui/icon-alo.html @@ -45,10 +45,10 @@
-
+
-
{{name}}
-
60.000
+
{{name}}
+
60.000
{{/controls.6}} @@ -59,9 +59,10 @@
-
-
{{name}}
-
40.000
+
+ {{name}} + 40.000 + Input level-90.000
{{/controls.7}}
@@ -71,9 +72,9 @@
-
-
{{name}}
-
2.000
+
+
{{name}}
+
2.000
{{/controls.8}}
diff --git a/source/alo.lv2/modgui/script-alo.js b/source/alo.lv2/modgui/script-alo.js new file mode 100644 index 0000000..150c92a --- /dev/null +++ b/source/alo.lv2/modgui/script-alo.js @@ -0,0 +1,15 @@ +function (event) { + + function handle_event (symbol, value) { + switch (symbol) { + case 'level': + event.icon.find ('[mod-role=level]').text (value.toFixed(2)); + default: + break; + } + } + + if (event.type == 'change') { + handle_event (event.symbol, event.value); + } +} \ No newline at end of file diff --git a/source/alo.lv2/modgui/stylesheet-alo.css b/source/alo.lv2/modgui/stylesheet-alo.css index 5c27a09..65d1870 100644 --- a/source/alo.lv2/modgui/stylesheet-alo.css +++ b/source/alo.lv2/modgui/stylesheet-alo.css @@ -18,11 +18,11 @@ color: #3c3; font-size: 18px; font-weight: bold; - left: 80px; + left: 40px; line-height: 1; margin: 0; position: absolute; - top: 23px; + top: 13px; } /* PLUGIN'S NAME */ @@ -33,8 +33,8 @@ line-height: 1; margin: 0; position: absolute; - right: 80px; - top: 23px; + right: 50px; + top: 10px; } /* CONTROLS */ @@ -52,7 +52,7 @@ left: 40px; position: absolute; right: 40px; - top: 47px; + top: 40px; } .alo{{{cns}}} .mod-control-group > div { @@ -133,8 +133,9 @@ height:50px; margin-top: 10px; position:relative; - width: 58px; + width: 60px; } + .alo{{{cns}}} .mod-control-group .mod-knob .mod-knob-image-wrapper { padding: 1px; } @@ -165,6 +166,55 @@ text-align: center; } + +/* KNOB VALUES */ +.alo{{{cns}}} .mod-control-group .midi-base { + display:inline-block; + float: left; + height: 50px; + margin-top: 10px; + position:relative; + width: 58px; +} + +.alo{{{cns}}} .mod-control-group .threshold { + display:inline-block; + float: left; + height:50px; + margin-top: 5px; + position:relative; + width: 80px; +} + +.alo{{{cns}}} .mod-control-group .bars { + display:inline-block; + float: left; + height:50px; + margin-top: 10px; + position:relative; + width: 35px; +} + +.alo{{{cns}}} .mod-control-group .knob-title { + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + color: #fff; + display:block; + font-size: 12px; + font-weight: bold; + height:18px; + text-align: center; +} + +.alo{{{cns}}} .mod-control-group .knob-value { + border-radius: 0 0 8px 8px; + display:block; + color: #fff; + font-size: 12px; + font-weight: bold; + text-align: center; +} + + /* ENUMERATED LIST */ .alo{{{cns}}} .mod-enumerated { color:#fff;