36fa5c54578b46b6977ee29986cbb13a7a8681dd
[platform/upstream/SDL.git] / src / audio / fusionsound / SDL_fsaudio.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2018 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
81 UnloadFusionSoundLibrary()
82 {
83     if (fs_handle != NULL) {
84         SDL_UnloadObject(fs_handle);
85         fs_handle = NULL;
86     }
87 }
88
89 static int
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
116 UnloadFusionSoundLibrary()
117 {
118     return;
119 }
120
121 static int
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
131 SDL_FS_WaitDevice(_THIS)
132 {
133     this->hidden->stream->Wait(this->hidden->stream,
134                                this->hidden->mixsamples);
135 }
136
137 static void
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 *
156 SDL_FS_GetDeviceBuf(_THIS)
157 {
158     return (this->hidden->mixbuf);
159 }
160
161
162 static void
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
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
285 SDL_FS_Deinitialize(void)
286 {
287     UnloadFusionSoundLibrary();
288 }
289
290
291 static int
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: */