1 /* vim: set sts=2 sw=2 et: */
3 * Copyright (C) 2006 Josep Torra <j.torra@telefonica.net>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
26 #define TUNER_VERSION "0.4"
30 # include <hildon/hildon-defines.h>
31 # include <hildon/hildon-program.h>
32 # include <hildon/hildon-number-editor.h>
33 # elif defined(MAEMO1)
34 # include <hildon-widgets/hildon-app.h>
35 # include <hildon-widgets/hildon-appview.h>
37 # include <hildon-widgets/hildon-program.h>
42 #define OSSO_PACKAGE "tuner-tool"
43 #define OSSO_VERSION TUNER_VERSION
45 #endif /* ifdef HILDON */
48 # define DEFAULT_AUDIOSRC "dsppcmsrc"
49 # define DEFAULT_AUDIOSINK "dsppcmsink"
51 # define DEFAULT_AUDIOSRC "alsasrc"
52 # define DEFAULT_AUDIOSINK "alsasink"
61 #define between(x,a,b) (((x)>=(a)) && ((x)<=(b)))
63 #define MAGIC (1.059463094359f) /* 2^(1/2) */
65 extern gboolean plugin_pitch_init (GstPlugin * plugin);
66 extern gboolean plugin_tonesrc_init (GstPlugin * plugin);
87 #define NUM_WKEYS (15) /* # of white keys in the piano keyboard */
88 #define WKEY_WIDTH (45)
90 static Note equal_tempered_scale[] = {
164 {"C#6/Db6", 1108.73},
166 {"D#6/Eb6", 1244.51},
169 {"F#6/Gb6", 1479.98},
171 {"G#6/Ab6", 1661.22},
173 {"A#6/Bb6", 1864.66},
176 {"C#7/Db7", 2217.46},
178 {"D#7/Eb7", 2489.02},
181 {"F#7/Gb7", 2959.96},
183 {"G#7/Ab7", 3322.44},
185 {"A#7/Bb7", 3729.31},
189 static GdkColor ledOnColor = { 0, 0 * 255, 180 * 255, 95 * 255 };
190 static GdkColor ledOnColor2 = { 0, 180 * 255, 180 * 255, 0 * 255 };
191 static GdkColor ledOffColor = { 0, 80 * 255, 80 * 255, 80 * 255 };
193 static GtkWidget *targetFrequency;
194 static GtkWidget *currentFrequency;
195 static GtkWidget *drawingarea1;
196 static GtkWidget *drawingarea2;
198 static GstElement *bin1 = NULL;
199 static GstElement *bin2 = NULL;
202 recalculate_scale (double a4)
206 for (i = 0; i < NUM_NOTES; i++) {
207 equal_tempered_scale[i].frequency = a4 * pow (MAGIC, i - 57);
208 /* fprintf(stdout, "%s: %.2f\n", equal_tempered_scale[i].name, equal_tempered_scale[i].frequency); */
214 fix_hildon_number_editor (GtkWidget * widget, gpointer data)
216 if (GTK_IS_EDITABLE (widget)) {
217 gtk_editable_set_editable (GTK_EDITABLE (widget), FALSE);
218 g_object_set (G_OBJECT (widget), "can-focus", FALSE, NULL);
224 calibration_changed (GObject * object, GParamSpec * pspec, gpointer user_data)
229 value = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (object));
231 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (object));
234 if (value >= CALIB_MIN && value <= CALIB_MAX) {
235 recalculate_scale (value);
236 g_debug ("Calibration changed to %d Hz", value);
241 on_window_destroy (GtkObject * object, gpointer user_data)
247 toggle_fullscreen (GtkWindow * window)
249 static gboolean fullscreen = FALSE;
251 fullscreen = !fullscreen;
253 gtk_window_fullscreen (GTK_WINDOW (window));
255 gtk_window_unfullscreen (GTK_WINDOW (window));
259 key_press_event (GtkWidget * widget, GdkEventKey * event, GtkWindow * window)
261 switch (event->keyval) {
263 case HILDON_HARDKEY_FULLSCREEN:
264 toggle_fullscreen(window);
278 static GdkGC *gc = NULL;
279 gint width = drawingarea1->allocation.width;
280 gint led_width = ((gfloat) width / (gfloat) (NUM_LEDS)) * 0.8;
281 gint led_space = ((gfloat) width / (gfloat) (NUM_LEDS)) * 0.2;
282 gint led_total = led_width + led_space;
283 gint padding = (width - NUM_LEDS * led_total) / 2;
286 gc = gdk_gc_new (drawingarea1->window);
288 gdk_gc_set_rgb_fg_color (gc, &drawingarea1->style->fg[0]);
290 gdk_draw_rectangle (drawingarea1->window, gc, TRUE, 0, 0,
291 drawingarea1->allocation.width, drawingarea1->allocation.height);
293 if (abs (n) > (NUM_LEDS / 2))
294 n = n / n * (NUM_LEDS / 2);
297 j = NUM_LEDS / 2 + 1;
298 k = NUM_LEDS / 2 + n;
300 j = NUM_LEDS / 2 + n;
301 k = NUM_LEDS / 2 - 1;
305 for (i = 0; i < NUM_LEDS; i++) {
306 if (i == NUM_LEDS / 2) {
308 gdk_gc_set_rgb_fg_color (gc, &ledOnColor2);
310 gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
312 gdk_draw_rectangle (drawingarea1->window, gc, TRUE, padding + (i * led_total) + ((led_total - 4) / 2), 2, 4,
315 if ((i >= j) && (i <= k))
316 gdk_gc_set_rgb_fg_color (gc, &ledOnColor);
318 gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
320 gdk_draw_rectangle (drawingarea1->window, gc, TRUE, padding + (i * led_total), 10, led_width,
326 /* update frequency info */
328 update_frequency (gint frequency)
332 gfloat diff, min_diff;
334 min_diff = frequency - (equal_tempered_scale[0].frequency - 10);
335 for (i = j = 0; i < NUM_NOTES; i++) {
336 diff = frequency - equal_tempered_scale[i].frequency;
337 if (fabs (diff) <= fabs (min_diff)) {
346 g_strdup_printf ("Nearest note is %s with %.2f Hz frequency",
347 equal_tempered_scale[j].name, equal_tempered_scale[j].frequency);
348 gtk_label_set_text (GTK_LABEL (targetFrequency), buffer);
351 buffer = g_strdup_printf ("Played frequency is %d Hz", frequency);
352 gtk_label_set_text (GTK_LABEL (currentFrequency), buffer);
355 draw_leds ((gint) roundf (min_diff));
358 /* receive spectral data from element message */
360 message_handler (GstBus * bus, GstMessage * message, gpointer data)
362 if (message->type == GST_MESSAGE_ELEMENT) {
363 const GstStructure *s = gst_message_get_structure (message);
364 const gchar *name = gst_structure_get_name (s);
366 if (strcmp (name, "pitch") == 0) {
369 frequency = g_value_get_int (gst_structure_get_value (s, "frequency"));
370 update_frequency (frequency);
373 /* we handled the message we want, and ignored the ones we didn't want.
374 * so the core can unref the message for us */
379 keynote2freq (gint x, gint y)
381 gint i, j, height, found;
382 gfloat frequency = 0;
384 height = drawingarea2->allocation.height;
388 for (i = 0; i < NUM_WKEYS; i++) {
389 // Test for a white key
391 if (between (x, i * WKEY_WIDTH, i * WKEY_WIDTH + (WKEY_WIDTH - 1)) && between (y, 0, height))
393 // Test for a black key
394 if (((i % 7) != 2) && ((i % 7) != 6) && (i != 14)) {
396 if (between (x, 24 + i * 45, 24 + i * 45 + 42)
397 && between (y, 0, height / 2))
401 frequency = equal_tempered_scale[48 + found - 1].frequency;
409 expose_event (GtkWidget * widget, GdkEventExpose * event)
412 static GdkGC *gc = NULL;
415 gc = gdk_gc_new (drawingarea2->window);
417 gdk_gc_set_rgb_fg_color (gc, &drawingarea2->style->fg[0]);
419 gdk_draw_rectangle (drawingarea2->window, gc, FALSE, 0, 0,
420 NUM_WKEYS * WKEY_WIDTH, drawingarea2->allocation.height - 1);
422 for (i = 0; i < NUM_WKEYS - 1; i++)
423 gdk_draw_rectangle (drawingarea2->window, gc, FALSE, i * WKEY_WIDTH, 0,
424 WKEY_WIDTH, drawingarea2->allocation.height - 1);
426 for (i = 0; i < NUM_WKEYS - 1; i++) {
427 if (((i % 7) != 2) && ((i % 7) != 6))
428 gdk_draw_rectangle (drawingarea2->window, gc, TRUE, 24 + i * WKEY_WIDTH, 0,
429 42, drawingarea2->allocation.height / 2);
435 button_press_event (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
437 GstElement *piano = GST_ELEMENT (user_data);
439 if (event->button == 1) {
440 g_object_set (piano, "freq", (gdouble) keynote2freq (event->x, event->y),
441 "volume", 0.8, NULL);
448 button_release_event (GtkWidget * widget, GdkEventButton * event,
451 GstElement *piano = GST_ELEMENT (user_data);
453 if (event->button == 1) {
454 g_object_set (piano, "volume", 0.0, NULL);
461 window_is_active_notify (GObject * object, GParamSpec * pspec, gpointer user_data)
463 if (gtk_window_is_active (GTK_WINDOW (object))) {
465 gst_element_set_state(bin1, GST_STATE_PLAYING);
467 gst_element_set_state(bin2, GST_STATE_PLAYING);
471 gst_element_set_state(bin1, GST_STATE_PAUSED);
473 gst_element_set_state(bin2, GST_STATE_PAUSED);
480 main (int argc, char *argv[])
484 HildonApp *app = NULL;
485 HildonAppView *view = NULL;
487 HildonProgram *app = NULL;
488 HildonWindow *view = NULL;
490 osso_context_t *osso_context = NULL; /* handle to osso */
493 GstElement *src1, *pitch, *sink1;
494 GstElement *src2, *sink2;
501 GtkWidget *alignment;
502 GtkWidget *calibrate;
506 GdkPixbuf *icon = NULL;
507 GError *error = NULL;
509 gboolean piano_enabled = TRUE;
512 gst_init (&argc, &argv);
513 /* Register the GStreamer plugins */
514 plugin_pitch_init (NULL);
515 plugin_tonesrc_init (NULL);
517 recalculate_scale (CALIB_DEFAULT);
519 /* Init the gtk - must be called before any hildon stuff */
520 gtk_init (&argc, &argv);
524 /* Create the hildon application and setup the title */
525 app = HILDON_APP (hildon_app_new ());
526 hildon_app_set_title (app, "Tuner Tool");
527 hildon_app_set_two_part_title (app, TRUE);
529 app = HILDON_PROGRAM (hildon_program_get_instance ());
530 g_set_application_name ("Tuner Tool");
533 /* Initialize maemo application */
534 osso_context = osso_initialize (OSSO_PACKAGE, OSSO_VERSION, TRUE, NULL);
536 /* Check that initialization was ok */
537 if (osso_context == NULL) {
538 g_print ("Bummer, osso failed\n");
540 g_assert (osso_context);
542 mainBox = gtk_vbox_new (FALSE, 0);
543 gtk_container_set_border_width (GTK_CONTAINER (mainBox), 0);
545 view = HILDON_APPVIEW (hildon_appview_new ("Tuner"));
546 hildon_appview_set_fullscreen_key_allowed (view, TRUE);
547 mainWin = GTK_WIDGET (app);
549 view = HILDON_WINDOW (hildon_window_new ());
550 mainWin = GTK_WIDGET (view);
553 mainWin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
554 gtk_window_set_title (GTK_WINDOW (mainWin), "Tuner " TUNER_VERSION);
555 icon = gdk_pixbuf_new_from_file ("tuner64.png", &error);
557 g_print ("Setting icon\n");
558 gtk_window_set_icon (GTK_WINDOW (mainWin), icon);
560 mainBox = gtk_vbox_new (FALSE, 0);
561 gtk_container_set_border_width (GTK_CONTAINER (mainBox), 0);
564 if (GTK_IS_WINDOW (mainWin))
565 g_signal_connect (G_OBJECT (mainWin), "notify::is-active", G_CALLBACK (window_is_active_notify), NULL);
567 /* Bin for tuner functionality */
568 bin1 = gst_pipeline_new ("bin1");
570 src1 = gst_element_factory_make (DEFAULT_AUDIOSRC, "src1");
571 pitch = gst_element_factory_make ("pitch", "pitch");
572 g_object_set (G_OBJECT (pitch), "message", TRUE, "minfreq", 10,
573 "maxfreq", 4000, NULL);
575 sink1 = gst_element_factory_make ("fakesink", "sink1");
576 g_object_set (G_OBJECT (sink1), "silent", 1, NULL);
578 gst_bin_add_many (GST_BIN (bin1), src1, pitch, sink1, NULL);
579 if (!gst_element_link_many (src1, pitch, sink1, NULL)) {
580 fprintf (stderr, "cant link elements\n");
584 bus = gst_element_get_bus (bin1);
585 gst_bus_add_watch (bus, message_handler, NULL);
586 gst_object_unref (bus);
588 /* Bin for piano functionality */
589 bin2 = gst_pipeline_new ("bin2");
591 //src2 = gst_element_factory_make ("audiotestsrc", "src2");
592 //g_object_set (G_OBJECT (src2), "volume", 0.0, "wave", 7, NULL);
593 src2 = gst_element_factory_make ("tonesrc", "src2");
594 g_object_set (G_OBJECT (src2), "volume", 0.0, NULL);
595 sink2 = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink2");
597 gst_bin_add_many (GST_BIN (bin2), src2, sink2, NULL);
598 if (!gst_element_link_many (src2, sink2, NULL)) {
599 piano_enabled = FALSE;
603 g_signal_connect (G_OBJECT (mainWin), "destroy",
604 G_CALLBACK (on_window_destroy), NULL);
605 g_signal_connect (G_OBJECT(mainWin), "key_press_event",
606 G_CALLBACK(key_press_event), mainWin);
609 targetFrequency = gtk_label_new ("");
610 gtk_box_pack_start (GTK_BOX (mainBox), targetFrequency, FALSE, FALSE, 5);
613 drawingarea1 = gtk_drawing_area_new ();
614 gtk_widget_set_size_request (drawingarea1, 636, 40);
615 gtk_box_pack_start (GTK_BOX (mainBox), drawingarea1, FALSE, FALSE, 5);
617 /* Current frequency lable */
618 currentFrequency = gtk_label_new ("");
619 gtk_box_pack_start (GTK_BOX (mainBox), currentFrequency, FALSE, FALSE, 5);
621 /* Calibration spinner */
622 box = gtk_hbox_new (FALSE, 0);
623 alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
624 label = gtk_label_new ("Calibration");
625 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 5);
628 calibrate = hildon_number_editor_new (CALIB_MIN, CALIB_MAX);
629 hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (calibrate),
631 /* we don't want that ugly cursor there */
632 gtk_container_forall (GTK_CONTAINER (calibrate),
633 (GtkCallback) fix_hildon_number_editor, NULL);
634 g_signal_connect (G_OBJECT (calibrate), "notify::value",
635 G_CALLBACK (calibration_changed), NULL);
637 calibrate = gtk_spin_button_new_with_range (CALIB_MIN, CALIB_MAX, 1);
638 gtk_spin_button_set_value (GTK_SPIN_BUTTON (calibrate), CALIB_DEFAULT);
639 g_signal_connect (G_OBJECT (calibrate), "value_changed",
640 G_CALLBACK (calibration_changed), NULL);
642 gtk_box_pack_start (GTK_BOX (box), calibrate, FALSE, FALSE, 5);
643 gtk_container_add (GTK_CONTAINER (alignment), box);
644 gtk_box_pack_start (GTK_BOX (mainBox), alignment, FALSE, FALSE, 5);
647 sep = gtk_hseparator_new ();
650 gtk_box_pack_start (GTK_BOX (mainBox), sep, FALSE, FALSE, 5);
652 label = gtk_label_new ("Tuner Tool developed by Josep Torra.\n"
653 "http://n770galaxy.blogspot.com/");
654 gtk_box_pack_start (GTK_BOX (mainBox), label, FALSE, FALSE, 5);
657 alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
658 drawingarea2 = gtk_drawing_area_new ();
659 gtk_widget_set_size_request (drawingarea2, NUM_WKEYS * WKEY_WIDTH + 1, 130);
660 gtk_container_add (GTK_CONTAINER (alignment), drawingarea2);
661 gtk_box_pack_start (GTK_BOX (mainBox), alignment, FALSE, FALSE, 5);
663 g_signal_connect (G_OBJECT (drawingarea2), "expose_event",
664 G_CALLBACK (expose_event), NULL);
666 g_signal_connect (G_OBJECT (drawingarea2), "button_press_event",
667 G_CALLBACK (button_press_event), (gpointer) src2);
669 g_signal_connect (G_OBJECT (drawingarea2), "button_release_event",
670 G_CALLBACK (button_release_event), (gpointer) src2);
672 gtk_widget_set_events (drawingarea2, GDK_EXPOSURE_MASK
673 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
675 gtk_widget_set_events (drawingarea2, GDK_EXPOSURE_MASK);
678 gtk_container_add (GTK_CONTAINER (view), mainBox);
680 hildon_app_set_appview (app, view);
681 gtk_widget_show_all (GTK_WIDGET (app));
683 hildon_program_add_window (app, view);
684 gtk_widget_show_all (GTK_WIDGET (view));
687 gtk_container_add (GTK_CONTAINER (mainWin), mainBox);
688 gtk_widget_show_all (GTK_WIDGET (mainWin));
691 gst_element_set_state (bin1, GST_STATE_PLAYING);
692 gst_element_set_state (bin2, GST_STATE_PLAYING);
694 gst_element_set_state (bin2, GST_STATE_NULL);
695 gst_element_set_state (bin1, GST_STATE_NULL);
697 gst_object_unref (bin1);
698 gst_object_unref (bin2);