diff --git a/libctru/include/ctr/CSND.h b/libctru/include/ctr/CSND.h new file mode 100644 index 0000000..36d3352 --- /dev/null +++ b/libctru/include/ctr/CSND.h @@ -0,0 +1,18 @@ +#ifndef CSND_H +#define CSND_H + +#define CSND_SHAREDMEM_DEFAULT 0x10004000 + +Result CSND_initialize(u32* sharedMem); +Result CSND_shutdown(); + +Result CSND_playsound(u32 channel, u32 looping, u32 encoding, u32 samplerate, u32 *vaddr0, u32 *vaddr1, u32 totalbytesize, u32 unk0, u32 unk1); +void CSND_setchannel_playbackstate(u32 channel, u32 value); +void CSND_sharedmemtype0_cmd0(u32 channel, u32 value); +Result CSND_sharedmemtype0_cmdupdatestate(int waitdone); + +Result CSND_getchannelstate(u32 entryindex, u32 *out); +Result CSND_getchannelstate_isplaying(u32 entryindex, u8 *status); + +#endif + diff --git a/libctru/source/CSND.c b/libctru/source/CSND.c new file mode 100644 index 0000000..6ab2a7c --- /dev/null +++ b/libctru/source/CSND.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include + +#include +#include +#include + +//See here regarding CSND shared-mem commands, etc: http://3dbrew.org/wiki/CSND_Shared_Memory + +Handle CSND_handle = 0; +Handle CSND_mutexhandle = 0; +Handle CSND_sharedmemhandle = 0; +u32 *CSND_sharedmem = NULL; +static u32 CSND_bitmask=0; + +static u32 CSND_sharedmem_cmdblocksize = 0x2000; +static u32 CSND_sharedmem_startcmdoff = 0; +static u32 CSND_sharedmem_currentcmdoff = 0; + +Result CSND_cmd1(Handle *mutexhandle, Handle *sharedmemhandle, u32 sharedmem_size, u32 off0, u32 off1, u32 off2, u32 off3) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00010140; + cmdbuf[1] = sharedmem_size; + cmdbuf[2] = off0; + cmdbuf[3] = off1; + cmdbuf[4] = off2; + cmdbuf[5] = off3; + + if((ret = svc_sendSyncRequest(CSND_handle))!=0)return ret; + + *mutexhandle = cmdbuf[3]; + *sharedmemhandle = cmdbuf[4]; + + return (Result)cmdbuf[1]; +} + +Result CSND_cmd2() +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00020000; + + if((ret = svc_sendSyncRequest(CSND_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result CSND_cmd5(u32 *bitmask) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00050000; + + if((ret = svc_sendSyncRequest(CSND_handle))!=0)return ret; + + *bitmask = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result CSND_initialize(u32* sharedMem) +{ + Result ret=0; + + if(sharedMem==NULL)sharedMem = (u32*)CSND_SHAREDMEM_DEFAULT; + CSND_sharedmem = sharedMem; + + ret = srv_getServiceHandle(NULL, &CSND_handle, "csnd:SND"); + if(ret!=0)return ret; + + ret = CSND_cmd1(&CSND_mutexhandle, &CSND_sharedmemhandle, CSND_sharedmem_cmdblocksize+0x114, CSND_sharedmem_cmdblocksize, CSND_sharedmem_cmdblocksize+8, CSND_sharedmem_cmdblocksize+0xc8, CSND_sharedmem_cmdblocksize+0xd8); + if(ret!=0)return ret; + + ret = svc_mapMemoryBlock(CSND_sharedmemhandle, (u32)CSND_sharedmem, 3, 0x10000000); + if(ret!=0)return ret; + + memset(CSND_sharedmem, 0, 0x2114); + + ret = CSND_cmd5(&CSND_bitmask); + if(ret!=0)return ret; + + return 0; +} + +Result CSND_shutdown() +{ + Result ret; + + svc_unmapMemoryBlock(CSND_sharedmemhandle, (u32)CSND_sharedmem); + svc_closeHandle(CSND_sharedmemhandle); + + ret = CSND_cmd2(); + if(ret!=0)return ret; + + return svc_closeHandle(CSND_handle); +} + +Result CSND_cmd3(u32 offset) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00030040; + cmdbuf[1] = offset; + + if((ret = svc_sendSyncRequest(CSND_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +void CSND_writesharedmem_cmdtype0(u16 cmdid, u8 *cmdparams) +{ + u16 *ptr; + u32 prevoff; + s32 outindex=0; + + svc_waitSynchronizationN(&outindex, &CSND_mutexhandle, 1, 0, ~0); + + if(CSND_sharedmem_startcmdoff != CSND_sharedmem_currentcmdoff) + { + if(CSND_sharedmem_currentcmdoff>=0x20) + { + prevoff = CSND_sharedmem_currentcmdoff-0x20; + } + else + { + prevoff = CSND_sharedmem_cmdblocksize-0x20; + } + + ptr = (u16*)&CSND_sharedmem[prevoff>>2]; + *ptr = CSND_sharedmem_currentcmdoff; + } + + ptr = (u16*)&CSND_sharedmem[CSND_sharedmem_currentcmdoff>>2]; + + ptr[0] = 0xffff; + ptr[1] = cmdid; + ptr[2] = 0; + ptr[3] = 0; + memcpy(&ptr[8>>1], cmdparams, 0x18); + + CSND_sharedmem_currentcmdoff+= 0x20; + if(CSND_sharedmem_currentcmdoff >= CSND_sharedmem_cmdblocksize)CSND_sharedmem_currentcmdoff = 0; + + svc_releaseMutex(CSND_mutexhandle); +} + +Result CSND_processtype0cmds() +{ + Result ret=0; + + if(CSND_sharedmem_startcmdoff == CSND_sharedmem_currentcmdoff)return 0; + + ret = CSND_cmd3(CSND_sharedmem_startcmdoff); + CSND_sharedmem_startcmdoff = CSND_sharedmem_currentcmdoff; + + return ret; +} + +u32 CSND_convertsamplerate(u32 samplerate) +{ + return (u32)(6.7027964E+07f / ((float)samplerate)); +} + +void CSND_sharedmemtype0_cmd0(u32 channel, u32 value) +{ + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = value; + + CSND_writesharedmem_cmdtype0(0x0, (u8*)&cmdparams); +} + +void CSND_setchannel_playbackstate(u32 channel, u32 value) +{ + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = value; + + CSND_writesharedmem_cmdtype0(0x1, (u8*)&cmdparams); +} + +void CSND_sharedmemtype0_cmd3(u32 channel, u32 physaddr, u32 size) +{ + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = physaddr; + cmdparams[2] = size; + + CSND_writesharedmem_cmdtype0(0x3, (u8*)&cmdparams); +} + +void CSND_sharedmemtype0_cmd9(u32 channel, u16 value) +{ + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = value | (value<<16); + + CSND_writesharedmem_cmdtype0(0x9, (u8*)&cmdparams); +} + +void CSND_sharedmemtype0_cmd8(u32 channel, u32 samplerate) +{ + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + cmdparams[0] = channel & 0x1f; + cmdparams[1] = CSND_convertsamplerate(samplerate); + + CSND_writesharedmem_cmdtype0(0x8, (u8*)&cmdparams); +} + +void CSND_sharedmemtype0_cmde(u32 channel, u32 looping, u32 encoding, u32 samplerate, u32 unk0, u32 unk1, u32 physaddr0, u32 physaddr1, u32 totalbytesize) +{ + u32 val; + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + cmdparams[0] = channel & 0x1f; + cmdparams[0] |= (unk0 & 0xf) << 6; + if(!looping)cmdparams[0] |= 2 << 10; + if(looping)cmdparams[0] |= 1 << 10; + cmdparams[0] |= (encoding & 3) << 12; + cmdparams[0] |= (unk1 & 3) << 14; + + val = CSND_convertsamplerate(samplerate); + if(val<0x42)val = 0x42; + if(val>0xffff)val = 0xffff; + cmdparams[0] |= val<<16; + + cmdparams[3] = physaddr0; + cmdparams[4] = physaddr1; + cmdparams[5] = totalbytesize; + + CSND_writesharedmem_cmdtype0(0xe, (u8*)&cmdparams); +} + +Result CSND_sharedmemtype0_cmdupdatestate(int waitdone) +{ + u8 *ptr; + int ret=0; + + u32 cmdparams[0x18>>2]; + + memset(cmdparams, 0, 0x18); + + ptr = (u8*)&CSND_sharedmem[CSND_sharedmem_startcmdoff>>2]; + + CSND_writesharedmem_cmdtype0(0x300, (u8*)&cmdparams); + + ret = CSND_processtype0cmds(); + if(ret!=0)return ret; + + if(waitdone) + { + while(*ptr == 0); + } + + return 0; +} + +Result CSND_playsound(u32 channel, u32 looping, u32 encoding, u32 samplerate, u32 *vaddr0, u32 *vaddr1, u32 totalbytesize, u32 unk0, u32 unk1) +{ + u32 physaddr0 = 0; + u32 physaddr1 = 0; + + if((u32)vaddr0 >= 0x14000000 && (u32)vaddr0<0x1c000000)physaddr0 = (u32)vaddr0 + 0x0c000000; + if((u32)vaddr1 >= 0x14000000 && (u32)vaddr1<0x1c000000)physaddr1 = (u32)vaddr1 + 0x0c000000; + + CSND_sharedmemtype0_cmde(channel, looping, encoding, samplerate, unk0, unk1, physaddr0, physaddr1, totalbytesize); + CSND_sharedmemtype0_cmd8(channel, samplerate); + if(looping)CSND_sharedmemtype0_cmd3(channel, physaddr0, totalbytesize); + CSND_sharedmemtype0_cmd8(channel, samplerate); + CSND_sharedmemtype0_cmd9(channel, 0xffff); + CSND_setchannel_playbackstate(channel, 1); + + return CSND_sharedmemtype0_cmdupdatestate(0); +} + +Result CSND_getchannelstate(u32 entryindex, u32 *out) +{ + Result ret=0; + + if((ret = CSND_sharedmemtype0_cmdupdatestate(1))!=0)return ret; + + memcpy(out, &CSND_sharedmem[(CSND_sharedmem_cmdblocksize+8 + entryindex*0xc) >> 2], 0xc); + out[2] -= 0x0c000000; + + return 0; +} + +Result CSND_getchannelstate_isplaying(u32 entryindex, u8 *status) +{ + Result ret; + u32 entry[0xc>>2]; + + ret = CSND_getchannelstate(entryindex, entry); + if(ret!=0)return ret; + + *status = entry[0] & 0xff; + + return 0; +} +