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