]> err.no Git - mapper/blob - src/audio-note.c
Add header and cast properly.
[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
104 /* Bah, stupid FAT can't use : in filenames */
105 strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H%M%S", tmp);
106
107 if (ui->cfile)
108         g_free(ui->cfile);
109
110 ui->cfile=g_strdup_printf("%s/an-%s.wav", basedir, buffer);
111
112 if (audio_note_record(ui->cfile)==TRUE) {
113         audio_note_position_display(ui, TRUE);
114         MACRO_BANNER_SHOW_INFO(_window, _("Recording..."));
115         gtk_widget_set_sensitive(ui->btn_play, FALSE);
116         gtk_widget_set_sensitive(ui->btn_record, FALSE);
117         gtk_widget_set_sensitive(ui->btn_stop, TRUE);
118 } else {
119         popup_error(_window, _("Failed to start recording."));
120 }
121
122 return TRUE;
123 }
124
125 static gboolean
126 audio_note_play_cb(GtkWidget *widget, gpointer data)
127 {
128 audio_note_ui *ui=(audio_note_ui *)data;
129
130 if (!ui->cfile) {
131         MACRO_BANNER_SHOW_INFO(_window, _("No active audio note. Record something first."));
132         return TRUE;
133 }
134
135 if (audio_note_play(ui->cfile)==TRUE) {
136         audio_note_position_display(ui, TRUE);
137         MACRO_BANNER_SHOW_INFO(_window, _("Playing..."));
138         gtk_widget_set_sensitive(ui->btn_play, FALSE);
139         gtk_widget_set_sensitive(ui->btn_record, FALSE);
140         gtk_widget_set_sensitive(ui->btn_stop, TRUE);
141 } else {
142         popup_error(_window, _("Failed to start playing."));
143 }
144 return TRUE;
145 }
146
147 static gboolean
148 audio_note_stop_cb(GtkWidget *widget, gpointer data)
149 {
150 audio_note_ui *ui=(audio_note_ui *)data;
151
152 if (audio_note_stop(ui)==TRUE)
153         MACRO_BANNER_SHOW_INFO(_window, _("Stopped..."));
154
155 audio_note_position_display(ui, FALSE);
156 gtk_widget_set_sensitive(ui->btn_record, TRUE);
157 gtk_widget_set_sensitive(ui->btn_play, TRUE);
158 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
159
160 return TRUE;
161 }
162
163 audio_note_ui *
164 audio_note_new(void)
165 {
166 audio_note_ui *ui;
167 ui=g_slice_new(audio_note_ui);
168 ui->pos_sid=0;
169 ui->cfile=NULL;
170 ui->vbox=gtk_vbox_new(FALSE, 0);
171 ui->lbl_time=gtk_label_new("");
172 ui->btn_record=gtk_button_new_from_stock(GTK_STOCK_MEDIA_RECORD);
173 ui->btn_play=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
174 ui->btn_stop=gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
175
176 gtk_widget_set_sensitive(ui->btn_play, FALSE);
177 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
178
179 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_time, TRUE, TRUE, 0);
180 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_record, TRUE, TRUE, 0);
181 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_play, TRUE, TRUE, 0);
182 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_stop, TRUE, TRUE, 0);
183
184 ui->note_play=&note_play;
185 ui->note_record=&note_record;
186
187 g_signal_connect(G_OBJECT(ui->btn_record), "clicked", G_CALLBACK(audio_note_record_cb), ui);
188 g_signal_connect(G_OBJECT(ui->btn_play), "clicked", G_CALLBACK(audio_note_play_cb), ui);
189 g_signal_connect(G_OBJECT(ui->btn_stop), "clicked", G_CALLBACK(audio_note_stop_cb), ui);
190
191 return ui;
192 }
193
194 static gboolean
195 audio_note_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
196 {
197 gchar *debug;
198 GError *err;
199 note_pipeline *np=(note_pipeline *)data;
200
201 switch (GST_MESSAGE_TYPE (msg)) {
202         case GST_MESSAGE_EOS:
203                 np->active=FALSE;
204         g_debug("EOS\n");
205         break;
206         case GST_MESSAGE_ERROR:
207                 gst_message_parse_error (msg, &err, &debug);
208                 g_debug("Error: %s", err->message);
209                 g_free(debug);
210                 g_error_free(err);
211                 np->active=FALSE;
212         break;
213         case GST_MESSAGE_STATE_CHANGED:
214                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
215         break;
216     default:
217                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
218         break;
219         }
220 return TRUE;
221 }
222
223 static gboolean
224 audio_new_pad_cb(GstElement *wavparse, GstPad *new_pad, gpointer data)
225 {
226 GstElement *sink=(GstElement *)data;
227
228 if (!gst_element_link(wavparse, sink))
229                 g_printerr("Failed to link wavparse to sink\n");
230
231 gst_element_sync_state_with_parent(sink);
232
233 return FALSE;
234 }
235
236 static gboolean
237 audio_create_pipeline(note_pipeline *np, gboolean rec)
238 {
239 if (rec==TRUE) {
240         g_debug("GST: Creating record pipeline");
241         np->pipeline=gst_pipeline_new("rpipeline");
242         g_assert(np->pipeline);
243
244         np->src=gst_element_factory_make(AUDIO_SRC, "asource");
245         g_assert(np->src);
246
247         np->filter=gst_element_factory_make("wavenc", "wavfilter");
248         g_assert(np->filter);
249
250         np->sink=gst_element_factory_make("filesink", "fsink");
251         g_assert(np->sink);
252
253         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
254
255 #ifndef WITH_DEVICE_770
256         /* Don't waste space with some hifi format */
257         np->srccaps=gst_caps_new_simple ("audio/x-raw-int",
258                         "depth", G_TYPE_INT, 16,
259                         "signed", G_TYPE_BOOLEAN, TRUE, 
260                         "width", G_TYPE_INT, 16,
261                         "rate", G_TYPE_INT, 11025,
262                         "channels", G_TYPE_INT, 1,
263                         NULL);
264         if (!gst_element_link_filtered(np->src, np->filter, np->srccaps))
265                 g_printerr("Failed to set caps for source\n");
266         else
267                 gst_element_link(np->src, np->filter);
268         gst_caps_unref(np->srccaps);
269 #else
270         if (!gst_element_link(np->src, np->filter))
271                 g_printerr("Failed to link source to filter\n");
272 #endif
273         if (!gst_element_link(np->filter, np->sink))
274                 g_printerr("Failed to link filter to source\n");
275 } else {
276         g_debug("GST: Creating playback pipeline");
277         np->pipeline=gst_pipeline_new("ppipeline");
278         g_assert(np->pipeline);
279
280         np->src=gst_element_factory_make("filesrc", "fsource");
281         g_assert(np->src);
282
283         np->filter=gst_element_factory_make("wavparse", "wavparse");
284         g_assert(np->filter);
285
286         np->sink=gst_element_factory_make(AUDIO_SINK, "asink");
287         g_assert(np->sink);
288
289         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
290
291         if (!gst_element_link(np->src, np->filter))
292                 g_printerr("Failed to link source to filter\n");
293
294         g_signal_connect(np->filter, "pad_added", G_CALLBACK(audio_new_pad_cb), np->sink);
295 }
296 bus=gst_pipeline_get_bus(GST_PIPELINE(np->pipeline));
297 gst_bus_add_watch(bus, audio_note_bus_cb, np);
298 gst_object_unref(bus);
299 np->rec=rec;
300 np->active=FALSE;
301 return TRUE;
302 }
303
304 /**
305  * Set the given elements (filesrc or filesink) file location
306  */
307 static void
308 audio_set_filename(GstElement *e, gchar *file)
309 {
310 g_object_set(G_OBJECT(e), "location", file, NULL);
311 }
312
313 /**
314  * Play given audio note file
315  */
316 gboolean
317 audio_note_play(gchar *file)
318 {
319 if (note_play.active==TRUE)
320         return TRUE;
321 audio_set_filename(note_play.src, file);
322 if (gst_element_set_state(note_play.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
323         g_printerr("Failed to play file %s\n", file);
324         note_play.active=FALSE;
325         return FALSE;
326 }
327 g_debug("Playing");
328 note_play.active=TRUE;
329 return TRUE;
330 }
331
332 /**
333  * Record to given file
334  */
335 gboolean
336 audio_note_record(gchar *file)
337 {
338 if (note_record.active==TRUE)
339         return TRUE;
340 audio_set_filename(note_record.sink, file);
341 if (gst_element_set_state(note_record.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
342         g_printerr("Failed to record to file %s\n", file);
343         note_record.active=FALSE;
344         return FALSE;
345 }
346 g_debug("Recording");
347 note_record.active=TRUE;
348 return TRUE;
349 }
350
351 static gboolean
352 audio_note_stop(audio_note_ui *ui)
353 {
354 GstState current;
355 GstState pending;
356
357 audio_note_position_display(ui, FALSE);
358
359 gst_element_get_state(note_record.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
360 if (current==GST_STATE_PLAYING) {
361         g_debug("Stop: recording");
362         gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
363         note_record.active=FALSE;
364         return TRUE;
365 }
366
367 gst_element_get_state(note_play.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
368 if (current==GST_STATE_PLAYING) {
369         g_debug("Stop: playing");
370         gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
371         note_play.active=FALSE;
372         return TRUE;
373 }
374
375 return FALSE;
376 }
377
378 /**
379  * Init gst pipelines for note play and record
380  */
381 gboolean
382 audio_note_init(void)
383 {
384 audio_create_pipeline(&note_play, FALSE);
385 audio_create_pipeline(&note_record, TRUE);
386 return TRUE;
387 }
388
389 void
390 audio_note_deinit(void)
391 {
392 gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
393 gst_object_unref(note_play.pipeline);
394
395 gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
396 gst_object_unref(note_record.pipeline);
397 }
398