1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #if SDL_AUDIO_DRIVER_FUSIONSOUND
24
25 /* !!! FIXME: why is this is SDL_FS_* instead of FUSIONSOUND_*? */
26
27 /* Allow access to a raw mixing buffer */
28
29 #ifdef HAVE_SIGNAL_H
30 #include <signal.h>
31 #endif
32 #include <unistd.h>
33
34 #include "SDL_timer.h"
35 #include "SDL_audio.h"
36 #include "../SDL_audio_c.h"
37 #include "SDL_fsaudio.h"
38
39 #include <fusionsound/fusionsound_version.h>
40
41 /* #define SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC "libfusionsound.so" */
42
43 #ifdef SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC
44 #include "SDL_name.h"
45 #include "SDL_loadso.h"
46 #else
47 #define SDL_NAME(X) X
48 #endif
49
50 #if (FUSIONSOUND_MAJOR_VERSION == 1) && (FUSIONSOUND_MINOR_VERSION < 1)
51 typedef DFBResult DirectResult;
52 #endif
53
54 /* Buffers to use - more than 2 gives a lot of latency */
55 #define FUSION_BUFFERS (2)
56
57 #ifdef SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC
58
59 static const char *fs_library = SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC;
60 static void *fs_handle = NULL;
61
62 static DirectResult (*SDL_NAME(FusionSoundInit)) (int *argc, char *(*argv[]));
63 static DirectResult (*SDL_NAME(FusionSoundCreate)) (IFusionSound **
64 ret_interface);
65
66 #define SDL_FS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
67 static struct
68 {
69 const char *name;
70 void **func;
71 } fs_functions[] = {
72 /* *INDENT-OFF* */
73 SDL_FS_SYM(FusionSoundInit),
74 SDL_FS_SYM(FusionSoundCreate),
75 /* *INDENT-ON* */
76 };
77
78 #undef SDL_FS_SYM
79
80 static void
UnloadFusionSoundLibrary()81 UnloadFusionSoundLibrary()
82 {
83 if (fs_handle != NULL) {
84 SDL_UnloadObject(fs_handle);
85 fs_handle = NULL;
86 }
87 }
88
89 static int
LoadFusionSoundLibrary(void)90 LoadFusionSoundLibrary(void)
91 {
92 int i, retval = -1;
93
94 if (fs_handle == NULL) {
95 fs_handle = SDL_LoadObject(fs_library);
96 if (fs_handle != NULL) {
97 retval = 0;
98 for (i = 0; i < SDL_arraysize(fs_functions); ++i) {
99 *fs_functions[i].func =
100 SDL_LoadFunction(fs_handle, fs_functions[i].name);
101 if (!*fs_functions[i].func) {
102 retval = -1;
103 UnloadFusionSoundLibrary();
104 break;
105 }
106 }
107 }
108 }
109
110 return retval;
111 }
112
113 #else
114
115 static void
UnloadFusionSoundLibrary()116 UnloadFusionSoundLibrary()
117 {
118 return;
119 }
120
121 static int
LoadFusionSoundLibrary(void)122 LoadFusionSoundLibrary(void)
123 {
124 return 0;
125 }
126
127 #endif /* SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC */
128
129 /* This function waits until it is possible to write a full sound buffer */
130 static void
SDL_FS_WaitDevice(_THIS)131 SDL_FS_WaitDevice(_THIS)
132 {
133 this->hidden->stream->Wait(this->hidden->stream,
134 this->hidden->mixsamples);
135 }
136
137 static void
SDL_FS_PlayDevice(_THIS)138 SDL_FS_PlayDevice(_THIS)
139 {
140 DirectResult ret;
141
142 ret = this->hidden->stream->Write(this->hidden->stream,
143 this->hidden->mixbuf,
144 this->hidden->mixsamples);
145 /* If we couldn't write, assume fatal error for now */
146 if (ret) {
147 SDL_OpenedAudioDeviceDisconnected(this);
148 }
149 #ifdef DEBUG_AUDIO
150 fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
151 #endif
152 }
153
154
155 static Uint8 *
SDL_FS_GetDeviceBuf(_THIS)156 SDL_FS_GetDeviceBuf(_THIS)
157 {
158 return (this->hidden->mixbuf);
159 }
160
161
162 static void
SDL_FS_CloseDevice(_THIS)163 SDL_FS_CloseDevice(_THIS)
164 {
165 if (this->hidden->stream) {
166 this->hidden->stream->Release(this->hidden->stream);
167 }
168 if (this->hidden->fs) {
169 this->hidden->fs->Release(this->hidden->fs);
170 }
171 SDL_free(this->hidden->mixbuf);
172 SDL_free(this->hidden);
173 }
174
175
176 static int
SDL_FS_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)177 SDL_FS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
178 {
179 int bytes;
180 SDL_AudioFormat test_format = 0, format = 0;
181 FSSampleFormat fs_format;
182 FSStreamDescription desc;
183 DirectResult ret;
184
185 /* Initialize all variables that we clean on shutdown */
186 this->hidden = (struct SDL_PrivateAudioData *)
187 SDL_malloc((sizeof *this->hidden));
188 if (this->hidden == NULL) {
189 return SDL_OutOfMemory();
190 }
191 SDL_zerop(this->hidden);
192
193 /* Try for a closest match on audio format */
194 for (test_format = SDL_FirstAudioFormat(this->spec.format);
195 !format && test_format;) {
196 #ifdef DEBUG_AUDIO
197 fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
198 #endif
199 switch (test_format) {
200 case AUDIO_U8:
201 fs_format = FSSF_U8;
202 bytes = 1;
203 format = 1;
204 break;
205 case AUDIO_S16SYS:
206 fs_format = FSSF_S16;
207 bytes = 2;
208 format = 1;
209 break;
210 case AUDIO_S32SYS:
211 fs_format = FSSF_S32;
212 bytes = 4;
213 format = 1;
214 break;
215 case AUDIO_F32SYS:
216 fs_format = FSSF_FLOAT;
217 bytes = 4;
218 format = 1;
219 break;
220 default:
221 format = 0;
222 break;
223 }
224 if (!format) {
225 test_format = SDL_NextAudioFormat();
226 }
227 }
228
229 if (format == 0) {
230 return SDL_SetError("Couldn't find any hardware audio formats");
231 }
232 this->spec.format = test_format;
233
234 /* Retrieve the main sound interface. */
235 ret = SDL_NAME(FusionSoundCreate) (&this->hidden->fs);
236 if (ret) {
237 return SDL_SetError("Unable to initialize FusionSound: %d", ret);
238 }
239
240 this->hidden->mixsamples = this->spec.size / bytes / this->spec.channels;
241
242 /* Fill stream description. */
243 desc.flags = FSSDF_SAMPLERATE | FSSDF_BUFFERSIZE |
244 FSSDF_CHANNELS | FSSDF_SAMPLEFORMAT | FSSDF_PREBUFFER;
245 desc.samplerate = this->spec.freq;
246 desc.buffersize = this->spec.size * FUSION_BUFFERS;
247 desc.channels = this->spec.channels;
248 desc.prebuffer = 10;
249 desc.sampleformat = fs_format;
250
251 ret =
252 this->hidden->fs->CreateStream(this->hidden->fs, &desc,
253 &this->hidden->stream);
254 if (ret) {
255 return SDL_SetError("Unable to create FusionSoundStream: %d", ret);
256 }
257
258 /* See what we got */
259 desc.flags = FSSDF_SAMPLERATE | FSSDF_BUFFERSIZE |
260 FSSDF_CHANNELS | FSSDF_SAMPLEFORMAT;
261 ret = this->hidden->stream->GetDescription(this->hidden->stream, &desc);
262
263 this->spec.freq = desc.samplerate;
264 this->spec.size =
265 desc.buffersize / FUSION_BUFFERS * bytes * desc.channels;
266 this->spec.channels = desc.channels;
267
268 /* Calculate the final parameters for this audio specification */
269 SDL_CalculateAudioSpec(&this->spec);
270
271 /* Allocate mixing buffer */
272 this->hidden->mixlen = this->spec.size;
273 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
274 if (this->hidden->mixbuf == NULL) {
275 return SDL_OutOfMemory();
276 }
277 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
278
279 /* We're ready to rock and roll. :-) */
280 return 0;
281 }
282
283
284 static void
SDL_FS_Deinitialize(void)285 SDL_FS_Deinitialize(void)
286 {
287 UnloadFusionSoundLibrary();
288 }
289
290
291 static int
SDL_FS_Init(SDL_AudioDriverImpl * impl)292 SDL_FS_Init(SDL_AudioDriverImpl * impl)
293 {
294 if (LoadFusionSoundLibrary() < 0) {
295 return 0;
296 } else {
297 DirectResult ret;
298
299 ret = SDL_NAME(FusionSoundInit) (NULL, NULL);
300 if (ret) {
301 UnloadFusionSoundLibrary();
302 SDL_SetError
303 ("FusionSound: SDL_FS_init failed (FusionSoundInit: %d)",
304 ret);
305 return 0;
306 }
307 }
308
309 /* Set the function pointers */
310 impl->OpenDevice = SDL_FS_OpenDevice;
311 impl->PlayDevice = SDL_FS_PlayDevice;
312 impl->WaitDevice = SDL_FS_WaitDevice;
313 impl->GetDeviceBuf = SDL_FS_GetDeviceBuf;
314 impl->CloseDevice = SDL_FS_CloseDevice;
315 impl->Deinitialize = SDL_FS_Deinitialize;
316 impl->OnlyHasDefaultOutputDevice = 1;
317
318 return 1; /* this audio target is available. */
319 }
320
321
322 AudioBootStrap FUSIONSOUND_bootstrap = {
323 "fusionsound", "FusionSound", SDL_FS_Init, 0
324 };
325
326 #endif /* SDL_AUDIO_DRIVER_FUSIONSOUND */
327
328 /* vi: set ts=4 sw=4 expandtab: */
329