]> err.no Git - mapper/blob - src/audio-note.c
Include settings.h so the banner macro hildon version works.
[mapper] / src / audio-note.c
1 /*
2  * This file is part of mapper
3  *
4  * Copyright (C) 2007 Kaj-Michael Lang
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 /*
22  * Quick record audio notes on the road.
23  */
24
25 #include "config.h"
26 #include <glib.h>
27 #include <gst/gst.h>
28 #include <gtk/gtk.h>
29
30 #include "ui-common.h"
31 #include "audio.h"
32 #include "audio-note.h"
33 #include "settings.h"
34
35 static note_pipeline note_play;
36 static note_pipeline note_record;
37 static GstBus *bus;
38
39 static gboolean audio_note_stop(audio_note_ui *ui);
40
41 static gboolean
42 audio_note_position_cb(gpointer data)
43 {
44 GstFormat fmt=GST_FORMAT_TIME;
45 GstElement *pipe;
46 gint64 pos, len;
47 gchar buffer[128];
48 audio_note_ui *ui=(audio_note_ui *)data;
49
50 if (ui->note_record->active)
51         pipe=ui->note_record->pipeline;
52 else
53         if (ui->note_play->active)
54                 pipe=ui->note_play->pipeline;
55 else
56         return TRUE;
57
58 if (gst_element_query_position (pipe, &fmt, &pos) && gst_element_query_duration (pipe, &fmt, &len)) {
59         g_snprintf(buffer, sizeof(buffer), "Time: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT, GST_TIME_ARGS (pos), GST_TIME_ARGS (len));
60         gtk_label_set_text(GTK_LABEL(ui->lbl_time), buffer);
61 }
62
63 return TRUE;
64 }
65
66 static void
67 audio_note_position_display(audio_note_ui *ui, gboolean ena)
68 {
69 if (ui->pos_sid!=0) {
70         g_source_remove(ui->pos_sid);
71         ui->pos_sid=0;
72 }
73 if (ena)
74         ui->pos_sid=g_timeout_add(250, (GSourceFunc)audio_note_position_cb, ui);
75 }
76
77 static gboolean
78 audio_note_record_cb(GtkWidget *widget, gpointer data)
79 {
80 const gchar *basedir;
81 gchar buffer[128];
82 audio_note_ui *ui=(audio_note_ui *)data;
83 time_t t;
84 struct tm *tmp;
85
86 /* XXX: Make this a configuration option */
87 #ifdef WITH_DEVICE_770
88 basedir="/media/mmc1/MapperAudioNotes";
89 if (g_mkdir_with_parents(basedir, 0775)==-1) {
90         MACRO_BANNER_SHOW_INFO(_window, _("Failed to create directory for sound files!"));
91         return TRUE;
92 }
93 #else
94 basedir=g_get_home_dir();
95 #endif
96
97 t=time(NULL);
98 tmp=localtime(&t);
99 if (tmp == NULL) {
100         MACRO_BANNER_SHOW_INFO(_window, _("Failed to get timestamp for file!"));
101         return TRUE;
102 }
103 strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H:%M:%S", tmp);
104
105 if (ui->cfile)
106         g_free(ui->cfile);
107
108 ui->cfile=g_strdup_printf("%s/an-%s.wav", basedir, buffer);
109
110 if (audio_note_record(ui->cfile)==TRUE) {
111         audio_note_position_display(ui, TRUE);
112         MACRO_BANNER_SHOW_INFO(_window, _("Recording..."));
113         gtk_widget_set_sensitive(ui->btn_play, FALSE);
114         gtk_widget_set_sensitive(ui->btn_record, FALSE);
115         gtk_widget_set_sensitive(ui->btn_stop, TRUE);
116 }
117
118 return TRUE;
119 }
120
121 static gboolean
122 audio_note_play_cb(GtkWidget *widget, gpointer data)
123 {
124 audio_note_ui *ui=(audio_note_ui *)data;
125
126 if (!ui->cfile) {
127         MACRO_BANNER_SHOW_INFO(_window, _("No active audio note."));
128         return TRUE;
129 }
130
131 if (audio_note_play(ui->cfile)==TRUE) {
132         audio_note_position_display(ui, TRUE);
133         MACRO_BANNER_SHOW_INFO(_window, _("Playing..."));
134         gtk_widget_set_sensitive(ui->btn_play, FALSE);
135         gtk_widget_set_sensitive(ui->btn_record, FALSE);
136         gtk_widget_set_sensitive(ui->btn_stop, TRUE);
137 }
138 return TRUE;
139 }
140
141 static gboolean
142 audio_note_stop_cb(GtkWidget *widget, gpointer data)
143 {
144 audio_note_ui *ui=(audio_note_ui *)data;
145
146 if (audio_note_stop(ui)==TRUE)
147         MACRO_BANNER_SHOW_INFO(_window, _("Stopped..."));
148
149 audio_note_position_display(ui, FALSE);
150 gtk_widget_set_sensitive(ui->btn_record, TRUE);
151 gtk_widget_set_sensitive(ui->btn_play, TRUE);
152 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
153
154 return TRUE;
155 }
156
157 audio_note_ui *
158 audio_note_new(void)
159 {
160 audio_note_ui *ui;
161 ui=g_slice_new(audio_note_ui);
162 ui->pos_sid=0;
163 ui->cfile=NULL;
164 ui->vbox=gtk_vbox_new(FALSE, 0);
165 ui->lbl_time=gtk_label_new("");
166 ui->btn_record=gtk_button_new_from_stock(GTK_STOCK_MEDIA_RECORD);
167 ui->btn_play=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
168 ui->btn_stop=gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
169
170 gtk_widget_set_sensitive(ui->btn_play, FALSE);
171 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
172
173 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_time, TRUE, TRUE, 0);
174 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_record, TRUE, TRUE, 0);
175 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_play, TRUE, TRUE, 0);
176 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_stop, TRUE, TRUE, 0);
177
178 ui->note_play=&note_play;
179 ui->note_record=&note_record;
180
181 g_signal_connect(G_OBJECT(ui->btn_record), "clicked", G_CALLBACK(audio_note_record_cb), ui);
182 g_signal_connect(G_OBJECT(ui->btn_play), "clicked", G_CALLBACK(audio_note_play_cb), ui);
183 g_signal_connect(G_OBJECT(ui->btn_stop), "clicked", G_CALLBACK(audio_note_stop_cb), ui);
184
185 return ui;
186 }
187
188 static gboolean
189 audio_note_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
190 {
191 gchar *debug;
192 GError *err;
193 note_pipeline *np=(note_pipeline *)data;
194
195 switch (GST_MESSAGE_TYPE (msg)) {
196         case GST_MESSAGE_EOS:
197                 np->active=FALSE;
198         g_debug("EOS\n");
199         break;
200         case GST_MESSAGE_ERROR:
201                 gst_message_parse_error (msg, &err, &debug);
202                 g_debug("Error: %s", err->message);
203                 g_free(debug);
204                 g_error_free(err);
205                 np->active=FALSE;
206         break;
207         case GST_MESSAGE_STATE_CHANGED:
208                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
209         break;
210     default:
211                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
212         break;
213         }
214 return TRUE;
215 }
216
217 static gboolean
218 audio_new_pad_cb(GstElement *wavparse, GstPad *new_pad, gpointer data)
219 {
220 GstElement *sink=(GstElement *)data;
221
222 if (!gst_element_link(wavparse, sink))
223                 g_printerr("Failed to link wavparse to sink\n");
224
225 gst_element_sync_state_with_parent(sink);
226
227 return FALSE;
228 }
229
230 static gboolean
231 audio_create_pipeline(note_pipeline *np, gboolean rec)
232 {
233 if (rec==TRUE) {
234         g_debug("GST: Creating record pipeline");
235         np->pipeline=gst_pipeline_new("rpipeline");
236         g_assert(np->pipeline);
237
238         np->src=gst_element_factory_make(AUDIO_SRC, "asource");
239         g_assert(np->src);
240
241         np->filter=gst_element_factory_make("wavenc", "wavfilter");
242         g_assert(np->filter);
243
244         np->sink=gst_element_factory_make("filesink", "fsink");
245         g_assert(np->sink);
246
247         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
248
249 #ifndef WITH_DEVICE_770
250         /* Don't waste space with some hifi format */
251         np->srccaps=gst_caps_new_simple ("audio/x-raw-int",
252                         "depth", G_TYPE_INT, 16,
253                         "signed", G_TYPE_BOOLEAN, TRUE, 
254                         "width", G_TYPE_INT, 16,
255                         "rate", G_TYPE_INT, 11025,
256                         "channels", G_TYPE_INT, 1,
257                         NULL);
258         if (!gst_element_link_filtered(np->src, np->filter, np->srccaps))
259                 g_printerr("Failed to set caps for source\n");
260         else
261                 gst_element_link(np->src, np->filter);
262         gst_caps_unref(np->srccaps);
263 #else
264         if (!gst_element_link(np->src, np->filter))
265                 g_printerr("Failed to link source to filter\n");
266 #endif
267         if (!gst_element_link(np->filter, np->sink))
268                 g_printerr("Failed to link filter to source\n");
269 } else {
270         g_debug("GST: Creating playback pipeline");
271         np->pipeline=gst_pipeline_new("ppipeline");
272         g_assert(np->pipeline);
273
274         np->src=gst_element_factory_make("filesrc", "fsource");
275         g_assert(np->src);
276
277         np->filter=gst_element_factory_make("wavparse", "wavparse");
278         g_assert(np->filter);
279
280         np->sink=gst_element_factory_make(AUDIO_SINK, "asink");
281         g_assert(np->sink);
282
283         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
284
285         if (!gst_element_link(np->src, np->filter))
286                 g_printerr("Failed to link source to filter\n");
287
288         g_signal_connect(np->filter, "pad_added", G_CALLBACK(audio_new_pad_cb), np->sink);
289 }
290 bus=gst_pipeline_get_bus(GST_PIPELINE(np->pipeline));
291 gst_bus_add_watch(bus, audio_note_bus_cb, np);
292 gst_object_unref(bus);
293 np->rec=rec;
294 np->active=FALSE;
295 return TRUE;
296 }
297
298 /**
299  * Set the given elements (filesrc or filesink) file location
300  */
301 static void
302 audio_set_filename(GstElement *e, gchar *file)
303 {
304 g_object_set(G_OBJECT(e), "location", file, NULL);
305 }
306
307 /**
308  * Play given audio note file
309  */
310 gboolean
311 audio_note_play(gchar *file)
312 {
313 if (note_play.active==TRUE)
314         return TRUE;
315 audio_set_filename(note_play.src, file);
316 if (gst_element_set_state(note_play.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
317         g_printerr("Failed to play file %s\n", file);
318         note_play.active=FALSE;
319         return FALSE;
320 }
321 g_debug("Playing");
322 note_play.active=TRUE;
323 return TRUE;
324 }
325
326 /**
327  * Record to given file
328  */
329 gboolean
330 audio_note_record(gchar *file)
331 {
332 if (note_record.active==TRUE)
333         return TRUE;
334 audio_set_filename(note_record.sink, file);
335 if (gst_element_set_state(note_record.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
336         g_printerr("Failed to record to file %s\n", file);
337         note_record.active=FALSE;
338         return FALSE;
339 }
340 g_debug("Recording");
341 note_record.active=TRUE;
342 return TRUE;
343 }
344
345 static gboolean
346 audio_note_stop(audio_note_ui *ui)
347 {
348 GstState current;
349 GstState pending;
350
351 audio_note_position_display(ui, FALSE);
352
353 gst_element_get_state(note_record.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
354 if (current==GST_STATE_PLAYING) {
355         g_debug("Stop: recording");
356         gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
357         note_record.active=FALSE;
358         return TRUE;
359 }
360
361 gst_element_get_state(note_play.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
362 if (current==GST_STATE_PLAYING) {
363         g_debug("Stop: playing");
364         gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
365         note_play.active=FALSE;
366         return TRUE;
367 }
368
369 return FALSE;
370 }
371
372 /**
373  * Init gst pipelines for note play and record
374  */
375 gboolean
376 audio_note_init(void)
377 {
378 audio_create_pipeline(&note_play, FALSE);
379 audio_create_pipeline(&note_record, TRUE);
380 return TRUE;
381 }
382
383 void
384 audio_note_deinit(void)
385 {
386 gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
387 gst_object_unref(note_play.pipeline);
388
389 gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
390 gst_object_unref(note_record.pipeline);
391 }
392