]> err.no Git - mapper/blob - src/audio-note.c
Add simple position text. Add more checks.
[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
34 static note_pipeline note_play;
35 static note_pipeline note_record;
36 static GstBus *bus;
37 static gchar *cfile=NULL;
38
39 static gboolean
40 audio_note_position_cb(gpointer data)
41 {
42 GstFormat fmt=GST_FORMAT_TIME;
43 GstElement *pipe;
44 gint64 pos, len;
45 gchar buffer[128];
46 audio_note_ui *ui=(audio_note_ui *)data;
47
48 pipe=ui->note_record->pipeline;
49
50 if (gst_element_query_position (pipe, &fmt, &pos) && gst_element_query_duration (pipe, &fmt, &len)) {
51         g_snprintf(buffer, sizeof(buffer), "Time: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT, GST_TIME_ARGS (pos), GST_TIME_ARGS (len));
52         gtk_label_set_text(ui->lbl_time, buffer);
53 }
54
55 return TRUE;
56 }
57
58 static gboolean
59 audio_note_record_cb(GtkWidget *widget, gpointer data)
60 {
61 const gchar *basedir;
62 gchar buffer[128];
63 audio_note_ui *ui=(audio_note_ui *)data;
64 GDate *gd;
65 time_t t;
66 struct tm *tmp;
67
68 /* XXX: Make this a configuration option */
69 #ifdef WITH_DEVICE_770
70 basedir="/media/mmc1/MapperAudioNotes";
71 if (g_mkdir_with_parents(basedir, 0775)==-1) {
72         MACRO_BANNER_SHOW_INFO(_window, _("Failed to create directory for sound files!"));
73         return TRUE;
74 }
75 #else
76 basedir=g_get_home_dir();
77 #endif
78
79 t=time(NULL);
80 tmp=localtime(&t);
81 if (tmp == NULL) {
82         MACRO_BANNER_SHOW_INFO(_window, _("Failed to get timestamp for file!"));
83         return TRUE;
84 }
85 strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H:%M:%S", tmp);
86
87 if (cfile)
88         g_free(cfile);
89
90 cfile=g_strdup_printf("%s/an-%s.wav", basedir, buffer);
91
92 audio_note_record(cfile);
93 ui->pos_sid=g_timeout_add (250, (GSourceFunc)audio_note_position_cb, ui);
94
95 MACRO_BANNER_SHOW_INFO(_window, _("Recording..."));
96
97 return TRUE;
98 }
99
100 static gboolean
101 audio_note_play_cb(GtkWidget *widget, gpointer data)
102 {
103 audio_note_ui *ui=(audio_note_ui *)data;
104
105 audio_note_play(cfile);
106 MACRO_BANNER_SHOW_INFO(_window, _("Playing..."));
107 return TRUE;
108 }
109
110 static gboolean
111 audio_note_stop_cb(GtkWidget *widget, gpointer data)
112 {
113 if (audio_note_stop()==TRUE)
114         MACRO_BANNER_SHOW_INFO(_window, _("Stopped..."));
115 return TRUE;
116 }
117
118 audio_note_ui *
119 audio_note_new(void)
120 {
121 audio_note_ui *ui;
122 ui=g_slice_new(audio_note_ui);
123 ui->pos_sid=0;
124 ui->vbox=gtk_vbox_new(FALSE, 0);
125 ui->lbl_time=gtk_label_new("");
126 ui->btn_record=gtk_button_new_from_stock(GTK_STOCK_MEDIA_RECORD);
127 ui->btn_play=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
128 ui->btn_stop=gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
129
130 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->lbl_time, TRUE, TRUE, 0);
131 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_record, TRUE, TRUE, 0);
132 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_play, TRUE, TRUE, 0);
133 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->btn_stop, TRUE, TRUE, 0);
134
135 ui->note_play=&note_play;
136 ui->note_record=&note_record;
137
138 g_signal_connect(G_OBJECT(ui->btn_record), "clicked", G_CALLBACK(audio_note_record_cb), ui);
139 g_signal_connect(G_OBJECT(ui->btn_play), "clicked", G_CALLBACK(audio_note_play_cb), ui);
140 g_signal_connect(G_OBJECT(ui->btn_stop), "clicked", G_CALLBACK(audio_note_stop_cb), ui);
141
142 return ui;
143 }
144
145 static gboolean
146 audio_note_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
147 {
148 gchar *debug;
149 GError *err;
150 note_pipeline *np=(note_pipeline *)data;
151
152 switch (GST_MESSAGE_TYPE (msg)) {
153         case GST_MESSAGE_EOS:
154                 np->active=FALSE;
155         g_debug("EOS\n");
156         break;
157         case GST_MESSAGE_ERROR:
158                 gst_message_parse_error (msg, &err, &debug);
159                 g_debug("Error: %s", err->message);
160                 g_free(debug);
161                 g_error_free(err);
162                 np->active=FALSE;
163         break;
164         case GST_MESSAGE_STATE_CHANGED:
165                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
166         break;
167     default:
168                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
169         break;
170         }
171 return TRUE;
172 }
173
174 static gboolean
175 audio_create_pipeline(note_pipeline *np, gboolean rec)
176 {
177 if (rec==TRUE) {
178         g_debug("GST: Creating record pipeline");
179         np->pipeline=gst_pipeline_new("rpipeline");
180         g_assert(np->pipeline);
181
182         np->src=gst_element_factory_make(AUDIO_SRC, "asource");
183         g_assert(np->src);
184
185         np->filter=gst_element_factory_make("wavenc", "wavfilter");
186         g_assert(np->filter);
187
188         np->sink=gst_element_factory_make("filesink", "fsink");
189         g_assert(np->sink);
190
191         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
192
193 #ifndef WITH_DEVICE_770
194         /* Don't waste space with some hifi format */
195         np->srccaps=gst_caps_new_simple ("audio/x-raw-int",
196                         "depth", G_TYPE_INT, 16,
197                         "signed", G_TYPE_BOOLEAN, TRUE, 
198                         "width", G_TYPE_INT, 16,
199                         "rate", G_TYPE_INT, 11025,
200                         "channels", G_TYPE_INT, 1,
201                         NULL);
202         if (!gst_element_link_filtered(np->src, np->filter, np->srccaps))
203                 g_printerr("Failed to set caps for source\n");
204         else
205                 gst_element_link(np->src, np->filter);
206         gst_caps_unref(np->srccaps);
207 #else
208         if (!gst_element_link(np->src, np->filter))
209                 g_printerr("Failed to link source to filter\n");
210 #endif
211         if (!gst_element_link(np->filter, np->sink))
212                 g_printerr("Failed to link filter to source\n");
213 } else {
214         g_debug("GST: Creating playback pipeline");
215         np->pipeline=gst_pipeline_new("ppipeline");
216         g_assert(np->pipeline);
217
218         np->src=gst_element_factory_make("filesrc", "fsource");
219         g_assert(np->src);
220
221         np->filter=gst_element_factory_make("wavparse", "wavparse");
222         g_assert(np->filter);
223
224         np->sink=gst_element_factory_make(AUDIO_SINK, "asink");
225         g_assert(np->sink);
226
227         gst_bin_add_many(GST_BIN(np->pipeline), np->src, np->filter, np->sink, NULL);
228
229         if (!gst_element_link(np->src, np->filter))
230                 g_printerr("Failed to link source to filter\n");
231         if (!gst_element_link(np->filter, np->sink))
232                 g_printerr("Failed to link filter to source\n");
233 }
234 bus=gst_pipeline_get_bus(GST_PIPELINE(np->pipeline));
235 gst_bus_add_watch(bus, audio_note_bus_cb, np);
236 gst_object_unref(bus);
237 np->rec=rec;
238 np->active=FALSE;
239 return TRUE;
240 }
241
242 /**
243  * Set the given elements (filesrc or filesink) file location
244  */
245 static void
246 audio_set_filename(GstElement *e, gchar *file)
247 {
248 g_object_set(G_OBJECT(e), "location", file, NULL);
249 }
250
251 /**
252  * Play given audio note file
253  */
254 gboolean
255 audio_note_play(gchar *file)
256 {
257 if (note_play.active==TRUE)
258         return TRUE;
259 audio_set_filename(note_play.src, file);
260 if (gst_element_set_state(note_play.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
261         g_printerr("Failed to play file %s\n", file);
262         note_play.active=FALSE;
263         return FALSE;
264 }
265 g_debug("Playing");
266 note_play.active=TRUE;
267 return TRUE;
268 }
269
270 /**
271  * Record to given file
272  */
273 gboolean
274 audio_note_record(gchar *file)
275 {
276 if (note_record.active==TRUE)
277         return TRUE;
278 audio_set_filename(note_record.sink, file);
279 if (gst_element_set_state(note_record.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
280         g_printerr("Failed to record to file %s\n", file);
281         note_record.active=FALSE;
282         return FALSE;
283 }
284 g_debug("Recording");
285 note_record.active=TRUE;
286 return TRUE;
287 }
288
289 gboolean
290 audio_note_stop()
291 {
292 GstState current;
293 GstState pending;
294
295 gst_element_get_state(note_record.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
296 if (current==GST_STATE_PLAYING) {
297         gst_element_set_state(note_record.pipeline, GST_STATE_PAUSED);
298         note_record.active=FALSE;
299         return TRUE;
300 }
301
302 gst_element_get_state(note_play.pipeline, &current, &pending, GST_CLOCK_TIME_NONE);
303 if (current==GST_STATE_PLAYING) {
304         gst_element_set_state(note_play.pipeline, GST_STATE_PAUSED);
305         note_play.active=FALSE;
306         return TRUE;
307 }
308
309 return FALSE;
310 }
311
312 /**
313  * Init gst pipelines for note play and record
314  */
315 gboolean
316 audio_note_init(void)
317 {
318 audio_create_pipeline(&note_play, FALSE);
319 audio_create_pipeline(&note_record, TRUE);
320 return TRUE;
321 }
322
323 void
324 audio_note_deinit(void)
325 {
326 gst_element_set_state(note_play.pipeline, GST_STATE_NULL);
327 gst_object_unref(note_play.pipeline);
328
329 gst_element_set_state(note_record.pipeline, GST_STATE_NULL);
330 gst_object_unref(note_record.pipeline);
331 }
332