123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2022 Sam Lantinga <[email protected]>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- #include "SDL_internal.h"
- #ifdef SDL_AUDIO_DRIVER_N3DS
- /* N3DS Audio driver */
- #include "../SDL_sysaudio.h"
- #include "SDL_n3dsaudio.h"
- #define N3DSAUDIO_DRIVER_NAME "n3ds"
- static dspHookCookie dsp_hook;
- static SDL_AudioDevice *audio_device;
- static void FreePrivateData(_THIS);
- static int FindAudioFormat(_THIS);
- static SDL_INLINE void
- contextLock(_THIS)
- {
- LightLock_Lock(&this->hidden->lock);
- }
- static SDL_INLINE void
- contextUnlock(_THIS)
- {
- LightLock_Unlock(&this->hidden->lock);
- }
- static void
- N3DSAUD_LockAudio(_THIS)
- {
- contextLock(this);
- }
- static void
- N3DSAUD_UnlockAudio(_THIS)
- {
- contextUnlock(this);
- }
- static void
- N3DSAUD_DspHook(DSP_HookType hook)
- {
- if (hook == DSPHOOK_ONCANCEL) {
- contextLock(audio_device);
- audio_device->hidden->isCancelled = SDL_TRUE;
- SDL_AtomicSet(&audio_device->enabled, SDL_FALSE);
- CondVar_Broadcast(&audio_device->hidden->cv);
- contextUnlock(audio_device);
- }
- }
- static void
- AudioFrameFinished(void *device)
- {
- bool shouldBroadcast = false;
- unsigned i;
- SDL_AudioDevice *this = (SDL_AudioDevice *) device;
- contextLock(this);
- for (i = 0; i < NUM_BUFFERS; i++) {
- if (this->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
- this->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
- shouldBroadcast = SDL_TRUE;
- }
- }
- if (shouldBroadcast) {
- CondVar_Broadcast(&this->hidden->cv);
- }
- contextUnlock(this);
- }
- static int
- N3DSAUDIO_OpenDevice(_THIS, const char *devname)
- {
- Result ndsp_init_res;
- Uint8 *data_vaddr;
- float mix[12];
- this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof *this->hidden);
- if (this->hidden == NULL) {
- return SDL_OutOfMemory();
- }
- /* Initialise the DSP service */
- ndsp_init_res = ndspInit();
- if (R_FAILED(ndsp_init_res)) {
- if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) {
- SDL_SetError("DSP init failed: dspfirm.cdc missing!");
- } else {
- SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res);
- }
- return -1;
- }
- /* Initialise internal state */
- LightLock_Init(&this->hidden->lock);
- CondVar_Init(&this->hidden->cv);
- if (this->spec.channels > 2) {
- this->spec.channels = 2;
- }
- /* Should not happen but better be safe. */
- if (FindAudioFormat(this) < 0) {
- return SDL_SetError("No supported audio format found.");
- }
- /* Update the fragment size as size in bytes */
- SDL_CalculateAudioSpec(&this->spec);
- /* Allocate mixing buffer */
- if (this->spec.size >= SDL_MAX_UINT32 / 2) {
- return SDL_SetError("Mixing buffer is too large.");
- }
- this->hidden->mixlen = this->spec.size;
- this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
- if (this->hidden->mixbuf == NULL) {
- return SDL_OutOfMemory();
- }
- SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
- data_vaddr = (Uint8 *) linearAlloc(this->hidden->mixlen * NUM_BUFFERS);
- if (data_vaddr == NULL) {
- return SDL_OutOfMemory();
- }
- SDL_memset(data_vaddr, 0, this->hidden->mixlen * NUM_BUFFERS);
- DSP_FlushDataCache(data_vaddr, this->hidden->mixlen * NUM_BUFFERS);
- this->hidden->nextbuf = 0;
- this->hidden->channels = this->spec.channels;
- this->hidden->samplerate = this->spec.freq;
- ndspChnReset(0);
- ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
- ndspChnSetRate(0, this->spec.freq);
- ndspChnSetFormat(0, this->hidden->format);
- SDL_memset(mix, 0, sizeof(mix));
- mix[0] = 1.0;
- mix[1] = 1.0;
- ndspChnSetMix(0, mix);
- SDL_memset(this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
- for (unsigned i = 0; i < NUM_BUFFERS; i++) {
- this->hidden->waveBuf[i].data_vaddr = data_vaddr;
- this->hidden->waveBuf[i].nsamples = this->hidden->mixlen / this->hidden->bytePerSample;
- data_vaddr += this->hidden->mixlen;
- }
- /* Setup callback */
- audio_device = this;
- ndspSetCallback(AudioFrameFinished, this);
- dspHook(&dsp_hook, N3DSAUD_DspHook);
- return 0;
- }
- static int
- N3DSAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
- {
- /* Delay to make this sort of simulate real audio input. */
- SDL_Delay((this->spec.samples * 1000) / this->spec.freq);
- /* always return a full buffer of silence. */
- SDL_memset(buffer, this->spec.silence, buflen);
- return buflen;
- }
- static void
- N3DSAUDIO_PlayDevice(_THIS)
- {
- size_t nextbuf;
- size_t sampleLen;
- contextLock(this);
- nextbuf = this->hidden->nextbuf;
- sampleLen = this->hidden->mixlen;
- if (this->hidden->isCancelled ||
- this->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
- contextUnlock(this);
- return;
- }
- this->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
- contextUnlock(this);
- memcpy((void *) this->hidden->waveBuf[nextbuf].data_vaddr,
- this->hidden->mixbuf, sampleLen);
- DSP_FlushDataCache(this->hidden->waveBuf[nextbuf].data_vaddr, sampleLen);
- ndspChnWaveBufAdd(0, &this->hidden->waveBuf[nextbuf]);
- }
- static void
- N3DSAUDIO_WaitDevice(_THIS)
- {
- contextLock(this);
- while (!this->hidden->isCancelled &&
- this->hidden->waveBuf[this->hidden->nextbuf].status != NDSP_WBUF_FREE) {
- CondVar_Wait(&this->hidden->cv, &this->hidden->lock);
- }
- contextUnlock(this);
- }
- static Uint8 *
- N3DSAUDIO_GetDeviceBuf(_THIS)
- {
- return this->hidden->mixbuf;
- }
- static void
- N3DSAUDIO_CloseDevice(_THIS)
- {
- contextLock(this);
- dspUnhook(&dsp_hook);
- ndspSetCallback(NULL, NULL);
- if (!this->hidden->isCancelled) {
- ndspChnReset(0);
- memset(this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
- CondVar_Broadcast(&this->hidden->cv);
- }
- contextUnlock(this);
- ndspExit();
- FreePrivateData(this);
- }
- static void
- N3DSAUDIO_ThreadInit(_THIS)
- {
- s32 current_priority;
- svcGetThreadPriority(¤t_priority, CUR_THREAD_HANDLE);
- current_priority--;
- /* 0x18 is reserved for video, 0x30 is the default for main thread */
- current_priority = SDL_clamp(current_priority, 0x19, 0x2F);
- svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority);
- }
- static SDL_bool
- N3DSAUDIO_Init(SDL_AudioDriverImpl *impl)
- {
- /* Set the function pointers */
- impl->OpenDevice = N3DSAUDIO_OpenDevice;
- impl->PlayDevice = N3DSAUDIO_PlayDevice;
- impl->WaitDevice = N3DSAUDIO_WaitDevice;
- impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf;
- impl->CloseDevice = N3DSAUDIO_CloseDevice;
- impl->ThreadInit = N3DSAUDIO_ThreadInit;
- impl->LockDevice = N3DSAUD_LockAudio;
- impl->UnlockDevice = N3DSAUD_UnlockAudio;
- impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
- /* Should be possible, but micInit would fail */
- impl->HasCaptureSupport = SDL_FALSE;
- impl->CaptureFromDevice = N3DSAUDIO_CaptureFromDevice;
- return SDL_TRUE; /* this audio target is available. */
- }
- AudioBootStrap N3DSAUDIO_bootstrap = {
- N3DSAUDIO_DRIVER_NAME,
- "SDL N3DS audio driver",
- N3DSAUDIO_Init,
- 0
- };
- /**
- * Cleans up all allocated memory, safe to call with null pointers
- */
- static void
- FreePrivateData(_THIS)
- {
- if (!this->hidden) {
- return;
- }
- if (this->hidden->waveBuf[0].data_vaddr) {
- linearFree((void *) this->hidden->waveBuf[0].data_vaddr);
- }
- if (this->hidden->mixbuf) {
- SDL_free(this->hidden->mixbuf);
- this->hidden->mixbuf = NULL;
- }
- SDL_free(this->hidden);
- this->hidden = NULL;
- }
- static int
- FindAudioFormat(_THIS)
- {
- SDL_bool found_valid_format = SDL_FALSE;
- Uint16 test_format = SDL_FirstAudioFormat(this->spec.format);
- while (!found_valid_format && test_format) {
- this->spec.format = test_format;
- switch (test_format) {
- case AUDIO_S8:
- /* Signed 8-bit audio supported */
- this->hidden->format = (this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
- this->hidden->isSigned = 1;
- this->hidden->bytePerSample = this->spec.channels;
- found_valid_format = SDL_TRUE;
- break;
- case AUDIO_S16:
- /* Signed 16-bit audio supported */
- this->hidden->format = (this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
- this->hidden->isSigned = 1;
- this->hidden->bytePerSample = this->spec.channels * 2;
- found_valid_format = SDL_TRUE;
- break;
- default:
- test_format = SDL_NextAudioFormat();
- break;
- }
- }
- return found_valid_format ? 0 : -1;
- }
- #endif /* SDL_AUDIO_DRIVER_N3DS */
- /* vi: set sts=4 ts=4 sw=4 expandtab: */
|