/* RetroArch - A frontend for libretro. * Copyright (C) 2014-2015 - Ali Bouhlel * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include "../../general.h" #include "../../driver.h" #include "../../performance.h" typedef struct { bool nonblocking; bool playing; int16_t* l; int16_t* r; uint32_t l_paddr; uint32_t r_paddr; uint32_t pos; uint32_t playpos; uint32_t cpu_ticks_per_sample; uint64_t cpu_ticks_last; int rate; } ctr_audio_t; #define CTR_AUDIO_COUNT (1u << 11u) #define CTR_AUDIO_COUNT_MASK (CTR_AUDIO_COUNT - 1u) #define CTR_AUDIO_SIZE (CTR_AUDIO_COUNT * sizeof(int16_t)) #define CTR_AUDIO_SIZE_MASK (CTR_AUDIO_SIZE - 1u) static void *ctr_audio_init(const char *device, unsigned rate, unsigned latency) { (void)device; (void)rate; (void)latency; // if(!csndInit()) // return NULL; ctr_audio_t *ctr = (ctr_audio_t*)calloc(1, sizeof(ctr_audio_t)); ctr->l = linearAlloc(CTR_AUDIO_SIZE); ctr->r = linearAlloc(CTR_AUDIO_SIZE); memset(ctr->l, 0, CTR_AUDIO_SIZE); memset(ctr->r, 0, CTR_AUDIO_SIZE); ctr->l_paddr = osConvertVirtToPhys((u32)ctr->l); ctr->r_paddr = osConvertVirtToPhys((u32)ctr->r); ctr->pos = 0; ctr->rate = rate; ctr->cpu_ticks_per_sample = CSND_TIMER(rate) * 4; GSPGPU_FlushDataCache(NULL, (u8*)ctr->l_paddr, CTR_AUDIO_SIZE); GSPGPU_FlushDataCache(NULL, (u8*)ctr->r_paddr, CTR_AUDIO_SIZE); csndPlaySound(0x8, SOUND_LOOPMODE(CSND_LOOPMODE_NORMAL)| SOUND_FORMAT(CSND_ENCODING_PCM16), rate, 1.0, -1.0, ctr->l, ctr->l, CTR_AUDIO_SIZE); csndPlaySound(0x9, SOUND_LOOPMODE(CSND_LOOPMODE_NORMAL)| SOUND_FORMAT(CSND_ENCODING_PCM16), rate, 1.0, 1.0, ctr->r, ctr->r, CTR_AUDIO_SIZE); ctr->playpos = 0; ctr->cpu_ticks_last = svcGetSystemTick(); ctr->playing = true; return ctr; } static void ctr_audio_free(void *data) { ctr_audio_t* ctr = (ctr_audio_t*)data; // csndExit(); CSND_SetPlayState(0x8, 0); CSND_SetPlayState(0x9, 0); csndExecCmds(false); linearFree(ctr->l); linearFree(ctr->r); free(ctr); } static ssize_t ctr_audio_write(void *data, const void *buf, size_t size) { (void)data; (void)buf; ctr_audio_t* ctr = (ctr_audio_t*)data; int i; const uint16_t* src = buf; RARCH_PERFORMANCE_INIT(ctraudio_f); RARCH_PERFORMANCE_START(ctraudio_f); uint64_t current_tick = svcGetSystemTick(); uint32_t samples_played = (current_tick - ctr->cpu_ticks_last) / ctr->cpu_ticks_per_sample; ctr->playpos = (ctr->playpos + samples_played) & CTR_AUDIO_COUNT_MASK; ctr->cpu_ticks_last += samples_played * ctr->cpu_ticks_per_sample; if((((ctr->playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 2)) || (((ctr->pos - ctr->playpos ) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 4)) || (((ctr->playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK) < (size >> 2))) { if (ctr->nonblocking) ctr->pos = (ctr->playpos + (CTR_AUDIO_COUNT >> 1)) & CTR_AUDIO_COUNT_MASK; else { do{ /* todo: compute the correct sleep period */ rarch_sleep(1); current_tick = svcGetSystemTick(); samples_played = (current_tick - ctr->cpu_ticks_last) / ctr->cpu_ticks_per_sample; ctr->playpos = (ctr->playpos + samples_played) & CTR_AUDIO_COUNT_MASK; ctr->cpu_ticks_last += samples_played * ctr->cpu_ticks_per_sample; }while (((ctr->playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 1) || (((ctr->pos - ctr->playpos) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 4))); } } for (i = 0; i < (size >> 1); i += 2) { ctr->l[ctr->pos] = src[i]; ctr->r[ctr->pos] = src[i + 1]; ctr->pos++; ctr->pos &= CTR_AUDIO_COUNT_MASK; } GSPGPU_FlushDataCache(NULL, (u8*)ctr->l, CTR_AUDIO_SIZE); GSPGPU_FlushDataCache(NULL, (u8*)ctr->r, CTR_AUDIO_SIZE); RARCH_PERFORMANCE_STOP(ctraudio_f); return size; } static bool ctr_audio_stop(void *data) { ctr_audio_t* ctr = (ctr_audio_t*)data; /* using SetPlayState would make tracking the playback * position more difficult */ // CSND_SetPlayState(0x8, 0); // CSND_SetPlayState(0x9, 0); /* setting the channel volume to 0 seems to make it * impossible to set it back to full volume later */ CSND_SetVol(0x8, 0x00000001, 0); CSND_SetVol(0x9, 0x00010000, 0); csndExecCmds(false); ctr->playing = false; return true; } static bool ctr_audio_alive(void *data) { ctr_audio_t* ctr = (ctr_audio_t*)data; return ctr->playing; } static bool ctr_audio_start(void *data) { ctr_audio_t* ctr = (ctr_audio_t*)data; rarch_system_info_t *system = rarch_system_info_get_ptr(); /* prevents restarting audio when the menu * is toggled off on shutdown */ if (system->shutdown) return true; // CSND_SetPlayState(0x8, 1); // CSND_SetPlayState(0x9, 1); CSND_SetVol(0x8, 0x00008000, 0); CSND_SetVol(0x9, 0x80000000, 0); csndExecCmds(false); ctr->playing = true; return true; } static void ctr_audio_set_nonblock_state(void *data, bool state) { ctr_audio_t* ctr = (ctr_audio_t*)data; if (ctr) ctr->nonblocking = state; } static bool ctr_audio_use_float(void *data) { (void)data; return false; } static size_t ctr_audio_write_avail(void *data) { /* stub */ (void)data; return 0; } audio_driver_t audio_ctr = { ctr_audio_init, ctr_audio_write, ctr_audio_stop, ctr_audio_start, ctr_audio_alive, ctr_audio_set_nonblock_state, ctr_audio_free, ctr_audio_use_float, "ctr", ctr_audio_write_avail, NULL };