add missing include
[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 #include <hildon/hildon-gtk.h>
32
33 #include <libosso.h>
34
35 #define OSSO_PACKAGE "tuner-tool"
36 #define OSSO_VERSION VERSION
37
38 #endif /* ifdef HILDON */
39
40 #ifdef MAEMO
41 # define DEFAULT_AUDIOSRC "pulsesrc"
42 # define DEFAULT_AUDIOSINK "pulsesink"
43 #else
44 # define DEFAULT_AUDIOSRC "alsasrc"
45 # define DEFAULT_AUDIOSINK "alsasink"
46 #endif
47
48
49 #include <string.h>
50 #include <math.h>
51 #include <gst/gst.h>
52 #include <gtk/gtk.h>
53 #include <gconf/gconf-client.h>
54
55 #include "gstpitch.h"
56 #include "settings.h"
57
58 #define between(x,a,b) (((x)>=(a)) && ((x)<=(b)))
59
60 #define MAGIC (1.059463094359f) /* 2^(1/2) */
61 #define CENT (1.0005777895f) /* 1/100th of a half-tone */
62 #define LOG_CENT (0.00057762265046662109f) /* ln (CENT) */
63
64 extern gboolean plugin_pitch_init (GstPlugin * plugin);
65 extern gboolean plugin_tonesrc_init (GstPlugin * plugin);
66
67 typedef struct
68 {
69   const gchar *name;
70   gfloat frequency;
71 } Note;
72
73 struct app_data
74 {
75   GtkWidget *targetFrequency;
76   GtkWidget *currentFrequency;
77   GtkWidget *drawingarea1;
78   GtkWidget *drawingarea2;
79
80   GstElement *bin1;
81   GstElement *bin2;
82   GstElement *tonesrc;
83   GstElement *pitch;
84   guint stop_timer_id;
85   guint vol_timer_id;
86   gdouble target_vol;
87
88   gboolean display_keepalive;
89 #ifdef MAEMO
90   osso_context_t *osso_context;
91   gpointer app;
92   guint display_timer_id;
93 #endif
94 };
95
96 typedef struct app_data AppData;
97
98 enum
99 {
100   NUM_NOTES = 96
101 };
102
103
104
105 #define NUM_LEDS (50)
106 #define NUM_WKEYS (15) /* # of white keys in the piano keyboard */
107 #define WKEY_WIDTH (45)
108
109 static Note equal_tempered_scale[] = {
110   {"C0", 16.35},
111   {"C#0/Db0", 17.32},
112   {"D0", 18.35},
113   {"D#0/Eb0", 19.45},
114   {"E0", 20.60},
115   {"F0", 21.83},
116   {"F#0/Gb0", 23.12},
117   {"G0", 24.50},
118   {"G#0/Ab0", 25.96},
119   {"A0", 27.50},
120   {"A#0/Bb0", 29.14},
121   {"B0", 30.87},
122   {"C1", 32.70},
123   {"C#1/Db1", 34.65},
124   {"D1", 36.71},
125   {"D#1/Eb1", 38.89},
126   {"E1", 41.20},
127   {"F1", 43.65},
128   {"F#1/Gb1", 46.25},
129   {"G1", 49.00},
130   {"G#1/Ab1", 51.91},
131   {"A1", 55.00},
132   {"A#1/Bb1", 58.27},
133   {"B1", 61.74},
134   {"C2", 65.41},
135   {"C#2/Db2", 69.30},
136   {"D2", 73.42},
137   {"D#2/Eb2", 77.78},
138   {"E2", 82.41},
139   {"F2", 87.31},
140   {"F#2/Gb2", 92.50},
141   {"G2", 98.00},
142   {"G#2/Ab2", 103.83},
143   {"A2", 110.00},
144   {"A#2/Bb2", 116.54},
145   {"B2", 123.47},
146   {"C3", 130.81},
147   {"C#3/Db3", 138.59},
148   {"D3", 146.83},
149   {"D#3/Eb3", 155.56},
150   {"E3", 164.81},
151   {"F3", 174.61},
152   {"F#3/Gb3", 185.00},
153   {"G3", 196.00},
154   {"G#3/Ab3", 207.65},
155   {"A3", 220.00},
156   {"A#3/Bb3", 233.08},
157   {"B3", 246.94},
158   {"C4", 261.63},
159   {"C#4/Db4", 277.18},
160   {"D4", 293.66},
161   {"D#4/Eb4", 311.13},
162   {"E4", 329.63},
163   {"F4", 349.23},
164   {"F#4/Gb4", 369.99},
165   {"G4", 392.00},
166   {"G#4/Ab4", 415.30},
167   {"A4", 440.00},
168   {"A#4/Bb4", 466.16},
169   {"B4", 493.88},
170   {"C5", 523.25},
171   {"C#5/Db5", 554.37},
172   {"D5", 587.33},
173   {"D#5/Eb5", 622.25},
174   {"E5", 659.26},
175   {"F5", 698.46},
176   {"F#5/Gb5", 739.99},
177   {"G5", 783.99},
178   {"G#5/Ab5", 830.61},
179   {"A5", 880.00},
180   {"A#5/Bb5", 932.33},
181   {"B5", 987.77},
182   {"C6", 1046.50},
183   {"C#6/Db6", 1108.73},
184   {"D6", 1174.66},
185   {"D#6/Eb6", 1244.51},
186   {"E6", 1318.51},
187   {"F6", 1396.91},
188   {"F#6/Gb6", 1479.98},
189   {"G6", 1567.98},
190   {"G#6/Ab6", 1661.22},
191   {"A6", 1760.00},
192   {"A#6/Bb6", 1864.66},
193   {"B6", 1975.53},
194   {"C7", 2093.00},
195   {"C#7/Db7", 2217.46},
196   {"D7", 2349.32},
197   {"D#7/Eb7", 2489.02},
198   {"E7", 2637.02},
199   {"F7", 2793.83},
200   {"F#7/Gb7", 2959.96},
201   {"G7", 3135.96},
202   {"G#7/Ab7", 3322.44},
203   {"A7", 3520.00},
204   {"A#7/Bb7", 3729.31},
205   {"B7", 3951.07},
206 };
207
208 static GdkColor ledOnColor = { 0, 0 * 255, 180 * 255, 95 * 255 };
209 static GdkColor ledOnColor2 = { 0, 180 * 255, 180 * 255, 0 * 255 };
210 static GdkColor ledOffColor = { 0, 80 * 255, 80 * 255, 80 * 255 };
211 static GdkColor whiteColor = { 0, 65535, 65535, 65535 };
212 static GdkColor blackColor = { 0, 0, 0, 0 };
213
214 static void
215 recalculate_scale (double a4)
216 {
217   int i;
218
219   for (i = 0; i < NUM_NOTES; i++) {
220     equal_tempered_scale[i].frequency = a4 * pow (MAGIC, i - 57);
221     /* fprintf(stdout, "%s: %.2f\n", equal_tempered_scale[i].name, equal_tempered_scale[i].frequency); */
222   }
223 }
224
225 static void
226 calibration_changed (GObject * object, GParamSpec * pspec, gpointer user_data)
227 {
228   gint value;
229
230 #ifdef HILDON
231   value = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (object));
232 #else
233   value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (object));
234 #endif
235
236   if (value >= CALIB_MIN && value <= CALIB_MAX) {
237     recalculate_scale (value);
238     settings_set_calibration (value);
239   }
240 }
241
242 static void
243 on_window_destroy (GtkObject * object, gpointer user_data)
244 {
245   gtk_main_quit ();
246 }
247
248 static void
249 toggle_fullscreen (GtkWindow * window)
250 {
251   static gboolean fullscreen = FALSE;
252
253   fullscreen = !fullscreen;
254   if (fullscreen)
255     gtk_window_fullscreen (GTK_WINDOW (window));
256   else
257     gtk_window_unfullscreen (GTK_WINDOW (window));
258 }
259
260 static gboolean 
261 key_press_event (GtkWidget * widget, GdkEventKey * event, GtkWindow * window)
262 {
263   switch (event->keyval) {
264 #ifdef HILDON
265     case HILDON_HARDKEY_FULLSCREEN:
266       toggle_fullscreen (window);
267       break;
268 #endif
269     default:
270       break;
271   }
272
273   return FALSE;
274 }
275
276 static void
277 draw_leds (AppData * appdata, gint n)
278 {
279   gint i, j, k;
280   static GdkGC *gc = NULL;
281   gint width = appdata->drawingarea1->allocation.width;
282   gint led_width = ((gfloat) width / (gfloat) (NUM_LEDS)) * 0.8;
283   gint led_space = ((gfloat) width / (gfloat) (NUM_LEDS)) * 0.2;
284   gint led_total = led_width + led_space;
285   gint padding = (width - NUM_LEDS * led_total) / 2;
286
287   if (!gc) {
288     gc = gdk_gc_new (appdata->drawingarea1->window);
289   }
290   gdk_gc_set_rgb_fg_color (gc, &appdata->drawingarea1->style->fg[0]);
291
292   gdk_draw_rectangle (appdata->drawingarea1->window, gc, TRUE, 0, 0,
293       appdata->drawingarea1->allocation.width, appdata->drawingarea1->allocation.height);
294
295   if (abs (n) > (NUM_LEDS / 2))
296     n = n / n * (NUM_LEDS / 2);
297
298   if (n > 0) {
299     j = NUM_LEDS / 2 + 1;
300     k = NUM_LEDS / 2 + n;
301   } else {
302     j = NUM_LEDS / 2 + n;
303     k = NUM_LEDS / 2 - 1;
304   }
305
306   // Draw all leds
307   for (i = 0; i < NUM_LEDS; i++) {
308     if (i == NUM_LEDS / 2) {
309       if (n == 0)
310         gdk_gc_set_rgb_fg_color (gc, &ledOnColor2);
311       else
312         gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
313
314       gdk_draw_rectangle (appdata->drawingarea1->window, gc, TRUE, padding + (i * led_total) + ((led_total - 4) / 2), 2, 4,
315           36);
316     } else {
317       if ((i >= j) && (i <= k))
318         gdk_gc_set_rgb_fg_color (gc, &ledOnColor);
319       else
320         gdk_gc_set_rgb_fg_color (gc, &ledOffColor);
321
322       gdk_draw_rectangle (appdata->drawingarea1->window, gc, TRUE, padding + (i * led_total), 10, led_width,
323           20);
324     }
325   }
326 }
327
328 /* translate the interval (ratio of two freqs) into cents */
329 gfloat
330 interval2cent (gfloat freq, gfloat note)
331 {
332   //return (gfloat) (log (freq / note) / log (CENT));
333   return (gfloat) (log (freq / note) / LOG_CENT);
334 }
335
336 /* update frequency info */
337 static void
338 update_frequency (AppData * appdata, gfloat frequency)
339 {
340   gchar *buffer;
341   gint i, j;
342   gfloat diff, min_diff;
343
344   min_diff = frequency - (equal_tempered_scale[0].frequency - 10);
345   for (i = j = 0; i < NUM_NOTES; i++) {
346     diff = frequency - equal_tempered_scale[i].frequency;
347     if (fabs (diff) <= fabs (min_diff)) {
348       min_diff = diff;
349       j = i;
350     } else {
351       break;
352     }
353   }
354
355   buffer =
356       g_strdup_printf ("Nearest note is %s with %.2f Hz frequency",
357       equal_tempered_scale[j].name, equal_tempered_scale[j].frequency);
358   gtk_label_set_text (GTK_LABEL (appdata->targetFrequency), buffer);
359   g_free (buffer);
360
361   buffer = g_strdup_printf ("Played frequency is %.2f Hz", frequency);
362   gtk_label_set_text (GTK_LABEL (appdata->currentFrequency), buffer);
363   g_free (buffer);
364
365   /* make leds display the difference in steps of two cents */
366   diff = interval2cent (frequency, equal_tempered_scale[j].frequency);
367   draw_leds (appdata, (gint) roundf (diff / 2.0));
368 }
369
370 /* receive spectral data from element message */
371 gboolean
372 message_handler (GstBus * bus, GstMessage * message, gpointer data)
373 {
374   if (message->type == GST_MESSAGE_ELEMENT) {
375     const GstStructure *s = gst_message_get_structure (message);
376     const gchar *name = gst_structure_get_name (s);
377
378     if (strcmp (name, "pitch") == 0) {
379       gfloat frequency;
380
381       frequency = g_value_get_float (gst_structure_get_value (s, "frequency"));
382       if (frequency != 0)
383         update_frequency (data, frequency);
384     }
385   }
386   /* we handled the message we want, and ignored the ones we didn't want.
387    * so the core can unref the message for us */
388   return TRUE;
389 }
390
391 gfloat
392 keynote2freq (AppData * appdata, gint x, gint y)
393 {
394   gint i, j, height, found;
395   gfloat frequency = 0;
396
397   height = appdata->drawingarea2->allocation.height;
398
399   j = 0;
400   found = 0;
401   for (i = 0; i < NUM_WKEYS; i++) {
402     // Test for a white key  
403     j++;
404     if (between (x, i * WKEY_WIDTH, i * WKEY_WIDTH + (WKEY_WIDTH - 1)) && between (y, 0, height))
405       found = j;
406     // Test for a black key
407     if (((i % 7) != 2) && ((i % 7) != 6) && (i != 14)) {
408       j++;
409       if (between (x, 24 + i * 45, 24 + i * 45 + 42)
410           && between (y, 0, height / 2))
411         found = j;
412     }
413     if (found) {
414       frequency = equal_tempered_scale[48 + found - 1].frequency;
415       break;
416     }
417   }
418   return frequency;
419 }
420
421 static gboolean
422 expose_event (GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
423 {
424   AppData * appdata = (AppData *) user_data;
425   gint i;
426   static GdkGC *gc = NULL;
427
428   if (!gc) {
429     gc = gdk_gc_new (appdata->drawingarea2->window);
430   }
431   gdk_gc_set_rgb_fg_color (gc, &whiteColor);
432   gdk_draw_rectangle (appdata->drawingarea2->window, gc, TRUE, 0, 0,
433       NUM_WKEYS * WKEY_WIDTH, appdata->drawingarea2->allocation.height - 1);
434
435   gdk_gc_set_rgb_fg_color (gc, &blackColor);
436   gdk_draw_rectangle (appdata->drawingarea2->window, gc, FALSE, 0, 0,
437       NUM_WKEYS * WKEY_WIDTH, appdata->drawingarea2->allocation.height - 1);
438
439   for (i = 0; i < NUM_WKEYS - 1; i++)
440     gdk_draw_rectangle (appdata->drawingarea2->window, gc, FALSE, i * WKEY_WIDTH, 0,
441         WKEY_WIDTH, appdata->drawingarea2->allocation.height - 1);
442
443   for (i = 0; i < NUM_WKEYS - 1; i++) {
444     if (((i % 7) != 2) && ((i % 7) != 6))
445       gdk_draw_rectangle (appdata->drawingarea2->window, gc, TRUE, 24 + i * WKEY_WIDTH, 0,
446           42, appdata->drawingarea2->allocation.height / 2);
447   }
448   return FALSE;
449 }
450
451 static gboolean
452 button_press_event (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
453 {
454   AppData * appdata = (AppData *) user_data;
455
456   if (event->button == 1) {
457     g_object_set (appdata->tonesrc, "freq", (gdouble) keynote2freq (appdata, event->x, event->y),
458         "volume", 0.8, NULL);
459   }
460
461   return TRUE;
462 }
463
464 static gboolean
465 button_release_event (GtkWidget * widget, GdkEventButton * event,
466     gpointer user_data)
467 {
468   AppData * appdata = (AppData *) user_data;
469
470   if (event->button == 1) {
471     g_object_set (appdata->tonesrc, "volume", 0.0, NULL);
472   }
473
474   return TRUE;
475 }
476
477 static void
478 set_pipeline_states (AppData * appdata, GstState state)
479 {
480     if (appdata->bin1)
481       gst_element_set_state (appdata->bin1, state);
482
483     if (appdata->bin2)
484       gst_element_set_state (appdata->bin2, state);
485 }
486
487 static gboolean
488 stop_pipelines (gpointer user_data)
489 {
490   AppData * appdata = (AppData *) user_data;
491
492   /* dsppcmsrc needs to go to READY or NULL state to make 
493    * the DSP sleep and OMAP reach retention mode */
494   set_pipeline_states (appdata, GST_STATE_READY); 
495   appdata->stop_timer_id = 0;
496
497   return FALSE;
498 }
499
500 #ifdef FAKE_FREQUENCY
501 static gboolean
502 fake_frequency (gpointer user_data)
503 {
504   AppData * appdata = (AppData *) user_data;
505
506   update_frequency (appdata, 440.0);
507
508   return TRUE;
509 }
510 #endif
511
512 #ifdef MAEMO
513 static void
514 osso_hw_state_cb (osso_hw_state_t *state, gpointer user_data)
515 {
516   AppData * appdata = (AppData *) user_data;
517
518   if (state->shutdown_ind) {
519     gtk_main_quit ();
520     return;
521   }
522
523   if (state->system_inactivity_ind) {
524     /* do not stop pipelines if the app is on foreground 
525      * and display is kept on */
526     if (appdata->display_timer_id == 0) {
527       if (appdata->stop_timer_id != 0)
528         g_source_remove (appdata->stop_timer_id);
529
530       appdata->stop_timer_id = g_timeout_add (5000, (GSourceFunc) stop_pipelines, user_data);
531     }
532   }
533   else {
534 #if HILDON == 1
535     if (hildon_program_get_is_topmost (HILDON_PROGRAM (appdata->app))) {
536       if (appdata->stop_timer_id != 0) {
537         g_source_remove (appdata->stop_timer_id);
538         appdata->stop_timer_id = 0;
539       }
540
541       set_pipeline_states (appdata, GST_STATE_PLAYING);
542     }
543     /* not topmost => topmost_notify will set pipelines to PLAYING 
544      * when the application is on the foreground again */
545 #else
546     if (appdata->stop_timer_id != 0) {
547       g_source_remove (appdata->stop_timer_id);
548       appdata->stop_timer_id = 0;
549     }
550
551     set_pipeline_states (appdata, GST_STATE_PLAYING);
552 #endif
553
554   }
555 }
556 #endif /* MAEMO */
557
558 #if HILDON == 1
559 static gboolean
560 display_keepalive (gpointer user_data)
561 {
562   AppData * appdata = (AppData *) user_data;
563
564   /* first (direct) call: call blanking_pause and set up timer */
565   if (appdata->display_timer_id == 0) {
566     osso_display_blanking_pause (appdata->osso_context);
567     appdata->display_timer_id = g_timeout_add (55000, (GSourceFunc) display_keepalive, user_data);
568     return TRUE; /* does not really matter */
569   }
570
571   /* callback from main loop */
572   if (hildon_program_get_is_topmost (HILDON_PROGRAM (appdata->app))) {
573     osso_display_blanking_pause (appdata->osso_context);
574     return TRUE;
575   }
576   /* else */
577   appdata->display_timer_id = 0;
578   return FALSE;
579 }
580
581 static void
582 display_keepalive_stop (AppData * appdata)
583 {
584   if (appdata->display_timer_id) {
585     g_source_remove (appdata->display_timer_id);
586     appdata->display_timer_id = 0;
587   }
588 }
589
590 static gboolean
591 topmost_notify (GObject * object, GParamSpec * pspec, gpointer user_data)
592 {
593   AppData * appdata = (AppData *) user_data;
594
595   if (hildon_program_get_is_topmost (HILDON_PROGRAM (object))) {
596     /* cancel pipeline stop timer if it is ticking */
597     if (appdata->stop_timer_id != 0) {
598       g_source_remove (appdata->stop_timer_id);
599       appdata->stop_timer_id = 0;
600     }
601
602     set_pipeline_states (appdata, GST_STATE_PLAYING);
603
604     /* keep display on */
605     if (appdata->display_keepalive && appdata->display_timer_id == 0)
606       display_keepalive (user_data);
607   }
608   else {
609     /* pause pipelines so that we don't update the UI needlessly */
610     set_pipeline_states (appdata, GST_STATE_PAUSED);
611     /* stop pipelines fully if the app stays in the background for 30 seconds */
612     appdata->stop_timer_id = g_timeout_add (30000, (GSourceFunc) stop_pipelines, user_data);
613     /* let display dim and switch off */
614     display_keepalive_stop (appdata);
615   }
616
617   return FALSE;
618 }
619 #endif
620
621 static void
622 settings_notify (GConfClient * client, guint cnxn_id, GConfEntry * entry, gpointer user_data)
623 {
624   AppData * appdata = (AppData *) user_data;
625
626   g_debug ("%s changed", gconf_entry_get_key (entry));
627
628   if (strcmp (gconf_entry_get_key (entry), GCONF_KEY_ALGORITHM) == 0) {
629     if (gconf_entry_get_value (entry) != NULL && gconf_entry_get_value (entry)->type == GCONF_VALUE_INT) {
630       g_object_set (G_OBJECT (appdata->pitch), 
631           "algorithm", gconf_value_get_int (gconf_entry_get_value (entry)),
632           NULL);
633     }
634   }
635   else if (strcmp (gconf_entry_get_key (entry), GCONF_KEY_CALIBRATION) == 0) {
636     /* TODO */
637   }
638   else if (strcmp (gconf_entry_get_key (entry), GCONF_KEY_DISPLAY_KEEPALIVE) == 0) {
639     if (gconf_entry_get_value (entry) != NULL && gconf_entry_get_value (entry)->type == GCONF_VALUE_BOOL) {
640       appdata->display_keepalive = gconf_value_get_bool (gconf_entry_get_value (entry));
641
642       if (appdata->display_keepalive && appdata->display_timer_id == 0)
643         display_keepalive (user_data);
644       else
645         display_keepalive_stop (appdata);
646     }
647   }
648   else {
649     g_warning ("unknown GConf key `%s'", gconf_entry_get_key (entry));
650   }
651 }
652
653 static void 
654 settings_activate (GtkWidget * widget, GtkWidget * main_win)
655 {
656   settings_dialog_show (GTK_WINDOW (main_win));
657 }
658
659 static void 
660 about_activate (GtkWidget * widget, GtkWindow * main_win)
661 {
662   GtkWidget *vbox;
663   GtkWidget *label;
664   GtkWidget *dialog;
665  
666   dialog = gtk_dialog_new_with_buttons("About Tuner", main_win,
667       GTK_DIALOG_MODAL | 
668       GTK_DIALOG_DESTROY_WITH_PARENT |
669       GTK_DIALOG_NO_SEPARATOR,
670       NULL, NULL);
671
672   g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (gtk_widget_destroy), NULL);
673
674   vbox = gtk_vbox_new (FALSE, HILDON_MARGIN_DEFAULT);
675   gtk_container_set_border_width (GTK_CONTAINER (vbox), HILDON_MARGIN_DEFAULT);
676   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
677   label = gtk_label_new ("Version " VERSION 
678       "\nTuner 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 }