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