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