make the leds display the difference in steps of two cents
[tunertool] / src / tuner.c
1 /* vim: set sts=2 sw=2 et: */
2 /* Tuner
3  * Copyright (C) 2006 Josep Torra <j.torra@telefonica.net>
4  *               2008-2009 Jari Tenhunen <jari.tenhunen@iki.fi>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #define TUNER_VERSION "0.4"
28
29 #ifdef HILDON
30 #include <hildon/hildon-defines.h>
31 #include <hildon/hildon-program.h>
32 #include <hildon/hildon-number-editor.h>
33
34 #include <libosso.h>
35
36 #define OSSO_PACKAGE "tuner-tool"
37 #define OSSO_VERSION TUNER_VERSION
38
39 #endif /* ifdef HILDON */
40
41 #ifdef MAEMO
42 # define DEFAULT_AUDIOSRC "pulsesrc"
43 # define DEFAULT_AUDIOSINK "pulsesink"
44 #else
45 # define DEFAULT_AUDIOSRC "alsasrc"
46 # define DEFAULT_AUDIOSINK "alsasink"
47 #endif
48
49
50 #include <string.h>
51 #include <math.h>
52 #include <gst/gst.h>
53 #include <gtk/gtk.h>
54 #include <gconf/gconf-client.h>
55
56 #include "gstpitch.h"
57 #include "settings.h"
58
59 #define between(x,a,b) (((x)>=(a)) && ((x)<=(b)))
60
61 #define MAGIC (1.059463094359f) /* 2^(1/2) */
62 #define CENT (1.0005777895f) /* 1/100th of a half-tone */
63 #define LOG_CENT (0.00057762265046662109f) /* ln (CENT) */
64
65 extern gboolean plugin_pitch_init (GstPlugin * plugin);
66 extern gboolean plugin_tonesrc_init (GstPlugin * plugin);
67
68 typedef struct
69 {
70   const gchar *name;
71   gfloat frequency;
72 } Note;
73
74 struct app_data
75 {
76   GtkWidget *targetFrequency;
77   GtkWidget *currentFrequency;
78   GtkWidget *drawingarea1;
79   GtkWidget *drawingarea2;
80
81   GstElement *bin1;
82   GstElement *bin2;
83   GstElement *tonesrc;
84   GstElement *pitch;
85   guint stop_timer_id;
86   guint vol_timer_id;
87   gdouble target_vol;
88
89   gboolean display_keepalive;
90 #ifdef MAEMO
91   osso_context_t *osso_context;
92   gpointer app;
93   guint display_timer_id;
94 #endif
95 };
96
97 typedef struct app_data AppData;
98
99 enum
100 {
101   NUM_NOTES = 96
102 };
103
104
105
106 #define NUM_LEDS (50)
107 #define NUM_WKEYS (15) /* # of white keys in the piano keyboard */
108 #define WKEY_WIDTH (45)
109
110 static Note equal_tempered_scale[] = {
111   {"C0", 16.35},
112   {"C#0/Db0", 17.32},
113   {"D0", 18.35},
114   {"D#0/Eb0", 19.45},
115   {"E0", 20.60},
116   {"F0", 21.83},
117   {"F#0/Gb0", 23.12},
118   {"G0", 24.50},
119   {"G#0/Ab0", 25.96},
120   {"A0", 27.50},
121   {"A#0/Bb0", 29.14},
122   {"B0", 30.87},
123   {"C1", 32.70},
124   {"C#1/Db1", 34.65},
125   {"D1", 36.71},
126   {"D#1/Eb1", 38.89},
127   {"E1", 41.20},
128   {"F1", 43.65},
129   {"F#1/Gb1", 46.25},
130   {"G1", 49.00},
131   {"G#1/Ab1", 51.91},
132   {"A1", 55.00},
133   {"A#1/Bb1", 58.27},
134   {"B1", 61.74},
135   {"C2", 65.41},
136   {"C#2/Db2", 69.30},
137   {"D2", 73.42},
138   {"D#2/Eb2", 77.78},
139   {"E2", 82.41},
140   {"F2", 87.31},
141   {"F#2/Gb2", 92.50},
142   {"G2", 98.00},
143   {"G#2/Ab2", 103.83},
144   {"A2", 110.00},
145   {"A#2/Bb2", 116.54},
146   {"B2", 123.47},
147   {"C3", 130.81},
148   {"C#3/Db3", 138.59},
149   {"D3", 146.83},
150   {"D#3/Eb3", 155.56},
151   {"E3", 164.81},
152   {"F3", 174.61},
153   {"F#3/Gb3", 185.00},
154   {"G3", 196.00},
155   {"G#3/Ab3", 207.65},
156   {"A3", 220.00},
157   {"A#3/Bb3", 233.08},
158   {"B3", 246.94},
159   {"C4", 261.63},
160   {"C#4/Db4", 277.18},
161   {"D4", 293.66},
162   {"D#4/Eb4", 311.13},
163   {"E4", 329.63},
164   {"F4", 349.23},
165   {"F#4/Gb4", 369.99},
166   {"G4", 392.00},
167   {"G#4/Ab4", 415.30},
168   {"A4", 440.00},
169   {"A#4/Bb4", 466.16},
170   {"B4", 493.88},
171   {"C5", 523.25},
172   {"C#5/Db5", 554.37},
173   {"D5", 587.33},
174   {"D#5/Eb5", 622.25},
175   {"E5", 659.26},
176   {"F5", 698.46},
177   {"F#5/Gb5", 739.99},
178   {"G5", 783.99},
179   {"G#5/Ab5", 830.61},
180   {"A5", 880.00},
181   {"A#5/Bb5", 932.33},
182   {"B5", 987.77},
183   {"C6", 1046.50},
184   {"C#6/Db6", 1108.73},
185   {"D6", 1174.66},
186   {"D#6/Eb6", 1244.51},
187   {"E6", 1318.51},
188   {"F6", 1396.91},
189   {"F#6/Gb6", 1479.98},
190   {"G6", 1567.98},
191   {"G#6/Ab6", 1661.22},
192   {"A6", 1760.00},
193   {"A#6/Bb6", 1864.66},
194   {"B6", 1975.53},
195   {"C7", 2093.00},
196   {"C#7/Db7", 2217.46},
197   {"D7", 2349.32},
198   {"D#7/Eb7", 2489.02},
199   {"E7", 2637.02},
200   {"F7", 2793.83},
201   {"F#7/Gb7", 2959.96},
202   {"G7", 3135.96},
203   {"G#7/Ab7", 3322.44},
204   {"A7", 3520.00},
205   {"A#7/Bb7", 3729.31},
206   {"B7", 3951.07},
207 };
208
209 static GdkColor ledOnColor = { 0, 0 * 255, 180 * 255, 95 * 255 };
210 static GdkColor ledOnColor2 = { 0, 180 * 255, 180 * 255, 0 * 255 };
211 static GdkColor ledOffColor = { 0, 80 * 255, 80 * 255, 80 * 255 };
212 static GdkColor whiteColor = { 0, 65535, 65535, 65535 };
213 static GdkColor blackColor = { 0, 0, 0, 0 };
214
215 static void
216 recalculate_scale (double a4)
217 {
218   int i;
219
220   for (i = 0; i < NUM_NOTES; i++) {
221     equal_tempered_scale[i].frequency = a4 * pow (MAGIC, i - 57);
222     /* fprintf(stdout, "%s: %.2f\n", equal_tempered_scale[i].name, equal_tempered_scale[i].frequency); */
223   }
224 }
225
226 static void
227 calibration_changed (GObject * object, GParamSpec * pspec, gpointer user_data)
228 {
229   gint value;
230
231 #ifdef HILDON
232   value = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (object));
233 #else
234   value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (object));
235 #endif
236
237   if (value >= CALIB_MIN && value <= CALIB_MAX) {
238     recalculate_scale (value);
239     settings_set_calibration (value);
240   }
241 }
242
243 static void
244 on_window_destroy (GtkObject * object, gpointer user_data)
245 {
246   gtk_main_quit ();
247 }
248
249 static void
250 toggle_fullscreen (GtkWindow * window)
251 {
252   static gboolean fullscreen = FALSE;
253
254   fullscreen = !fullscreen;
255   if (fullscreen)
256     gtk_window_fullscreen (GTK_WINDOW (window));
257   else
258     gtk_window_unfullscreen (GTK_WINDOW (window));
259 }
260
261 static gboolean 
262 key_press_event (GtkWidget * widget, GdkEventKey * event, GtkWindow * window)
263 {
264   switch (event->keyval) {
265 #ifdef HILDON
266     case HILDON_HARDKEY_FULLSCREEN:
267       toggle_fullscreen (window);
268       break;
269 #endif
270     default:
271       break;
272   }
273
274   return FALSE;
275 }
276
277 static void
278 draw_leds (AppData * appdata, gint n)
279 {
280   gint i, j, k;
281   static GdkGC *gc = NULL;
282   gint width = appdata->drawingarea1->allocation.width;
283   gint led_width = ((gfloat) width / (gfloat) (NUM_LEDS)) * 0.8;
284   gint led_space = ((gfloat) width / (gfloat) (NUM_LEDS)) * 0.2;
285   gint led_total = led_width + led_space;
286   gint padding = (width - NUM_LEDS * led_total) / 2;
287
288   if (!gc) {
289     gc = gdk_gc_new (appdata->drawingarea1->window);
290   }
291   gdk_gc_set_rgb_fg_color (gc, &appdata->drawingarea1->style->fg[0]);
292
293   gdk_draw_rectangle (appdata->drawingarea1->window, gc, TRUE, 0, 0,
294       appdata->drawingarea1->allocation.width, appdata->drawingarea1->allocation.height);
295
296   if (abs (n) > (NUM_LEDS / 2))
297     n = n / n * (NUM_LEDS / 2);
298
299   if (n > 0) {
300     j = NUM_LEDS / 2 + 1;
301     k = NUM_LEDS / 2 + n;
302   } else {
303     j = NUM_LEDS / 2 + n;
304     k = NUM_LEDS / 2 - 1;
305   }
306
307   // Draw all leds
308   for (i = 0; i < NUM_LEDS; i++) {
309     if (i == NUM_LEDS / 2) {
310       if (n == 0)
311         gdk_gc_set_rgb_fg_color (gc, &ledOnColor2);
312       else
313         gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
314
315       gdk_draw_rectangle (appdata->drawingarea1->window, gc, TRUE, padding + (i * led_total) + ((led_total - 4) / 2), 2, 4,
316           36);
317     } else {
318       if ((i >= j) && (i <= k))
319         gdk_gc_set_rgb_fg_color (gc, &ledOnColor);
320       else
321         gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
322
323       gdk_draw_rectangle (appdata->drawingarea1->window, gc, TRUE, padding + (i * led_total), 10, led_width,
324           20);
325     }
326   }
327 }
328
329 /* translate the interval (ratio of two freqs) into cents */
330 gfloat
331 interval2cent (gfloat freq, gfloat note)
332 {
333   //return (gfloat) (log (freq / note) / log (CENT));
334   return (gfloat) (log (freq / note) / LOG_CENT);
335 }
336
337 /* update frequency info */
338 static void
339 update_frequency (AppData * appdata, gfloat frequency)
340 {
341   gchar *buffer;
342   gint i, j;
343   gfloat diff, min_diff;
344
345   min_diff = frequency - (equal_tempered_scale[0].frequency - 10);
346   for (i = j = 0; i < NUM_NOTES; i++) {
347     diff = frequency - equal_tempered_scale[i].frequency;
348     if (fabs (diff) <= fabs (min_diff)) {
349       min_diff = diff;
350       j = i;
351     } else {
352       break;
353     }
354   }
355
356   buffer =
357       g_strdup_printf ("Nearest note is %s with %.2f Hz frequency",
358       equal_tempered_scale[j].name, equal_tempered_scale[j].frequency);
359   gtk_label_set_text (GTK_LABEL (appdata->targetFrequency), buffer);
360   g_free (buffer);
361
362   buffer = g_strdup_printf ("Played frequency is %.2f Hz", frequency);
363   gtk_label_set_text (GTK_LABEL (appdata->currentFrequency), buffer);
364   g_free (buffer);
365
366   /* make leds display the difference in steps of two cents */
367   diff = interval2cent (frequency, equal_tempered_scale[j].frequency);
368   draw_leds (appdata, (gint) roundf (diff / 2.0));
369 }
370
371 /* receive spectral data from element message */
372 gboolean
373 message_handler (GstBus * bus, GstMessage * message, gpointer data)
374 {
375   if (message->type == GST_MESSAGE_ELEMENT) {
376     const GstStructure *s = gst_message_get_structure (message);
377     const gchar *name = gst_structure_get_name (s);
378
379     if (strcmp (name, "pitch") == 0) {
380       gfloat frequency;
381
382       frequency = g_value_get_float (gst_structure_get_value (s, "frequency"));
383       if (frequency != 0)
384         update_frequency (data, frequency);
385     }
386   }
387   /* we handled the message we want, and ignored the ones we didn't want.
388    * so the core can unref the message for us */
389   return TRUE;
390 }
391
392 gfloat
393 keynote2freq (AppData * appdata, gint x, gint y)
394 {
395   gint i, j, height, found;
396   gfloat frequency = 0;
397
398   height = appdata->drawingarea2->allocation.height;
399
400   j = 0;
401   found = 0;
402   for (i = 0; i < NUM_WKEYS; i++) {
403     // Test for a white key  
404     j++;
405     if (between (x, i * WKEY_WIDTH, i * WKEY_WIDTH + (WKEY_WIDTH - 1)) && between (y, 0, height))
406       found = j;
407     // Test for a black key
408     if (((i % 7) != 2) && ((i % 7) != 6) && (i != 14)) {
409       j++;
410       if (between (x, 24 + i * 45, 24 + i * 45 + 42)
411           && between (y, 0, height / 2))
412         found = j;
413     }
414     if (found) {
415       frequency = equal_tempered_scale[48 + found - 1].frequency;
416       break;
417     }
418   }
419   return frequency;
420 }
421
422 static gboolean
423 expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
424 {
425   AppData * appdata = (AppData *) user_data;
426   gint i;
427   static GdkGC *gc = NULL;
428
429   if (!gc) {
430     gc = gdk_gc_new (appdata->drawingarea2->window);
431   }
432   gdk_gc_set_rgb_fg_color (gc, &whiteColor);
433   gdk_draw_rectangle (appdata->drawingarea2->window, gc, TRUE, 0, 0,
434       NUM_WKEYS * WKEY_WIDTH, appdata->drawingarea2->allocation.height - 1);
435
436   gdk_gc_set_rgb_fg_color (gc, &blackColor);
437   gdk_draw_rectangle (appdata->drawingarea2->window, gc, FALSE, 0, 0,
438       NUM_WKEYS * WKEY_WIDTH, appdata->drawingarea2->allocation.height - 1);
439
440   for (i = 0; i < NUM_WKEYS - 1; i++)
441     gdk_draw_rectangle (appdata->drawingarea2->window, gc, FALSE, i * WKEY_WIDTH, 0,
442         WKEY_WIDTH, appdata->drawingarea2->allocation.height - 1);
443
444   for (i = 0; i < NUM_WKEYS - 1; i++) {
445     if (((i % 7) != 2) && ((i % 7) != 6))
446       gdk_draw_rectangle (appdata->drawingarea2->window, gc, TRUE, 24 + i * WKEY_WIDTH, 0,
447           42, appdata->drawingarea2->allocation.height / 2);
448   }
449   return FALSE;
450 }
451
452 static gboolean
453 button_press_event (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
454 {
455   AppData * appdata = (AppData *) user_data;
456
457   if (event->button == 1) {
458     g_object_set (appdata->tonesrc, "freq", (gdouble) keynote2freq (appdata, event->x, event->y),
459         "volume", 0.8, NULL);
460   }
461
462   return TRUE;
463 }
464
465 static gboolean
466 button_release_event (GtkWidget * widget, GdkEventButton * event,
467     gpointer user_data)
468 {
469   AppData * appdata = (AppData *) user_data;
470
471   if (event->button == 1) {
472     g_object_set (appdata->tonesrc, "volume", 0.0, NULL);
473   }
474
475   return TRUE;
476 }
477
478 static void
479 set_pipeline_states (AppData * appdata, GstState state)
480 {
481     if (appdata->bin1)
482       gst_element_set_state (appdata->bin1, state);
483
484     if (appdata->bin2)
485       gst_element_set_state (appdata->bin2, state);
486 }
487
488 static gboolean
489 stop_pipelines (gpointer user_data)
490 {
491   AppData * appdata = (AppData *) user_data;
492
493   /* dsppcmsrc needs to go to READY or NULL state to make 
494    * the DSP sleep and OMAP reach retention mode */
495   set_pipeline_states (appdata, GST_STATE_READY); 
496   appdata->stop_timer_id = 0;
497
498   return FALSE;
499 }
500
501 #ifdef FAKE_FREQUENCY
502 static gboolean
503 fake_frequency (gpointer user_data)
504 {
505   AppData * appdata = (AppData *) user_data;
506
507   update_frequency (appdata, 440.0);
508
509   return TRUE;
510 }
511 #endif
512
513 #ifdef MAEMO
514 static void
515 osso_hw_state_cb (osso_hw_state_t *state, gpointer user_data)
516 {
517   AppData * appdata = (AppData *) user_data;
518
519   if (state->shutdown_ind) {
520     gtk_main_quit ();
521     return;
522   }
523
524   if (state->system_inactivity_ind) {
525     /* do not stop pipelines if the app is on foreground 
526      * and display is kept on */
527     if (appdata->display_timer_id == 0) {
528       if (appdata->stop_timer_id != 0)
529         g_source_remove (appdata->stop_timer_id);
530
531       appdata->stop_timer_id = g_timeout_add (5000, (GSourceFunc) stop_pipelines, user_data);
532     }
533   }
534   else {
535 #if HILDON == 1
536     if (hildon_program_get_is_topmost (HILDON_PROGRAM (appdata->app))) {
537       if (appdata->stop_timer_id != 0) {
538         g_source_remove (appdata->stop_timer_id);
539         appdata->stop_timer_id = 0;
540       }
541
542       set_pipeline_states (appdata, GST_STATE_PLAYING);
543     }
544     /* not topmost => topmost_notify will set pipelines to PLAYING 
545      * when the application is on the foreground again */
546 #else
547     if (appdata->stop_timer_id != 0) {
548       g_source_remove (appdata->stop_timer_id);
549       appdata->stop_timer_id = 0;
550     }
551
552     set_pipeline_states (appdata, GST_STATE_PLAYING);
553 #endif
554
555   }
556 }
557 #endif /* MAEMO */
558
559 #if HILDON == 1
560 static gboolean
561 display_keepalive (gpointer user_data)
562 {
563   AppData * appdata = (AppData *) user_data;
564
565   /* first (direct) call: call blanking_pause and set up timer */
566   if (appdata->display_timer_id == 0) {
567     osso_display_blanking_pause (appdata->osso_context);
568     appdata->display_timer_id = g_timeout_add (55000, (GSourceFunc) display_keepalive, user_data);
569     return TRUE; /* does not really matter */
570   }
571
572   /* callback from main loop */
573   if (hildon_program_get_is_topmost (HILDON_PROGRAM (appdata->app))) {
574     osso_display_blanking_pause (appdata->osso_context);
575     return TRUE;
576   }
577   /* else */
578   appdata->display_timer_id = 0;
579   return FALSE;
580 }
581
582 static void
583 display_keepalive_stop (AppData * appdata)
584 {
585   if (appdata->display_timer_id) {
586     g_source_remove (appdata->display_timer_id);
587     appdata->display_timer_id = 0;
588   }
589 }
590
591 static gboolean
592 topmost_notify (GObject * object, GParamSpec * pspec, gpointer user_data)
593 {
594   AppData * appdata = (AppData *) user_data;
595
596   if (hildon_program_get_is_topmost (HILDON_PROGRAM (object))) {
597     /* cancel pipeline stop timer if it is ticking */
598     if (appdata->stop_timer_id != 0) {
599       g_source_remove (appdata->stop_timer_id);
600       appdata->stop_timer_id = 0;
601     }
602
603     set_pipeline_states (appdata, GST_STATE_PLAYING);
604
605     /* keep display on */
606     if (appdata->display_keepalive && appdata->display_timer_id == 0)
607       display_keepalive (user_data);
608   }
609   else {
610     /* pause pipelines so that we don't update the UI needlessly */
611     set_pipeline_states (appdata, GST_STATE_PAUSED);
612     /* stop pipelines fully if the app stays in the background for 30 seconds */
613     appdata->stop_timer_id = g_timeout_add (30000, (GSourceFunc) stop_pipelines, user_data);
614     /* let display dim and switch off */
615     display_keepalive_stop (appdata);
616   }
617
618   return FALSE;
619 }
620 #endif
621
622 static void
623 settings_notify (GConfClient * client, guint cnxn_id, GConfEntry * entry, gpointer user_data)
624 {
625   AppData * appdata = (AppData *) user_data;
626
627   g_debug ("%s changed", gconf_entry_get_key (entry));
628
629   if (strcmp (gconf_entry_get_key (entry), GCONF_KEY_ALGORITHM) == 0) {
630     if (gconf_entry_get_value (entry) != NULL && gconf_entry_get_value (entry)->type == GCONF_VALUE_INT) {
631       g_object_set (G_OBJECT (appdata->pitch), 
632           "algorithm", gconf_value_get_int (gconf_entry_get_value (entry)),
633           NULL);
634     }
635   }
636   else if (strcmp (gconf_entry_get_key (entry), GCONF_KEY_CALIBRATION) == 0) {
637     /* TODO */
638   }
639   else if (strcmp (gconf_entry_get_key (entry), GCONF_KEY_DISPLAY_KEEPALIVE) == 0) {
640     if (gconf_entry_get_value (entry) != NULL && gconf_entry_get_value (entry)->type == GCONF_VALUE_BOOL) {
641       appdata->display_keepalive = gconf_value_get_bool (gconf_entry_get_value (entry));
642
643       if (appdata->display_keepalive && appdata->display_timer_id == 0)
644         display_keepalive (user_data);
645       else
646         display_keepalive_stop (appdata);
647     }
648   }
649   else {
650     g_warning ("unknown GConf key `%s'", gconf_entry_get_key (entry));
651   }
652 }
653
654 static void 
655 settings_activate (GtkWidget * widget, GtkWidget * main_win)
656 {
657   settings_dialog_show (GTK_WINDOW (main_win));
658 }
659
660 static void 
661 about_activate (GtkWidget * widget, GtkWindow * main_win)
662 {
663   GtkWidget *vbox;
664   GtkWidget *label;
665   GtkWidget *dialog;
666  
667   dialog = gtk_dialog_new_with_buttons("About tuner", main_win,
668       GTK_DIALOG_MODAL | 
669       GTK_DIALOG_DESTROY_WITH_PARENT |
670       GTK_DIALOG_NO_SEPARATOR,
671       NULL, NULL);
672
673   g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (gtk_widget_destroy), NULL);
674
675   vbox = gtk_vbox_new (FALSE, HILDON_MARGIN_DEFAULT);
676   gtk_container_set_border_width (GTK_CONTAINER (vbox), HILDON_MARGIN_DEFAULT);
677   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
678   label = gtk_label_new ("Tuner Tool is developed by Josep Torra and Jari Tenhunen.\n"
679       "http://n770galaxy.blogspot.com/\n");
680   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
681
682   gtk_widget_show_all (dialog);
683   gtk_dialog_run (GTK_DIALOG (dialog));
684
685   gtk_widget_destroy (dialog);
686 }
687
688 static HildonAppMenu *
689 create_menu (GtkWidget *parent)
690 {
691   HildonSizeType button_size = HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH;
692   HildonAppMenu *menu = HILDON_APP_MENU (hildon_app_menu_new ());
693   GtkButton *button;
694
695   button = GTK_BUTTON (hildon_gtk_button_new (button_size));
696   gtk_button_set_label (button, "Settings");
697   g_signal_connect_after (G_OBJECT (button), "clicked",
698       G_CALLBACK (settings_activate), parent);
699   hildon_app_menu_append (menu, button);
700
701   button = GTK_BUTTON (hildon_gtk_button_new (button_size));
702   gtk_button_set_label (button, "About");
703   g_signal_connect_after (G_OBJECT (button), "clicked",
704       G_CALLBACK (about_activate), parent);
705   hildon_app_menu_append (menu, button);
706
707   gtk_widget_show_all (GTK_WIDGET (menu));
708
709   return menu;
710 }
711
712 int
713 main (int argc, char *argv[])
714 {
715   AppData * appdata = NULL;
716 #ifdef HILDON
717   HildonProgram *app = NULL;
718   osso_hw_state_t hw_state_mask = { TRUE, FALSE, FALSE, TRUE, 0 };
719 #endif
720   gint calib;
721
722   GstElement *src1, *src2, *sink1;
723   GstElement *sink2;
724   GstBus *bus;
725
726   GtkWidget *mainWin;
727   GtkWidget *mainBox;
728   GtkWidget *box;
729   GtkWidget *label;
730   GtkWidget *alignment;
731   GtkWidget *calibrate;
732   GtkWidget *sep;
733   HildonAppMenu *menu;
734
735 #ifndef HILDON
736   GdkPixbuf *icon = NULL;
737   GError *error = NULL;
738 #endif
739   gboolean piano_enabled = TRUE;
740
741   appdata = g_new0(AppData, 1);
742
743   /* Init GStreamer */
744   gst_init (&argc, &argv);
745   /* Register the GStreamer plugins */
746   plugin_pitch_init (NULL);
747   plugin_tonesrc_init (NULL);
748
749
750   /* Init the gtk - must be called before any hildon stuff */
751   gtk_init (&argc, &argv);
752
753   app = HILDON_PROGRAM (hildon_program_get_instance ());
754   g_set_application_name ("Tuner");
755
756   appdata->app = app;
757
758   /* Initialize maemo application */
759   appdata->osso_context = osso_initialize (OSSO_PACKAGE, OSSO_VERSION, TRUE, NULL);
760
761   /* Check that initialization was ok */
762   if (appdata->osso_context == NULL) {
763     g_print ("Bummer, osso failed\n");
764   }
765   g_assert (appdata->osso_context);
766
767   /* could use also display_event_cb but it is available only from chinook onwards */
768   if (osso_hw_set_event_cb (appdata->osso_context, &hw_state_mask, osso_hw_state_cb, appdata) != OSSO_OK)
769     g_warning ("setting osso_hw_state_cb failed!");
770
771   settings_init (&settings_notify, appdata);
772
773   calib = settings_get_calibration (CALIB_DEFAULT);
774   recalculate_scale (calib);
775
776   mainBox = gtk_vbox_new (FALSE, 0);
777   gtk_container_set_border_width (GTK_CONTAINER (mainBox), 0);
778   mainWin = hildon_stackable_window_new ();
779   g_signal_connect (G_OBJECT (app), "notify::is-topmost", G_CALLBACK (topmost_notify), appdata);
780
781   menu = create_menu (mainWin);
782   hildon_program_set_common_app_menu (app, menu);
783
784   /* Bin for tuner functionality */
785   appdata->bin1 = gst_pipeline_new ("bin1");
786
787   src1 = gst_element_factory_make (DEFAULT_AUDIOSRC, "src1");
788   g_object_set (G_OBJECT (src1), "device", "source.voice.raw", NULL);
789
790   appdata->pitch = gst_element_factory_make ("pitch", "pitch");
791
792   g_object_set (G_OBJECT (appdata->pitch), "message", TRUE, "minfreq", 10,
793       "maxfreq", 4000, 
794       "algorithm", settings_get_algorithm (DEFAULT_ALGORITHM),
795       NULL);
796
797   sink1 = gst_element_factory_make ("fakesink", "sink1");
798   g_object_set (G_OBJECT (sink1), "silent", 1, NULL);
799
800   gst_bin_add_many (GST_BIN (appdata->bin1), src1, appdata->pitch, sink1, NULL);
801   if (!gst_element_link_many (src1, appdata->pitch, sink1, NULL)) {
802     fprintf (stderr, "cant link elements\n");
803     exit (1);
804   }
805
806   bus = gst_element_get_bus (appdata->bin1);
807   gst_bus_add_watch (bus, message_handler, appdata);
808   gst_object_unref (bus);
809
810   /* Bin for piano functionality */
811   appdata->bin2 = gst_pipeline_new ("bin2");
812
813   //src2 = gst_element_factory_make ("audiotestsrc", "src2");
814   //g_object_set (G_OBJECT (src2), "volume", 0.0, "wave", 7, NULL);
815   src2 = gst_element_factory_make ("tonesrc", "src2");
816   g_object_set (G_OBJECT (src2), "volume", 0.0, NULL);
817   sink2 = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink2");
818
819   gst_bin_add_many (GST_BIN (appdata->bin2), src2, sink2, NULL);
820   if (!gst_element_link_many (src2, sink2, NULL)) {
821     piano_enabled = FALSE;
822   }
823
824   appdata->tonesrc = src2;
825
826   /* GUI */
827   g_signal_connect (G_OBJECT (mainWin), "destroy",
828       G_CALLBACK (on_window_destroy), NULL);
829   g_signal_connect (G_OBJECT(mainWin), "key_press_event", 
830       G_CALLBACK (key_press_event), mainWin);
831
832   /* Note label */
833   appdata->targetFrequency = gtk_label_new ("");
834   gtk_box_pack_start (GTK_BOX (mainBox), appdata->targetFrequency, FALSE, FALSE, 5);
835
836   /* Leds */
837   appdata->drawingarea1 = gtk_drawing_area_new ();
838   gtk_widget_set_size_request (appdata->drawingarea1, 636, 40);
839   gtk_box_pack_start (GTK_BOX (mainBox), appdata->drawingarea1, FALSE, FALSE, 5);
840
841   /* Current frequency lable */
842   appdata->currentFrequency = gtk_label_new ("");
843   gtk_box_pack_start (GTK_BOX (mainBox), appdata->currentFrequency, FALSE, FALSE, 5);
844
845   /* Calibration spinner */
846   box = gtk_hbox_new (FALSE, 0);
847   alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
848   label = gtk_label_new ("Calibration");
849   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 5);
850
851 #ifdef HILDON
852   calibrate = calibration_editor_new (CALIB_MIN, CALIB_MAX);
853   hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (calibrate),
854       calib);
855   g_signal_connect (G_OBJECT (calibrate), "notify::value",
856       G_CALLBACK (calibration_changed), NULL);
857 #else
858   calibrate = gtk_spin_button_new_with_range (CALIB_MIN, CALIB_MAX, 1);
859   gtk_spin_button_set_value (GTK_SPIN_BUTTON (calibrate), calib);
860   g_signal_connect (G_OBJECT (calibrate), "value_changed",
861       G_CALLBACK (calibration_changed), NULL);
862 #endif
863   gtk_box_pack_start (GTK_BOX (box), calibrate, FALSE, FALSE, 5);
864   gtk_container_add (GTK_CONTAINER (alignment), box);
865   gtk_box_pack_start (GTK_BOX (mainBox), alignment, FALSE, FALSE, 5);
866
867   /* Separator */
868   sep = gtk_hseparator_new ();
869   gtk_box_pack_start (GTK_BOX (mainBox), sep, FALSE, FALSE, 5);
870
871   /* Piano keyboard */
872   alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
873   appdata->drawingarea2 = gtk_drawing_area_new ();
874   gtk_widget_set_size_request (appdata->drawingarea2, NUM_WKEYS * WKEY_WIDTH + 1, 130);
875   gtk_container_add (GTK_CONTAINER (alignment), appdata->drawingarea2);
876   gtk_box_pack_start (GTK_BOX (mainBox), alignment, FALSE, FALSE, 5);
877
878   g_signal_connect (G_OBJECT (appdata->drawingarea2), "expose_event",
879       G_CALLBACK (expose_event), appdata);
880   if (piano_enabled) {
881     g_signal_connect (G_OBJECT (appdata->drawingarea2), "button_press_event",
882         G_CALLBACK (button_press_event), (gpointer) appdata);
883
884     g_signal_connect (G_OBJECT (appdata->drawingarea2), "button_release_event",
885         G_CALLBACK (button_release_event), (gpointer) appdata);
886
887     gtk_widget_set_events (appdata->drawingarea2, GDK_EXPOSURE_MASK
888         | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
889   } else {
890     gtk_widget_set_events (appdata->drawingarea2, GDK_EXPOSURE_MASK);
891   }
892
893   gtk_container_add (GTK_CONTAINER (mainWin), mainBox);
894   hildon_program_add_window (app, HILDON_WINDOW (mainWin));
895   gtk_widget_show_all (GTK_WIDGET (mainWin));
896
897   appdata->display_keepalive = settings_get_display_keepalive (DEFAULT_DISPLAY_KEEPALIVE);
898
899   if (appdata->display_keepalive)
900     display_keepalive (appdata);
901
902   draw_leds (appdata, 0);
903
904   set_pipeline_states (appdata, GST_STATE_PLAYING);
905
906 #ifdef FAKE_FREQUENCY
907   g_timeout_add (2000, (GSourceFunc) fake_frequency, appdata);
908 #endif
909
910   gtk_main ();
911
912   set_pipeline_states (appdata, GST_STATE_NULL);
913
914   gst_object_unref (appdata->bin1);
915   gst_object_unref (appdata->bin2);
916
917   return 0;
918 }