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