SOURCES := source \
source/allocator \
source/gpu \
+ source/ndsp \
source/services \
source/services/soc \
source/util/rbtree \
#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>
--- /dev/null
+#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
--- /dev/null
+#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);
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#pragma once
+#include <stdlib.h>
+#include <string.h>
+#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);
--- /dev/null
+#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;
+}