]> err.no Git - mapper/blob - src/audio-note.c
Remove old map sources
[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 #include "dialogs.h"
35
36 static note_pipeline note_play;
37 static note_pipeline note_record;
38 static GstBus *bus;
39
40 static gboolean audio_note_stop(audio_note_ui *ui);
41
42 #define GST_TIME_TO_SECS(t) \
43         (gdouble) (((gdouble)(t)) / (gdouble) GST_SECOND) /* GST_SECOND should be 1e9 */
44
45 #define NOTES_BASEDIR_MMC1 "/media/mmc1/MapperAudioNotes"
46 #define NOTES_BASEDIR_MMC2 "/media/mmc2/MapperAudioNotes"
47
48 static void
49 audio_note_set_length(audio_note_ui *ui, gdouble secs)
50 {
51 gchar buffer[16];
52 guint min=0;
53
54 if (secs<0.0) {
55         gtk_label_set_text(GTK_LABEL(ui->lbl_time), "--:--");
56         return;
57 }
58
59 if (secs>=60.0) {
60         min=secs/60.0;
61         secs-=min*60.0;
62 }
63
64 g_snprintf(buffer, sizeof(buffer), "%u:%05.2f", min, secs);
65 gtk_label_set_text(GTK_LABEL(ui->lbl_time), buffer);
66 }
67
68 static gboolean
69 audio_note_position_cb(gpointer data)
70 {
71 GstFormat fmt=GST_FORMAT_TIME;
72 GstElement *pipe;
73 gint64 pos, len;
74 audio_note_ui *ui=(audio_note_ui *)data;
75
76 if (ui->note_record->active)
77         pipe=ui->note_record->pipeline;
78 else
79         if (ui->note_play->active)
80                 pipe=ui->note_play->pipeline;
81 else
82         return TRUE;
83
84 if (gst_element_query_position(pipe, &fmt, &pos) && gst_element_query_duration(pipe, &fmt, &len)) {
85         audio_note_set_length(ui, GST_TIME_TO_SECS(pos));
86 }
87
88 return TRUE;
89 }
90
91 static void
92 audio_note_position_display(audio_note_ui *ui, gboolean ena)
93 {
94 if (ui->pos_sid!=0) {
95         g_source_remove(ui->pos_sid);
96         ui->pos_sid=0;
97 }
98 if (ena)
99         ui->pos_sid=g_timeout_add(250, (GSourceFunc)audio_note_position_cb, ui);
100 }
101
102 static gboolean
103 audio_note_record_cb(GtkWidget *widget, gpointer data)
104 {
105 gchar buffer[128];
106 audio_note_ui *ui=(audio_note_ui *)data;
107 time_t t;
108 struct tm *tmp;
109
110 t=time(NULL);
111 tmp=localtime(&t);
112 if (tmp == NULL) {
113         MACRO_BANNER_SHOW_INFO(_window, _("Failed to get timestamp for file!"));
114         return TRUE;
115 }
116
117 /* Bah, stupid FAT can't use : in filenames */
118 strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H%M%S", tmp);
119
120 if (ui->cfile)
121         g_free(ui->cfile);
122
123 ui->cfile=g_strdup_printf("%s/an-%s.wav", ui->basedir, buffer);
124
125 if (audio_note_record(ui->cfile)==TRUE) {
126         audio_note_position_display(ui, TRUE);
127         MACRO_BANNER_SHOW_INFO(_window, _("Recording..."));
128         gtk_widget_set_sensitive(ui->btn_play, FALSE);
129         gtk_widget_set_sensitive(ui->btn_record, FALSE);
130         gtk_widget_set_sensitive(ui->btn_stop, TRUE);
131 } else {
132         popup_error(_window, _("Failed to start recording."));
133 }
134
135 return TRUE;
136 }
137
138 static gboolean
139 audio_note_play_cb(GtkWidget *widget, gpointer data)
140 {
141 audio_note_ui *ui=(audio_note_ui *)data;
142
143 if (!ui->cfile) {
144         MACRO_BANNER_SHOW_INFO(_window, _("No active audio note. Record something first."));
145         return TRUE;
146 }
147
148 if (audio_note_play(ui->cfile)==TRUE) {
149         audio_note_position_display(ui, TRUE);
150         MACRO_BANNER_SHOW_INFO(_window, _("Playing..."));
151         gtk_widget_set_sensitive(ui->btn_play, FALSE);
152         gtk_widget_set_sensitive(ui->btn_record, FALSE);
153         gtk_widget_set_sensitive(ui->btn_stop, TRUE);
154 } else {
155         popup_error(_window, _("Failed to start playing."));
156 }
157 return TRUE;
158 }
159
160 static gboolean
161 audio_note_stop_cb(GtkWidget *widget, gpointer data)
162 {
163 audio_note_ui *ui=(audio_note_ui *)data;
164
165 if (audio_note_stop(ui)==TRUE)
166         MACRO_BANNER_SHOW_INFO(_window, _("Stopped..."));
167
168 audio_note_position_display(ui, FALSE);
169 gtk_widget_set_sensitive(ui->btn_record, TRUE);
170 gtk_widget_set_sensitive(ui->btn_play, TRUE);
171 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
172
173 return TRUE;
174 }
175
176 audio_note_ui *
177 audio_note_new(void)
178 {
179 audio_note_ui *ui;
180 GtkWidget *v;
181
182 ui=g_slice_new(audio_note_ui);
183 ui->pos_sid=0;
184 ui->cfile=NULL;
185
186 ui->vbox=gtk_vbox_new(FALSE, 3);
187 v=gtk_vbox_new(FALSE, 3);
188 ui->hbox=gtk_hbox_new(FALSE, 3);
189 ui->lbl_time=gtk_label_new("");
190
191 ui->btn_record=gtk_button_new_from_stock(GTK_STOCK_MEDIA_RECORD);
192 ui->btn_play=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
193 ui->btn_stop=gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
194
195 ui->file_tree=gtk_tree_view_new();
196
197 gtk_widget_set_sensitive(ui->btn_play, FALSE);
198 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
199
200 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_time, FALSE, FALSE, 0);
201 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->hbox, TRUE, TRUE, 0);
202
203 gtk_box_pack_start(GTK_BOX(v), ui->btn_record, TRUE, TRUE, 0);
204 gtk_box_pack_start(GTK_BOX(v), ui->btn_play, TRUE, TRUE, 0);
205 gtk_box_pack_start(GTK_BOX(v), ui->btn_stop, TRUE, TRUE, 0);
206
207 gtk_box_pack_start(GTK_BOX(ui->hbox), ui->file_tree, TRUE, TRUE, 0);
208 gtk_box_pack_start(GTK_BOX(ui->hbox), v, TRUE, TRUE, 0);
209
210 gtk_label_set_text(GTK_LABEL(ui->lbl_time), "--:--");
211
212 ui->note_play=&note_play;
213 ui->note_record=&note_record;
214 ui->basedir=NULL;
215
216 /* XXX: Make this a configuration option */
217 #ifdef WITH_DEVICE_770
218 audio_note_set_basedir(ui, NOTES_BASEDIR_MMC1);
219 #else
220 audio_note_set_basedir(ui, g_get_home_dir());
221 #endif
222
223 g_signal_connect(G_OBJECT(ui->btn_record), "clicked", G_CALLBACK(audio_note_record_cb), ui);
224 g_signal_connect(G_OBJECT(ui->btn_play), "clicked", G_CALLBACK(audio_note_play_cb), ui);
225 g_signal_connect(G_OBJECT(ui->btn_stop), "clicked", G_CALLBACK(audio_note_stop_cb), ui);
226
227 return ui;
228 }
229
230 void
231 audio_note_set_basedir(audio_note_ui *ui, const gchar *basedir)
232 {
233 if (ui->basedir)
234         g_free(ui->basedir);
235 ui->basedir=g_strdup(basedir);
236 if (g_mkdir_with_parents(ui->basedir, 0775)==-1) {
237 }
238 }
239
240 static gboolean
241 audio_note_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
242 {
243 gchar *debug;
244 GError *err;
245 note_pipeline *np=(note_pipeline *)data;
246
247 switch (GST_MESSAGE_TYPE (msg)) {
248         case GST_MESSAGE_EOS:
249                 np->active=FALSE;
250         g_debug("EOS\n");
251         break;
252         case GST_MESSAGE_ERROR:
253                 gst_message_parse_error (msg, &err, &debug);
254                 g_debug("Error: %s", err->message);
255                 g_free(debug);
256                 g_error_free(err);
257                 np->active=FALSE;
258         break;
259         case GST_MESSAGE_STATE_CHANGED:
260                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
261         break;
262     default:
263                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
264         break;
265         }
266 return TRUE;
267 }
268
269 static gboolean
270 audio_new_pad_cb(GstElement *wavparse, GstPad *new_pad, gpointer data)
271 {
272 GstElement *sink=(GstElement *)data;
273
274 if (!gst_element_link(wavparse, sink))
275                 g_printerr("Failed to link wavparse to sink\n");
276
277 gst_element_sync_state_with_parent(sink);
278
279 return FALSE;
280 }
281
282 static gboolean
283 audio_create_pipeline(note_pipeline *np, gboolean rec)
284 {
285 if (rec==TRUE) {
286         g_debug("GST: Creating record pipeline");
287         np->pipeline=gst_pipeline_new("rpipeline");
288         g_assert(np->pipeline);
289
290         np->src=gst_element_factory_make(AUDIO_SRC, "asource");
291         g_assert(np->src);
292
293         np->filter=gst_element_factory_make("wavenc", "wavfilter");
294         g_assert(np->filter);
295
296         np->sink=gst_element_factory_make("filesink", "fsink");
297         g_assert(np->sink);
298
299         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
300
301 #ifndef WITH_DEVICE_770
302         /* Don't waste space with some hifi format */
303         np->srccaps=gst_caps_new_simple ("audio/x-raw-int",
304                         "depth", G_TYPE_INT, 16,
305                         "signed", G_TYPE_BOOLEAN, TRUE, 
306                         "width", G_TYPE_INT, 16,
307                         "rate", G_TYPE_INT, 11025,
308                         "channels", G_TYPE_INT, 1,
309                         NULL);
310         if (!gst_element_link_filtered(np->src, np->filter, np->srccaps))
311                 g_printerr("Failed to set caps for source\n");
312         else
313                 gst_element_link(np->src, np->filter);
314         gst_caps_unref(np->srccaps);
315 #else
316         if (!gst_element_link(np->src, np->filter))
317                 g_printerr("Failed to link source to filter\n");
318 #endif
319         if (!gst_element_link(np->filter, np->sink))
320                 g_printerr("Failed to link filter to source\n");
321 } else {
322         g_debug("GST: Creating playback pipeline");
323         np->pipeline=gst_pipeline_new("ppipeline");
324         g_assert(np->pipeline);
325
326         np->src=gst_element_factory_make("filesrc", "fsource");
327         g_assert(np->src);
328
329         np->filter=gst_element_factory_make("wavparse", "wavparse");
330         g_assert(np->filter);
331
332         np->sink=gst_element_factory_make(AUDIO_SINK, "asink");
333         g_assert(np->sink);
334
335         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
336
337         if (!gst_element_link(np->src, np->filter))
338                 g_printerr("Failed to link source to filter\n");
339
340         g_signal_connect(np->filter, "pad_added", G_CALLBACK(audio_new_pad_cb), np->sink);
341 }
342 bus=gst_pipeline_get_bus(GST_PIPELINE(np->pipeline));
343 gst_bus_add_watch(bus, audio_note_bus_cb, np);
344 gst_object_unref(bus);
345 np->rec=rec;
346 np->active=FALSE;
347 return TRUE;
348 }
349
350 /**
351  * Set the given elements (filesrc or filesink) file location
352  */
353 static void
354 audio_set_filename(GstElement *e, gchar *file)
355 {
356 g_object_set(G_OBJECT(e), "location", file, NULL);
357 }
358
359 /**
360  * Play given audio note file
361  */
362 gboolean
363 audio_note_play(gchar *file)
364 {
365 if (note_play.active==TRUE)
366         return TRUE;
367 audio_set_filename(note_play.src, file);
368 if (gst_element_set_state(note_play.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
369         g_printerr("Failed to play file %s\n", file);
370         note_play.active=FALSE;
371         return FALSE;
372 }
373 g_debug("Playing");
374 note_play.active=TRUE;
375 return TRUE;
376 }
377
378 /**
379  * Record to given file
380  */
381 gboolean
382 audio_note_record(gchar *file)
383 {
384 if (note_record.active==TRUE)
385         return TRUE;
386 audio_set_filename(note_record.sink, file);
387 if (gst_element_set_state(note_record.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
388         g_printerr("Failed to record to file %s\n", file);
389         note_record.active=FALSE;
390         return FALSE;
391 }
392 g_debug("Recording");
393 note_record.active=TRUE;
394 return TRUE;
395 }
396
397 static gboolean
398 audio_note_stop(audio_note_ui *ui)
399 {
400 GstState current;
401 GstState pending;
402
403 audio_note_position_display(ui, FALSE);
404
405 gst_element_get_state(note_record.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
406 if (current==GST_STATE_PLAYING) {
407         g_debug("Stop: recording");
408         gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
409         note_record.active=FALSE;
410         return TRUE;
411 }
412
413 gst_element_get_state(note_play.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
414 if (current==GST_STATE_PLAYING) {
415         g_debug("Stop: playing");
416         gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
417         note_play.active=FALSE;
418         return TRUE;
419 }
420
421 return FALSE;
422 }
423
424 /**
425  * Init gst pipelines for note play and record
426  */
427 gboolean
428 audio_note_init(void)
429 {
430 audio_create_pipeline(&note_play, FALSE);
431 audio_create_pipeline(&note_record, TRUE);
432 return TRUE;
433 }
434
435 void
436 audio_note_deinit(void)
437 {
438 gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
439 gst_object_unref(note_play.pipeline);
440
441 gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
442 gst_object_unref(note_record.pipeline);
443 }
444