Add a work-in-progress API for using Nintendo's default DSP component

This commit is contained in:
fincs 2015-10-02 21:54:18 +02:00
parent 9e43411a0e
commit f1ce480ed9
7 changed files with 1150 additions and 0 deletions

View File

@ -27,6 +27,7 @@ BUILD := build
SOURCES := source \ SOURCES := source \
source/allocator \ source/allocator \
source/gpu \ source/gpu \
source/ndsp \
source/services \ source/services \
source/services/soc \ source/services/soc \
source/util/rbtree \ source/util/rbtree \

View File

@ -49,6 +49,9 @@ extern "C" {
#include <3ds/gpu/shbin.h> #include <3ds/gpu/shbin.h>
#include <3ds/gpu/shaderProgram.h> #include <3ds/gpu/shaderProgram.h>
#include <3ds/ndsp/ndsp.h>
#include <3ds/ndsp/channel.h>
#include <3ds/sdmc.h> #include <3ds/sdmc.h>
#include <3ds/romfs.h> #include <3ds/romfs.h>

View File

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

View File

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

View File

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

View File

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

543
libctru/source/ndsp/ndsp.c Normal file
View File

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