Atlas - SDL_n3dsaudio.c
Home / ext / SDL / src / audio / n3ds Lines: 1 | Size: 8579 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#ifdef SDL_AUDIO_DRIVER_N3DS 24 25// N3DS Audio driver 26 27#include "../SDL_sysaudio.h" 28#include "SDL_n3dsaudio.h" 29 30#define N3DSAUDIO_DRIVER_NAME "n3ds" 31 32static dspHookCookie dsp_hook; 33static SDL_AudioDevice *audio_device; 34 35// fully local functions related to the wavebufs / DSP, not the same as the `device->lock` SDL_Mutex! 36static SDL_INLINE void contextLock(SDL_AudioDevice *device) 37{ 38 LightLock_Lock(&device->hidden->lock); 39} 40 41static SDL_INLINE void contextUnlock(SDL_AudioDevice *device) 42{ 43 LightLock_Unlock(&device->hidden->lock); 44} 45 46static void N3DSAUD_DspHook(DSP_HookType hook) 47{ 48 if (hook == DSPHOOK_ONCANCEL) { 49 contextLock(audio_device); 50 audio_device->hidden->isCancelled = true; 51 SDL_AudioDeviceDisconnected(audio_device); 52 CondVar_Broadcast(&audio_device->hidden->cv); 53 contextUnlock(audio_device); 54 } 55} 56 57static void AudioFrameFinished(void *vdevice) 58{ 59 bool shouldBroadcast = false; 60 unsigned i; 61 SDL_AudioDevice *device = (SDL_AudioDevice *)vdevice; 62 63 contextLock(device); 64 65 for (i = 0; i < NUM_BUFFERS; i++) { 66 if (device->hidden->waveBuf[i].status == NDSP_WBUF_DONE) { 67 device->hidden->waveBuf[i].status = NDSP_WBUF_FREE; 68 shouldBroadcast = true; 69 } 70 } 71 72 if (shouldBroadcast) { 73 CondVar_Broadcast(&device->hidden->cv); 74 } 75 76 contextUnlock(device); 77} 78 79static bool N3DSAUDIO_OpenDevice(SDL_AudioDevice *device) 80{ 81 Result ndsp_init_res; 82 Uint8 *data_vaddr; 83 float mix[12]; 84 85 device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); 86 if (!device->hidden) { 87 return false; 88 } 89 90 // Initialise the DSP service 91 ndsp_init_res = ndspInit(); 92 if (R_FAILED(ndsp_init_res)) { 93 if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) { 94 return SDL_SetError("DSP init failed: dspfirm.cdc missing!"); 95 } else { 96 return SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res); 97 } 98 } 99 100 // Initialise internal state 101 LightLock_Init(&device->hidden->lock); 102 CondVar_Init(&device->hidden->cv); 103 104 if (device->spec.channels > 2) { 105 device->spec.channels = 2; 106 } 107 108 Uint32 format = 0; 109 SDL_AudioFormat test_format; 110 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); 111 while ((test_format = *(closefmts++)) != 0) { 112 if (test_format == SDL_AUDIO_S8) { // Signed 8-bit audio supported 113 format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8; 114 break; 115 } else if (test_format == SDL_AUDIO_S16) { // Signed 16-bit audio supported 116 format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16; 117 break; 118 } 119 } 120 121 if (!test_format) { // shouldn't happen, but just in case... 122 return SDL_SetError("No supported audio format found."); 123 } 124 125 device->spec.format = test_format; 126 127 // Update the fragment size as size in bytes 128 SDL_UpdatedAudioDeviceFormat(device); 129 130 // Allocate mixing buffer 131 if (device->buffer_size >= SDL_MAX_UINT32 / 2) { 132 return SDL_SetError("Mixing buffer is too large."); 133 } 134 135 device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); 136 if (!device->hidden->mixbuf) { 137 return false; 138 } 139 140 SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); 141 142 data_vaddr = (Uint8 *)linearAlloc(device->buffer_size * NUM_BUFFERS); 143 if (!data_vaddr) { 144 return SDL_OutOfMemory(); 145 } 146 147 SDL_memset(data_vaddr, 0, device->buffer_size * NUM_BUFFERS); 148 DSP_FlushDataCache(data_vaddr, device->buffer_size * NUM_BUFFERS); 149 150 device->hidden->nextbuf = 0; 151 152 ndspChnReset(0); 153 154 ndspChnSetInterp(0, NDSP_INTERP_LINEAR); 155 ndspChnSetRate(0, device->spec.freq); 156 ndspChnSetFormat(0, format); 157 158 SDL_zeroa(mix); 159 mix[0] = mix[1] = 1.0f; 160 ndspChnSetMix(0, mix); 161 162 SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); 163 164 const int sample_frame_size = SDL_AUDIO_FRAMESIZE(device->spec); 165 for (unsigned i = 0; i < NUM_BUFFERS; i++) { 166 device->hidden->waveBuf[i].data_vaddr = data_vaddr; 167 device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size; 168 data_vaddr += device->buffer_size; 169 } 170 171 // Setup callback 172 audio_device = device; 173 ndspSetCallback(AudioFrameFinished, device); 174 dspHook(&dsp_hook, N3DSAUD_DspHook); 175 176 return true; 177} 178 179static bool N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) 180{ 181 contextLock(device); 182 183 const size_t nextbuf = device->hidden->nextbuf; 184 185 if (device->hidden->isCancelled || 186 device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) { 187 contextUnlock(device); 188 return true; // !!! FIXME: is this a fatal error? If so, this should return false. 189 } 190 191 device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS; 192 193 contextUnlock(device); 194 195 SDL_memcpy((void *)device->hidden->waveBuf[nextbuf].data_vaddr, buffer, buflen); 196 DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen); 197 198 ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]); 199 200 return true; 201} 202 203static bool N3DSAUDIO_WaitDevice(SDL_AudioDevice *device) 204{ 205 contextLock(device); 206 while (!device->hidden->isCancelled && !SDL_GetAtomicInt(&device->shutdown) && 207 device->hidden->waveBuf[device->hidden->nextbuf].status != NDSP_WBUF_FREE) { 208 CondVar_Wait(&device->hidden->cv, &device->hidden->lock); 209 } 210 contextUnlock(device); 211 return true; 212} 213 214static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) 215{ 216 return device->hidden->mixbuf; 217} 218 219static void N3DSAUDIO_CloseDevice(SDL_AudioDevice *device) 220{ 221 if (!device->hidden) { 222 return; 223 } 224 225 contextLock(device); 226 227 dspUnhook(&dsp_hook); 228 ndspSetCallback(NULL, NULL); 229 230 if (!device->hidden->isCancelled) { 231 ndspChnReset(0); 232 SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); 233 CondVar_Broadcast(&device->hidden->cv); 234 } 235 236 contextUnlock(device); 237 238 ndspExit(); 239 240 if (device->hidden->waveBuf[0].data_vaddr) { 241 linearFree((void *)device->hidden->waveBuf[0].data_vaddr); 242 } 243 244 if (device->hidden->mixbuf) { 245 SDL_free(device->hidden->mixbuf); 246 device->hidden->mixbuf = NULL; 247 } 248 249 SDL_free(device->hidden); 250 device->hidden = NULL; 251} 252 253static void N3DSAUDIO_ThreadInit(SDL_AudioDevice *device) 254{ 255 s32 current_priority = 0x30; 256 svcGetThreadPriority(¤t_priority, CUR_THREAD_HANDLE); 257 current_priority--; 258 // 0x18 is reserved for video, 0x30 is the default for main thread 259 current_priority = SDL_clamp(current_priority, 0x19, 0x2F); 260 svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority); 261} 262 263static bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl) 264{ 265 impl->OpenDevice = N3DSAUDIO_OpenDevice; 266 impl->PlayDevice = N3DSAUDIO_PlayDevice; 267 impl->WaitDevice = N3DSAUDIO_WaitDevice; 268 impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf; 269 impl->CloseDevice = N3DSAUDIO_CloseDevice; 270 impl->ThreadInit = N3DSAUDIO_ThreadInit; 271 impl->OnlyHasDefaultPlaybackDevice = true; 272 273 // Should be possible, but micInit would fail 274 impl->HasRecordingSupport = false; 275 276 return true; 277} 278 279AudioBootStrap N3DSAUDIO_bootstrap = { 280 N3DSAUDIO_DRIVER_NAME, 281 "SDL N3DS audio driver", 282 N3DSAUDIO_Init, 283 false, 284 false 285}; 286 287#endif // SDL_AUDIO_DRIVER_N3DS 288[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.