RetroArch/audio/drivers/ctr_audio.c

304 lines
7.7 KiB
C
Raw Normal View History

2015-04-01 21:14:13 +00:00
/* RetroArch - A frontend for libretro.
2015-04-01 23:43:28 +00:00
* Copyright (C) 2014-2015 - Ali Bouhlel
2015-04-01 21:14:13 +00:00
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <3ds.h>
2015-04-01 21:14:13 +00:00
#include "../../general.h"
#include "../../driver.h"
#include "../../performance.h"
2015-04-01 21:14:13 +00:00
2015-04-10 18:10:34 +00:00
typedef struct
{
bool nonblocking;
bool playing;
2015-04-10 18:10:34 +00:00
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;
2015-04-10 18:10:34 +00:00
int rate;
} ctr_audio_t;
#define CTR_AUDIO_COUNT (1u << 11u)
2015-04-10 18:10:34 +00:00
#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_update_playpos(ctr_audio_t* ctr)
{
uint32_t samples_played;
uint64_t current_tick;
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;
}
Result csndPlaySound_custom(int chn, u32 flags, u32 sampleRate, float vol, float pan, void* data0, void* data1, u32 size)
{
if (!(csndChannels & BIT(chn)))
return 1;
u32 paddr0 = 0, paddr1 = 0;
int encoding = (flags >> 12) & 3;
int loopMode = (flags >> 10) & 3;
if (!loopMode) flags |= SOUND_ONE_SHOT;
if (encoding != CSND_ENCODING_PSG)
{
if (data0) paddr0 = osConvertVirtToPhys((u32)data0);
if (data1) paddr1 = osConvertVirtToPhys((u32)data1);
if (data0 && encoding == CSND_ENCODING_ADPCM)
{
int adpcmSample = ((s16*)data0)[-2];
int adpcmIndex = ((u8*)data0)[-2];
CSND_SetAdpcmState(chn, 0, adpcmSample, adpcmIndex);
}
}
u32 timer = CSND_TIMER(sampleRate);
if (timer < 0x0042) timer = 0x0042;
else if (timer > 0xFFFF) timer = 0xFFFF;
flags &= ~0xFFFF001F;
flags |= SOUND_ENABLE | SOUND_CHANNEL(chn) | (timer << 16);
u32 volumes = CSND_VOL(vol, pan);
CSND_SetChnRegs(flags, paddr0, paddr1, size, volumes, volumes);
if (loopMode == CSND_LOOPMODE_NORMAL && paddr1 > paddr0)
{
// Now that the first block is playing, configure the size of the subsequent blocks
size -= paddr1 - paddr0;
CSND_SetBlock(chn, 1, paddr1, size);
}
return 0;
}
2015-04-01 21:14:13 +00:00
static void *ctr_audio_init(const char *device, unsigned rate, unsigned latency)
{
2015-09-20 08:02:47 +00:00
ctr_audio_t *ctr = (ctr_audio_t*)calloc(1, sizeof(ctr_audio_t));
2015-04-10 18:10:34 +00:00
2015-09-20 08:02:47 +00:00
if (!ctr)
return NULL;
2015-04-10 18:10:34 +00:00
2015-04-01 21:14:13 +00:00
(void)device;
(void)rate;
(void)latency;
2015-04-10 18:10:34 +00:00
2015-09-20 08:02:47 +00:00
ctr->l = linearAlloc(CTR_AUDIO_SIZE);
ctr->r = linearAlloc(CTR_AUDIO_SIZE);
2015-04-10 18:10:34 +00:00
memset(ctr->l, 0, CTR_AUDIO_SIZE);
memset(ctr->r, 0, CTR_AUDIO_SIZE);
2015-04-10 18:10:34 +00:00
2015-09-20 08:02:47 +00:00
ctr->l_paddr = osConvertVirtToPhys((u32)ctr->l);
ctr->r_paddr = osConvertVirtToPhys((u32)ctr->r);
2015-04-10 18:10:34 +00:00
2015-09-20 08:02:47 +00:00
ctr->pos = 0;
ctr->rate = rate;
ctr->cpu_ticks_per_sample = CSND_TIMER(rate) * 4;
2015-04-10 18:10:34 +00:00
GSPGPU_FlushDataCache(NULL, (u8*)ctr->l_paddr, CTR_AUDIO_SIZE);
GSPGPU_FlushDataCache(NULL, (u8*)ctr->r_paddr, CTR_AUDIO_SIZE);
csndPlaySound_custom(0x8, SOUND_LOOPMODE(CSND_LOOPMODE_NORMAL)| SOUND_FORMAT(CSND_ENCODING_PCM16),
rate, 1.0, -1.0, ctr->l, ctr->l, CTR_AUDIO_SIZE);
2015-04-10 18:10:34 +00:00
csndPlaySound_custom(0x9, SOUND_LOOPMODE(CSND_LOOPMODE_NORMAL)| SOUND_FORMAT(CSND_ENCODING_PCM16),
rate, 1.0, 1.0, ctr->r, ctr->r, CTR_AUDIO_SIZE);
2015-04-10 18:10:34 +00:00
csndExecCmds(true);
2015-09-20 08:02:47 +00:00
ctr->playpos = 0;
ctr->cpu_ticks_last = svcGetSystemTick();
ctr->playing = true;
2015-04-10 18:10:34 +00:00
return ctr;
2015-04-01 21:14:13 +00:00
}
static void ctr_audio_free(void *data)
{
2015-04-10 18:10:34 +00:00
ctr_audio_t* ctr = (ctr_audio_t*)data;
// csndExit();
CSND_SetPlayState(0x8, 0);
CSND_SetPlayState(0x9, 0);
csndExecCmds(false);
2015-04-10 18:10:34 +00:00
linearFree(ctr->l);
linearFree(ctr->r);
free(ctr);
2015-04-01 21:14:13 +00:00
}
static ssize_t ctr_audio_write(void *data, const void *buf, size_t size)
{
2015-09-20 08:02:47 +00:00
int i;
uint32_t samples_played;
uint64_t current_tick;
static struct retro_perf_counter ctraudio_f = {0};
const uint16_t *src = buf;
ctr_audio_t *ctr = (ctr_audio_t*)data;
2015-04-01 21:14:13 +00:00
(void)data;
(void)buf;
2015-09-20 08:02:47 +00:00
rarch_perf_init(&ctraudio_f, "ctraudio_f");
retro_perf_start(&ctraudio_f);
ctr_audio_update_playpos(ctr);
2015-04-10 18:10:34 +00:00
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)))
2015-04-10 18:10:34 +00:00
{
if (ctr->nonblocking)
ctr->pos = (ctr->playpos + (CTR_AUDIO_COUNT >> 1)) & CTR_AUDIO_COUNT_MASK;
2015-04-10 18:10:34 +00:00
else
{
do{
/* todo: compute the correct sleep period */
2015-09-22 16:55:14 +00:00
retro_sleep(1);
ctr_audio_update_playpos(ctr);
}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)));
2015-04-10 18:10:34 +00:00
}
}
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;
}
2015-09-20 08:02:47 +00:00
GSPGPU_FlushDataCache(NULL, (u8*)ctr->l, CTR_AUDIO_SIZE);
GSPGPU_FlushDataCache(NULL, (u8*)ctr->r, CTR_AUDIO_SIZE);
2015-09-20 08:02:47 +00:00
retro_perf_stop(&ctraudio_f);
2015-04-10 18:10:34 +00:00
2015-04-01 21:14:13 +00:00
return size;
}
static bool ctr_audio_stop(void *data)
2015-04-10 18:10:34 +00:00
{
ctr_audio_t* ctr = (ctr_audio_t*)data;
/* using SetPlayState would make tracking the playback
* position more difficult */
2015-09-20 08:02:47 +00:00
#if 0
CSND_SetPlayState(0x8, 0);
CSND_SetPlayState(0x9, 0);
#endif
/* 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);
2015-04-10 18:10:34 +00:00
ctr->playing = false;
2015-04-01 21:14:13 +00:00
return true;
}
static bool ctr_audio_alive(void *data)
{
ctr_audio_t* ctr = (ctr_audio_t*)data;
return ctr->playing;
2015-04-01 21:14:13 +00:00
}
static bool ctr_audio_start(void *data)
{
2015-04-10 18:10:34 +00:00
ctr_audio_t* ctr = (ctr_audio_t*)data;
2015-06-25 11:46:32 +00:00
rarch_system_info_t *system = rarch_system_info_get_ptr();
2015-09-20 08:02:47 +00:00
/* Prevents restarting audio when the menu
* is toggled off on shutdown */
2015-06-25 11:46:32 +00:00
if (system->shutdown)
return true;
2015-04-10 18:10:34 +00:00
2015-09-20 08:02:47 +00:00
#if 0
CSND_SetPlayState(0x8, 1);
CSND_SetPlayState(0x9, 1);
#endif
CSND_SetVol(0x8, 0x00008000, 0);
CSND_SetVol(0x9, 0x80000000, 0);
2015-04-10 18:10:34 +00:00
csndExecCmds(false);
2015-04-10 18:10:34 +00:00
ctr->playing = true;
2015-04-01 21:14:13 +00:00
return true;
}
static void ctr_audio_set_nonblock_state(void *data, bool state)
{
2015-04-10 18:10:34 +00:00
ctr_audio_t* ctr = (ctr_audio_t*)data;
if (ctr)
ctr->nonblocking = state;
2015-04-01 21:14:13 +00:00
}
static bool ctr_audio_use_float(void *data)
{
(void)data;
2015-04-10 18:10:34 +00:00
return false;
2015-04-01 21:14:13 +00:00
}
static size_t ctr_audio_write_avail(void *data)
{
ctr_audio_t* ctr = (ctr_audio_t*)data;
ctr_audio_update_playpos(ctr);
return (ctr->playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK;
}
static size_t ctr_audio_buffer_size(void *data)
{
(void)data;
return CTR_AUDIO_COUNT;
}
2015-04-01 21:14:13 +00:00
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,
ctr_audio_buffer_size
2015-04-01 21:14:13 +00:00
};