From f1ce480ed91984c936ede6c034b5e21c56018f63 Mon Sep 17 00:00:00 2001 From: fincs Date: Fri, 2 Oct 2015 21:54:18 +0200 Subject: [PATCH] Add a work-in-progress API for using Nintendo's default DSP component --- libctru/Makefile | 1 + libctru/include/3ds.h | 3 + libctru/include/3ds/ndsp/channel.h | 52 +++ libctru/include/3ds/ndsp/ndsp.h | 59 +++ libctru/source/ndsp/ndsp-channel.c | 385 ++++++++++++++++++++ libctru/source/ndsp/ndsp-internal.h | 107 ++++++ libctru/source/ndsp/ndsp.c | 543 ++++++++++++++++++++++++++++ 7 files changed, 1150 insertions(+) create mode 100644 libctru/include/3ds/ndsp/channel.h create mode 100644 libctru/include/3ds/ndsp/ndsp.h create mode 100644 libctru/source/ndsp/ndsp-channel.c create mode 100644 libctru/source/ndsp/ndsp-internal.h create mode 100644 libctru/source/ndsp/ndsp.c diff --git a/libctru/Makefile b/libctru/Makefile index 090e762..7136508 100644 --- a/libctru/Makefile +++ b/libctru/Makefile @@ -27,6 +27,7 @@ BUILD := build SOURCES := source \ source/allocator \ source/gpu \ + source/ndsp \ source/services \ source/services/soc \ source/util/rbtree \ diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index e86864a..a187949 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -49,6 +49,9 @@ extern "C" { #include <3ds/gpu/shbin.h> #include <3ds/gpu/shaderProgram.h> +#include <3ds/ndsp/ndsp.h> +#include <3ds/ndsp/channel.h> + #include <3ds/sdmc.h> #include <3ds/romfs.h> diff --git a/libctru/include/3ds/ndsp/channel.h b/libctru/include/3ds/ndsp/channel.h new file mode 100644 index 0000000..64d389d --- /dev/null +++ b/libctru/include/3ds/ndsp/channel.h @@ -0,0 +1,52 @@ +#pragma once + +enum +{ + NDSP_ENCODING_PCM8 = 0, + NDSP_ENCODING_PCM16, + NDSP_ENCODING_ADPCM, // DSPADPCM (GameCube format) +}; + +#define NDSP_CHANNELS(n) ((u32)(n) & 3) +#define NDSP_ENCODING(n) (((u32)(n) & 3) << 2) + +enum +{ + NDSP_FORMAT_MONO_PCM8 = NDSP_CHANNELS(1) | NDSP_ENCODING(NDSP_ENCODING_PCM8), + NDSP_FORMAT_MONO_PCM16 = NDSP_CHANNELS(1) | NDSP_ENCODING(NDSP_ENCODING_PCM16), + NDSP_FORMAT_MONO_ADPCM = NDSP_CHANNELS(1) | NDSP_ENCODING(NDSP_ENCODING_ADPCM), + NDSP_FORMAT_STEREO_PCM8 = NDSP_CHANNELS(2) | NDSP_ENCODING(NDSP_ENCODING_PCM8), + NDSP_FORMAT_STEREO_PCM16 = NDSP_CHANNELS(2) | NDSP_ENCODING(NDSP_ENCODING_PCM16), + + NDSP_FORMAT_PCM8 = NDSP_FORMAT_MONO_PCM8, + NDSP_FORMAT_PCM16 = NDSP_FORMAT_MONO_PCM16, + NDSP_FORMAT_ADPCM = NDSP_FORMAT_MONO_ADPCM, + + // Flags + NDSP_FRONT_BYPASS = BIT(4), + NDSP_3D_SURROUND_PREPROCESSED = BIT(6), //? +}; + +// Basic channel operation +void ndspChnReset(int id); +void ndspChnInitParams(int id); +bool ndspChnIsPlaying(int id); +u32 ndspChnGetSamplePos(int id); +u16 ndspChnGetWaveBufSeq(int id); + +// Configuration +void ndspChnSetFormat(int id, u16 format); +void ndspChnSetInterp(int id, int type); +void ndspChnSetRate(int id, float rate); +void ndspChnSetMix(int id, float mix[12]); +void ndspChnSetAdpcmCoefs(int id, u16 coefs[16]); + +// Wave buffers +void ndspChnWaveBufClear(int id); +void ndspChnWaveBufAdd(int id, ndspWaveBuf* buf); + +// IIR filters +void ndspChnIirMonoSetEnable(int id, bool enable); +// ndspChnIirMonoSetParams +void ndspChnIirBiquadSetEnable(int id, bool enable); +// ndspChnIirBiquadSetParams diff --git a/libctru/include/3ds/ndsp/ndsp.h b/libctru/include/3ds/ndsp/ndsp.h new file mode 100644 index 0000000..9100a87 --- /dev/null +++ b/libctru/include/3ds/ndsp/ndsp.h @@ -0,0 +1,59 @@ +#pragma once + +typedef struct +{ + u16 index; + s16 history0, history1; +} ndspAdpcmData; + +typedef struct tag_ndspWaveBuf ndspWaveBuf; + +struct tag_ndspWaveBuf +{ + union + { + s8* data_pcm8; + s16* data_pcm16; + u8* data_adpcm; + u32 data_vaddr; + }; + u32 nsamples; + ndspAdpcmData* adpcm_data; + + u32 offset; // only used for capture + bool looping; + u8 padding; + + // The following fields are used internally + u16 sequence_id; + ndspWaveBuf* next; +}; + +typedef void (*ndspCallback)(void* data); +typedef void (*ndspAuxCallback)(void* data, int nsamples, void* samples[4]); + +// Initialization and basic operations +void ndspUseComponent(const void* binary, u32 size, u16 progMask, u16 dataMask); +Result ndspInit(void); +void ndspExit(void); +u32 ndspGetDroppedFrames(void); +u32 ndspGetFrameCount(void); + +// General parameters +void ndspSetMasterVol(float volume); +void ndspSetOutputMode(int mode); +void ndspSetClippingMode(int mode); +void ndspSetOutputCount(int count); +void ndspSetCapture(ndspWaveBuf* capture); +void ndspSetCallback(ndspCallback callback, void* data); + +// Surround +void ndspSurroundSetDepth(u16 depth); +void ndspSurroundSetPos(u16 pos); +void ndspSurroundSetRearRatio(u16 ratio); + +// Auxiliary output +void ndspAuxSetEnable(int id, bool enable); +void ndspAuxSetFrontBypass(int id, bool bypass); +void ndspAuxSetVolume(int id, float volume); +void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data); diff --git a/libctru/source/ndsp/ndsp-channel.c b/libctru/source/ndsp/ndsp-channel.c new file mode 100644 index 0000000..b07a234 --- /dev/null +++ b/libctru/source/ndsp/ndsp-channel.c @@ -0,0 +1,385 @@ +#include "ndsp-internal.h" +#include <3ds/ndsp/channel.h> + +enum +{ + CFLAG_INITPARAMS = BIT(0), + CFLAG_SYNCCOUNT = BIT(1), + CFLAG_PLAYSTATUS = BIT(2), + CFLAG_INTERPTYPE = BIT(3), + CFLAG_IIRFILTERTYPE = BIT(4), + CFLAG_RATE = BIT(5), + CFLAG_MIX = BIT(6), + CFLAG_ADPCMCOEFS = BIT(7), +}; + +typedef struct +{ + u32 flags; + + LightLock lock; + u16 syncCount, waveBufSeqPos; + u32 samplePos; + + ndspWaveBuf* waveBuf; + u16 wavBufCount, wavBufIdNext; + + bool playing; + u8 interpType, iirFilterType; + + u16 format; + u16 wavBufSeq; + + float rate; + float mix[12]; + + u16 adpcmCoefs[16]; + +} ndspChnSt; + +static ndspChnSt ndspChn[24]; + +void ndspChnReset(int id) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + chn->flags = ~0; + chn->syncCount = 1; + chn->waveBufSeqPos = 0; + chn->samplePos = 0; + chn->waveBuf = NULL; + chn->wavBufCount = 0; + chn->wavBufIdNext = 0; + chn->wavBufSeq = 0; + chn->playing = false; + chn->interpType = 0; + chn->iirFilterType = 0; + chn->format = NDSP_FORMAT_PCM16; + chn->rate = 1.0f; + chn->mix[0] = chn->mix[1] = 1.0f; + memset(&chn->mix[2], 0, 14*sizeof(float)); + LightLock_Unlock(&chn->lock); +} + +void ndspChnInitParams(int id) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + chn->flags |= CFLAG_INITPARAMS; + LightLock_Unlock(&chn->lock); +} + +bool ndspChnIsPlaying(int id) +{ + return ndspChn[id].playing; +} + +u32 ndspChnGetSamplePos(int id) +{ + return ndspChn[id].samplePos; +} + +u16 ndspChnGetWaveBufSeq(int id) +{ + return ndspChn[id].waveBufSeqPos; +} + +void ndspChnSetFormat(int id, u16 format) +{ + ndspChn[id].format = format; +} + +void ndspChnSetInterp(int id, int type) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + chn->interpType = type; + chn->flags |= CFLAG_INTERPTYPE; + LightLock_Unlock(&chn->lock); +} + +void ndspChnSetRate(int id, float rate) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + chn->rate = rate/32728.0f; + chn->flags |= CFLAG_RATE; + LightLock_Unlock(&chn->lock); +} + +void ndspChnSetMix(int id, float mix[12]) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + memcpy(&chn->mix, mix, sizeof(ndspChn[id].mix)); + chn->flags |= CFLAG_MIX; + LightLock_Unlock(&chn->lock); +} + +void ndspChnSetAdpcmCoefs(int id, u16 coefs[16]) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + memcpy(&chn->adpcmCoefs, coefs, sizeof(ndspChn[id].adpcmCoefs)); + chn->flags |= CFLAG_ADPCMCOEFS; + LightLock_Unlock(&chn->lock); +} + +void ndspChnWaveBufClear(int id) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + chn->waveBuf = NULL; + chn->waveBufSeqPos = 0; + chn->wavBufCount = 0; + chn->wavBufIdNext = 0; + chn->wavBufSeq = 0; + chn->playing = false; + chn->syncCount ++; + chn->flags |= CFLAG_SYNCCOUNT | CFLAG_PLAYSTATUS; + LightLock_Unlock(&chn->lock); +} + +void ndspChnWaveBufAdd(int id, ndspWaveBuf* buf) +{ + ndspChnSt* chn = &ndspChn[id]; + ndspWaveBuf* cb = chn->waveBuf; + if (!buf->nsamples) return; + + buf->next = NULL; + LightLock_Lock(&chn->lock); + + if (cb) + { + while (cb->next) cb = cb->next; + cb->next = buf; + } else + chn->waveBuf = buf; + + u16 seq = chn->wavBufSeq; + if (!seq) seq = 1; + buf->sequence_id = seq; + chn->wavBufSeq = seq + 1; + + LightLock_Unlock(&chn->lock); +} + +void ndspChnIirMonoSetEnable(int id, bool enable) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + u16 f = chn->iirFilterType &~ BIT(0); + if (enable) f |= BIT(0); + chn->iirFilterType = f; + chn->flags |= CFLAG_IIRFILTERTYPE; + LightLock_Unlock(&chn->lock); +} + +void ndspChnIirBiquadSetEnable(int id, bool enable) +{ + ndspChnSt* chn = &ndspChn[id]; + LightLock_Lock(&chn->lock); + u16 f = chn->iirFilterType &~ BIT(1); + if (enable) f |= BIT(1); + chn->iirFilterType = f; + chn->flags |= CFLAG_IIRFILTERTYPE; + LightLock_Unlock(&chn->lock); +} + +void ndspiInitChn(void) +{ + int i; + for (i = 0; i < 24; i ++) + { + LightLock_Init(&ndspChn[i].lock); + ndspChnReset(i); + } +} + +void ndspiDirtyChn(void) +{ + int i; + for (i = 0; i < 24; i ++) + ndspChn[i].flags |= ~CFLAG_INITPARAMS; +} + +void ndspiUpdateChn(void) +{ + int i; + for (i = 0; i < 24; i ++) + { + ndspChnSt* chn = &ndspChn[i]; + DspChnStruct* st = ndspiGetChnStruct(i); + LightLock_Lock(&chn->lock); + + u32 flags = chn->flags; + u32 stflags = st->flags; + + if (flags & CFLAG_INITPARAMS) + stflags |= 0x20000000; + + if (flags & CFLAG_MIX) + { + memcpy(st->mix, chn->mix, sizeof(st->mix)); + stflags |= 0xE000000; + } + + if (flags & CFLAG_RATE) + { + st->rate = chn->rate; + stflags |= 0x40000; + if (chn->interpType == 0) + flags |= CFLAG_INTERPTYPE; + } + + if (flags & CFLAG_IIRFILTERTYPE) + { + st->iirFilterType = chn->iirFilterType; + stflags |= 0x400000; + } + + // TODO: IIR filter coefficent update + + if (flags & CFLAG_INTERPTYPE) + { + st->rim[0] = chn->interpType; + if (chn->interpType == 0) + { + if (chn->rate <= 1.0f) + st->rim[1] = 2; + else if (chn->rate <= (4.0f/3)) + st->rim[1] = 1; + else + st->rim[1] = 0; + } else + st->rim[1] = 1; + stflags |= 0x20000; + } + + if (flags & CFLAG_ADPCMCOEFS) + { + memcpy(ndspiGetChnAdpcmCoefs(i), chn->adpcmCoefs, sizeof(chn->adpcmCoefs)); + stflags |= 4; + } + + // Do wavebuf stuff + int wvcount = chn->wavBufCount; + ndspWaveBuf* wb = chn->waveBuf; + if (wb && !chn->playing) + { + chn->playing = true; + flags |= CFLAG_PLAYSTATUS; + } + while (wvcount && wb) + { + wb = wb->next; + wvcount--; + } + + int j; + for (j = chn->wavBufCount; wb && j < 5; j ++) + { + if (chn->wavBufCount == 0) + { + // This is the first buffer - set it up + chn->wavBufIdNext = 0; + st->seqId = wb->sequence_id; + st->sampleCount = ndspiRotateVal(wb->nsamples); + st->paddr = ndspiRotateVal(osConvertVirtToPhys(wb->data_vaddr)); + st->cntFlags = chn->format; + st->moreFlags = (st->moreFlags &~ BIT(1)) | (wb->looping ? BIT(1) : 0); + st->unknown = 0; + if ((chn->format & NDSP_ENCODING(3)) == NDSP_ENCODING(NDSP_ENCODING_ADPCM)) + { + if (wb->adpcm_data) + { + st->adpcmData.index = wb->adpcm_data->index; + st->adpcmData.history0 = wb->adpcm_data->history0; + st->adpcmData.history1 = wb->adpcm_data->history1; + st->moreFlags |= BIT(0); + } else + st->moreFlags &= ~BIT(0); + } + stflags |= 0x10 | 0x40200000; + } else + { + // Queue the next buffer + DspChnBuf* cbuf = &st->buffers[chn->wavBufIdNext]; + cbuf->seqId = wb->sequence_id; + cbuf->paddr = ndspiRotateVal(osConvertVirtToPhys(wb->data_vaddr)); + cbuf->sampleCount = ndspiRotateVal(wb->nsamples); + if (wb->adpcm_data) + { + cbuf->adpcmData.index = wb->adpcm_data->index; + cbuf->adpcmData.history0 = wb->adpcm_data->history0; + cbuf->adpcmData.history1 = wb->adpcm_data->history1; + cbuf->hasAdpcmData = 1; + } else + cbuf->hasAdpcmData = 0; + cbuf->looping = wb->looping ? 1 : 0; + st->activeBuffers |= BIT(chn->wavBufIdNext); + chn->wavBufIdNext = (chn->wavBufIdNext+1) & 3; + stflags |= 0x80000; + } + wb = wb->next; + chn->wavBufCount++; + } + + if (flags & CFLAG_SYNCCOUNT) + { + st->syncCount = chn->syncCount; + stflags |= 0x10000000; + } + + if (flags & CFLAG_PLAYSTATUS) + { + u16 playStatus = st->playStatus &~ 0xFF; + if (chn->playing) + playStatus |= 1; + st->playStatus = playStatus; + stflags |= 0x10000; + } + + chn->flags = 0; + st->flags = stflags; + + LightLock_Unlock(&chn->lock); + } +} + +void ndspiReadChnState(void) +{ + int i; + for (i = 0; i < 24; i ++) + { + ndspChnSt* chn = &ndspChn[i]; + DspChnStatus* st = ndspiGetChnStatus(i); + + if (chn->syncCount == st->syncCount) + { + u16 seqId = st->curSeqId; + ndspWaveBuf* wb = chn->waveBuf; + chn->waveBufSeqPos = seqId; + chn->samplePos = ndspiRotateVal(st->samplePos); + + if ((st->flags & 0xFF00) && wb) + { + LightLock_Lock(&chn->lock); + + while (wb->sequence_id != seqId) + { + chn->wavBufCount--; + bool shouldBreak = seqId == 0 && (wb->sequence_id == st->lastSeqId || st->lastSeqId == 0); + wb = wb->next; + if (shouldBreak || chn->wavBufCount == 0) + break; + } + if (seqId == 0) + chn->wavBufCount = 0; + chn->waveBuf = wb; + LightLock_Unlock(&chn->lock); + } + } + chn->playing = (st->flags & 0xFF) ? true : false; + } +} diff --git a/libctru/source/ndsp/ndsp-internal.h b/libctru/source/ndsp/ndsp-internal.h new file mode 100644 index 0000000..bf37b82 --- /dev/null +++ b/libctru/source/ndsp/ndsp-internal.h @@ -0,0 +1,107 @@ +#pragma once +#include +#include +#include <3ds/types.h> +#include <3ds/svc.h> +#include <3ds/os.h> +#include <3ds/synchronization.h> +#include <3ds/services/dsp.h> +#include <3ds/services/apt.h> +#include <3ds/ndsp/ndsp.h> + +extern u16 ndspFrameId, ndspBufferCurId, ndspBufferId; +extern void* ndspVars[16][2]; + +typedef struct +{ + u32 paddr, sampleCount; + ndspAdpcmData adpcmData; + u8 hasAdpcmData, looping; + u16 seqId, padding; +} DspChnBuf; + +typedef struct +{ + u32 flags; + float mix[12]; + float rate; + u8 rim[2]; + u16 iirFilterType; + u16 iirFilter_mono[2]; + u16 iirFilter_biquad[5]; + u16 activeBuffers; + DspChnBuf buffers[4]; + u32 _pad0; + u16 playStatus, syncCount; + u32 unknown; + u32 _pad1; + + u32 paddr, sampleCount; + u16 cntFlags; + ndspAdpcmData adpcmData; + u16 moreFlags; + u16 seqId; +} DspChnStruct; + +typedef struct +{ + u16 flags, syncCount; + u32 samplePos; + u16 curSeqId, lastSeqId; +} DspChnStatus; + +typedef struct +{ + u32 flags; + float masterVol; + float auxReturnVol[2]; + u16 outBufCount; + u16 _pad0[2]; + u16 outputMode; + u16 clippingMode; + u16 headsetConnected; + u16 surroundDepth; + u16 surroundSpeakerPos; + u16 _pad1; + u16 rearRatio; + u16 auxFrontBypass[2]; + u16 auxBusEnable[2]; + u16 dspDelayEffect[2][10]; + u16 dspReverbEffect[2][26]; + u16 syncMode; + u16 _pad2; + u32 unknown; +} DspMasterStatus; + +static inline u32 ndspiRotateVal(u32 x) +{ + return (x << 16) | (x >> 16); +} + +static inline DspChnStruct* ndspiGetChnStruct(int id) +{ + DspChnStruct* them = (DspChnStruct*)ndspVars[1][ndspFrameId&1]; + return &them[id]; +} + +static inline DspChnStatus* ndspiGetChnStatus(int id) +{ + DspChnStatus* them = (DspChnStatus*)ndspVars[2][ndspBufferId]; + return &them[id]; +} + +static inline u16* ndspiGetChnAdpcmCoefs(int id) +{ + u16* them = (u16*)ndspVars[3][ndspBufferId]; + return &them[id*16]; +} + +static inline DspMasterStatus* ndspiGetMasterStatus(void) +{ + return (DspMasterStatus*)ndspVars[4][ndspBufferCurId]; +} + +void ndspiInitChn(void); +void ndspiDirtyChn(void); +void ndspiUpdateChn(void); +void ndspiReadChnState(void); diff --git a/libctru/source/ndsp/ndsp.c b/libctru/source/ndsp/ndsp.c new file mode 100644 index 0000000..fc41c1f --- /dev/null +++ b/libctru/source/ndsp/ndsp.c @@ -0,0 +1,543 @@ +#include "ndsp-internal.h" +#include <3ds/services/cfgu.h> + +#define NDSP_THREAD_STACK_SIZE 0x1000 + +u16 ndspFrameId, ndspBufferCurId, ndspBufferId; +void* ndspVars[16][2]; + +static bool bComponentLoaded = false, bDspReady = false, bSleeping = false, bNeedsSync = false; +static u32 droppedFrames, frameCount; + +static const void* componentBin; +static u32 componentSize; +static u16 componentProgMask, componentDataMask; + +static aptHookCookie aptCookie; + +static Handle irqEvent, dspSem, sleepEvent; +static LightLock ndspMutex; + +static u8 dspVar5Backup[0x1080]; + +static volatile bool ndspThreadRun; +static Handle ndspThread; +static u64 ndspThreadStack[NDSP_THREAD_STACK_SIZE/8]; // u64 so that it's 8-byte aligned + +static Result ndspLoadComponent(void) +{ + if (!componentBin) return 1; + return DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, &bComponentLoaded); +} + +static inline void ndspWaitForIrq(void) +{ + LightLock_Lock(&ndspMutex); + svcWaitSynchronization(irqEvent, U64_MAX); + svcClearEvent(irqEvent); + LightLock_Unlock(&ndspMutex); +} + +static inline void ndspSetCounter(int a, int counter) +{ + *(vu16*)ndspVars[0][a] = counter; +} + +static inline int ndspGetCounter(int a) +{ + return *(vu16*)ndspVars[0][a]; +} + +enum +{ + MFLAG_MASTERVOL = BIT(0), + MFLAG_OUTPUTMODE = BIT(1), + MFLAG_CLIPPINGMODE = BIT(2), + MFLAG_OUTPUTCOUNT = BIT(3), + MFLAG_SYNCMODE = BIT(4), + MFLAG_SURR_DEPTH = BIT(5), + MFLAG_SURR_POS = BIT(6), + MFLAG_SURR_RRATIO = BIT(7), + +#define MFLAG_AUX_ENABLE(i) BIT(8+(i)) +#define MFLAG_AUX_BYPASS(i) BIT(10+(i)) +#define MFLAG_AUX_VOLUME(i) BIT(12+(i)) +}; + +static struct +{ + LightLock lock; + u32 flags; + float masterVol; + u16 outputMode, clippingMode, outputCount, syncMode; + ndspWaveBuf* capture; + ndspCallback callback; + void* callbackData; + + struct + { + u16 depth, pos, rearRatio; + } surround; + + struct + { + u16 enable, frontBypass; + float volume; + ndspAuxCallback callback; + void* callbackData; + } aux[2]; +} ndspMaster; + +static void ndspDirtyMaster(void) +{ + ndspMaster.flags = ~0; +} + +static void ndspInitMaster(void) +{ + memset(&ndspMaster, 0, sizeof(ndspMaster)); + LightLock_Init(&ndspMaster.lock); + ndspMaster.masterVol = 1.0f; + ndspMaster.clippingMode = 1; + ndspMaster.outputCount = 2; + ndspMaster.surround.depth = 0x7FFF; + ndspMaster.surround.rearRatio = 0x8000; +} + +static void ndspUpdateMaster(void) +{ + DspMasterStatus* m = ndspiGetMasterStatus(); + LightLock_Lock(&ndspMaster.lock); + + u32 flags = m->flags, mflags = ndspMaster.flags; + int i; + + m->headsetConnected = *(vu8*)0x1FF810C0; + flags |= 0x10000000; + + if (mflags & MFLAG_MASTERVOL) + { + m->masterVol = ndspMaster.masterVol; + flags |= 0x00010000; + } + + if (mflags & MFLAG_OUTPUTMODE) + { + m->outputMode = ndspMaster.outputMode; + flags |= 0x04000000; + } + + if (mflags & MFLAG_CLIPPINGMODE) + { + m->clippingMode = ndspMaster.clippingMode; + flags |= 0x08000000; + } + + if (mflags & MFLAG_OUTPUTCOUNT) + { + m->outBufCount = ndspMaster.outputCount; + flags |= 0x00008000; + } + + if (mflags & MFLAG_SYNCMODE) + { + m->syncMode = ndspMaster.syncMode; + m->unknown |= 0x10000; //? + } + + if (mflags & MFLAG_SURR_DEPTH) + { + m->surroundDepth = ndspMaster.surround.depth; + flags |= 0x20000000; + } + + if (mflags & MFLAG_SURR_POS) + { + m->surroundSpeakerPos = ndspMaster.surround.pos; + flags |= 0x40000000; + } + + if (mflags & MFLAG_SURR_RRATIO) + { + m->rearRatio = ndspMaster.surround.rearRatio; + flags |= 0x80000000; + } + + for (i = 0; i < 2; i ++) + { + if (mflags & MFLAG_AUX_ENABLE(i)) + { + m->auxBusEnable[i] = ndspMaster.aux[i].enable; + flags |= 0x00000100 << i; + } + + if (mflags & MFLAG_AUX_BYPASS(i)) + { + m->auxFrontBypass[i] = ndspMaster.aux[i].frontBypass; + flags |= 0x00000040 << i; + } + + if (mflags & MFLAG_AUX_VOLUME(i)) + { + m->auxReturnVol[i] = ndspMaster.aux[i].volume; + flags |= 0x01000000 << i; + } + } + + m->flags = flags; + ndspMaster.flags = 0; + + LightLock_Unlock(&ndspMaster.lock); +} + +static void ndspUpdateCapture(s16* samples, u32 count) +{ + ndspWaveBuf* buf = ndspMaster.capture; + if (!buf) return; + memcpy(&buf->data_pcm16[buf->offset*2], samples, count*4); + buf->offset += count; + if (buf->offset >= buf->nsamples) + buf->offset = 0; +} + +static Result ndspInitialize(bool resume) +{ + Result rc; + + rc = ndspLoadComponent(); + if (rc) return rc; + + rc = svcCreateEvent(&irqEvent, 1); + if (rc) goto _fail1; + + rc = DSP_RegisterInterruptEvents(irqEvent, 2, 2); + if (rc) goto _fail2; + + rc = DSP_GetSemaphoreHandle(&dspSem); + if (rc) goto _fail3; + + DSP_SetSemaphoreMask(0x2000); + + u16 val = resume ? 2 : 0; + if (resume) + memcpy(ndspVars[5][0], dspVar5Backup, sizeof(dspVar5Backup)); + DSP_WriteProcessPipe(2, &val, 4); + DSP_SetSemaphore(0x4000); + ndspWaitForIrq(); + + DSP_ReadPipeIfPossible(2, 0, &val, sizeof(val), NULL); + u16 vars[16]; + DSP_ReadPipeIfPossible(2, 0, vars, val*2, NULL); + int i; + for (i = 0; i < val; i ++) + { + DSP_ConvertProcessAddressFromDspDram(vars[i], (u32*)&ndspVars[i][0]); + DSP_ConvertProcessAddressFromDspDram(vars[i] | 0x10000, (u32*)&ndspVars[i][1]); + } + + DSP_SetSemaphore(0x4000); + ndspFrameId = 4; + ndspSetCounter(0, 4); + ndspFrameId++; + svcSignalEvent(dspSem); + ndspBufferCurId = ndspFrameId & 1; + ndspBufferId = ndspFrameId & 1; + bDspReady = true; + + ndspDirtyMaster(); + ndspUpdateMaster(); + + if (resume) + { + ndspiDirtyChn(); + ndspiUpdateChn(); + // Force update effect params here + } + + return 0; + +_fail3: + DSP_RegisterInterruptEvents(0, 2, 2); +_fail2: + svcCloseHandle(irqEvent); +_fail1: + DSP_UnloadComponent(); + return rc; +} + +static void ndspFinalize(bool suspend) +{ + LightLock_Lock(&ndspMutex); + u16 val = suspend ? 3 : 1; + DSP_WriteProcessPipe(2, &val, 4); + for (;;) + { + bool ready; + DSP_RecvDataIsReady(0, &ready); + if (ready) + DSP_RecvData(0, &val); + if (val == 1) + break; + } + if (suspend) + memcpy(dspVar5Backup, ndspVars[5][0], sizeof(dspVar5Backup)); + + DSP_RegisterInterruptEvents(0, 2, 2); + svcCloseHandle(irqEvent); + svcCloseHandle(dspSem); + DSP_UnloadComponent(); + bComponentLoaded = false; + bDspReady = false; + LightLock_Unlock(&ndspMutex); +} + +static void ndspAptHook(int hook, void* param) +{ + switch (hook) + { + case APTHOOK_ONRESTORE: + case APTHOOK_ONWAKEUP: + bSleeping = false; + ndspInitialize(true); + svcSignalEvent(sleepEvent); + break; + + case APTHOOK_ONSUSPEND: + case APTHOOK_ONSLEEP: + bSleeping = true; + ndspFinalize(true); + break; + } +} + +static void ndspSync(void) +{ + if (bSleeping) + { + svcWaitSynchronization(sleepEvent, U64_MAX); + svcClearEvent(sleepEvent); + } + + ndspWaitForIrq(); + if (bDspReady) + { + int counter = ndspGetCounter(~ndspFrameId & 1); + if (counter) + { + int next = (counter + 1) & 0xFFFF; + ndspFrameId = next ? next : 2; + ndspBufferId = ndspFrameId & 1; + ndspiReadChnState(); + //memcpy(dspVar9Backup, dspVars[9][ndspBufferId], sizeof(dspVar9Backup)); + ndspUpdateCapture((s16*)ndspVars[6][ndspBufferId], 160); + droppedFrames += *((u16*)ndspVars[5][ndspBufferId] + 1); + } + bNeedsSync = false; + } +} + +static void ndspThreadMain(void* arg) +{ + ndspThreadRun = true; + while (ndspThreadRun) + { + ndspSync(); + + // Call callbacks here + if (ndspMaster.callback) + ndspMaster.callback(ndspMaster.callbackData); + + if (bSleeping || !bDspReady) + continue; + + if (bNeedsSync) + ndspSync(); + + ndspUpdateMaster(); + // Call aux user callback here if enabled + // Execute DSP effects here + ndspiUpdateChn(); + + ndspSetCounter(ndspBufferCurId, ndspFrameId++); + svcSignalEvent(dspSem); + ndspBufferCurId = ndspFrameId & 1; + + frameCount++; + bNeedsSync = true; + } + + svcExitThread(); +} + +void ndspUseComponent(const void* binary, u32 size, u16 progMask, u16 dataMask) +{ + componentBin = binary; + componentSize = size; + componentProgMask = progMask; + componentDataMask = dataMask; +} + +static int ndspRefCount = 0; + +Result ndspInit(void) +{ + Result rc; + if (ndspRefCount++) return 0; + + LightLock_Init(&ndspMutex); + ndspInitMaster(); + ndspiInitChn(); + + rc = initCfgu(); + if (rc) + { + u8 outMode; + CFGU_GetConfigInfoBlk2(sizeof(outMode), 0x70001, &outMode); + ndspMaster.outputMode = outMode; + exitCfgu(); + } + + rc = dspInit(); + if (rc) return rc; + + rc = ndspInitialize(false); + if (rc) goto _fail1; + + rc = svcCreateEvent(&sleepEvent, 0); + if (rc) goto _fail2; + + rc = svcCreateThread(&ndspThread, ndspThreadMain, 0x0, (u32*)(&ndspThreadStack[NDSP_THREAD_STACK_SIZE/8]), 0x31, -2); + if (rc) goto _fail3; + + aptHook(&aptCookie, ndspAptHook, NULL); + return 0; + +_fail3: + svcCloseHandle(sleepEvent); +_fail2: + ndspFinalize(false); +_fail1: + dspExit(); + ndspRefCount--; + return rc; +} + +void ndspExit(void) +{ + if (--ndspRefCount) return; + if (!bDspReady) return; + ndspThreadRun = false; + svcWaitSynchronization(ndspThread, U64_MAX); + svcCloseHandle(ndspThread); + svcCloseHandle(sleepEvent); + aptUnhook(&aptCookie); + ndspFinalize(false); + dspExit(); +} + +u32 ndspGetDroppedFrames(void) +{ + return droppedFrames; +} + +u32 ndspGetFrameCount(void) +{ + return frameCount; +} + +void ndspSetMasterVol(float volume) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.masterVol = volume; + ndspMaster.flags |= MFLAG_MASTERVOL; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspSetOutputMode(int mode) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.outputMode = mode; + ndspMaster.flags |= MFLAG_OUTPUTMODE; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspSetClippingMode(int mode) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.clippingMode = mode; + ndspMaster.flags |= MFLAG_CLIPPINGMODE; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspSetOutputCount(int count) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.outputCount = count; + ndspMaster.flags |= MFLAG_OUTPUTCOUNT; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspSetCapture(ndspWaveBuf* capture) +{ + ndspMaster.capture = capture; +} + +void ndspSetCallback(ndspCallback callback, void* data) +{ + ndspMaster.callback = callback; + ndspMaster.callbackData = data; +} + +void ndspSurroundSetDepth(u16 depth) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.surround.depth = depth; + ndspMaster.flags |= MFLAG_SURR_DEPTH; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspSurroundSetPos(u16 pos) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.surround.pos = pos; + ndspMaster.flags |= MFLAG_SURR_POS; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspSurroundSetRearRatio(u16 ratio) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.surround.rearRatio = ratio; + ndspMaster.flags |= MFLAG_SURR_RRATIO; + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspAuxSetEnable(int id, bool enable) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.aux[id].enable = enable ? 1 : 0; + ndspMaster.flags |= MFLAG_AUX_ENABLE(id); + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspAuxSetFrontBypass(int id, bool bypass) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.aux[id].frontBypass = bypass ? 1 : 0; + ndspMaster.flags |= MFLAG_AUX_BYPASS(id); + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspAuxSetVolume(int id, float volume) +{ + LightLock_Lock(&ndspMaster.lock); + ndspMaster.aux[id].volume = volume; + ndspMaster.flags |= MFLAG_AUX_VOLUME(id); + LightLock_Unlock(&ndspMaster.lock); +} + +void ndspAuxSetCallback(int id, ndspAuxCallback callback, void* data) +{ + ndspMaster.aux[id].callback = callback; + ndspMaster.aux[id].callbackData = data; +}