]> Chaos Git - corbenik/ctrulib.git/commitdiff
Add a work-in-progress API for using Nintendo's default DSP component
authorfincs <fincs.alt1@gmail.com>
Fri, 2 Oct 2015 19:54:18 +0000 (21:54 +0200)
committerfincs <fincs.alt1@gmail.com>
Fri, 2 Oct 2015 19:54:18 +0000 (21:54 +0200)
libctru/Makefile
libctru/include/3ds.h
libctru/include/3ds/ndsp/channel.h [new file with mode: 0644]
libctru/include/3ds/ndsp/ndsp.h [new file with mode: 0644]
libctru/source/ndsp/ndsp-channel.c [new file with mode: 0644]
libctru/source/ndsp/ndsp-internal.h [new file with mode: 0644]
libctru/source/ndsp/ndsp.c [new file with mode: 0644]

index 090e762a36b9bf2a7df09f9433959a61960940c6..7136508e7e9eead16bbc2a4028c0bf13618334d2 100644 (file)
@@ -27,6 +27,7 @@ BUILD         :=      build
 SOURCES                :=      source \
                        source/allocator \
                        source/gpu \
+                       source/ndsp \
                        source/services \
                        source/services/soc \
                        source/util/rbtree \
index e86864afe3f03eb81a30fcc2672b1194409c13ba..a1879492d542fdbd63e40473ff2058fd9cfcba48 100644 (file)
@@ -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 (file)
index 0000000..64d389d
--- /dev/null
@@ -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 (file)
index 0000000..9100a87
--- /dev/null
@@ -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 (file)
index 0000000..b07a234
--- /dev/null
@@ -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 (file)
index 0000000..bf37b82
--- /dev/null
@@ -0,0 +1,107 @@
+#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);
diff --git a/libctru/source/ndsp/ndsp.c b/libctru/source/ndsp/ndsp.c
new file mode 100644 (file)
index 0000000..fc41c1f
--- /dev/null
@@ -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;
+}