(CTR/3DS) audio driver: use cpu tick count instead of csndGetState to

determine the playback position.
fixes a random lockup that can happen when calling csndGetState, and
also removes the need to rely on samplePAddr since it doesn't seem to
available on all systems.
This commit is contained in:
aliaspider 2015-04-17 19:50:36 +01:00
parent 2076846a5c
commit 1575b7e87f

View File

@ -21,20 +21,24 @@
typedef struct
{
bool nonblocking;
bool playing;
int16_t* l;
int16_t* r;
int16_t* silence;
uint32_t l_paddr;
uint32_t r_paddr;
uint32_t silence_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 << 12u)
#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)
@ -52,31 +56,30 @@ static void *ctr_audio_init(const char *device, unsigned rate, unsigned latency)
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);
ctr->silence = linearAlloc(CTR_AUDIO_SIZE);
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);
memset(ctr->silence, 0, 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->silence_paddr = osConvertVirtToPhys((u32)ctr->silence);
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->silence, CTR_AUDIO_SIZE);
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, ctr->silence, ctr->silence, CTR_AUDIO_SIZE);
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, ctr->silence, ctr->silence, CTR_AUDIO_SIZE);
rate, 1.0, 1.0, ctr->r, ctr->r, CTR_AUDIO_SIZE);
CSND_SetVol(0x8, 0xFFFF, 0);
CSND_SetVol(0x9, 0, 0xFFFF);
csndExecCmds(false);
ctr->playpos = 0;
ctr->cpu_ticks_last = svcGetSystemTick();
ctr->playing = true;
return ctr;
}
@ -92,7 +95,6 @@ static void ctr_audio_free(void *data)
linearFree(ctr->l);
linearFree(ctr->r);
linearFree(ctr->silence);
free(ctr);
}
@ -107,45 +109,35 @@ static ssize_t ctr_audio_write(void *data, const void *buf, size_t size)
int i;
const uint16_t* src = buf;
RARCH_PERFORMANCE_INIT(ctraudio_f);
RARCH_PERFORMANCE_INIT(ctraudio_f);
RARCH_PERFORMANCE_START(ctraudio_f);
CSND_ChnInfo channel_info;
csndGetState(0x8, &channel_info);
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;
uint32_t playpos;
if((channel_info.samplePAddr >= (ctr->l_paddr)) &&
(channel_info.samplePAddr < (ctr->l_paddr + CTR_AUDIO_SIZE)))
{
playpos = (channel_info.samplePAddr - ctr->l_paddr) / sizeof(uint16_t);
}
else
{
CSND_SetBlock(0x8, 1, ctr->l_paddr, CTR_AUDIO_SIZE);
CSND_SetBlock(0x9, 1, ctr->r_paddr, CTR_AUDIO_SIZE);
csndExecCmds(false);
playpos = 0;
}
if((((playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 2)) ||
(((ctr->pos - playpos ) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 4)) ||
(((playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK) < (size >> 2)))
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 = (playpos + (CTR_AUDIO_COUNT >> 1)) & CTR_AUDIO_COUNT_MASK;
ctr->pos = (ctr->playpos + (CTR_AUDIO_COUNT >> 1)) & CTR_AUDIO_COUNT_MASK;
else
{
do{
/* todo: compute the correct sleep period */
svcSleepThread(100000);
// svcSleepThread(((s64)(CTR_AUDIO_COUNT >> 8) * 1000000000) / ctr->rate);
csndGetState(0x8, &channel_info);
playpos = (channel_info.samplePAddr - ctr->l_paddr) / sizeof(uint16_t);
}while (((playpos - ctr->pos) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 1)
|| (((ctr->pos - playpos) & CTR_AUDIO_COUNT_MASK) < (CTR_AUDIO_COUNT >> 4)));
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];
@ -153,6 +145,9 @@ static ssize_t ctr_audio_write(void *data, const void *buf, size_t size)
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);
@ -163,31 +158,51 @@ static bool ctr_audio_stop(void *data)
{
ctr_audio_t* ctr = (ctr_audio_t*)data;
CSND_SetBlock(0x8, 1, ctr->silence_paddr, CTR_AUDIO_SIZE);
CSND_SetBlock(0x9, 1, ctr->silence_paddr, CTR_AUDIO_SIZE);
/* 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)
{
(void)data;
return true;
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;
global_t *global = global_get_ptr();
// csndPlaySound(0x8, SOUND_LOOPMODE(CSND_LOOPMODE_NORMAL)| SOUND_FORMAT(CSND_ENCODING_PCM16),
// ctr->rate, ctr->l_paddr + ((ctr->pos + (CTR_AUDIO_SIZE / 2)) & CTR_AUDIO_SIZE_MASK),
// (u32*)ctr->silence_paddr, CTR_AUDIO_SIZE);
/* prevents restarting audio when the menu
* is toggled off on shutdown */
if (global->system.shutdown)
return true;
// CSND_SetPlayState(0x8, 1);
// CSND_SetPlayState(0x9, 1);
CSND_SetVol(0x8, 0x00008000, 0);
CSND_SetVol(0x9, 0x80000000, 0);
CSND_SetBlock(0x8, 1, ctr->l_paddr, CTR_AUDIO_SIZE);
CSND_SetBlock(0x9, 1, ctr->r_paddr, CTR_AUDIO_SIZE);
csndExecCmds(false);
ctr->playing = true;
return true;
}