2 * This file is part of mapper
4 * Copyright (C) 2007 Kaj-Michael Lang
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.
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.
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.
22 * Quick record audio notes on the road.
30 #include "ui-common.h"
32 #include "audio-note.h"
36 static note_pipeline note_play;
37 static note_pipeline note_record;
40 static gboolean audio_note_stop(audio_note_ui *ui);
42 #define GST_TIME_TO_SECS(t) \
43 (gdouble) (((gdouble)(t)) / (gdouble) GST_SECOND) /* GST_SECOND should be 1e9 */
45 #define NOTES_BASEDIR_MMC1 "/media/mmc1/MapperAudioNotes"
46 #define NOTES_BASEDIR_MMC2 "/media/mmc2/MapperAudioNotes"
49 audio_note_set_length(audio_note_ui *ui, gdouble secs)
55 gtk_label_set_text(GTK_LABEL(ui->lbl_time), "--:--");
64 g_snprintf(buffer, sizeof(buffer), "%u:%05.2f", min, secs);
65 gtk_label_set_text(GTK_LABEL(ui->lbl_time), buffer);
69 audio_note_position_cb(gpointer data)
71 GstFormat fmt=GST_FORMAT_TIME;
74 audio_note_ui *ui=(audio_note_ui *)data;
76 if (ui->note_record->active)
77 pipe=ui->note_record->pipeline;
79 if (ui->note_play->active)
80 pipe=ui->note_play->pipeline;
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));
92 audio_note_position_display(audio_note_ui *ui, gboolean ena)
95 g_source_remove(ui->pos_sid);
99 ui->pos_sid=g_timeout_add(250, (GSourceFunc)audio_note_position_cb, ui);
103 audio_note_record_cb(GtkWidget *widget, gpointer data)
106 audio_note_ui *ui=(audio_note_ui *)data;
113 MACRO_BANNER_SHOW_INFO(_window, _("Failed to get timestamp for file!"));
117 /* Bah, stupid FAT can't use : in filenames */
118 strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H%M%S", tmp);
123 ui->cfile=g_strdup_printf("%s/an-%s.wav", ui->basedir, buffer);
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);
132 popup_error(_window, _("Failed to start recording."));
139 audio_note_play_cb(GtkWidget *widget, gpointer data)
141 audio_note_ui *ui=(audio_note_ui *)data;
144 MACRO_BANNER_SHOW_INFO(_window, _("No active audio note. Record something first."));
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);
155 popup_error(_window, _("Failed to start playing."));
161 audio_note_stop_cb(GtkWidget *widget, gpointer data)
163 audio_note_ui *ui=(audio_note_ui *)data;
165 if (audio_note_stop(ui)==TRUE)
166 MACRO_BANNER_SHOW_INFO(_window, _("Stopped..."));
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);
180 ui=g_slice_new(audio_note_ui);
183 ui->vbox=gtk_vbox_new(FALSE, 0);
184 ui->lbl_time=gtk_label_new("");
185 ui->btn_record=gtk_button_new_from_stock(GTK_STOCK_MEDIA_RECORD);
186 ui->btn_play=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
187 ui->btn_stop=gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
189 gtk_widget_set_sensitive(ui->btn_play, FALSE);
190 gtk_widget_set_sensitive(ui->btn_stop, FALSE);
192 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_time, TRUE, FALSE, 0);
194 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_record, TRUE, TRUE, 0);
195 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_play, TRUE, TRUE, 0);
196 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_stop, TRUE, TRUE, 0);
198 ui->note_play=¬e_play;
199 ui->note_record=¬e_record;
202 /* XXX: Make this a configuration option */
203 #ifdef WITH_DEVICE_770
204 audio_note_set_basedir(ui, NOTES_BASEDIR_MMC1);
206 audio_note_set_basedir(ui, g_get_home_dir());
209 g_signal_connect(G_OBJECT(ui->btn_record), "clicked", G_CALLBACK(audio_note_record_cb), ui);
210 g_signal_connect(G_OBJECT(ui->btn_play), "clicked", G_CALLBACK(audio_note_play_cb), ui);
211 g_signal_connect(G_OBJECT(ui->btn_stop), "clicked", G_CALLBACK(audio_note_stop_cb), ui);
217 audio_note_set_basedir(audio_note_ui *ui, const gchar *basedir)
221 ui->basedir=g_strdup(basedir);
222 if (g_mkdir_with_parents(ui->basedir, 0775)==-1) {
227 audio_note_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
231 note_pipeline *np=(note_pipeline *)data;
233 switch (GST_MESSAGE_TYPE (msg)) {
234 case GST_MESSAGE_EOS:
238 case GST_MESSAGE_ERROR:
239 gst_message_parse_error (msg, &err, &debug);
240 g_debug("Error: %s", err->message);
245 case GST_MESSAGE_STATE_CHANGED:
246 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
249 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
256 audio_new_pad_cb(GstElement *wavparse, GstPad *new_pad, gpointer data)
258 GstElement *sink=(GstElement *)data;
260 if (!gst_element_link(wavparse, sink))
261 g_printerr("Failed to link wavparse to sink\n");
263 gst_element_sync_state_with_parent(sink);
269 audio_create_pipeline(note_pipeline *np, gboolean rec)
272 g_debug("GST: Creating record pipeline");
273 np->pipeline=gst_pipeline_new("rpipeline");
274 g_assert(np->pipeline);
276 np->src=gst_element_factory_make(AUDIO_SRC, "asource");
279 np->filter=gst_element_factory_make("wavenc", "wavfilter");
280 g_assert(np->filter);
282 np->sink=gst_element_factory_make("filesink", "fsink");
285 gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
287 #ifndef WITH_DEVICE_770
288 /* Don't waste space with some hifi format */
289 np->srccaps=gst_caps_new_simple ("audio/x-raw-int",
290 "depth", G_TYPE_INT, 16,
291 "signed", G_TYPE_BOOLEAN, TRUE,
292 "width", G_TYPE_INT, 16,
293 "rate", G_TYPE_INT, 11025,
294 "channels", G_TYPE_INT, 1,
296 if (!gst_element_link_filtered(np->src, np->filter, np->srccaps))
297 g_printerr("Failed to set caps for source\n");
299 gst_element_link(np->src, np->filter);
300 gst_caps_unref(np->srccaps);
302 if (!gst_element_link(np->src, np->filter))
303 g_printerr("Failed to link source to filter\n");
305 if (!gst_element_link(np->filter, np->sink))
306 g_printerr("Failed to link filter to source\n");
308 g_debug("GST: Creating playback pipeline");
309 np->pipeline=gst_pipeline_new("ppipeline");
310 g_assert(np->pipeline);
312 np->src=gst_element_factory_make("filesrc", "fsource");
315 np->filter=gst_element_factory_make("wavparse", "wavparse");
316 g_assert(np->filter);
318 np->sink=gst_element_factory_make(AUDIO_SINK, "asink");
321 gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
323 if (!gst_element_link(np->src, np->filter))
324 g_printerr("Failed to link source to filter\n");
326 g_signal_connect(np->filter, "pad_added", G_CALLBACK(audio_new_pad_cb), np->sink);
328 bus=gst_pipeline_get_bus(GST_PIPELINE(np->pipeline));
329 gst_bus_add_watch(bus, audio_note_bus_cb, np);
330 gst_object_unref(bus);
337 * Set the given elements (filesrc or filesink) file location
340 audio_set_filename(GstElement *e, gchar *file)
342 g_object_set(G_OBJECT(e), "location", file, NULL);
346 * Play given audio note file
349 audio_note_play(gchar *file)
351 if (note_play.active==TRUE)
353 audio_set_filename(note_play.src, file);
354 if (gst_element_set_state(note_play.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
355 g_printerr("Failed to play file %s\n", file);
356 note_play.active=FALSE;
360 note_play.active=TRUE;
365 * Record to given file
368 audio_note_record(gchar *file)
370 if (note_record.active==TRUE)
372 audio_set_filename(note_record.sink, file);
373 if (gst_element_set_state(note_record.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
374 g_printerr("Failed to record to file %s\n", file);
375 note_record.active=FALSE;
378 g_debug("Recording");
379 note_record.active=TRUE;
384 audio_note_stop(audio_note_ui *ui)
389 audio_note_position_display(ui, FALSE);
391 gst_element_get_state(note_record.pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
392 if (current==GST_STATE_PLAYING) {
393 g_debug("Stop: recording");
394 gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
395 note_record.active=FALSE;
399 gst_element_get_state(note_play.pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
400 if (current==GST_STATE_PLAYING) {
401 g_debug("Stop: playing");
402 gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
403 note_play.active=FALSE;
411 * Init gst pipelines for note play and record
414 audio_note_init(void)
416 audio_create_pipeline(¬e_play, FALSE);
417 audio_create_pipeline(¬e_record, TRUE);
422 audio_note_deinit(void)
424 gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
425 gst_object_unref(note_play.pipeline);
427 gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
428 gst_object_unref(note_record.pipeline);