diff options
author | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2017-03-23 17:34:21 +0100 |
---|---|---|
committer | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2017-03-23 17:34:21 +0100 |
commit | 02de8e72284619db9d9529380710458420518fa4 (patch) | |
tree | def53302fe8f06b905d5aa0553607daf4409b828 | |
parent | 3a9f0f747a5356b6ec304867db716f0e3ccb07ae (diff) | |
download | vm.lv2-02de8e72284619db9d9529380710458420518fa4.tar.xz |
prototype audio mixer widget.
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | vm.c | 12 | ||||
-rw-r--r-- | vm.h | 23 | ||||
-rw-r--r-- | vm.ttl | 40 | ||||
-rw-r--r-- | vm_ui.c | 158 |
5 files changed, 182 insertions, 53 deletions
@@ -1 +1 @@ -0.1.2889 +0.1.2891 @@ -56,6 +56,8 @@ struct _plughandle_t { LV2_Atom_Forge forge; LV2_Atom_Forge_Ref ref; + vm_plug_enum_t vm_plug; + LV2_URID vm_graph; LV2_Log_Log *log; @@ -181,6 +183,8 @@ instantiate(const LV2_Descriptor* descriptor, num_t rate, if(!handle) return NULL; + handle->vm_plug = vm_plug_type(descriptor->URI); + for(unsigned i=0; features[i]; i++) { if(!strcmp(features[i]->URI, LV2_URID__map)) @@ -279,7 +283,9 @@ run_internal(plughandle_t *handle, uint32_t frames, bool notify, { for(unsigned i = 0; i < CTRL_MAX; i++) { - const float in1 = CLIP(VM_MIN, *in[i], VM_MAX); + const float in1 = (handle->vm_plug == VM_PLUG_AUDIO) + ? *in[i] // don't clip audio + : CLIP(VM_MIN, *in[i], VM_MAX); if(handle->in0[i] != in1) { @@ -832,7 +838,9 @@ loop: { for(unsigned i = 0; i < CTRL_MAX; i++) { - const float out1 = CLIP(VM_MIN, handle->out0[i], VM_MAX); + const float out1 = (handle->vm_plug == VM_PLUG_AUDIO) + ? handle->out0[i] // don't clip audio + :CLIP(VM_MIN, handle->out0[i], VM_MAX); if(*out[i] != out1) { @@ -59,6 +59,7 @@ #include <props.lv2/props.h> +typedef enum _vm_plug_enum_t vm_plug_enum_t; typedef enum _vm_status_t vm_status_t; typedef enum _vm_opcode_enum_t vm_opcode_enum_t; typedef enum _vm_command_enum_t vm_command_enum_t; @@ -67,6 +68,13 @@ typedef struct _vm_api_def_t vm_api_def_t; typedef struct _vm_api_impl_t vm_api_impl_t; typedef struct _plugstate_t plugstate_t; +enum _vm_plug_enum_t { + VM_PLUG_CONTROL = 0, + VM_PLUG_CV, + VM_PLUG_AUDIO, + VM_PLUG_ATOM +}; + enum _vm_status_t { VM_STATUS_STATIC = (0 << 0), VM_STATUS_HAS_TIME = (1 << 1), @@ -797,6 +805,21 @@ static const vm_api_def_t vm_api_def [OP_MAX] = { }, }; +static vm_plug_enum_t +vm_plug_type(const char *plugin_uri) +{ + if(!strcmp(plugin_uri, VM_PREFIX"control")) + return VM_PLUG_CONTROL; + else if(!strcmp(plugin_uri, VM_PREFIX"cv")) + return VM_PLUG_CV; + else if(!strcmp(plugin_uri, VM_PREFIX"audio")) + return VM_PLUG_AUDIO; + else if(!strcmp(plugin_uri, VM_PREFIX"atom")) + return VM_PLUG_ATOM; + + return VM_PLUG_CONTROL; +} + static inline void vm_api_init(vm_api_impl_t *impl, LV2_URID_Map *map) { @@ -608,136 +608,96 @@ vm:audio lv2:index 2 ; lv2:symbol "audio_in_0" ; lv2:name "Audio In 0" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 3 ; lv2:symbol "audio_in_1" ; lv2:name "Audio In 1" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 4 ; lv2:symbol "audio_in_2" ; lv2:name "Audio In 2" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 5 ; lv2:symbol "audio_in_3" ; lv2:name "Audio In 3" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 6 ; lv2:symbol "audio_in_4" ; lv2:name "Audio In 4" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 7 ; lv2:symbol "audio_in_5" ; lv2:name "Audio In 5" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 8 ; lv2:symbol "audio_in_6" ; lv2:name "Audio In 6" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:InputPort, lv2:AudioPort; lv2:index 9 ; lv2:symbol "audio_in_7" ; lv2:name "Audio In 7" ; - lv2:minimum -1.0; - lv2:maximum 1.0; - lv2:default 0.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 10 ; lv2:symbol "audio_out_0" ; lv2:name "Audio Out 0" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 11 ; lv2:symbol "audio_out_1" ; lv2:name "Audio Out 1" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 12 ; lv2:symbol "audio_out_2" ; lv2:name "Audio Out 2" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 13 ; lv2:symbol "audio_out_3" ; lv2:name "Audio Out 3" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 14 ; lv2:symbol "audio_out_4" ; lv2:name "Audio Out 4" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 15 ; lv2:symbol "audio_out_5" ; lv2:name "Audio Out 5" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 16 ; lv2:symbol "audio_out_6" ; lv2:name "Audio Out 6" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] , [ a lv2:OutputPort, lv2:AudioPort; lv2:index 17 ; lv2:symbol "audio_out_7" ; lv2:name "Audio Out 7" ; - lv2:minimum -1.0; - lv2:maximum 1.0; ] ; #patch:writable @@ -59,6 +59,8 @@ struct _plughandle_t { LV2_URID_Unmap *unmap; LV2_Atom_Forge forge; + vm_plug_enum_t vm_plug; + LV2_URID atom_eventTransfer; LV2_URID vm_graph; @@ -84,6 +86,9 @@ struct _plughandle_t { float in0 [CTRL_MAX]; float out0 [CTRL_MAX]; + float in1 [CTRL_MAX]; + float out1 [CTRL_MAX]; + int64_t off; plot_t inp [CTRL_MAX]; plot_t outp [CTRL_MAX]; @@ -252,6 +257,99 @@ _draw_plot(struct nk_context *ctx, const float *vals) } } +static const int dBFS6_min = -54; +static const int dBFS6_max = 6; +static const int dBFS6_rng = dBFS6_max - dBFS6_min; +static const int dBFS6_div = 2; + +static const float mx1 = (float)(dBFS6_rng - 2*dBFS6_max) / dBFS6_rng; +static const float mx2 = (float)(2*dBFS6_max) / dBFS6_rng; + +static inline float +dBFS6(float peak) +{ + const float d = dBFS6_max + 20.f * log10f(fabsf(peak) / dBFS6_div); + const float e = (d - dBFS6_min) / dBFS6_rng; + return NK_CLAMP(0.f, e, 1.f); +} + +static inline void +_draw_mixer(struct nk_context *ctx, float peak) +{ + peak = dBFS6(peak); + + struct nk_rect bounds; + const enum nk_widget_layout_states states = nk_widget(&bounds, ctx); + if(states != NK_WIDGET_INVALID) + { + struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); + + const struct nk_color bg = ctx->style.property.normal.data.color; + nk_fill_rect(canvas, bounds, ctx->style.property.rounding, bg); + nk_stroke_rect(canvas, bounds, ctx->style.property.rounding, ctx->style.property.border, bg); + + const struct nk_rect orig = bounds; + struct nk_rect outline; + const uint8_t alph = 0x7f; + + // <= -6dBFS + { + const float dbfs = NK_MIN(peak, mx1); + const uint8_t dcol = 0xff * dbfs / mx1; + const struct nk_color left = nk_rgba(0x00, 0xff, 0xff, alph); + const struct nk_color bottom = left; + const struct nk_color right = nk_rgba(dcol, 0xff, 0xff-dcol, alph); + const struct nk_color top = right; + + const float ox = ctx->style.font->height/2 + ctx->style.property.border + ctx->style.property.padding.x; + const float oy = ctx->style.property.border + ctx->style.property.padding.y; + bounds.x += ox; + bounds.y += oy; + bounds.w -= 2*ox; + bounds.h -= 2*oy; + outline = bounds; + bounds.w *= dbfs; + + nk_fill_rect_multi_color(canvas, bounds, left, top, right, bottom); + } + + // > 6dBFS + if(peak > mx1) + { + const float dbfs = peak - mx1; + const uint8_t dcol = 0xff * dbfs / mx2; + const struct nk_color left = nk_rgba(0xff, 0xff, 0x00, alph); + const struct nk_color bottom = left; + const struct nk_color right = nk_rgba(0xff, 0xff - dcol, 0x00, alph); + const struct nk_color top = right; + + bounds = outline; + bounds.x += bounds.w * mx1; + bounds.w *= dbfs; + nk_fill_rect_multi_color(canvas, bounds, left, top, right, bottom); + } + + // draw 6dBFS lines from -60 to +6 + for(unsigned i = 0; i <= dBFS6_rng; i += dBFS6_max) + { + const bool is_zero = (i == dBFS6_rng - dBFS6_max); + const float dx = outline.w * i / dBFS6_rng; + + const float x0 = outline.x + dx; + const float y0 = is_zero ? orig.y + 2.f : outline.y; + + const float border = (is_zero ? 2.f : 1.f) * ctx->style.window.group_border; + + const float x1 = x0; + const float y1 = is_zero ? orig.y + orig.h - 2.f : outline.y + outline.h; + + nk_stroke_line(canvas, x0, y0, x1, y1, border, ctx->style.window.group_border_color); + } + + nk_stroke_rect(canvas, outline, 0.f, ctx->style.window.group_border, ctx->style.window.group_border_color); + } +} + static void _expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) { @@ -292,15 +390,25 @@ _expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) _draw_plot(ctx, handle->inp[i].vals); nk_layout_row_dynamic(ctx, dy, 2); - if(i == 0) // calculate only once + if( (handle->vm_plug == VM_PLUG_CONTROL) + || (handle->vm_plug == VM_PLUG_CV) + || (handle->vm_plug == VM_PLUG_ATOM) ) { - stp = scl * VM_STP; - fpp = scl * VM_RNG / nk_widget_width(ctx); + if(i == 0) // calculate only once + { + stp = scl * VM_STP; + fpp = scl * VM_RNG / nk_widget_width(ctx); + } + const float old_val = handle->in0[i]; + nk_property_float(ctx, input_labels[i], VM_MIN, &handle->in0[i], VM_MAX, stp, fpp); + if(old_val != handle->in0[i]) + handle->writer(handle->controller, i + 2, sizeof(float), 0, &handle->in0[i]); + } + else if(handle->vm_plug == VM_PLUG_AUDIO) + { + _draw_mixer(ctx, handle->in1[i]); + handle->in1[i] *= 0.9; //FIXME make dependent on ui:updateRate } - const float old_val = handle->in0[i]; - nk_property_float(ctx, input_labels[i], VM_MIN, &handle->in0[i], VM_MAX, stp, fpp); - if(old_val != handle->in0[i]) - handle->writer(handle->controller, i + 2, sizeof(float), 0, &handle->in0[i]); const int old_window = handle->inp[i].window; nk_property_int(ctx, ms_label, 10, &handle->inp[i].window, 100000, 1, 1.f); @@ -534,7 +642,17 @@ _expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) _draw_plot(ctx, handle->outp[i].vals); nk_layout_row_dynamic(ctx, dy, 2); - nk_labelf(ctx, NK_TEXT_LEFT, "Out %u: %+f", i, handle->out0[i]); + if( (handle->vm_plug == VM_PLUG_CONTROL) + || (handle->vm_plug == VM_PLUG_CV) + || (handle->vm_plug == VM_PLUG_ATOM) ) + { + nk_labelf(ctx, NK_TEXT_LEFT, "Out %u: %+f", i, handle->out0[i]); + } + else if(handle->vm_plug == VM_PLUG_AUDIO) + { + _draw_mixer(ctx, handle->out1[i]); + handle->out1[i] *= 0.9; //FIXME make dependent on ui:updateRate + } const int old_window = handle->outp[i].window; nk_property_int(ctx, ms_label, 10, &handle->outp[i].window, 100000, 1, 1.f); @@ -559,6 +677,8 @@ instantiate(const LV2UI_Descriptor *descriptor, const char *plugin_uri, if(!handle) return NULL; + handle->vm_plug = vm_plug_type(plugin_uri); + void *parent = NULL; LV2UI_Resize *host_resize = NULL; LV2_Options_Option *opts = NULL; @@ -776,9 +896,27 @@ port_event(LV2UI_Handle instance, uint32_t index, uint32_t size, const LV2_Atom_Float *val = buf + 8 + 16; if(idx->body < 10) - handle->in0[idx->body - 2] = val->body; + { + const unsigned j = idx->body - 2; + + if(handle->vm_plug == VM_PLUG_AUDIO) + { + handle->in0[j] = val->body; + if(val->body > handle->in1[j]) + handle->in1[j] = val->body; + } + } else - handle->out0[idx->body - 10] = val->body; + { + const unsigned j = idx->body - 10; + + if(handle->vm_plug == VM_PLUG_AUDIO) + { + handle->out0[j] = val->body; + if(val->body > handle->out1[j]) + handle->out1[j] = val->body; + } + } } else // !tuple { |