1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 *
16 * Copyright (C) 2006-2016 XNeur Team
17 *
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #ifdef WITH_SOUND
25
26 #ifdef WITH_GSTREAMER
27
28 #include <gst/gst.h>
29
30 #elif WITH_OPENAL
31
32 #include <AL/al.h>
33 #include <AL/alc.h>
34 #include <AL/alut.h>
35
36 #elif WITH_APLAY
37
38 #include <signal.h>
39 #include <stdio.h>
40
41 #endif
42
43 #include <pthread.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <string.h>
47
48 #include "xnconfig.h"
49 #include "xnconfig_files.h"
50
51 #include "debug.h"
52 #include "log.h"
53
54 extern struct _xneur_config *xconfig;
55
56 #ifdef WITH_GSTREAMER
57
sound_init(void)58 void sound_init(void)
59 {
60 if (!xconfig->play_sounds)
61 return;
62
63 gst_init(NULL, NULL);
64 }
65
sound_uninit(void)66 void sound_uninit(void)
67 {
68 if (!xconfig->play_sounds)
69 return;
70
71 /*
72 It is normally not needed to call this function in a normal application as
73 the resources will automatically be freed when the program terminates.
74 */
75 //gst_deinit();
76 }
77
bus_call(GstBus * bus,GstMessage * msg,gpointer data)78 static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
79 {
80 if (bus){}
81
82 GMainLoop *loop = (GMainLoop *) data;
83
84 switch (GST_MESSAGE_TYPE(msg))
85 {
86 case GST_MESSAGE_EOS:
87 {
88 //g_print ("End of stream\n");
89 g_main_loop_quit(loop);
90 break;
91 }
92 case GST_MESSAGE_ERROR:
93 {
94 gchar *debug;
95 GError *err;
96
97 gst_message_parse_error(msg, &err, &debug);
98 g_free(debug);
99 g_printerr ("Error: %s\n", err->message);
100 g_error_free(err);
101
102 g_main_loop_quit(loop);
103 break;
104 }
105 default:
106 break;
107 }
108
109 return TRUE;
110 }
111
on_pad_added(GstElement * element,GstPad * pad,gpointer data)112 static void on_pad_added(GstElement *element, GstPad *pad, gpointer data)
113 {
114 if (element){}
115
116 GstPad *sinkpad;
117 GstElement *decoder = (GstElement *) data;
118
119 // We can now link this pad with the vorbis-decoder sink pad
120 //g_print ("Dynamic pad created, linking demuxer/decoder\n");
121
122 sinkpad = gst_element_get_static_pad (decoder, "sink");
123
124 gst_pad_link (pad, sinkpad);
125
126 gst_object_unref (sinkpad);
127 }
128
play_file_thread(void * param)129 void *play_file_thread(void *param)
130 {
131 char *path = (char *) param;
132 log_message(TRACE, _("Play sound sample %s (use Gstreamer engine)"), path);
133
134
135 // Initialize GStreamer
136 GMainLoop *loop = g_main_loop_new(NULL, FALSE);
137
138 GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink, *volume;
139 GstBus *bus;
140 guint bus_watch_id;
141 /* Create gstreamer elements */
142 pipeline = gst_pipeline_new ("audio-player");
143 gst_element_set_state (pipeline, GST_STATE_NULL);
144
145 source = gst_element_factory_make ("filesrc", NULL);
146 demuxer = gst_element_factory_make("decodebin", NULL);
147 decoder = gst_element_factory_make("audioconvert", NULL);
148 conv = gst_element_factory_make ("audioconvert", NULL);
149 sink = gst_element_factory_make ("autoaudiosink", NULL);
150 volume = gst_element_factory_make("volume", NULL);
151
152 if (!pipeline)
153 {
154 free(path);
155 log_message(ERROR, _("Failed to create gstreamer context (pipeline)"));
156 g_main_loop_unref(loop);
157 return NULL;
158 }
159
160 if (!source)
161 {
162 free(path);
163 log_message(ERROR, _("Failed to create gstreamer context (source)"));
164 g_main_loop_unref(loop);
165 return NULL;
166 }
167
168 if (!demuxer)
169 {
170 free(path);
171 log_message(ERROR, _("Failed to create gstreamer context (demuxer)"));
172 g_main_loop_unref(loop);
173 return NULL;
174 }
175
176 if (!decoder)
177 {
178 free(path);
179 log_message(ERROR, _("Failed to create gstreamer context (decoder)"));
180 g_main_loop_unref(loop);
181 return NULL;
182 }
183
184 if (!conv)
185 {
186 free(path);
187 log_message(ERROR, _("Failed to create gstreamer context (conv)"));
188 g_main_loop_unref(loop);
189 return NULL;
190 }
191
192 if (!volume)
193 {
194 free(path);
195 log_message(ERROR, _("Failed to create gstreamer context (volume)"));
196 g_main_loop_unref(loop);
197 return NULL;
198 }
199
200 if (!sink)
201 {
202 free(path);
203 log_message(ERROR, _("Failed to create gstreamer context (sink)"));
204 g_main_loop_unref(loop);
205 return NULL;
206 }
207
208 // Set volume
209 double i = (double) xconfig->volume_percent / 100.0;
210 g_object_set (G_OBJECT (volume), "volume", (double)i, NULL);
211
212 /* we set the input filename to the source element */
213 g_object_set (G_OBJECT (source), "location", path, NULL);
214
215 g_signal_connect(demuxer, "pad-added", G_CALLBACK(on_pad_added), decoder);
216
217 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, volume, conv, sink, NULL);
218
219 gst_element_link (source, demuxer);
220 gst_element_link_many (decoder, volume, conv, sink, NULL);
221
222 /* we add a message handler */
223 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
224 bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
225 gst_object_unref (bus);
226
227
228 /* Set the pipeline to "playing" state*/
229 //g_print ("Now playing: %s\n", path);
230 gst_element_set_state (pipeline, GST_STATE_PLAYING);
231
232 /* Iterate */
233 //g_print ("Running...\n");
234 g_main_loop_run (loop);
235
236 /* Out of the main loop, clean up nicely */
237 //g_print ("Returned, stopping playback\n");
238 gst_element_set_state (pipeline, GST_STATE_NULL);
239
240 //g_print ("Deleting pipeline\n");
241 gst_object_unref (GST_OBJECT (pipeline));
242 g_source_remove (bus_watch_id);
243 g_main_loop_unref (loop);
244
245 free(path);
246
247 return NULL;
248 }
249
250 #elif WITH_OPENAL /* WITH_OPENAL */
251
sound_init(void)252 void sound_init(void)
253 {
254 if (!xconfig->play_sounds)
255 return;
256
257 alutInit(NULL, NULL);
258 alGetError();
259 ALCcontext *pContext = alcGetCurrentContext();
260 ALCdevice *pDevice = alcGetContextsDevice(pContext);
261 log_message(TRACE, _("Initializing ALCdevice: %s "), alcGetString(pDevice, ALC_DEVICE_SPECIFIER));
262 }
263
sound_uninit(void)264 void sound_uninit(void)
265 {
266 if (!xconfig->play_sounds)
267 return;
268
269 alutExit();
270 }
271
play_file_thread(void * param)272 void *play_file_thread(void *param)
273 {
274 char *path = (char *) param;
275 log_message(TRACE, _("Play sound sample %s (use OpenAL library)"), path);
276
277 ALuint AlutBuffer = alutCreateBufferFromFile(path);
278 if (!AlutBuffer)
279 {
280 free(path);
281 log_message(ERROR, _("Failed to create OpenAL buffer"));
282 return NULL;
283 }
284
285 ALuint AlutSource;
286 alGenSources(1, &AlutSource);
287 alSourcei(AlutSource, AL_BUFFER, AlutBuffer);
288 double i = (double) xconfig->volume_percent / 100.0;
289 alSourcef(AlutSource, AL_GAIN, (double)i);
290 alSourcePlay(AlutSource);
291
292 ALint result;
293 alGetSourcei(AlutSource, AL_SOURCE_STATE, &result);
294 if (result == AL_PLAYING)
295 {
296 sleep(1);
297 alGetSourcei(AlutSource, AL_SOURCE_STATE, &result);
298 }
299
300 do
301 {
302 alDeleteSources(1, &AlutSource);
303 }
304 while (alGetError() != AL_NO_ERROR);
305
306 do
307 {
308 alDeleteBuffers(1, &AlutBuffer);
309 }
310 while (alGetError() != AL_NO_ERROR);
311
312 free(path);
313 return NULL;
314 }
315
316 #endif
317 #ifdef WITH_APLAY /* WITH_APLAY */
318
sound_init(void)319 void sound_init(void)
320 {
321 }
322
sound_uninit(void)323 void sound_uninit(void)
324 {
325 }
326
play_file_thread(void * param)327 void *play_file_thread(void *param)
328 {
329 char *path = (char *) param;
330 log_message(TRACE, _("Play sound sample %s (use aplay)"), path);
331
332 static const char *program_name = "aplay";
333
334 char *command = malloc((strlen(path) + strlen(program_name) + 1) * sizeof(char));
335 sprintf(command, "%s %s", program_name, path);
336 if (system(command) == -1)
337 log_message(ERROR, _("Can't execute command '%s'"), command);
338
339 free(command);
340
341 free(path);
342 return NULL;
343 }
344
345 #endif /* WITH_APLAY */
346
play_file(int file_type)347 void play_file(int file_type)
348 {
349 if (!xconfig->play_sounds)
350 return;
351
352 if (xconfig->sounds[file_type].file == NULL)
353 return;
354
355 if (!xconfig->sounds[file_type].enabled)
356 return;
357
358 char *path = get_file_path_name(SOUNDDIR, xconfig->sounds[file_type].file);
359 if (path == NULL)
360 return;
361
362 pthread_attr_t sound_thread_attr;
363 pthread_attr_init(&sound_thread_attr);
364 pthread_attr_setdetachstate(&sound_thread_attr, PTHREAD_CREATE_DETACHED);
365
366 pthread_t sound_thread;
367 pthread_create(&sound_thread, &sound_thread_attr, &play_file_thread, (void *) path);
368
369 pthread_attr_destroy(&sound_thread_attr);
370 }
371
372 #else /* WITH_SOUND */
373
sound_init(void)374 void sound_init(void)
375 {
376 }
377
sound_uninit(void)378 void sound_uninit(void)
379 {
380 }
381
play_file(int file_type)382 void play_file(int file_type)
383 {
384 if (file_type){}
385 }
386
387 #endif /* WITH_SOUND */
388