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-program.h>
31 # include <hildon/hildon-number-editor.h>
32 # elif defined(MAEMO1)
33 # include <hildon-widgets/hildon-app.h>
34 # include <hildon-widgets/hildon-appview.h>
36 # include <hildon-widgets/hildon-program.h>
41 #define OSSO_PACKAGE "tuner-tool"
42 #define OSSO_VERSION TUNER_VERSION
44 #endif /* ifdef HILDON */
47 # define DEFAULT_AUDIOSRC "dsppcmsrc"
48 # define DEFAULT_AUDIOSINK "dsppcmsink"
50 # define DEFAULT_AUDIOSRC "alsasrc"
51 # define DEFAULT_AUDIOSINK "alsasink"
60 #define between(x,a,b) (((x)>=(a)) && ((x)<=(b)))
62 #define MAGIC (1.059463094359f) /* 2^(1/2) */
64 extern gboolean plugin_pitch_init (GstPlugin * plugin);
65 extern gboolean plugin_tonesrc_init (GstPlugin * plugin);
87 static Note equal_tempered_scale[] = {
161 {"C#6/Db6", 1108.73},
163 {"D#6/Eb6", 1244.51},
166 {"F#6/Gb6", 1479.98},
168 {"G#6/Ab6", 1661.22},
170 {"A#6/Bb6", 1864.66},
173 {"C#7/Db7", 2217.46},
175 {"D#7/Eb7", 2489.02},
178 {"F#7/Gb7", 2959.96},
180 {"G#7/Ab7", 3322.44},
182 {"A#7/Bb7", 3729.31},
186 static GdkColor ledOnColor = { 0, 0 * 255, 180 * 255, 95 * 255 };
187 static GdkColor ledOnColor2 = { 0, 180 * 255, 180 * 255, 0 * 255 };
188 static GdkColor ledOffColor = { 0, 80 * 255, 80 * 255, 80 * 255 };
191 GtkWidget *targetFrequency;
192 GtkWidget *currentFrequency;
193 GtkWidget *drawingarea1;
194 GtkWidget *drawingarea2;
197 recalculate_scale (double a4)
201 for (i = 0; i < NUM_NOTES; i++) {
202 equal_tempered_scale[i].frequency = a4 * pow (MAGIC, i - 57);
203 /* fprintf(stdout, "%s: %.2f\n", equal_tempered_scale[i].name, equal_tempered_scale[i].frequency); */
209 fix_hildon_number_editor (GtkWidget * widget, gpointer data)
211 if (GTK_IS_EDITABLE (widget)) {
212 gtk_editable_set_editable (GTK_EDITABLE (widget), FALSE);
213 g_object_set (G_OBJECT (widget), "can-focus", FALSE, NULL);
219 calibration_changed (GObject * object, GParamSpec * pspec, gpointer user_data)
224 value = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (object));
226 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (object));
229 if (value >= CALIB_MIN && value <= CALIB_MAX) {
230 recalculate_scale (value);
231 g_debug ("Calibration changed to %d Hz", value);
236 on_window_destroy (GtkObject * object, gpointer user_data)
245 static GdkGC *gc = NULL;
248 gc = gdk_gc_new (drawingarea1->window);
250 gdk_gc_set_rgb_fg_color (gc, &drawingarea1->style->fg[0]);
252 gdk_draw_rectangle (drawingarea1->window, gc, TRUE, 0, 0,
253 drawingarea1->allocation.width, drawingarea1->allocation.height);
255 if (abs (n) > (NUM_LEDS / 2))
256 n = n / n * (NUM_LEDS / 2);
259 j = NUM_LEDS / 2 + 1;
260 k = NUM_LEDS / 2 + n;
262 j = NUM_LEDS / 2 + n;
263 k = NUM_LEDS / 2 - 1;
267 for (i = 0; i < NUM_LEDS; i++) {
268 if (i == NUM_LEDS / 2) {
270 gdk_gc_set_rgb_fg_color (gc, &ledOnColor2);
272 gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
274 gdk_draw_rectangle (drawingarea1->window, gc, TRUE, (i * 10) + 8, 2, 4,
277 if ((i >= j) && (i <= k))
278 gdk_gc_set_rgb_fg_color (gc, &ledOnColor);
280 gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
282 gdk_draw_rectangle (drawingarea1->window, gc, TRUE, (i * 10) + 6, 10, 8,
288 /* update frequency info */
290 update_frequency (gint frequency)
294 gfloat diff, min_diff;
296 min_diff = fabs (frequency - (equal_tempered_scale[0].frequency - 10));
297 for (i = j = 0; i < NUM_NOTES; i++) {
298 diff = fabs (frequency - equal_tempered_scale[i].frequency);
299 if (diff <= min_diff) {
308 g_strdup_printf ("Nearest note is %s with %.2f Hz frequency",
309 equal_tempered_scale[j].name, equal_tempered_scale[j].frequency);
310 gtk_label_set_text (GTK_LABEL (targetFrequency), buffer);
313 buffer = g_strdup_printf ("Played frequency is %d Hz", frequency);
314 gtk_label_set_text (GTK_LABEL (currentFrequency), buffer);
317 draw_leds ((gint) roundf (min_diff));
320 /* receive spectral data from element message */
322 message_handler (GstBus * bus, GstMessage * message, gpointer data)
324 if (message->type == GST_MESSAGE_ELEMENT) {
325 const GstStructure *s = gst_message_get_structure (message);
326 const gchar *name = gst_structure_get_name (s);
328 if (strcmp (name, "pitch") == 0) {
331 frequency = g_value_get_int (gst_structure_get_value (s, "frequency"));
332 update_frequency (frequency);
335 /* we handled the message we want, and ignored the ones we didn't want.
336 * so the core can unref the message for us */
341 keynote2freq (gint x, gint y)
343 gint i, j, height, found;
344 gfloat frequency = 0;
346 height = drawingarea2->allocation.height;
350 for (i = 0; i < 15; i++) {
351 // Test for a white key
353 if (between (x, i * 45, i * 45 + 44) && between (y, 0, height))
355 // Test for a black key
356 if (((i % 7) != 2) && ((i % 7) != 6) && (i != 14)) {
358 if (between (x, 24 + i * 45, 24 + i * 45 + 42)
359 && between (y, 0, height / 2))
363 frequency = equal_tempered_scale[48 + found - 1].frequency;
371 expose_event (GtkWidget * widget, GdkEventExpose * event)
374 static GdkGC *gc = NULL;
377 gc = gdk_gc_new (drawingarea2->window);
379 gdk_gc_set_rgb_fg_color (gc, &drawingarea2->style->fg[0]);
381 gdk_draw_rectangle (drawingarea2->window, gc, FALSE, 0, 0,
382 drawingarea2->allocation.width - 1, drawingarea2->allocation.height - 1);
384 for (i = 0; i < 14; i++)
385 gdk_draw_rectangle (drawingarea2->window, gc, FALSE, i * 45, 0,
386 45, drawingarea2->allocation.height - 1);
388 for (i = 0; i < 14; i++) {
389 if (((i % 7) != 2) && ((i % 7) != 6))
390 gdk_draw_rectangle (drawingarea2->window, gc, TRUE, 24 + i * 45, 0,
391 42, drawingarea2->allocation.height / 2);
397 key_press_event (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
399 GstElement *piano = GST_ELEMENT (user_data);
401 if (event->button == 1) {
402 g_object_set (piano, "freq", (gdouble) keynote2freq (event->x, event->y),
403 "volume", 0.8, NULL);
410 key_release_event (GtkWidget * widget, GdkEventButton * event,
413 GstElement *piano = GST_ELEMENT (user_data);
415 if (event->button == 1) {
416 g_object_set (piano, "volume", 0.0, NULL);
423 main (int argc, char *argv[])
427 HildonApp *app = NULL;
428 HildonAppView *view = NULL;
430 HildonProgram *app = NULL;
431 HildonWindow *view = NULL;
433 osso_context_t *osso_context = NULL; /* handle to osso */
436 GstElement *bin1, *bin2;
437 GstElement *src1, *pitch, *sink1;
438 GstElement *src2, *sink2;
444 GtkWidget *alignment;
445 GtkWidget *calibrate;
449 GdkPixbuf *icon = NULL;
450 GError *error = NULL;
452 gboolean piano_enabled = TRUE;
455 gst_init (&argc, &argv);
456 /* Register the GStreamer plugins */
457 plugin_pitch_init (NULL);
458 plugin_tonesrc_init (NULL);
460 recalculate_scale (CALIB_DEFAULT);
462 /* Init the gtk - must be called before any hildon stuff */
463 gtk_init (&argc, &argv);
467 /* Create the hildon application and setup the title */
468 app = HILDON_APP (hildon_app_new ());
469 hildon_app_set_title (app, "Tuner Tool");
470 hildon_app_set_two_part_title (app, TRUE);
472 app = HILDON_PROGRAM (hildon_program_get_instance ());
473 g_set_application_name ("Tuner Tool");
476 /* Initialize maemo application */
477 osso_context = osso_initialize (OSSO_PACKAGE, OSSO_VERSION, TRUE, NULL);
479 /* Check that initialization was ok */
480 if (osso_context == NULL) {
481 g_print ("Bummer, osso failed\n");
483 g_assert (osso_context);
485 mainBox = gtk_vbox_new (FALSE, 0);
486 gtk_container_set_border_width (GTK_CONTAINER (mainBox), 0);
488 view = HILDON_APPVIEW (hildon_appview_new ("Tuner"));
489 hildon_appview_set_fullscreen_key_allowed (view, TRUE);
490 mainWin = GTK_WIDGET (app);
492 view = HILDON_WINDOW (hildon_window_new ());
493 mainWin = GTK_WIDGET (view);
496 mainWin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
497 gtk_window_set_title (GTK_WINDOW (mainWin), "Tuner " TUNER_VERSION);
498 icon = gdk_pixbuf_new_from_file ("tuner64.png", &error);
500 g_print ("Setting icon\n");
501 gtk_window_set_icon (GTK_WINDOW (mainWin), icon);
503 mainBox = gtk_vbox_new (FALSE, 0);
504 gtk_container_set_border_width (GTK_CONTAINER (mainBox), 0);
507 /* Bin for tuner functionality */
508 bin1 = gst_pipeline_new ("bin1");
510 src1 = gst_element_factory_make (DEFAULT_AUDIOSRC, "src1");
511 pitch = gst_element_factory_make ("pitch", "pitch");
512 g_object_set (G_OBJECT (pitch), "message", TRUE, "minfreq", 10,
513 "maxfreq", 4000, NULL);
515 sink1 = gst_element_factory_make ("fakesink", "sink1");
516 g_object_set (G_OBJECT (sink1), "silent", 1, NULL);
518 gst_bin_add_many (GST_BIN (bin1), src1, pitch, sink1, NULL);
519 if (!gst_element_link_many (src1, pitch, sink1, NULL)) {
520 fprintf (stderr, "cant link elements\n");
524 bus = gst_element_get_bus (bin1);
525 gst_bus_add_watch (bus, message_handler, NULL);
526 gst_object_unref (bus);
528 /* Bin for piano functionality */
529 bin2 = gst_pipeline_new ("bin2");
531 //src2 = gst_element_factory_make ("audiotestsrc", "src2");
532 //g_object_set (G_OBJECT (src2), "volume", 0.0, "wave", 7, NULL);
533 src2 = gst_element_factory_make ("tonesrc", "src2");
534 g_object_set (G_OBJECT (src2), "volume", 0.0, NULL);
535 sink2 = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink2");
537 gst_bin_add_many (GST_BIN (bin2), src2, sink2, NULL);
538 if (!gst_element_link_many (src2, sink2, NULL)) {
539 piano_enabled = FALSE;
543 g_signal_connect (G_OBJECT (mainWin), "destroy",
544 G_CALLBACK (on_window_destroy), NULL);
547 targetFrequency = gtk_label_new ("");
548 gtk_box_pack_start (GTK_BOX (mainBox), targetFrequency, FALSE, FALSE, 5);
551 drawingarea1 = gtk_drawing_area_new ();
552 gtk_widget_set_size_request (drawingarea1, 636, 40);
553 gtk_box_pack_start (GTK_BOX (mainBox), drawingarea1, FALSE, FALSE, 5);
555 /* Current frequency lable */
556 currentFrequency = gtk_label_new ("");
557 gtk_box_pack_start (GTK_BOX (mainBox), currentFrequency, FALSE, FALSE, 5);
559 /* Calibration spinner */
560 box = gtk_hbox_new (FALSE, 0);
561 alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
562 label = gtk_label_new ("Calibration");
563 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 5);
566 calibrate = hildon_number_editor_new (CALIB_MIN, CALIB_MAX);
567 hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (calibrate),
569 /* we don't want that ugly cursor there */
570 gtk_container_forall (GTK_CONTAINER (calibrate),
571 (GtkCallback) fix_hildon_number_editor, NULL);
572 g_signal_connect (G_OBJECT (calibrate), "notify::value",
573 G_CALLBACK (calibration_changed), NULL);
575 calibrate = gtk_spin_button_new_with_range (CALIB_MIN, CALIB_MAX, 1);
576 gtk_spin_button_set_value (GTK_SPIN_BUTTON (calibrate), CALIB_DEFAULT);
577 g_signal_connect (G_OBJECT (calibrate), "value_changed",
578 G_CALLBACK (calibration_changed), NULL);
580 gtk_box_pack_start (GTK_BOX (box), calibrate, FALSE, FALSE, 5);
581 gtk_container_add (GTK_CONTAINER (alignment), box);
582 gtk_box_pack_start (GTK_BOX (mainBox), alignment, FALSE, FALSE, 5);
585 sep = gtk_hseparator_new ();
588 gtk_box_pack_start (GTK_BOX (mainBox), sep, FALSE, FALSE, 5);
590 label = gtk_label_new ("Tuner Tool developed by Josep Torra.\n"
591 "http://n770galaxy.blogspot.com/");
592 gtk_box_pack_start (GTK_BOX (mainBox), label, FALSE, FALSE, 5);
595 drawingarea2 = gtk_drawing_area_new ();
596 gtk_widget_set_size_request (drawingarea2, 636, 130);
597 gtk_box_pack_start (GTK_BOX (mainBox), drawingarea2, FALSE, FALSE, 5);
599 g_signal_connect (G_OBJECT (drawingarea2), "expose_event",
600 G_CALLBACK (expose_event), NULL);
602 g_signal_connect (G_OBJECT (drawingarea2), "button_press_event",
603 G_CALLBACK (key_press_event), (gpointer) src2);
605 g_signal_connect (G_OBJECT (drawingarea2), "button_release_event",
606 G_CALLBACK (key_release_event), (gpointer) src2);
608 gtk_widget_set_events (drawingarea2, GDK_EXPOSURE_MASK
609 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
611 gtk_widget_set_events (drawingarea2, GDK_EXPOSURE_MASK);
614 gtk_container_add (GTK_CONTAINER (view), mainBox);
616 hildon_app_set_appview (app, view);
617 gtk_widget_show_all (GTK_WIDGET (app));
619 hildon_program_add_window (app, view);
620 gtk_widget_show_all (GTK_WIDGET (view));
623 gtk_container_add (GTK_CONTAINER (mainWin), mainBox);
624 gtk_widget_show_all (GTK_WIDGET (mainWin));
627 gst_element_set_state (bin1, GST_STATE_PLAYING);
628 gst_element_set_state (bin2, GST_STATE_PLAYING);
630 gst_element_set_state (bin2, GST_STATE_NULL);
631 gst_element_set_state (bin1, GST_STATE_NULL);
633 gst_object_unref (bin1);
634 gst_object_unref (bin2);