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_HAIKU
24 
25 /* Allow access to the audio stream on Haiku */
26 
27 #include <SoundPlayer.h>
28 #include <signal.h>
29 
30 #include "../../main/haiku/SDL_BeApp.h"
31 
32 extern "C"
33 {
34 
35 #include "SDL_audio.h"
36 #include "../SDL_audio_c.h"
37 #include "../SDL_sysaudio.h"
38 #include "SDL_haikuaudio.h"
39 #include "SDL_assert.h"
40 
41 }
42 
43 
44 /* !!! FIXME: have the callback call the higher level to avoid code dupe. */
45 /* The Haiku callback for handling the audio buffer */
46 static void
FillSound(void * device,void * stream,size_t len,const media_raw_audio_format & format)47 FillSound(void *device, void *stream, size_t len,
48           const media_raw_audio_format & format)
49 {
50     SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
51     SDL_AudioCallback callback = audio->callbackspec.callback;
52 
53     /* Only do something if audio is enabled */
54     if (!SDL_AtomicGet(&audio->enabled) || SDL_AtomicGet(&audio->paused)) {
55         if (audio->stream) {
56             SDL_AudioStreamClear(audio->stream);
57         }
58         SDL_memset(stream, audio->spec.silence, len);
59         return;
60     }
61 
62     SDL_assert(audio->spec.size == len);
63 
64     if (audio->stream == NULL) {  /* no conversion necessary. */
65         SDL_LockMutex(audio->mixer_lock);
66         callback(audio->callbackspec.userdata, (Uint8 *) stream, len);
67         SDL_UnlockMutex(audio->mixer_lock);
68     } else {  /* streaming/converting */
69         const int stream_len = audio->callbackspec.size;
70         const int ilen = (int) len;
71         while (SDL_AudioStreamAvailable(audio->stream) < ilen) {
72             callback(audio->callbackspec.userdata, audio->work_buffer, stream_len);
73             if (SDL_AudioStreamPut(audio->stream, audio->work_buffer, stream_len) == -1) {
74                 SDL_AudioStreamClear(audio->stream);
75                 SDL_AtomicSet(&audio->enabled, 0);
76                 break;
77             }
78         }
79 
80         const int got = SDL_AudioStreamGet(audio->stream, stream, ilen);
81         SDL_assert((got < 0) || (got == ilen));
82         if (got != ilen) {
83             SDL_memset(stream, audio->spec.silence, len);
84         }
85     }
86 }
87 
88 static void
HAIKUAUDIO_CloseDevice(_THIS)89 HAIKUAUDIO_CloseDevice(_THIS)
90 {
91     if (_this->hidden->audio_obj) {
92         _this->hidden->audio_obj->Stop();
93         delete _this->hidden->audio_obj;
94     }
95     delete _this->hidden;
96 }
97 
98 
99 static const int sig_list[] = {
100     SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH, 0
101 };
102 
103 static inline void
MaskSignals(sigset_t * omask)104 MaskSignals(sigset_t * omask)
105 {
106     sigset_t mask;
107     int i;
108 
109     sigemptyset(&mask);
110     for (i = 0; sig_list[i]; ++i) {
111         sigaddset(&mask, sig_list[i]);
112     }
113     sigprocmask(SIG_BLOCK, &mask, omask);
114 }
115 
116 static inline void
UnmaskSignals(sigset_t * omask)117 UnmaskSignals(sigset_t * omask)
118 {
119     sigprocmask(SIG_SETMASK, omask, NULL);
120 }
121 
122 
123 static int
HAIKUAUDIO_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)124 HAIKUAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
125 {
126     int valid_datatype = 0;
127     media_raw_audio_format format;
128     SDL_AudioFormat test_format = SDL_FirstAudioFormat(_this->spec.format);
129 
130     /* Initialize all variables that we clean on shutdown */
131     _this->hidden = new SDL_PrivateAudioData;
132     if (_this->hidden == NULL) {
133         return SDL_OutOfMemory();
134     }
135     SDL_zerop(_this->hidden);
136 
137     /* Parse the audio format and fill the Be raw audio format */
138     SDL_zero(format);
139     format.byte_order = B_MEDIA_LITTLE_ENDIAN;
140     format.frame_rate = (float) _this->spec.freq;
141     format.channel_count = _this->spec.channels;        /* !!! FIXME: support > 2? */
142     while ((!valid_datatype) && (test_format)) {
143         valid_datatype = 1;
144         _this->spec.format = test_format;
145         switch (test_format) {
146         case AUDIO_S8:
147             format.format = media_raw_audio_format::B_AUDIO_CHAR;
148             break;
149 
150         case AUDIO_U8:
151             format.format = media_raw_audio_format::B_AUDIO_UCHAR;
152             break;
153 
154         case AUDIO_S16LSB:
155             format.format = media_raw_audio_format::B_AUDIO_SHORT;
156             break;
157 
158         case AUDIO_S16MSB:
159             format.format = media_raw_audio_format::B_AUDIO_SHORT;
160             format.byte_order = B_MEDIA_BIG_ENDIAN;
161             break;
162 
163         case AUDIO_S32LSB:
164             format.format = media_raw_audio_format::B_AUDIO_INT;
165             break;
166 
167         case AUDIO_S32MSB:
168             format.format = media_raw_audio_format::B_AUDIO_INT;
169             format.byte_order = B_MEDIA_BIG_ENDIAN;
170             break;
171 
172         case AUDIO_F32LSB:
173             format.format = media_raw_audio_format::B_AUDIO_FLOAT;
174             break;
175 
176         case AUDIO_F32MSB:
177             format.format = media_raw_audio_format::B_AUDIO_FLOAT;
178             format.byte_order = B_MEDIA_BIG_ENDIAN;
179             break;
180 
181         default:
182             valid_datatype = 0;
183             test_format = SDL_NextAudioFormat();
184             break;
185         }
186     }
187 
188     if (!valid_datatype) {      /* shouldn't happen, but just in case... */
189         return SDL_SetError("Unsupported audio format");
190     }
191 
192     /* Calculate the final parameters for this audio specification */
193     SDL_CalculateAudioSpec(&_this->spec);
194 
195     format.buffer_size = _this->spec.size;
196 
197     /* Subscribe to the audio stream (creates a new thread) */
198     sigset_t omask;
199     MaskSignals(&omask);
200     _this->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio",
201                                                 FillSound, NULL, _this);
202     UnmaskSignals(&omask);
203 
204     if (_this->hidden->audio_obj->Start() == B_NO_ERROR) {
205         _this->hidden->audio_obj->SetHasData(true);
206     } else {
207         return SDL_SetError("Unable to start Be audio");
208     }
209 
210     /* We're running! */
211     return 0;
212 }
213 
214 static void
HAIKUAUDIO_Deinitialize(void)215 HAIKUAUDIO_Deinitialize(void)
216 {
217     SDL_QuitBeApp();
218 }
219 
220 static int
HAIKUAUDIO_Init(SDL_AudioDriverImpl * impl)221 HAIKUAUDIO_Init(SDL_AudioDriverImpl * impl)
222 {
223     /* Initialize the Be Application, if it's not already started */
224     if (SDL_InitBeApp() < 0) {
225         return 0;
226     }
227 
228     /* Set the function pointers */
229     impl->OpenDevice = HAIKUAUDIO_OpenDevice;
230     impl->CloseDevice = HAIKUAUDIO_CloseDevice;
231     impl->Deinitialize = HAIKUAUDIO_Deinitialize;
232     impl->ProvidesOwnCallbackThread = 1;
233     impl->OnlyHasDefaultOutputDevice = 1;
234 
235     return 1;   /* this audio target is available. */
236 }
237 
238 extern "C"
239 {
240     extern AudioBootStrap HAIKUAUDIO_bootstrap;
241 }
242 AudioBootStrap HAIKUAUDIO_bootstrap = {
243     "haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, 0
244 };
245 
246 #endif /* SDL_AUDIO_DRIVER_HAIKU */
247 
248 /* vi: set ts=4 sw=4 expandtab: */
249