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