From 0626dbb8320acfb973ef32105c8a37c3995df4ab Mon Sep 17 00:00:00 2001 From: Alexandros Theodotou Date: Fri, 7 Feb 2020 16:24:05 +0000 Subject: [PATCH] let plugin know when UI is on or off --- src/zlfo.c | 234 ++++++++++++++++++++++++++++++++++++++++++--- src/zlfo_common.h | 60 +++++++++++- src/zlfo_ttl_gen.c | 10 +- src/zlfo_ui.c | 48 ++++++++-- 4 files changed, 326 insertions(+), 26 deletions(-) diff --git a/src/zlfo.c b/src/zlfo.c index c3b8f99..8df8f34 100644 --- a/src/zlfo.c +++ b/src/zlfo.c @@ -62,9 +62,43 @@ typedef struct ZLFO float * rnd_out; float * custom_out; + /** Transport speed (0.0 is stopped, 1.0 is + * normal playback, -1.0 is reverse playback, + * etc.). */ + float speed; + + float bpm; + + /** Frames per beat. */ + float frames_per_beat; + + int beat_unit; + + /** This is how far we are inside a beat, from 0.0 + * to 1.0. */ + float beat_offset; + + /** + * Effective frequency. + * + * This is either the free-running frequency, + * or the frequency corresponding to the current + * sync rate. + */ + float effective_freq; + /** Frequency during the last run. */ float last_freq; + /** + * Whether the plugin was freerunning in the + * last cycle. + * + * This is used to detect changes in freerunning/ + * sync. + */ + int was_freerunning; + /** Plugin samplerate. */ double samplerate; @@ -78,6 +112,9 @@ typedef struct ZLFO */ long current_sample; + /** Global current sample in the host. */ + long host_current_sample; + /** Atom forge. */ LV2_Atom_Forge forge; LV2_Atom_Forge_Frame notify_frame; @@ -143,8 +180,7 @@ instantiate ( /* map uris */ map_uris (self->map, &self->uris); - lv2_atom_forge_init ( - &self->forge, self->map); + lv2_atom_forge_init (&self->forge, self->map); return (LV2_Handle) self; } @@ -284,10 +320,49 @@ send_messages_to_ui ( lv2_atom_forge_pop (&self->forge, &frame); } +/** + * Gets the current frequency. + */ +static float +get_freq ( + ZLFO * self) +{ + if (*self->freerun > 0.f) + { + return *self->freq; + } + else + { + /* if host does not send position info, + * send frequency back instead */ + if (self->beat_unit == 0) + { + log_error ( + self->log, &self->uris, + "Have not received time info from host " + "yet. Beat unit is unknown."); + return * self->freq; + } + + /* bpm / (60 * BU * sync note) */ + float sync_note = + sync_rate_to_float ( + (SyncRate) *self->sync_rate, + (SyncRateType) *self->sync_rate_type); + return + self->bpm / + (60.f * self->beat_unit * sync_note); + } +} + static void recalc_multipliers ( ZLFO * self) { + self->effective_freq = get_freq (self); + + fprintf (stderr, "effective freq is %f\n", (double) self->effective_freq); + /* * F = frequency * X = samples processed @@ -309,7 +384,8 @@ recalc_multipliers ( * ? radians = X samples * sine_multiplier */ self->sine_multiplier = - (*self->freq / (float) self->samplerate) * + (self->effective_freq / + (float) self->samplerate) * 2.f * PI; /* @@ -332,12 +408,43 @@ recalc_multipliers ( * ? value = ((X samples * saw_multiplier) % 1) * 2 - 1 */ self->saw_up_multiplier = - (*self->freq / (float) self->samplerate); + (self->effective_freq / + (float) self->samplerate); - self->period_size = - (uint32_t) - ((float) self->samplerate / * self->freq); - self->current_sample = 0; + if (*self->freerun > 0.0001f) /* freerunning */ + { + self->period_size = + (uint32_t) + ((float) self->samplerate / + self->effective_freq); + self->current_sample = 0; + } + else /* synced */ + { + if (self->beat_unit == 0) + { + /* set reasonable values if host does not + * send time info */ + self->period_size = + (uint32_t) + ((float) self->samplerate / + self->effective_freq); + self->current_sample = 0; + } + else + { + self->period_size = + (uint32_t) + (self->frames_per_beat * + self->beat_unit * + sync_rate_to_float ( + *self->sync_rate, + *self->sync_rate_type)); + self->current_sample = + (uint32_t) + (self->beat_offset * self->period_size); + } + } } static void @@ -349,6 +456,49 @@ activate ( recalc_multipliers (self); } +static void +update_position ( + ZLFO * self, + const LV2_Atom_Object * obj) +{ + ZLfoUris * uris = &self->uris; + + /* Received new transport position/speed */ + LV2_Atom *beat = NULL, + *bpm = NULL, + *beat_unit = NULL, + *speed = NULL; + lv2_atom_object_get ( + obj, uris->time_barBeat, &beat, + uris->time_beatUnit, &beat_unit, + uris->time_beatsPerMinute, &bpm, + uris->time_speed, &speed, NULL); + 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_unit && beat_unit->type == uris->atom_Int) + { + self->beat_unit = + ((LV2_Atom_Int *) beat_unit)->body; + } + if (beat && beat->type == uris->atom_Float) + { + self->frames_per_beat = + 60.0f / self->bpm * (float) self->samplerate; + const float bar_beats = + ((LV2_Atom_Float *) beat)->body; + self->beat_offset = fmodf (bar_beats, 1.f); + } +} + static void run ( LV2_Handle instance, @@ -356,10 +506,58 @@ run ( { ZLFO * self = (ZLFO *) instance; - /* if freq changed, set the multipliers */ - if (fabsf (self->last_freq - *self->freq) > - 0.0001f) + int xport_changed = 0; + + /* read incoming events from host and UI */ + LV2_ATOM_SEQUENCE_FOREACH ( + self->control, ev) + { + self->host_current_sample = ev->time.frames; + 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.time_Position) + { + update_position (self, obj); + xport_changed = 1; + } + else if (obj->body.otype == + self->uris.ui_on) + { + self->ui_active = 1; + fprintf (stderr, "UI IS ACTIVE\n\n"); + } + else if (obj->body.otype == + self->uris.ui_off) + { + self->ui_active = 0; + fprintf (stderr, "UI IS OFF\n\n"); + } + } + } + + int freq_changed = + fabsf ( + self->last_freq - *self->freq) > + 0.0001f; + int is_freerunning = *self->freerun > 0.0001f; + int sync_or_freerun_changed = + self->was_freerunning && + !is_freerunning; + + /* if freq or transport changed, reset the + * multipliers */ + if (xport_changed || freq_changed || + sync_or_freerun_changed) { + fprintf ( + stderr, + "xport %d freq %d sync %d\n", + xport_changed, freq_changed, + sync_or_freerun_changed); recalc_multipliers (self); } @@ -483,16 +681,26 @@ run ( #undef ADJUST_RANGE - self->current_sample++; + if (is_freerunning || + (!is_freerunning && self->speed > + 0.00001f)) + { + self->current_sample++; + } if (self->current_sample == self->period_size) self->current_sample = 0; } + fprintf (stderr, "current sample %ld\n", self->current_sample); /* remember frequency */ self->last_freq = *self->freq; + self->was_freerunning = is_freerunning; - send_messages_to_ui (self); + if (self->ui_active) + { + send_messages_to_ui (self); + } } static void diff --git a/src/zlfo_common.h b/src/zlfo_common.h index 0fbefc8..8965ef6 100644 --- a/src/zlfo_common.h +++ b/src/zlfo_common.h @@ -34,6 +34,7 @@ #include "lv2/atom/atom.h" #include "lv2/log/log.h" #include "lv2/urid/urid.h" +#include "lv2/time/time.h" /** Min, max and default frequency. */ #define MIN_FREQ 0.1f @@ -54,13 +55,23 @@ typedef struct ZLfoUris LV2_URID log_Note; LV2_URID log_Trace; LV2_URID log_Warning; + LV2_URID time_Position; + LV2_URID time_bar; + LV2_URID time_barBeat; + LV2_URID time_beatsPerMinute; + LV2_URID time_beatUnit; + LV2_URID time_frame; + LV2_URID time_speed; /* custom URIs */ + /* object */ LV2_URID ui_state; - LV2_URID ui_on; - LV2_URID ui_off; LV2_URID ui_state_current_sample; LV2_URID ui_state_samplerate; + + /** Messages for UI on/off. */ + LV2_URID ui_on; + LV2_URID ui_off; } ZLfoUris; typedef enum PortIndex @@ -171,6 +182,40 @@ typedef enum CurveAlgorithm CURVE_ALGORITHM_SUPERELLIPSE, } CurveAlgorithm; +static inline float +sync_rate_to_float ( + SyncRate rate, + SyncRateType type) +{ + switch (rate) + { + case SYNC_1_128: + return 1.f / 128.f; + case SYNC_1_64: + return 1.f / 64.f; + case SYNC_1_32: + return 1.f / 32.f; + case SYNC_1_16: + return 1.f / 16.f; + case SYNC_1_8: + return 1.f / 8.f; + case SYNC_1_4: + return 1.f / 4.f; + case SYNC_1_2: + return 1.f / 2.f; + case SYNC_1_1: + return 1.f; + case SYNC_2_1: + return 2.f; + case SYNC_4_1: + return 4.f; + default: + break; + } + + return 0.f; +} + static inline void map_uris ( LV2_URID_Map* map, @@ -179,6 +224,7 @@ map_uris ( #define MAP(x,uri) \ uris->x = map->map (map->handle, uri) + /* official URIs */ MAP (atom_Blank, LV2_ATOM__Blank); MAP (atom_Object, LV2_ATOM__Object); MAP (atom_Float, LV2_ATOM__Float); @@ -191,6 +237,16 @@ map_uris ( MAP (log_Note, LV2_LOG__Note); MAP (log_Trace, LV2_LOG__Trace); MAP (log_Warning, LV2_LOG__Warning); + MAP (time_Position, LV2_TIME__Position); + MAP (time_bar, LV2_TIME__bar); + MAP (time_barBeat, LV2_TIME__barBeat); + MAP ( + time_beatsPerMinute, LV2_TIME__beatsPerMinute); + MAP (time_beatUnit, LV2_TIME__beatUnit); + MAP (time_frame, LV2_TIME__frame); + MAP (time_speed, LV2_TIME__speed); + + /* custom URIs */ MAP (ui_on, LFO_URI "#ui_on"); MAP (ui_off, LFO_URI "#ui_off"); MAP (ui_state, LFO_URI "#ui_state"); diff --git a/src/zlfo_ttl_gen.c b/src/zlfo_ttl_gen.c index acc1716..5cefa88 100644 --- a/src/zlfo_ttl_gen.c +++ b/src/zlfo_ttl_gen.c @@ -59,10 +59,11 @@ int main ( fprintf (f, "@prefix atom: .\n\ @prefix doap: .\n\ +@prefix foaf: .\n\ @prefix lv2: .\n\ @prefix midi: .\n\ @prefix rdfs: .\n\ -@prefix foaf: .\n\ +@prefix time: .\n\ @prefix urid: .\n\ @prefix ui: .\n\ @prefix log: .\n\n"); @@ -85,11 +86,12 @@ int main ( a lv2:InputPort ,\n\ atom:AtomPort ;\n\ atom:bufferType atom:Sequence ;\n\ + atom:supports time:Position ;\n\ lv2:index 0 ;\n\ lv2:designation lv2:control ;\n\ lv2:symbol \"control\" ;\n\ lv2:name \"Control\" ;\n\ - rdfs:comment \"GUI to plugin communication\" ;\n\ + rdfs:comment \"GUI/host to plugin communication\" ;\n\ ] , [\n\ a lv2:OutputPort ,\n\ atom:AtomPort ;\n\ @@ -363,9 +365,9 @@ int main ( fprintf (f, "<" LFO_UI_URI ">\n\ a ui:X11UI ;\n\ - lv2:requiredFeature urid:map ;\n\ + lv2:requiredFeature urid:map ,\n\ + ui:idleInterface ;\n\ lv2:optionalFeature log:log ,\n\ - ui:idleInterface,\n\ ui:noUserResize ;\n\ lv2:extensionData ui:idleInterface ,\n\ ui:showInterface ;\n\ diff --git a/src/zlfo_ui.c b/src/zlfo_ui.c index 2ef01a7..b4b9576 100644 --- a/src/zlfo_ui.c +++ b/src/zlfo_ui.c @@ -1110,27 +1110,27 @@ mid_region_bg_draw_cb ( widget->rect.y + 105); cairo_stroke (cr); -#if 0 +/*#if 0*/ /* draw current position */ long period_size = (long) ((float) self->samplerate / self->freq); - double total_space = 8 * space; + double total_space = GRID_WIDTH; double current_offset = (double) self->current_sample / (double) period_size; cairo_move_to ( cr, - widget->rect.x + hpadding + + widget->rect.x + GRID_HPADDING + current_offset * total_space, widget->rect.y + 46); cairo_line_to ( cr, - widget->rect.x + hpadding + + widget->rect.x + GRID_HPADDING + current_offset * total_space, widget->rect.y + 164); cairo_stroke (cr); -#endif +/*#endif*/ if (self->wave_mode == WAVE_CUSTOM) { @@ -1969,6 +1969,23 @@ instantiate ( (LV2UI_Widget) puglGetNativeWindow (self->app->view); + /* let the plugin know that the UI is active */ + uint8_t obj_buf[64]; + lv2_atom_forge_set_buffer ( + &self->forge, obj_buf, 64); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_frame_time (&self->forge, 0); + LV2_Atom* msg = + (LV2_Atom *) + lv2_atom_forge_object ( + &self->forge, &frame, 1, + self->uris.ui_on); + lv2_atom_forge_pop (&self->forge, &frame); + self->write ( + self->controller, 0, + lv2_atom_total_size (msg), + self->uris.atom_eventTransfer, msg); + return self; } @@ -1977,6 +1994,23 @@ cleanup (LV2UI_Handle handle) { ZLfoUi * self = (ZLfoUi *) handle; + /* let the plugin know that the UI is off */ + uint8_t obj_buf[64]; + lv2_atom_forge_set_buffer ( + &self->forge, obj_buf, 64); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_frame_time (&self->forge, 0); + LV2_Atom* msg = + (LV2_Atom *) + lv2_atom_forge_object ( + &self->forge, &frame, 1, + self->uris.ui_off); + lv2_atom_forge_pop (&self->forge, &frame); + self->write ( + self->controller, 0, + lv2_atom_total_size (msg), + self->uris.atom_eventTransfer, msg); + ztk_app_free (self->app); free (self); @@ -2121,7 +2155,7 @@ port_event ( "failed to get current sample"); } } -#if 0 +/*#if 0*/ PuglRect rect; rect.x = self->mid_region->rect.x; rect.y = self->mid_region->rect.y; @@ -2130,7 +2164,7 @@ port_event ( self->mid_region->rect.height; puglPostRedisplayRect ( self->app->view, rect); -#endif +/*#endif*/ } else {