From 50d000fe8f0b94b2824b7480659f7360043b6c7a Mon Sep 17 00:00:00 2001 From: Markus Schmidt Date: Wed, 22 Apr 2015 02:15:35 +0200 Subject: [PATCH] First implementation of tuner widget --- gui/gui-pitch.xml | 15 +++- src/calf/custom_ctl.h | 29 +++++++ src/calf/gui_controls.h | 11 +++ src/calf/lv2wrap.h | 8 +- src/calf/metadata.h | 2 +- src/custom_ctl.cpp | 227 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gui.cpp | 2 + src/gui_controls.cpp | 46 +++++++++- src/metadata.cpp | 5 +- src/modules_pitch.cpp | 8 +- 10 files changed, 337 insertions(+), 16 deletions(-) diff --git a/gui/gui-pitch.xml b/gui/gui-pitch.xml index 95acd2d..2ca5e1e 100644 --- a/gui/gui-pitch.xml +++ b/gui/gui-pitch.xml @@ -11,14 +11,23 @@ - + +
+ + + diff --git a/src/calf/custom_ctl.h b/src/calf/custom_ctl.h index 7086ca0..8cf366b 100644 --- a/src/calf/custom_ctl.h +++ b/src/calf/custom_ctl.h @@ -316,4 +316,33 @@ extern GType calf_tap_button_get_type(); G_END_DECLS + +/// TUNER //////////////////////////////////////////////////////// + + +#define CALF_TYPE_TUNER (calf_tuner_get_type()) +#define CALF_TUNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALF_TYPE_TUNER, CalfTuner)) +#define CALF_IS_TUNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALF_TYPE_TUNER)) +#define CALF_TUNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALF_TYPE_TUNER, CalfTunerClass)) +#define CALF_IS_TUNER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALF_TYPE_TUNER)) +#define CALF_TUNER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALF_TYPE_TUNER, CalfTunerClass)) + +struct CalfTuner +{ + GtkDrawingArea parent; + int note; + float cents; + cairo_surface_t *background; +}; + +struct CalfTunerClass +{ + GtkDrawingAreaClass parent_class; +}; + +extern GtkWidget *calf_tuner_new(); + +extern GType calf_tuner_get_type(); + + #endif diff --git a/src/calf/gui_controls.h b/src/calf/gui_controls.h index 5c553ac..355267c 100644 --- a/src/calf/gui_controls.h +++ b/src/calf/gui_controls.h @@ -247,6 +247,17 @@ struct phase_graph_param_control: public param_control virtual ~phase_graph_param_control(); }; +/// Tuner +struct tuner_param_control: public param_control +{ + virtual GtkWidget *create(plugin_gui *_gui, int _param_no); + virtual void get() {} + virtual void set(); + virtual void on_idle(); + int cents_no; + virtual ~tuner_param_control(); +}; + /// Knob struct knob_param_control: public param_control { diff --git a/src/calf/lv2wrap.h b/src/calf/lv2wrap.h index 743e925..0aecaca 100644 --- a/src/calf/lv2wrap.h +++ b/src/calf/lv2wrap.h @@ -307,8 +307,8 @@ static const void *cb_ext_data(const char *URI) return NULL; } static LV2_State_Status cb_state_save( - LV2_Handle Instance, LV2_State_Store_Function store, LV2_State_Handle handle, - uint32_t flags, const LV2_Feature *const * features) + LV2_Handle Instance, LV2_State_Store_Function store, LV2_State_Handle handle, + uint32_t flags, const LV2_Feature *const * features) { instance *const inst = (instance *)Instance; struct store_state: public send_configure_iface @@ -341,8 +341,8 @@ static const void *cb_ext_data(const char *URI) return LV2_STATE_SUCCESS; } static LV2_State_Status cb_state_restore( - LV2_Handle Instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle callback_data, - uint32_t flags, const LV2_Feature *const * features) + LV2_Handle Instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle callback_data, + uint32_t flags, const LV2_Feature *const * features) { instance *const inst = (instance *)Instance; if (inst->set_srate) diff --git a/src/calf/metadata.h b/src/calf/metadata.h index 1e5f0db..02143cb 100644 --- a/src/calf/metadata.h +++ b/src/calf/metadata.h @@ -933,7 +933,7 @@ struct pitch_metadata: public plugin_metadata enum { par_pd_threshold, par_pd_subdivide, - par_pd_tune, par_pd_midi, + par_tune, par_note, par_cents, param_count }; PLUGIN_NAME_ID_LABEL("pitch", "pitch", "Pitch Tools") diff --git a/src/custom_ctl.cpp b/src/custom_ctl.cpp index 218fa72..f756ae2 100644 --- a/src/custom_ctl.cpp +++ b/src/custom_ctl.cpp @@ -1712,3 +1712,230 @@ calf_tap_button_get_type (void) } return type; } + + +///////////////////////////////////////// tuner /////////////////////////////////////////////// + +static void calf_tuner_create_dot(cairo_t *ctx, int dots, int dot, float rad) +{ + cairo_save(ctx); + cairo_rotate(ctx, dot * M_PI / (dots * 8) * 2); + cairo_move_to(ctx, 0, -rad); + cairo_line_to(ctx, 0, 0); + cairo_stroke(ctx); + cairo_restore(ctx); +} + +static void +calf_tuner_draw_background( cairo_t *ctx, int sx, int sy, int ox, int oy ) +{ + int dw = 2; + int dm = 1; + int x0 = ox + 0.025; + int x1 = ox + sx - 0.025; + int a = x1 - x0; + int dots = a * 0.5 / (dw + dm); + float rad = sqrt(2.f) / 2.f * a; + int cx = ox + sx / 2; + int cy = ox + sy / 2; + int ccy = cy - sy / 3 + rad; + + line_graph_background(ctx, 0, 0, sx, sy, ox, oy); + cairo_stroke(ctx); + cairo_save(ctx); + + cairo_rectangle(ctx, ox * 2, oy * 2, sx - 2 * ox, sy - 2 * oy); + cairo_clip(ctx); + + cairo_set_source_rgba(ctx, 0.35, 0.4, 0.2, 0.3); + cairo_set_line_width(ctx, dw); + cairo_translate(ctx, cx, ccy); + + for(int i = 2; i < dots + 2; i++) { + calf_tuner_create_dot(ctx, dots, i, rad); + } + for(int i = -2; i > -dots - 2; i--) { + calf_tuner_create_dot(ctx, dots, i, rad); + } + cairo_set_line_width(ctx, dw * 3); + calf_tuner_create_dot(ctx, dots, 0, rad); +} + +static void calf_tuner_draw_dot(cairo_t * ctx, float cents, int sx, int sy, int ox, int oy) +{ + cairo_rectangle(ctx, ox * 2, oy * 2, sx - 2 * ox, sy - 2 * oy); + cairo_clip(ctx); + + int dw = 2; + int dm = 1; + int x0 = ox + 0.025; + int x1 = ox + sx - 0.025; + int a = x1 - x0; + int dots = a * 0.5 / (dw + dm); + int dot = cents * 2.f * dots; + float rad = sqrt(2.f) / 2.f * a; + int cx = ox + sx / 2; + int cy = ox + sy / 2; + int ccy = cy - sy / 3 + rad; + + int sign = (dot > 0) - (dot < 0); + int marg = dot ? sign : 0; + cairo_save(ctx); + cairo_set_source_rgba(ctx, 0.35, 0.4, 0.2, 0.9); + cairo_translate(ctx, cx, ccy); + cairo_set_line_width(ctx, dw * (dot ? 1 : 3)); + calf_tuner_create_dot(ctx, dots, dot + marg, rad); + cairo_restore(ctx); +} + +static gboolean +calf_tuner_expose (GtkWidget *widget, GdkEventExpose *event) +{ + g_assert(CALF_IS_TUNER(widget)); + CalfTuner *tuner = CALF_TUNER(widget); + + //printf("%d %f\n", tuner->note, tuner->cents); + + // dimensions + int ox = 5, oy = 5; + int sx = widget->allocation.width - ox * 2, sy = widget->allocation.height - oy * 2; + int rad = sx / 2; + int cx = ox + sx / 2; + int cy = oy + sy / 2; + + // cairo initialization stuff + cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window)); + cairo_t *ctx_back; + + if( tuner->background == NULL ) { + // looks like its either first call or the widget has been resized. + // create the background surface (stolen from line graph)... + cairo_surface_t *window_surface = cairo_get_target(c); + tuner->background = cairo_surface_create_similar(window_surface, + CAIRO_CONTENT_COLOR, + widget->allocation.width, + widget->allocation.height ); + + // ...and draw some bling bling onto it... + ctx_back = cairo_create(tuner->background); + calf_tuner_draw_background(ctx_back, sx, sy, ox, oy); + } else { + ctx_back = cairo_create(tuner->background); + } + + cairo_set_source_surface(c, cairo_get_target(ctx_back), 0, 0); + cairo_paint(c); + + calf_tuner_draw_dot(c, tuner->cents / 100, sx, sy, ox, oy); + + static const char notenames[] = "C\0\0C#\0D\0\0D#\0E\0\0F\0\0F#\0G\0\0G#\0A\0\0A#\0B\0\0"; + const char * note = notenames + (tuner->note % 12) * 3; + //int oct = int(tuner->note / 12) - 2; + + if (tuner->note) { + cairo_select_font_face(c, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(c, 9 * sy / 25); + cairo_text_extents_t te; + cairo_text_extents (c, note, &te); + cairo_set_source_rgba(c, 0.35, 0.4, 0.2, 0.9); + cairo_move_to (c, ox + 10 - te.x_bearing, oy + 10 - te.y_bearing); + cairo_show_text (c, note); + } + + cairo_destroy(c); + cairo_destroy(ctx_back); + return TRUE; +} + +static void +calf_tuner_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + g_assert(CALF_IS_TUNER(widget)); + // CalfLineGraph *lg = CALF_LINE_GRAPH(widget); +} + +static void +calf_tuner_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + g_assert(CALF_IS_TUNER(widget)); + CalfTuner *lg = CALF_TUNER(widget); + + GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_TUNER_GET_CLASS( lg ) ); + + if(lg->background) + cairo_surface_destroy(lg->background); + lg->background = NULL; + + widget->allocation = *allocation; +} + +static void +calf_tuner_class_init (CalfTunerClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + widget_class->expose_event = calf_tuner_expose; + widget_class->size_request = calf_tuner_size_request; + widget_class->size_allocate = calf_tuner_size_allocate; +} + +static void +calf_tuner_unrealize (GtkWidget *widget, CalfTuner *tuner) +{ + if( tuner->background ) + cairo_surface_destroy(tuner->background); + tuner->background = NULL; +} + +static void +calf_tuner_init (CalfTuner *self) +{ + GtkWidget *widget = GTK_WIDGET(self); + widget->requisition.width = 40; + widget->requisition.height = 40; + self->background = NULL; + g_signal_connect(GTK_OBJECT(widget), "unrealize", G_CALLBACK(calf_tuner_unrealize), (gpointer)self); +} + +GtkWidget * +calf_tuner_new() +{ + return GTK_WIDGET(g_object_new (CALF_TYPE_TUNER, NULL)); +} + +GType +calf_tuner_get_type (void) +{ + static GType type = 0; + if (!type) { + static const GTypeInfo type_info = { + sizeof(CalfTunerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)calf_tuner_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(CalfTuner), + 0, /* n_preallocs */ + (GInstanceInitFunc)calf_tuner_init + }; + + GTypeInfo *type_info_copy = new GTypeInfo(type_info); + + for (int i = 0; ; i++) { + char *name = g_strdup_printf("CalfTuner%u%d", ((unsigned int)(intptr_t)calf_tuner_class_init) >> 16, i); + if (g_type_from_name(name)) { + free(name); + continue; + } + type = g_type_register_static( GTK_TYPE_DRAWING_AREA, + name, + type_info_copy, + (GTypeFlags)0); + free(name); + break; + } + } + return type; +} diff --git a/src/gui.cpp b/src/gui.cpp index 3f8e5eb..ff0bb17 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -79,6 +79,8 @@ control_base *plugin_gui::create_widget_from_xml(const char *element, const char return new line_graph_param_control; if (!strcmp(element, "phase-graph")) return new phase_graph_param_control; + if (!strcmp(element, "tuner")) + return new tuner_param_control; if (!strcmp(element, "keyboard")) return new keyboard_param_control; if (!strcmp(element, "curve")) diff --git a/src/gui_controls.cpp b/src/gui_controls.cpp index 923ab90..e1bc01e 100644 --- a/src/gui_controls.cpp +++ b/src/gui_controls.cpp @@ -1173,8 +1173,6 @@ GtkWidget *line_graph_param_control::create(plugin_gui *a_gui, int a_param_no) widget = calf_line_graph_new (); - gtk_widget_set_name(GTK_WIDGET(widget), "calf-graph"); - CalfLineGraph *clg = CALF_LINE_GRAPH(widget); widget->requisition.width = get_int("width", 40); widget->requisition.height = get_int("height", 40); @@ -1411,7 +1409,6 @@ GtkWidget *phase_graph_param_control::create(plugin_gui *_gui, int _param_no) gui = _gui; param_no = _param_no; widget = calf_phase_graph_new (); - gtk_widget_set_name(GTK_WIDGET(widget), "calf-phase"); CalfPhaseGraph *clg = CALF_PHASE_GRAPH(widget); widget->requisition.width = get_int("size", 40); widget->requisition.height = get_int("size", 40); @@ -1434,6 +1431,49 @@ phase_graph_param_control::~phase_graph_param_control() { } + +/******************************** Tuner ********************************/ + +void tuner_param_control::on_idle() +{ + if (get_int("refresh", 0)) + set(); +} + +GtkWidget *tuner_param_control::create(plugin_gui *_gui, int _param_no) +{ + gui = _gui; + param_no = _param_no; + widget = calf_tuner_new (); + //CalfTuner *tuner = CALF_TUNER(widget); + widget->requisition.width = get_int("width", 40); + widget->requisition.height = get_int("height", 40); + gtk_widget_set_name(GTK_WIDGET(widget), "Calf-Tuner"); + + const string ¢s_name = attribs["param_cents"]; + if (cents_name != "") + cents_no = gui->get_param_no_by_name(cents_name); + else + cents_no = 0; + return widget; +} + +void tuner_param_control::set() +{ + _GUARD_CHANGE_ + GtkWidget *tw = gtk_widget_get_toplevel(widget); + CalfTuner *tuner = CALF_TUNER(widget); + tuner->note = gui->plugin->get_param_value(param_no); + tuner->cents = gui->plugin->get_param_value(cents_no); + if (tw && GTK_WIDGET_TOPLEVEL(tw) && widget->window) { + gtk_widget_queue_draw(widget); + } +} + +tuner_param_control::~tuner_param_control() +{ +} + /******************************** List View ********************************/ GtkWidget *listview_param_control::create(plugin_gui *_gui, int _param_no) diff --git a/src/metadata.cpp b/src/metadata.cpp index 420e252..3fa1441 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -1949,8 +1949,9 @@ CALF_PORT_NAMES(pitch) = {"In L", "In R", "Out L", "Out R"}; CALF_PORT_PROPS(pitch) = { { 0.9, 0.1, 1, 0, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "pd_threshold", "Pitch Det:Peak Threshold" }, { 1, 1, 8, 3, PF_INT | PF_CTL_KNOB, NULL, "pd_subdivide", "Pitch Det:Subdiv" }, - { 440, 427, 453, 0.1, PF_FLOAT | PF_CTL_KNOB, NULL, "pd_tune", "Pitch Det:Tune" }, - { 0, 0, 127, 1, PF_INT | PF_PROP_OUTPUT, NULL, "pd_midi", "Pitch Det:MIDI" }, + { 440, 427, 453, 0.1, PF_FLOAT | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "tune", "Tune" }, + { 0, 0, 127, 1, PF_INT | PF_PROP_OUTPUT, NULL, "note", "MIDI Note" }, + { 0, -100, 100, 1, PF_FLOAT | PF_PROP_OUTPUT, NULL, "cents", "Cents" }, {} }; diff --git a/src/modules_pitch.cpp b/src/modules_pitch.cpp index 392af90..817bff1 100644 --- a/src/modules_pitch.cpp +++ b/src/modules_pitch.cpp @@ -121,7 +121,7 @@ void pitch_audio_module::recompute() float y3 = magarr[maxpos + 1]; float pos2 = maxpos + 0.5 * (y1 - y3) / (y1 - 2 * y2 + y3); - float f2 = (srate / pos2) / *params[par_pd_tune]; + float f2 = (srate / pos2) / *params[par_tune]; float lf2 = logf(f2); float rf2 = 1200 * lf2 / logf(2.f) - 300; rf2 -= 1200.f * floor(rf2 / 1200.f); @@ -130,8 +130,10 @@ void pitch_audio_module::recompute() if (note == 12) note -= 12; static const char notenames[] = "C\0\0C#\0D\0\0D#\0E\0\0F\0\0F#\0G\0\0G#\0A\0\0A#\0B\0\0"; - int mnote = 12 * lf2 + 69; - printf("pos %d mag %f freq %f posx %f freqx %f midi %d note %s%+fct\n", maxpos, maxpt, srate * 1.0 / maxpos, pos2, srate / pos2, mnote, notenames + 3 * note, rf2); + int mnote = round(12 * lf2 + 69); + //printf("pos %d mag %f freq %f posx %f freqx %f midi %d note %s%+fct\n", maxpos, maxpt, srate * 1.0 / maxpos, pos2, srate / pos2, mnote, notenames + 3 * note, rf2); + *params[par_note] = mnote; + *params[par_cents] = rf2; } } -- 2.11.4.GIT