1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/* Allow access to a raw mixing buffer */
25
26#ifdef HAVE_SIGNAL_H
27#include <signal.h>
28#endif
29#include <unistd.h>
30
31#include "SDL_timer.h"
32#include "SDL_audio.h"
33#include "../SDL_audiomem.h"
34#include "../SDL_audio_c.h"
35#include "../SDL_audiodev_c.h"
36#include "SDL_artsaudio.h"
37
38#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
39#include "SDL_name.h"
40#include "SDL_loadso.h"
41#else
42#define SDL_NAME(X)	X
43#endif
44
45/* The tag name used by artsc audio */
46#define ARTS_DRIVER_NAME         "arts"
47
48/* Audio driver functions */
49static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec);
50static void ARTS_WaitAudio(_THIS);
51static void ARTS_PlayAudio(_THIS);
52static Uint8 *ARTS_GetAudioBuf(_THIS);
53static void ARTS_CloseAudio(_THIS);
54
55#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
56
57static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
58static void *arts_handle = NULL;
59static int arts_loaded = 0;
60
61static int (*SDL_NAME(arts_init))(void);
62static void (*SDL_NAME(arts_free))(void);
63static arts_stream_t (*SDL_NAME(arts_play_stream))(int rate, int bits, int channels, const char *name);
64static int (*SDL_NAME(arts_stream_set))(arts_stream_t s, arts_parameter_t param, int value);
65static int (*SDL_NAME(arts_stream_get))(arts_stream_t s, arts_parameter_t param);
66static int (*SDL_NAME(arts_write))(arts_stream_t s, const void *buffer, int count);
67static void (*SDL_NAME(arts_close_stream))(arts_stream_t s);
68static int (*SDL_NAME(arts_suspend))(void);
69static int (*SDL_NAME(arts_suspended))(void);
70static const char *(*SDL_NAME(arts_error_text))(int errorcode);
71
72static struct {
73	const char *name;
74	void **func;
75} arts_functions[] = {
76	{ "arts_init",		(void **)&SDL_NAME(arts_init)		},
77	{ "arts_free",		(void **)&SDL_NAME(arts_free)		},
78	{ "arts_play_stream",	(void **)&SDL_NAME(arts_play_stream)	},
79	{ "arts_stream_set",	(void **)&SDL_NAME(arts_stream_set)	},
80	{ "arts_stream_get",	(void **)&SDL_NAME(arts_stream_get)	},
81	{ "arts_write",		(void **)&SDL_NAME(arts_write)		},
82	{ "arts_close_stream",	(void **)&SDL_NAME(arts_close_stream)	},
83	{ "arts_suspend",	(void **)&SDL_NAME(arts_suspend)	},
84	{ "arts_suspended",	(void **)&SDL_NAME(arts_suspended)	},
85	{ "arts_error_text",	(void **)&SDL_NAME(arts_error_text)	},
86};
87
88static void UnloadARTSLibrary()
89{
90	if ( arts_loaded ) {
91		SDL_UnloadObject(arts_handle);
92		arts_handle = NULL;
93		arts_loaded = 0;
94	}
95}
96
97static int LoadARTSLibrary(void)
98{
99	int i, retval = -1;
100
101	arts_handle = SDL_LoadObject(arts_library);
102	if ( arts_handle ) {
103		arts_loaded = 1;
104		retval = 0;
105		for ( i=0; i<SDL_arraysize(arts_functions); ++i ) {
106			*arts_functions[i].func = SDL_LoadFunction(arts_handle, arts_functions[i].name);
107			if ( !*arts_functions[i].func ) {
108				retval = -1;
109				UnloadARTSLibrary();
110				break;
111			}
112		}
113	}
114	return retval;
115}
116
117#else
118
119static void UnloadARTSLibrary()
120{
121	return;
122}
123
124static int LoadARTSLibrary(void)
125{
126	return 0;
127}
128
129#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
130
131/* Audio driver bootstrap functions */
132
133static int ARTS_Suspend(void)
134{
135	const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
136	while ( (!SDL_NAME(arts_suspended)()) && (SDL_GetTicks() < abortms) ) {
137		if ( SDL_NAME(arts_suspend)() ) {
138			break;
139		}
140	}
141
142	return SDL_NAME(arts_suspended)();
143}
144
145static int Audio_Available(void)
146{
147	int available = 0;
148
149	if ( LoadARTSLibrary() < 0 ) {
150		return available;
151	}
152	if ( SDL_NAME(arts_init)() == 0 ) {
153		if ( ARTS_Suspend() ) {
154			/* Play a stream so aRts doesn't crash */
155			arts_stream_t stream2;
156			stream2=SDL_NAME(arts_play_stream)(44100, 16, 2, "SDL");
157			SDL_NAME(arts_write)(stream2, "", 0);
158			SDL_NAME(arts_close_stream)(stream2);
159			available = 1;
160		}
161		SDL_NAME(arts_free)();
162	}
163	UnloadARTSLibrary();
164
165	return available;
166}
167
168static void Audio_DeleteDevice(SDL_AudioDevice *device)
169{
170	SDL_free(device->hidden);
171	SDL_free(device);
172	UnloadARTSLibrary();
173}
174
175static SDL_AudioDevice *Audio_CreateDevice(int devindex)
176{
177	SDL_AudioDevice *this;
178
179	/* Initialize all variables that we clean on shutdown */
180	LoadARTSLibrary();
181	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
182	if ( this ) {
183		SDL_memset(this, 0, (sizeof *this));
184		this->hidden = (struct SDL_PrivateAudioData *)
185				SDL_malloc((sizeof *this->hidden));
186	}
187	if ( (this == NULL) || (this->hidden == NULL) ) {
188		SDL_OutOfMemory();
189		if ( this ) {
190			SDL_free(this);
191		}
192		return(0);
193	}
194	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
195	stream = 0;
196
197	/* Set the function pointers */
198	this->OpenAudio = ARTS_OpenAudio;
199	this->WaitAudio = ARTS_WaitAudio;
200	this->PlayAudio = ARTS_PlayAudio;
201	this->GetAudioBuf = ARTS_GetAudioBuf;
202	this->CloseAudio = ARTS_CloseAudio;
203
204	this->free = Audio_DeleteDevice;
205
206	return this;
207}
208
209AudioBootStrap ARTS_bootstrap = {
210	ARTS_DRIVER_NAME, "Analog Realtime Synthesizer",
211	Audio_Available, Audio_CreateDevice
212};
213
214/* This function waits until it is possible to write a full sound buffer */
215static void ARTS_WaitAudio(_THIS)
216{
217	Sint32 ticks;
218
219	/* Check to see if the thread-parent process is still alive */
220	{ static int cnt = 0;
221		/* Note that this only works with thread implementations
222		   that use a different process id for each thread.
223		*/
224		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
225			if ( kill(parent, 0) < 0 ) {
226				this->enabled = 0;
227			}
228		}
229	}
230
231	/* Use timer for general audio synchronization */
232	ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
233	if ( ticks > 0 ) {
234		SDL_Delay(ticks);
235	}
236}
237
238static void ARTS_PlayAudio(_THIS)
239{
240	int written;
241
242	/* Write the audio data */
243	written = SDL_NAME(arts_write)(stream, mixbuf, mixlen);
244
245	/* If timer synchronization is enabled, set the next write frame */
246	if ( frame_ticks ) {
247		next_frame += frame_ticks;
248	}
249
250	/* If we couldn't write, assume fatal error for now */
251	if ( written < 0 ) {
252		this->enabled = 0;
253	}
254#ifdef DEBUG_AUDIO
255	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
256#endif
257}
258
259static Uint8 *ARTS_GetAudioBuf(_THIS)
260{
261	return(mixbuf);
262}
263
264static void ARTS_CloseAudio(_THIS)
265{
266	if ( mixbuf != NULL ) {
267		SDL_FreeAudioMem(mixbuf);
268		mixbuf = NULL;
269	}
270	if ( stream ) {
271		SDL_NAME(arts_close_stream)(stream);
272		stream = 0;
273	}
274	SDL_NAME(arts_free)();
275}
276
277static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec)
278{
279	int bits, frag_spec;
280	Uint16 test_format, format;
281	int error_code;
282
283	/* Reset the timer synchronization flag */
284	frame_ticks = 0.0;
285
286	mixbuf = NULL;
287
288	/* Try for a closest match on audio format */
289	format = 0;
290	bits = 0;
291	for ( test_format = SDL_FirstAudioFormat(spec->format);
292						! format && test_format; ) {
293#ifdef DEBUG_AUDIO
294		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
295#endif
296		switch ( test_format ) {
297			case AUDIO_U8:
298				bits = 8;
299				format = 1;
300				break;
301			case AUDIO_S16LSB:
302				bits = 16;
303				format = 1;
304				break;
305			default:
306				format = 0;
307				break;
308		}
309		if ( ! format ) {
310			test_format = SDL_NextAudioFormat();
311		}
312	}
313	if ( format == 0 ) {
314		SDL_SetError("Couldn't find any hardware audio formats");
315		return(-1);
316	}
317	spec->format = test_format;
318
319	error_code = SDL_NAME(arts_init)();
320	if ( error_code != 0 ) {
321		SDL_SetError("Unable to initialize ARTS: %s", SDL_NAME(arts_error_text)(error_code));
322		return(-1);
323	}
324	if ( ! ARTS_Suspend() ) {
325		SDL_SetError("ARTS can not open audio device");
326		return(-1);
327	}
328	stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL");
329
330	/* Calculate the final parameters for this audio specification */
331	SDL_CalculateAudioSpec(spec);
332
333	/* Determine the power of two of the fragment size */
334	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
335	if ( (0x01<<frag_spec) != spec->size ) {
336		SDL_SetError("Fragment size must be a power of two");
337		return(-1);
338	}
339	frag_spec |= 0x00020000;	/* two fragments, for low latency */
340
341#ifdef ARTS_P_PACKET_SETTINGS
342	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec);
343#else
344	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff);
345	SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16);
346#endif
347	spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE);
348
349	/* Allocate mixing buffer */
350	mixlen = spec->size;
351	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
352	if ( mixbuf == NULL ) {
353		return(-1);
354	}
355	SDL_memset(mixbuf, spec->silence, spec->size);
356
357	/* Get the parent process id (we're the parent of the audio thread) */
358	parent = getpid();
359
360	/* We're ready to rock and roll. :-) */
361	return(0);
362}
363