aboutsummaryrefslogtreecommitdiff
path: root/src/SPU.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/SPU.cpp')
-rw-r--r--src/SPU.cpp296
1 files changed, 149 insertions, 147 deletions
diff --git a/src/SPU.cpp b/src/SPU.cpp
index 5b74bda..fe798c7 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
+#include "Platform.h"
#include "NDS.h"
#include "DSi.h"
#include "SPU.h"
@@ -61,13 +62,15 @@ const s16 PSGTable[8][8] =
{-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF}
};
-const u32 kSamplesPerRun = 1;
+const u32 OutputBufferSize = 2*2048;
+s16 OutputBackbuffer[2 * OutputBufferSize];
+u32 OutputBackbufferWritePosition;
-const u32 OutputBufferSize = 2*1024;
-s16 OutputBuffer[2 * OutputBufferSize];
-volatile u32 OutputReadOffset;
-volatile u32 OutputWriteOffset;
+s16 OutputFrontBuffer[2 * OutputBufferSize];
+u32 OutputFrontBufferWritePosition;
+u32 OutputFrontBufferReadPosition;
+Platform::Mutex* AudioLock;
u16 Cnt;
u8 MasterVolume;
@@ -85,6 +88,8 @@ bool Init()
Capture[0] = new CaptureUnit(0);
Capture[1] = new CaptureUnit(1);
+ AudioLock = Platform::Mutex_Create();
+
return true;
}
@@ -95,6 +100,8 @@ void DeInit()
delete Capture[0];
delete Capture[1];
+
+ Platform::Mutex_Free(AudioLock);
}
void Reset()
@@ -111,15 +118,18 @@ void Reset()
Capture[0]->Reset();
Capture[1]->Reset();
- NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun);
+ NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0);
}
void Stop()
{
- memset(OutputBuffer, 0, 2*OutputBufferSize*2);
+ Platform::Mutex_Lock(AudioLock);
+ memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2);
- OutputReadOffset = 0;
- OutputWriteOffset = 0;
+ OutputBackbufferWritePosition = 0;
+ OutputFrontBufferReadPosition = 0;
+ OutputFrontBufferWritePosition = 0;
+ Platform::Mutex_Unlock(AudioLock);
}
void DoSavestate(Savestate* file)
@@ -416,11 +426,11 @@ void Channel::NextSample_Noise()
}
template<u32 type>
-void Channel::Run(s32* buf, u32 samples)
+s32 Channel::Run()
{
- if (!(Cnt & (1<<31))) return;
+ if (!(Cnt & (1<<31))) return 0;
- if ((type < 3) && ((Length+LoopPos) < 16)) return;
+ if ((type < 3) && ((Length+LoopPos) < 16)) return 0;
if (KeyOn)
{
@@ -428,45 +438,32 @@ void Channel::Run(s32* buf, u32 samples)
KeyOn = false;
}
- for (u32 s = 0; s < samples; s++)
+ Timer += 512; // 1 sample = 512 cycles at 16MHz
+
+ while (Timer >> 16)
{
- Timer += 512; // 1 sample = 512 cycles at 16MHz
+ Timer = TimerReload + (Timer - 0x10000);
- while (Timer >> 16)
+ switch (type)
{
- Timer = TimerReload + (Timer - 0x10000);
-
- switch (type)
- {
- case 0: NextSample_PCM8(); break;
- case 1: NextSample_PCM16(); break;
- case 2: NextSample_ADPCM(); break;
- case 3: NextSample_PSG(); break;
- case 4: NextSample_Noise(); break;
- }
+ case 0: NextSample_PCM8(); break;
+ case 1: NextSample_PCM16(); break;
+ case 2: NextSample_ADPCM(); break;
+ case 3: NextSample_PSG(); break;
+ case 4: NextSample_Noise(); break;
}
-
- s32 val = (s32)CurSample;
- val <<= VolumeShift;
- val *= Volume;
- buf[s] = val;
-
- if (!(Cnt & (1<<31))) break;
}
+
+ s32 val = (s32)CurSample;
+ val <<= VolumeShift;
+ val *= Volume;
+ return val;
}
-void Channel::PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf)
+void Channel::PanOutput(s32 in, s32& left, s32& right)
{
- for (u32 s = 0; s < samples; s++)
- {
- s32 val = (s32)inbuf[s];
-
- s32 l = ((s64)val * (128-Pan)) >> 10;
- s32 r = ((s64)val * Pan) >> 10;
-
- leftbuf[s] += l;
- rightbuf[s] += r;
- }
+ left += ((s64)in * (128-Pan)) >> 10;
+ right += ((s64)in * Pan) >> 10;
}
@@ -602,39 +599,31 @@ void CaptureUnit::Run(s32 sample)
}
-void Mix(u32 samples)
+void Mix(u32 dummy)
{
- s32 channelbuf[32];
- s32 leftbuf[32], rightbuf[32];
- s32 ch0buf[32], ch1buf[32], ch2buf[32], ch3buf[32];
- s32 leftoutput[32], rightoutput[32];
-
- for (u32 s = 0; s < samples; s++)
- {
- leftbuf[s] = 0; rightbuf[s] = 0;
- leftoutput[s] = 0; rightoutput[s] = 0;
- }
+ s32 left = 0, right = 0;
+ s32 leftoutput = 0, rightoutput = 0;
if (Cnt & (1<<15))
{
- Channels[0]->DoRun(ch0buf, samples);
- Channels[1]->DoRun(ch1buf, samples);
- Channels[2]->DoRun(ch2buf, samples);
- Channels[3]->DoRun(ch3buf, samples);
+ s32 ch0 = Channels[0]->DoRun();
+ s32 ch1 = Channels[1]->DoRun();
+ s32 ch2 = Channels[2]->DoRun();
+ s32 ch3 = Channels[3]->DoRun();
// TODO: addition from capture registers
- Channels[0]->PanOutput(ch0buf, samples, leftbuf, rightbuf);
- Channels[2]->PanOutput(ch2buf, samples, leftbuf, rightbuf);
+ Channels[0]->PanOutput(ch0, left, right);
+ Channels[2]->PanOutput(ch2, left, right);
- if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1buf, samples, leftbuf, rightbuf);
- if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3buf, samples, leftbuf, rightbuf);
+ if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1, left, right);
+ if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3, left, right);
for (int i = 4; i < 16; i++)
{
Channel* chan = Channels[i];
- chan->DoRun(channelbuf, samples);
- chan->PanOutput(channelbuf, samples, leftbuf, rightbuf);
+ s32 channel = chan->DoRun();
+ chan->PanOutput(channel, left, right);
}
// sound capture
@@ -642,32 +631,24 @@ void Mix(u32 samples)
if (Capture[0]->Cnt & (1<<7))
{
- for (u32 s = 0; s < samples; s++)
- {
- s32 val = leftbuf[s];
+ s32 val = left;
- val >>= 8;
- if (val < -0x8000) val = -0x8000;
- else if (val > 0x7FFF) val = 0x7FFF;
+ val >>= 8;
+ if (val < -0x8000) val = -0x8000;
+ else if (val > 0x7FFF) val = 0x7FFF;
- Capture[0]->Run(val);
- if (!(Capture[0]->Cnt & (1<<7))) break;
- }
+ Capture[0]->Run(val);
}
if (Capture[1]->Cnt & (1<<7))
{
- for (u32 s = 0; s < samples; s++)
- {
- s32 val = rightbuf[s];
+ s32 val = right;
- val >>= 8;
- if (val < -0x8000) val = -0x8000;
- else if (val > 0x7FFF) val = 0x7FFF;
+ val >>= 8;
+ if (val < -0x8000) val = -0x8000;
+ else if (val > 0x7FFF) val = 0x7FFF;
- Capture[1]->Run(val);
- if (!(Capture[1]->Cnt & (1<<7))) break;
- }
+ Capture[1]->Run(val);
}
// final output
@@ -675,31 +656,25 @@ void Mix(u32 samples)
switch (Cnt & 0x0300)
{
case 0x0000: // left mixer
- {
- for (u32 s = 0; s < samples; s++)
- leftoutput[s] = leftbuf[s];
- }
+ leftoutput = left;
break;
case 0x0100: // channel 1
{
s32 pan = 128 - Channels[1]->Pan;
- for (u32 s = 0; s < samples; s++)
- leftoutput[s] = ((s64)ch1buf[s] * pan) >> 10;
+ leftoutput = ((s64)ch1 * pan) >> 10;
}
break;
case 0x0200: // channel 3
{
s32 pan = 128 - Channels[3]->Pan;
- for (u32 s = 0; s < samples; s++)
- leftoutput[s] = ((s64)ch3buf[s] * pan) >> 10;
+ leftoutput = ((s64)ch3 * pan) >> 10;
}
break;
case 0x0300: // channel 1+3
{
s32 pan1 = 128 - Channels[1]->Pan;
s32 pan3 = 128 - Channels[3]->Pan;
- for (u32 s = 0; s < samples; s++)
- leftoutput[s] = (((s64)ch1buf[s] * pan1) >> 10) + (((s64)ch3buf[s] * pan3) >> 10);
+ leftoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10);
}
break;
}
@@ -707,105 +682,122 @@ void Mix(u32 samples)
switch (Cnt & 0x0C00)
{
case 0x0000: // right mixer
- {
- for (u32 s = 0; s < samples; s++)
- rightoutput[s] = rightbuf[s];
- }
+ rightoutput = right;
break;
case 0x0400: // channel 1
{
s32 pan = Channels[1]->Pan;
- for (u32 s = 0; s < samples; s++)
- rightoutput[s] = ((s64)ch1buf[s] * pan) >> 10;
+ rightoutput = ((s64)ch1 * pan) >> 10;
}
break;
case 0x0800: // channel 3
{
s32 pan = Channels[3]->Pan;
- for (u32 s = 0; s < samples; s++)
- rightoutput[s] = ((s64)ch3buf[s] * pan) >> 10;
+ rightoutput = ((s64)ch3 * pan) >> 10;
}
break;
case 0x0C00: // channel 1+3
{
s32 pan1 = Channels[1]->Pan;
s32 pan3 = Channels[3]->Pan;
- for (u32 s = 0; s < samples; s++)
- rightoutput[s] = (((s64)ch1buf[s] * pan1) >> 10) + (((s64)ch3buf[s] * pan3) >> 10);
+ rightoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10);
}
break;
}
}
- for (u32 s = 0; s < samples; s++)
+ leftoutput = ((s64)leftoutput * MasterVolume) >> 7;
+ rightoutput = ((s64)rightoutput * MasterVolume) >> 7;
+
+ leftoutput >>= 8;
+ if (leftoutput < -0x8000) leftoutput = -0x8000;
+ else if (leftoutput > 0x7FFF) leftoutput = 0x7FFF;
+ rightoutput >>= 8;
+ if (rightoutput < -0x8000) rightoutput = -0x8000;
+ else if (rightoutput > 0x7FFF) rightoutput = 0x7FFF;
+
+ // OutputBufferFrame can never get full because it's
+ // transfered to OutputBuffer at the end of the frame
+ OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1;
+ OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1;
+ OutputBackbufferWritePosition += 2;
+
+ NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0);
+}
+
+void TransferOutput()
+{
+ Platform::Mutex_Lock(AudioLock);
+ for (u32 i = 0; i < OutputBackbufferWritePosition; i += 2)
{
- s32 l = leftoutput[s];
- s32 r = rightoutput[s];
-
- l = ((s64)l * MasterVolume) >> 7;
- r = ((s64)r * MasterVolume) >> 7;
-
- l >>= 8;
- if (l < -0x8000) l = -0x8000;
- else if (l > 0x7FFF) l = 0x7FFF;
- r >>= 8;
- if (r < -0x8000) r = -0x8000;
- else if (r > 0x7FFF) r = 0x7FFF;
-
- OutputBuffer[OutputWriteOffset ] = l >> 1;
- OutputBuffer[OutputWriteOffset + 1] = r >> 1;
- OutputWriteOffset += 2;
- OutputWriteOffset &= ((2*OutputBufferSize)-1);
- if (OutputWriteOffset == OutputReadOffset)
+ OutputFrontBuffer[OutputFrontBufferWritePosition ] = OutputBackbuffer[i ];
+ OutputFrontBuffer[OutputFrontBufferWritePosition + 1] = OutputBackbuffer[i + 1];
+
+ OutputFrontBufferWritePosition += 2;
+ OutputFrontBufferWritePosition &= OutputBufferSize*2-1;
+ if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition)
{
- //printf("!! SOUND FIFO OVERFLOW %d\n", OutputWriteOffset>>1);
// advance the read position too, to avoid losing the entire FIFO
- OutputReadOffset += 2;
- OutputReadOffset &= ((2*OutputBufferSize)-1);
+ OutputFrontBufferReadPosition += 2;
+ OutputFrontBufferReadPosition &= OutputBufferSize*2-1;
}
}
-
- NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun);
+ OutputBackbufferWritePosition = 0;
+ Platform::Mutex_Unlock(AudioLock);
}
-
void TrimOutput()
{
+ Platform::Mutex_Lock(AudioLock);
const int halflimit = (OutputBufferSize / 2);
- int readpos = OutputWriteOffset - (halflimit*2);
+ int readpos = OutputFrontBufferWritePosition - (halflimit*2);
if (readpos < 0) readpos += (OutputBufferSize*2);
- OutputReadOffset = readpos;
+ OutputFrontBufferReadPosition = readpos;
+ Platform::Mutex_Unlock(AudioLock);
}
void DrainOutput()
{
- OutputReadOffset = 0;
- OutputWriteOffset = 0;
+ Platform::Mutex_Lock(AudioLock);
+ OutputFrontBufferWritePosition = 0;
+ OutputFrontBufferReadPosition = 0;
+ Platform::Mutex_Unlock(AudioLock);
}
void InitOutput()
{
- memset(OutputBuffer, 0, 2*OutputBufferSize*2);
- OutputReadOffset = 0;
- OutputWriteOffset = OutputBufferSize;
+ Platform::Mutex_Lock(AudioLock);
+ memset(OutputBackbuffer, 0, 2*OutputBufferSize*2);
+ memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2);
+ OutputFrontBufferReadPosition = 0;
+ OutputFrontBufferWritePosition = 0;
+ Platform::Mutex_Unlock(AudioLock);
}
int GetOutputSize()
{
+ Platform::Mutex_Lock(AudioLock);
+
int ret;
- if (OutputWriteOffset >= OutputReadOffset)
- ret = OutputWriteOffset - OutputReadOffset;
+ if (OutputFrontBufferWritePosition >= OutputFrontBufferReadPosition)
+ ret = OutputFrontBufferWritePosition - OutputFrontBufferReadPosition;
else
- ret = (OutputBufferSize*2) - OutputReadOffset + OutputWriteOffset;
+ ret = (OutputBufferSize*2) - OutputFrontBufferReadPosition + OutputFrontBufferWritePosition;
ret >>= 1;
+
+ Platform::Mutex_Unlock(AudioLock);
return ret;
}
void Sync(bool wait)
{
+ // this function is currently not used anywhere
+ // depending on the usage context the thread safety measures could be made
+ // a lot faster
+
// sync to audio output in case the core is running too fast
// * wait=true: wait until enough audio data has been played
// * wait=false: merely skip some audio data to avoid a FIFO overflow
@@ -819,32 +811,42 @@ void Sync(bool wait)
}
else if (GetOutputSize() > halflimit)
{
- int readpos = OutputWriteOffset - (halflimit*2);
+ Platform::Mutex_Lock(AudioLock);
+
+ int readpos = OutputFrontBufferWritePosition - (halflimit*2);
if (readpos < 0) readpos += (OutputBufferSize*2);
- OutputReadOffset = readpos;
+ OutputFrontBufferReadPosition = readpos;
+
+ Platform::Mutex_Unlock(AudioLock);
}
}
int ReadOutput(s16* data, int samples)
{
- if (OutputReadOffset == OutputWriteOffset)
+ Platform::Mutex_Lock(AudioLock);
+ if (OutputFrontBufferReadPosition == OutputFrontBufferWritePosition)
+ {
+ Platform::Mutex_Unlock(AudioLock);
return 0;
+ }
for (int i = 0; i < samples; i++)
{
- *data++ = OutputBuffer[OutputReadOffset];
- *data++ = OutputBuffer[OutputReadOffset + 1];
+ *data++ = OutputFrontBuffer[OutputFrontBufferReadPosition];
+ *data++ = OutputFrontBuffer[OutputFrontBufferReadPosition + 1];
+
+ OutputFrontBufferReadPosition += 2;
+ OutputFrontBufferReadPosition &= ((2*OutputBufferSize)-1);
- //if (OutputReadOffset != OutputWriteOffset)
+ if (OutputFrontBufferWritePosition == OutputFrontBufferReadPosition)
{
- OutputReadOffset += 2;
- OutputReadOffset &= ((2*OutputBufferSize)-1);
- }
- if (OutputReadOffset == OutputWriteOffset)
+ Platform::Mutex_Unlock(AudioLock);
return i+1;
+ }
}
+ Platform::Mutex_Unlock(AudioLock);
return samples;
}