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
This commit is contained in:
fincs 2015-04-15 22:17:19 +02:00
parent 7b059d9395
commit c443c34ad0
2 changed files with 215 additions and 102 deletions

View File

@ -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);

View File

@ -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<<channel)-1) & csndChannels);
}
CSND_ChnInfo* csndGetChnInfo(u32 channel)
{
channel = csndChnIdx[channel];
channel = chnGetSharedMemIdx(channel);
return (CSND_ChnInfo*)(&csndSharedMem[(csndOffsets[1] + channel*0xc) >> 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;