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_ALIOS
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <malloc.h>
29
30 #include "SDL_audio.h"
31 #include "SDL_error.h"
32 #include "SDL_timer.h"
33 #include "../SDL_audio_c.h"
34 #include "../SDL_audiodev_c.h"
35 #include "../SDL_sysaudio.h"
36 #include "SDL_alios_audio.h"
37 #include "ulog/ulog.h"
38
39 #define LOG_TAG "[SDL_alios]"
40 #define ALIOS_AUDIO_DRIVER_NAME "alios_sound"
41
42 static SDL_atomic_t ALSA_hotplug_shutdown;
43 static SDL_Thread *ALSA_hotplug_thread;
44 static int fBusyFlag = 0;
45 hint_list_t node;
46
47 static int SDLCALL
ALSA_HotplugThread(void * arg)48 ALSA_HotplugThread(void *arg)
49 {
50 int ret = -1;
51 SDL_sem *first_run_semaphore = (SDL_sem *) arg;
52 hint_list_t *hints = NULL;
53 Uint32 ticks;
54
55 SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
56 while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
57 if(!hints) {
58 LOGD(LOG_TAG, "%s:%d, search sound card ...", __func__, __LINE__);
59 //ret = snd_device_name_hint(-1, "pcm", node);
60 if(1) {
61 hints = (hint_list_t *)&node;
62 strncpy(hints->name, "/dev/pcmC0D0p", sizeof(hints->name));
63 if(hints) {
64 LOGD(LOG_TAG, "%s:%d, SDL_AddAudioDevice %s", __func__, __LINE__, hints->name);
65 SDL_AddAudioDevice(0, hints->name, (void *)hints);
66 SDL_SemPost(first_run_semaphore);
67 }
68 }
69 }
70
71 /* Block awhile before checking again, unless we're told to stop. */
72 ticks = SDL_GetTicks() + 5000;
73 while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
74 SDL_Delay(1000);
75 }
76 }
77 return 0;
78 }
79
80 static void
AOS_DetectDevices(void)81 AOS_DetectDevices(void)
82 {
83 /* Start the device detection thread here, wait for an initial iteration to complete. */
84 SDL_sem *semaphore = SDL_CreateSemaphore(0);
85 if (!semaphore) {
86 return; /* oh well. */
87 }
88
89 SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
90
91 ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
92 if (ALSA_hotplug_thread) {
93 SDL_SemWait(semaphore); /* wait for the first iteration to finish. */
94 }
95
96 SDL_DestroySemaphore(semaphore);
97 }
98
99
100 static int
AOS_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)101 AOS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
102 {
103 int status = 0;
104 snd_pcm_t *pcm_handle = NULL;
105 snd_pcm_hw_params_t *hwparams = NULL;
106 snd_pcm_sw_params_t *swparams = NULL;
107 snd_pcm_format_t format = SNDRV_PCM_FORMAT_ALL;
108 SDL_AudioFormat test_format = 0;
109 unsigned int rate = 0;
110 unsigned int channels = 0;
111
112 this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*this->hidden));
113 if (this->hidden == NULL) {
114 return SDL_OutOfMemory();
115 }
116 SDL_zerop(this->hidden);
117
118 status = snd_pcm_open(&pcm_handle, "hw", iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
119 if (status < 0) {
120 LOGE(LOG_TAG, "%s:%d, ALSA: Couldn't open audio device", __func__, __LINE__);
121 return -1;
122 }
123 this->hidden->pcm_handle = pcm_handle;
124
125 /* Try for a closest match on audio format */
126 status = -1;
127 for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format && (status < 0);) {
128 status = 0; /* if we can't support a format, it'll become -1. */
129 switch (test_format) {
130 case AUDIO_S8:
131 format = SNDRV_PCM_FORMAT_S8;
132 break;
133 case AUDIO_S16LSB:
134 format = SNDRV_PCM_FORMAT_S16_LE;
135 break;
136 case AUDIO_S32LSB:
137 format = SNDRV_PCM_FORMAT_S32_LE;
138 break;
139 default:
140 status = -1;
141 break;
142 }
143 if (status < 0) {
144 test_format = SDL_NextAudioFormat();
145 }
146 }
147 if (status < 0) {
148 LOGE(LOG_TAG, "%s:%d, status %d", __func__, __LINE__, status);
149 return -1;
150 }
151 this->spec.format = test_format;
152
153
154 /* Set the number of channels */
155 channels = this->spec.channels;
156
157 /* Set the audio rate */
158 rate = this->spec.freq;
159
160 /* Calculate the final parameters for this audio specification */
161 SDL_CalculateAudioSpec(&this->spec);
162
163 /* Allocate mixing buffer */
164 if (!iscapture) {
165 this->hidden->mixlen = this->spec.size;
166 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
167 if (this->hidden->mixbuf == NULL) {
168 return SDL_OutOfMemory();
169 }
170 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
171 }
172
173 snd_pcm_set_params(this->hidden->pcm_handle,
174 format,
175 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED,
176 channels,
177 rate,
178 0,
179 0);
180 snd_pcm_prepare(this->hidden->pcm_handle);
181 return 0;
182 }
183
184 /* This function waits until it is possible to write a full sound buffer */
185 static void
AOS_WaitDevice(_THIS)186 AOS_WaitDevice(_THIS)
187 {
188 //LOGD(LOG_TAG, "%s:%d", __func__, __LINE__);
189 }
190
191 static Uint8 *
AOS_GetDeviceBuf(_THIS)192 AOS_GetDeviceBuf(_THIS)
193 {
194 //LOGD(LOG_TAG, "%s:%d", __func__, __LINE__);
195 return (this->hidden->mixbuf);
196 }
197
198 static void
ALSA_PlayDevice(_THIS)199 ALSA_PlayDevice(_THIS)
200 {
201 const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
202 const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) * this->spec.channels;
203 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
204
205 if(this->hidden->swizzle_func)
206 this->hidden->swizzle_func(this, this->hidden->mixbuf, frames_left);
207
208 if(0 == fBusyFlag) {
209 fBusyFlag = 1;
210 snd_pcm_start(this->hidden->pcm_handle);
211 }
212 while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
213 int status = snd_pcm_writei(this->hidden->pcm_handle, sample_buf, frames_left);
214 if (status > 0) {
215 sample_buf += status * frame_size;
216 frames_left -= status;
217 //Uint32 delay = (frames_left / 2 * 1000) / this->spec.freq;
218 //SDL_Delay(delay);
219 }
220 }
221 }
222
223 static void
AOS_Deinitialize(void)224 AOS_Deinitialize(void)
225 {
226 if (ALSA_hotplug_thread != NULL) {
227 SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
228 SDL_WaitThread(ALSA_hotplug_thread, NULL);
229 ALSA_hotplug_thread = NULL;
230 }
231 }
232
233 static void
AOS_CloseDevice(_THIS)234 AOS_CloseDevice(_THIS)
235 {
236 if (this->hidden->pcm_handle) {
237 /* Wait for the submitted audio to drain
238 ALSA_snd_pcm_drop() can hang, so don't use that.
239 */
240 Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2;
241 SDL_Delay(delay);
242
243 snd_pcm_drop(this->hidden->pcm_handle);
244 snd_pcm_close(this->hidden->pcm_handle);
245 }
246 SDL_free(this->hidden->mixbuf);
247 SDL_free(this->hidden);
248 fBusyFlag = 0;
249 }
250
251 static int
AOSAUDIO_Init(SDL_AudioDriverImpl * impl)252 AOSAUDIO_Init(SDL_AudioDriverImpl * impl)
253 {
254 /* Set the function pointers */
255 impl->DetectDevices = AOS_DetectDevices;
256 impl->OpenDevice = AOS_OpenDevice;
257 impl->WaitDevice = AOS_WaitDevice;
258 impl->GetDeviceBuf = AOS_GetDeviceBuf;
259 impl->PlayDevice = ALSA_PlayDevice;
260 impl->CloseDevice = AOS_CloseDevice;
261 impl->Deinitialize = AOS_Deinitialize;
262 //impl->CaptureFromDevice = ALSA_CaptureFromDevice;
263 //impl->FlushCapture = ALSA_FlushCapture;
264
265 impl->HasCaptureSupport = SDL_FALSE;
266
267 return 1; /* this audio target is available. */
268 }
269
270 AudioBootStrap AOSAUDIO_bootstrap = {
271 "aos", "AOS audio driver", AOSAUDIO_Init, 0
272 };
273
274 /* SDL_AUDI */
275
276 #endif /* SDL_AUDIO_DRIVER_PSP */
277
278 /* vi: set ts=4 sw=4 expandtab: */
279