From c443c34ad0a866de0b3b95bd1acaffe5fe1a8ee7 Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 15 Apr 2015 22:17:19 +0200 Subject: [PATCH] Major CSND overhaul, see details: - Missing commands are now added - Volume and pan are now available in csndPlaySound - Minor optimization that saves one memcpy per command - Minor optimization in shared mem channel index computation --- libctru/include/3ds/services/csnd.h | 67 +++++++- libctru/source/services/csnd.c | 250 +++++++++++++++++----------- 2 files changed, 215 insertions(+), 102 deletions(-) diff --git a/libctru/include/3ds/services/csnd.h b/libctru/include/3ds/services/csnd.h index 5deac20..9264a27 100644 --- a/libctru/include/3ds/services/csnd.h +++ b/libctru/include/3ds/services/csnd.h @@ -6,6 +6,21 @@ #define CSND_TIMER(n) (0x3FEC3FC / ((u32)(n))) +// Convert a vol-pan pair into a left/right volume pair used by the hardware +static inline u32 CSND_VOL(float vol, float pan) +{ + if (vol < 0.0) vol = 0.0; + else if (vol > 1.0) vol = 1.0; + + float rpan = (pan+1) / 2; + if (rpan < 0.0) rpan = 0.0; + else if (rpan > 1.0) rpan = 1.0; + + u32 lvol = vol*(1-rpan) * 0x8000; + u32 rvol = vol*rpan * 0x8000; + return lvol | (rvol << 16); +} + enum { CSND_ENCODING_PCM8 = 0, @@ -38,6 +53,15 @@ enum SOUND_ENABLE = BIT(14), }; +enum +{ + CAPTURE_REPEAT = 0, + CAPTURE_ONE_SHOT = BIT(0), + CAPTURE_FORMAT_16BIT = 0, + CAPTURE_FORMAT_8BIT = BIT(1), + CAPTURE_ENABLE = BIT(15), +}; + // Duty cycles for a PSG channel enum { @@ -62,10 +86,22 @@ typedef union s16 adpcmSample; u8 adpcmIndex; u8 _pad3; - u32 samplePAddr; + u32 unknownZero; }; } CSND_ChnInfo; +typedef union +{ + u32 value[2]; + struct + { + u8 active; + u8 _pad1; + u16 _pad2; + u32 unknownZero; + }; +} CSND_CapInfo; + // See here regarding CSND shared-mem commands, etc: http://3dbrew.org/wiki/CSND_Shared_Memory extern vu32* csndSharedMem; @@ -75,32 +111,47 @@ extern u32 csndChannels; // Bitmask of channels that are allowed for usage Result CSND_AcquireCapUnit(u32* capUnit); Result CSND_ReleaseCapUnit(u32 capUnit); +Result CSND_Reset(void); // Currently breaks sound, don't use for now! + Result csndInit(void); Result csndExit(void); -void csndWriteCmd(int cmdid, u8 *cmdparams); +u32* csndAddCmd(int cmdid); // Adds a command to the list and returns the buffer to which write its arguments. +void csndWriteCmd(int cmdid, u8* cmdparams); // As above, but copies the arguments from an external buffer Result csndExecCmds(bool waitDone); void CSND_SetPlayStateR(u32 channel, u32 value); void CSND_SetPlayState(u32 channel, u32 value); +void CSND_SetEncoding(u32 channel, u32 value); void CSND_SetBlock(u32 channel, int block, u32 physaddr, u32 size); -void CSND_SetVol(u32 channel, u16 left, u16 right); -void CSND_SetTimer(u32 channel, u32 timer); +void CSND_SetLooping(u32 channel, u32 value); +void CSND_SetBit7(u32 channel, bool set); +void CSND_SetInterp(u32 channel, bool interp); void CSND_SetDuty(u32 channel, u32 duty); +void CSND_SetTimer(u32 channel, u32 timer); +void CSND_SetVol(u32 channel, u32 chnVolumes, u32 capVolumes); void CSND_SetAdpcmState(u32 channel, int block, int sample, int index); void CSND_SetAdpcmReload(u32 channel, bool reload); -void CSND_SetChnRegs(u32 flags, u32 physaddr0, u32 physaddr1, u32 totalbytesize); +void CSND_SetChnRegs(u32 flags, u32 physaddr0, u32 physaddr1, u32 totalbytesize, u32 chnVolumes, u32 capVolumes); +void CSND_SetChnRegsPSG(u32 flags, u32 chnVolumes, u32 capVolumes, u32 duty); +void CSND_SetChnRegsNoise(u32 flags, u32 chnVolumes, u32 capVolumes); void CSND_CapEnable(u32 capUnit, bool enable); -void CSND_CapSetBit(u32 capUnit, int bit, bool state); // Sets bit0..2 in the CNT register, purpose currently unknown +void CSND_CapSetRepeat(u32 capUnit, bool repeat); +void CSND_CapSetFormat(u32 capUnit, bool eightbit); +void CSND_CapSetBit2(u32 capUnit, bool set); void CSND_CapSetTimer(u32 capUnit, u32 timer); -void CSND_CapSetBuffer(u32 capUnit, u32 paddr, u32 size); +void CSND_CapSetBuffer(u32 capUnit, u32 addr, u32 size); +void CSND_SetCapRegs(u32 capUnit, u32 flags, u32 addr, u32 size); +Result CSND_SetDspFlags(bool waitDone); Result CSND_UpdateInfo(bool waitDone); -Result csndPlaySound(int chn, u32 flags, u32 sampleRate, void* data0, void* data1, u32 size); +Result csndPlaySound(int chn, u32 flags, u32 sampleRate, float vol, float pan, void* data0, void* data1, u32 size); +void csndGetDspFlags(u32* outSemFlags, u32* outIrqFlags); // Requires previous CSND_UpdateInfo() CSND_ChnInfo* csndGetChnInfo(u32 channel); // Requires previous CSND_UpdateInfo() +CSND_CapInfo* csndGetCapInfo(u32 capUnit); // Requires previous CSND_UpdateInfo() Result csndGetState(u32 channel, CSND_ChnInfo* out); Result csndIsPlaying(u32 channel, u8* status); diff --git a/libctru/source/services/csnd.c b/libctru/source/services/csnd.c index 29b8c66..d034237 100644 --- a/libctru/source/services/csnd.c +++ b/libctru/source/services/csnd.c @@ -17,7 +17,6 @@ static Handle csndHandle = 0; static Handle csndMutex = 0; static Handle csndSharedMemBlock = 0; -static u8 csndChnIdx[CSND_NUM_CHANNELS]; static u32 csndCmdBlockSize = 0x2000; static u32 csndCmdStartOff = 0; static u32 csndCmdCurOff = 0; @@ -104,6 +103,18 @@ Result CSND_ReleaseCapUnit(u32 capUnit) return (Result)cmdbuf[1]; } +Result CSND_Reset(void) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x000C0000; + + if((ret = svcSendSyncRequest(csndHandle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + Result csndInit(void) { Result ret=0; @@ -115,10 +126,10 @@ Result csndInit(void) if (ret != 0) return ret; // Calculate offsets and sizes required by the CSND module - csndOffsets[0] = csndCmdBlockSize; // Offset to some unknown DSP status flags + csndOffsets[0] = csndCmdBlockSize; // Offset to DSP semaphore and irq disable flags csndOffsets[1] = csndOffsets[0] + 8; // Offset to sound channel information - csndOffsets[2] = csndOffsets[1] + 32*sizeof(CSND_ChnInfo); // Offset to information for an 'unknown' sort of channels - csndOffsets[3] = csndOffsets[2] + 2*8; // Offset to more unknown information + csndOffsets[2] = csndOffsets[1] + 32*sizeof(CSND_ChnInfo); // Offset to capture unit information + csndOffsets[3] = csndOffsets[2] + 2*8; // Offset to the input of command 0x00040080 csndSharedMemSize = csndOffsets[3] + 0x3C; // Total size of the CSND shared memory ret = CSND_Initialize(); @@ -132,15 +143,6 @@ Result csndInit(void) ret = CSND_AcquireSoundChannels(&csndChannels); if (ret != 0) return ret; - // Build channel indices for the sound channel information table - int i, j = 0; - for (i = 0; i < CSND_NUM_CHANNELS; i ++) - { - csndChnIdx[i] = j; - if (csndChannels & BIT(i)) - j ++; - } - return 0; } @@ -148,6 +150,9 @@ Result csndExit(void) { Result ret; + //ret = CSND_Reset(); + //if (ret != 0) return ret; + ret = CSND_ReleaseSoundChannels(); if (ret != 0) return ret; @@ -159,7 +164,7 @@ Result csndExit(void) return ret; } -static Result CSND_ExecCmd0(u32 offset) +static Result CSND_ExecuteCommands(u32 offset) { Result ret=0; u32 *cmdbuf = getThreadCommandBuffer(); @@ -172,7 +177,7 @@ static Result CSND_ExecCmd0(u32 offset) return (Result)cmdbuf[1]; } -void csndWriteCmd(int cmdid, u8 *cmdparams) +u32* csndAddCmd(int cmdid) { vu16* ptr; u32 prevoff; @@ -197,13 +202,19 @@ void csndWriteCmd(int cmdid, u8 *cmdparams) ptr[1] = cmdid; ptr[2] = 0; ptr[3] = 0; - memcpy((void*)&ptr[4], cmdparams, 0x18); + u32* ret = (u32*)&ptr[4]; csndCmdCurOff += 0x20; if (csndCmdCurOff >= csndCmdBlockSize) csndCmdCurOff = 0; svcReleaseMutex(csndMutex); + return ret; +} + +void csndWriteCmd(int cmdid, u8 *cmdparams) +{ + memcpy(csndAddCmd(cmdid), cmdparams, 0x18); } Result csndExecCmds(bool waitDone) @@ -216,7 +227,7 @@ Result csndExecCmds(bool waitDone) vu8* flag = (vu8*)&csndSharedMem[(csndCmdStartOff + 4) >> 2]; - ret = CSND_ExecCmd0(csndCmdStartOff); + ret = CSND_ExecuteCommands(csndCmdStartOff); csndCmdStartOff = csndCmdCurOff; if (ret != 0) return ret; @@ -228,174 +239,206 @@ Result csndExecCmds(bool waitDone) void CSND_SetPlayStateR(u32 channel, u32 value) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x000); cmdparams[0] = channel & 0x1f; cmdparams[1] = value; - - csndWriteCmd(0x0, (u8*)&cmdparams); } void CSND_SetPlayState(u32 channel, u32 value) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x001); cmdparams[0] = channel & 0x1f; cmdparams[1] = value; +} - csndWriteCmd(0x1, (u8*)&cmdparams); +void CSND_SetEncoding(u32 channel, u32 value) +{ + u32* cmdparams = csndAddCmd(0x002); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = value; } void CSND_SetBlock(u32 channel, int block, u32 physaddr, u32 size) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(block ? 0x003 : 0x00A); cmdparams[0] = channel & 0x1f; cmdparams[1] = physaddr; cmdparams[2] = size; - - csndWriteCmd(block ? 0x3 : 0xA, (u8*)&cmdparams); } -void CSND_SetVol(u32 channel, u16 left, u16 right) +void CSND_SetLooping(u32 channel, u32 value) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x004); cmdparams[0] = channel & 0x1f; - cmdparams[1] = left | (right<<16); - - csndWriteCmd(0x9, (u8*)&cmdparams); + cmdparams[1] = value; } -void CSND_SetTimer(u32 channel, u32 timer) +void CSND_SetBit7(u32 channel, bool set) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x005); cmdparams[0] = channel & 0x1f; - cmdparams[1] = timer; + cmdparams[1] = set ? 1 : 0; +} - csndWriteCmd(0x8, (u8*)&cmdparams); +void CSND_SetInterp(u32 channel, bool interp) +{ + u32* cmdparams = csndAddCmd(0x006); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = interp ? 1 : 0; } void CSND_SetDuty(u32 channel, u32 duty) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x007); cmdparams[0] = channel & 0x1f; cmdparams[1] = duty; +} - csndWriteCmd(0x7, (u8*)&cmdparams); +void CSND_SetTimer(u32 channel, u32 timer) +{ + u32* cmdparams = csndAddCmd(0x008); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = timer; +} + +void CSND_SetVol(u32 channel, u32 chnVolumes, u32 capVolumes) +{ + u32* cmdparams = csndAddCmd(0x009); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = chnVolumes; + cmdparams[2] = capVolumes; } void CSND_SetAdpcmState(u32 channel, int block, int sample, int index) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(block ? 0x00C : 0x00B); cmdparams[0] = channel & 0x1f; cmdparams[1] = sample & 0xFFFF; cmdparams[2] = index & 0x7F; - - csndWriteCmd(block ? 0xC : 0xB, (u8*)&cmdparams); } void CSND_SetAdpcmReload(u32 channel, bool reload) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x00D); cmdparams[0] = channel & 0x1f; cmdparams[1] = reload ? 1 : 0; - - csndWriteCmd(0xD, (u8*)&cmdparams); } -void CSND_SetChnRegs(u32 flags, u32 physaddr0, u32 physaddr1, u32 totalbytesize) +void CSND_SetChnRegs(u32 flags, u32 physaddr0, u32 physaddr1, u32 totalbytesize, u32 chnVolumes, u32 capVolumes) { - u32 cmdparams[0x18>>2]; - - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x00E); cmdparams[0] = flags; - cmdparams[1] = 0x7FFF7FFF; // Volume - cmdparams[2] = 0; // Unknown + cmdparams[1] = chnVolumes; + cmdparams[2] = capVolumes; cmdparams[3] = physaddr0; cmdparams[4] = physaddr1; cmdparams[5] = totalbytesize; - - csndWriteCmd(0xe, (u8*)&cmdparams); } -Result CSND_UpdateInfo(bool waitDone) +void CSND_SetChnRegsPSG(u32 flags, u32 chnVolumes, u32 capVolumes, u32 duty) { - u32 cmdparams[0x18>>2]; + u32* cmdparams = csndAddCmd(0x00F); - memset(cmdparams, 0, 0x18); + cmdparams[0] = flags; + cmdparams[1] = chnVolumes; + cmdparams[2] = capVolumes; + cmdparams[3] = duty; +} - csndWriteCmd(0x300, (u8*)&cmdparams); - return csndExecCmds(waitDone); +void CSND_SetChnRegsNoise(u32 flags, u32 chnVolumes, u32 capVolumes) +{ + u32* cmdparams = csndAddCmd(0x010); + + cmdparams[0] = flags; + cmdparams[1] = chnVolumes; + cmdparams[2] = capVolumes; } void CSND_CapEnable(u32 capUnit, bool enable) { - u32 cmdparams[0x18>>2]; - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x100); cmdparams[0] = capUnit; cmdparams[1] = enable ? 1 : 0; - - csndWriteCmd(0x100, (u8*)&cmdparams); } -void CSND_CapSetBit(u32 capUnit, int bit, bool state) +void CSND_CapSetRepeat(u32 capUnit, bool repeat) { - u32 cmdparams[0x18>>2]; - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x101); cmdparams[0] = capUnit; - cmdparams[1] = state ? 1 : 0; + cmdparams[1] = repeat ? 0 : 1; +} - csndWriteCmd(0x101 + bit, (u8*)&cmdparams); +void CSND_CapSetFormat(u32 capUnit, bool eightbit) +{ + u32* cmdparams = csndAddCmd(0x102); + + cmdparams[0] = capUnit; + cmdparams[1] = eightbit ? 1 : 0; +} + +void CSND_CapSetBit2(u32 capUnit, bool set) +{ + u32* cmdparams = csndAddCmd(0x103); + + cmdparams[0] = capUnit; + cmdparams[1] = set ? 1 : 0; } void CSND_CapSetTimer(u32 capUnit, u32 timer) { - u32 cmdparams[0x18>>2]; - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x104); cmdparams[0] = capUnit; cmdparams[1] = timer & 0xFFFF; - - csndWriteCmd(0x104, (u8*)&cmdparams); } -void CSND_CapSetBuffer(u32 capUnit, u32 paddr, u32 size) +void CSND_CapSetBuffer(u32 capUnit, u32 addr, u32 size) { - u32 cmdparams[0x18>>2]; - memset(cmdparams, 0, 0x18); + u32* cmdparams = csndAddCmd(0x105); cmdparams[0] = capUnit; - cmdparams[1] = paddr; + cmdparams[1] = addr; cmdparams[2] = size; - - csndWriteCmd(0x105, (u8*)&cmdparams); } -Result csndPlaySound(int chn, u32 flags, u32 sampleRate, void* data0, void* data1, u32 size) +void CSND_SetCapRegs(u32 capUnit, u32 flags, u32 addr, u32 size) +{ + u32* cmdparams = csndAddCmd(0x106); + + cmdparams[0] = capUnit; + cmdparams[1] = flags; + cmdparams[2] = addr; + cmdparams[3] = size; +} + +Result CSND_SetDspFlags(bool waitDone) +{ + csndAddCmd(0x200); + return csndExecCmds(waitDone); +} + +Result CSND_UpdateInfo(bool waitDone) +{ + csndAddCmd(0x300); + return csndExecCmds(waitDone); +} + +Result csndPlaySound(int chn, u32 flags, u32 sampleRate, float vol, float pan, void* data0, void* data1, u32 size) { if (!(csndChannels & BIT(chn))) return 1; @@ -426,7 +469,8 @@ Result csndPlaySound(int chn, u32 flags, u32 sampleRate, void* data0, void* data flags &= ~0xFFFF001F; flags |= SOUND_ENABLE | SOUND_CHANNEL(chn) | (timer << 16); - CSND_SetChnRegs(flags, paddr0, paddr1, size); + u32 volumes = CSND_VOL(vol, pan); + CSND_SetChnRegs(flags, paddr0, paddr1, size, volumes, volumes); if (loopMode == CSND_LOOPMODE_NORMAL && paddr1 > paddr0) { @@ -438,16 +482,34 @@ Result csndPlaySound(int chn, u32 flags, u32 sampleRate, void* data0, void* data return csndExecCmds(true); } +void csndGetDspFlags(u32* outSemFlags, u32* outIrqFlags) +{ + if (outSemFlags) + *outSemFlags = csndSharedMem[(csndOffsets[0] + 0) >> 2]; + if (outIrqFlags) + *outIrqFlags = csndSharedMem[(csndOffsets[0] + 4) >> 2]; +} + +static inline u32 chnGetSharedMemIdx(u32 channel) +{ + return __builtin_popcount(((1<> 2]); } +CSND_CapInfo* csndGetCapInfo(u32 capUnit) +{ + return (CSND_CapInfo*)(&csndSharedMem[(csndOffsets[2] + capUnit*8) >> 2]); +} + Result csndGetState(u32 channel, CSND_ChnInfo* out) { Result ret = 0; - channel = csndChnIdx[channel]; + channel = chnGetSharedMemIdx(channel); if ((ret = CSND_UpdateInfo(true)) != 0)return ret;