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