aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStapleButter <thetotalworm@gmail.com>2017-04-08 22:59:27 +0200
committerStapleButter <thetotalworm@gmail.com>2017-04-08 22:59:27 +0200
commit3f3b2977d7decf8f691f5f999ae9b08a6d214bc9 (patch)
tree26effef45222d0aec60d373147281ea432e2d0c9
parenta4f436f8279477e683e4155bd280e19dff9d3f91 (diff)
* sound capture from left/right mixers
* support for appropriate output modes
-rw-r--r--src/GPU3D.cpp1
-rw-r--r--src/SPU.cpp313
-rw-r--r--src/SPU.h50
3 files changed, 321 insertions, 43 deletions
diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp
index 9881760..7de5ad0 100644
--- a/src/GPU3D.cpp
+++ b/src/GPU3D.cpp
@@ -49,7 +49,6 @@
//
// formula for clear depth: (GBAtek is wrong there)
// clearZ = (val * 0x200) + 0x1FF;
-// if (clearZ >= 0x010000 && clearZ < 0xFFFFFF) clearZ++;
//
// alpha is 5-bit
//
diff --git a/src/SPU.cpp b/src/SPU.cpp
index 28582db..f5a7b9b 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -66,6 +66,7 @@ u8 MasterVolume;
u16 Bias;
Channel* Channels[16];
+CaptureUnit* Capture[2];
bool Init()
@@ -73,6 +74,9 @@ bool Init()
for (int i = 0; i < 16; i++)
Channels[i] = new Channel(i);
+ Capture[0] = new CaptureUnit(0);
+ Capture[1] = new CaptureUnit(1);
+
return true;
}
@@ -80,6 +84,9 @@ void DeInit()
{
for (int i = 0; i < 16; i++)
delete Channels[i];
+
+ delete Capture[0];
+ delete Capture[1];
}
void Reset()
@@ -95,6 +102,9 @@ void Reset()
for (int i = 0; i < 16; i++)
Channels[i]->Reset();
+ Capture[0]->Reset();
+ Capture[1]->Reset();
+
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*16, Mix, 16);
}
@@ -285,8 +295,6 @@ void Channel::Run(s32* buf, u32 samples)
for (u32 s = 0; s < samples; s++)
buf[s] = 0;
- if (!(Cnt & 0x7F)) return;
-
for (u32 s = 0; s < samples; s++)
{
Timer += 512; // 1 sample = 512 cycles at 16MHz
@@ -305,7 +313,76 @@ void Channel::Run(s32* buf, u32 samples)
}
}
- buf[s] = (s32)CurSample;
+ s32 val = (s32)CurSample;
+ val <<= VolumeShift;
+ val *= Volume;
+ buf[s] = val;
+
+ if (!(Cnt & (1<<31))) break;
+ }
+}
+
+
+CaptureUnit::CaptureUnit(u32 num)
+{
+ Num = num;
+}
+
+CaptureUnit::~CaptureUnit()
+{
+}
+
+void CaptureUnit::Reset()
+{
+ SetCnt(0);
+ DstAddr = 0;
+ TimerReload = 0;
+ Length = 0;
+}
+
+void CaptureUnit::Run(s32 sample)
+{
+ Timer += 512;
+
+ if (Cnt & 0x08)
+ {
+ while (Timer >> 16)
+ {
+ Timer = TimerReload + (Timer - 0x10000);
+
+ NDS::ARM7Write8(DstAddr + Pos, (u8)(sample >> 8));
+ Pos++;
+ if (Pos >= Length)
+ {
+ if (Cnt & 0x04)
+ {
+ Cnt &= 0x7F;
+ return;
+ }
+ else
+ Pos = 0;
+ }
+ }
+ }
+ else
+ {
+ while (Timer >> 16)
+ {
+ Timer = TimerReload + (Timer - 0x10000);
+
+ NDS::ARM7Write16(DstAddr + Pos, (u16)sample);
+ Pos += 2;
+ if (Pos >= Length)
+ {
+ if (Cnt & 0x04)
+ {
+ Cnt &= 0x7F;
+ return;
+ }
+ else
+ Pos = 0;
+ }
+ }
}
}
@@ -313,66 +390,171 @@ void Channel::Run(s32* buf, u32 samples)
void Mix(u32 samples)
{
s32 channelbuf[32];
- s32 leftbuf[32];
- s32 rightbuf[32];
+ s32 leftbuf[32], rightbuf[32];
+ s32 ch1buf[32], ch3buf[32];
+ s32 leftoutput[32], rightoutput[32];
for (u32 s = 0; s < samples; s++)
{
- leftbuf[s] = 0;
- rightbuf[s] = 0;
+ leftbuf[s] = 0; rightbuf[s] = 0;
+ leftoutput[s] = 0; rightoutput[s] = 0;
}
- for (int i = 0; i < 16; i++)
+ if (Cnt & (1<<15))
{
- Channel* chan = Channels[i];
- if (!(chan->Cnt & (1<<31))) continue;
-
- // TODO: what happens if we use type 3 on channels 0-7??
- switch ((chan->Cnt >> 29) & 0x3)
- {
- case 0: chan->Run<0>(channelbuf, samples); break;
- case 1: chan->Run<1>(channelbuf, samples); break;
- case 2: chan->Run<2>(channelbuf, samples); break;
- case 3:
- if (i >= 14) chan->Run<4>(channelbuf, samples);
- else if (i >= 8) chan->Run<3>(channelbuf, samples);
- break;
+ u32 mixermask = 0xFFFF;
+ if (Cnt & (1<<12)) mixermask &= ~(1<<1);
+ if (Cnt & (1<<13)) mixermask &= ~(1<<3);
+
+ for (int i = 0; i < 16; i++)
+ {
+ if (!(mixermask & (1<<i))) continue;
+ Channel* chan = Channels[i];
+ if (!(chan->Cnt & (1<<31))) continue;
+
+ // TODO: what happens if we use type 3 on channels 0-7??
+ chan->DoRun(channelbuf, samples);
+
+ for (u32 s = 0; s < samples; s++)
+ {
+ s32 val = (s32)channelbuf[s];
+
+ s32 l = ((s64)val * (128-chan->Pan)) >> 10;
+ s32 r = ((s64)val * chan->Pan) >> 10;
+
+ leftbuf[s] += l;
+ rightbuf[s] += r;
+ }
}
- for (u32 s = 0; s < samples; s++)
+ // sound capture
+ // TODO: other sound capture sources, along with their bugs
+
+ if (Capture[0]->Cnt & (1<<7))
{
- s32 val = (s32)channelbuf[s];
+ for (u32 s = 0; s < samples; s++)
+ {
+ s32 val = leftbuf[s];
- val <<= chan->VolumeShift;
- val *= chan->Volume;
+ val >>= 8;
+ if (val < -0x8000) val = -0x8000;
+ else if (val > 0x7FFF) val = 0x7FFF;
- s32 l = ((s64)val * (128-chan->Pan)) >> 10;
- s32 r = ((s64)val * chan->Pan) >> 10;
+ Capture[0]->Run(val);
+ if (!((Capture[0]->Cnt & (1<<7)))) break;
+ }
+ }
- leftbuf[s] += l;
- rightbuf[s] += r;
+ if (Capture[1]->Cnt & (1<<7))
+ {
+ for (u32 s = 0; s < samples; s++)
+ {
+ s32 val = rightbuf[s];
+
+ val >>= 8;
+ if (val < -0x8000) val = -0x8000;
+ else if (val > 0x7FFF) val = 0x7FFF;
+
+ Capture[1]->Run(val);
+ if (!((Capture[1]->Cnt & (1<<7)))) break;
+ }
}
- }
- //
+ // final output
+
+ if (Cnt & 0x0500)
+ {
+ // mix channel 1 if needed
+ Channels[1]->DoRun(ch1buf, samples);
+ }
+ if (Cnt & 0x0A00)
+ {
+ // mix channel 3 if needed
+ Channels[3]->DoRun(ch3buf, samples);
+ }
+
+ switch (Cnt & 0x0300)
+ {
+ case 0x0000: // left mixer
+ {
+ for (u32 s = 0; s < samples; s++)
+ leftoutput[s] = leftbuf[s];
+ }
+ 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;
+ }
+ 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;
+ }
+ 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);
+ }
+ break;
+ }
+
+ switch (Cnt & 0x0C00)
+ {
+ case 0x0000: // right mixer
+ {
+ for (u32 s = 0; s < samples; s++)
+ rightoutput[s] = rightbuf[s];
+ }
+ break;
+ case 0x0400: // channel 1
+ {
+ s32 pan = Channels[1]->Pan;
+ for (u32 s = 0; s < samples; s++)
+ rightoutput[s] = ((s64)ch1buf[s] * 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;
+ }
+ 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);
+ }
+ break;
+ }
+ }
for (u32 s = 0; s < samples; s++)
{
- s32 l = (s32)leftbuf[s];
- s32 r = (s32)rightbuf[s];
+ s32 l = leftoutput[s];
+ s32 r = rightoutput[s];
l = ((s64)l * MasterVolume) >> 7;
r = ((s64)r * MasterVolume) >> 7;
- l >>= 12;
+ l >>= 8;
if (l < -0x8000) l = -0x8000;
else if (l > 0x7FFF) l = 0x7FFF;
- r >>= 12;
+ r >>= 8;
if (r < -0x8000) r = -0x8000;
else if (r > 0x7FFF) r = 0x7FFF;
- OutputBuffer[OutputWriteOffset ] = l << 3;
- OutputBuffer[OutputWriteOffset + 1] = r << 3;
+ OutputBuffer[OutputWriteOffset ] = l >> 1;
+ OutputBuffer[OutputWriteOffset + 1] = r >> 1;
OutputWriteOffset += 2;
OutputWriteOffset &= ((2*OutputBufferSize)-1);
}
@@ -418,10 +600,13 @@ u8 Read8(u32 addr)
{
case 0x04000500: return Cnt & 0x7F;
case 0x04000501: return Cnt >> 8;
+
+ case 0x04000508: return Capture[0]->Cnt;
+ case 0x04000509: return Capture[1]->Cnt;
}
}
- //printf("unknown SPU read8 %08X\n", addr);
+ printf("unknown SPU read8 %08X\n", addr);
return 0;
}
@@ -443,6 +628,8 @@ u16 Read16(u32 addr)
{
case 0x04000500: return Cnt;
case 0x04000504: return Bias;
+
+ case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8);
}
}
@@ -467,6 +654,11 @@ u32 Read32(u32 addr)
{
case 0x04000500: return Cnt;
case 0x04000504: return Bias;
+
+ case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8);
+
+ case 0x04000510: return Capture[0]->DstAddr;
+ case 0x04000518: return Capture[1]->DstAddr;
}
}
@@ -500,6 +692,15 @@ void Write8(u32 addr, u8 val)
case 0x04000501:
Cnt = (Cnt & 0x007F) | ((val & 0xBF) << 8);
return;
+
+ case 0x04000508:
+ Capture[0]->SetCnt(val);
+ if (val & 0x03) printf("!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val);
+ return;
+ case 0x04000509:
+ Capture[1]->SetCnt(val);
+ if (val & 0x03) printf("!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val);
+ return;
}
}
@@ -516,8 +717,15 @@ void Write16(u32 addr, u16 val)
{
case 0x0: chan->SetCnt((chan->Cnt & 0xFFFF0000) | val); return;
case 0x2: chan->SetCnt((chan->Cnt & 0x0000FFFF) | (val << 16)); return;
- case 0x8: chan->SetTimerReload(val); return;
+ case 0x8:
+ chan->SetTimerReload(val);
+ if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val);
+ else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val);
+ return;
case 0xA: chan->SetLoopPos(val); return;
+
+ case 0xC: chan->SetLength((chan->Length & 0xFFFF0000) | val); return;
+ case 0xE: chan->SetLength((chan->Length & 0x0000FFFF) | (val << 16)); return;
}
}
else
@@ -533,6 +741,15 @@ void Write16(u32 addr, u16 val)
case 0x04000504:
Bias = val & 0x3FF;
return;
+
+ case 0x04000508:
+ Capture[0]->SetCnt(val & 0xFF);
+ Capture[1]->SetCnt(val >> 8);
+ if (val & 0x0303) printf("!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val);
+ return;
+
+ case 0x04000514: Capture[0]->SetLength(val); return;
+ case 0x0400051C: Capture[1]->SetLength(val); return;
}
}
@@ -550,8 +767,11 @@ void Write32(u32 addr, u32 val)
case 0x0: chan->SetCnt(val); return;
case 0x4: chan->SetSrcAddr(val); return;
case 0x8:
- chan->SetTimerReload(val & 0xFFFF);
chan->SetLoopPos(val >> 16);
+ val &= 0xFFFF;
+ chan->SetTimerReload(val);
+ if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val);
+ else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val);
return;
case 0xC: chan->SetLength(val); return;
}
@@ -569,10 +789,19 @@ void Write32(u32 addr, u32 val)
case 0x04000504:
Bias = val & 0x3FF;
return;
+
+ case 0x04000508:
+ Capture[0]->SetCnt(val & 0xFF);
+ Capture[1]->SetCnt(val >> 8);
+ if (val & 0x0303) printf("!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val);
+ return;
+
+ case 0x04000510: Capture[0]->SetDstAddr(val); return;
+ case 0x04000514: Capture[0]->SetLength(val & 0xFFFF); return;
+ case 0x04000518: Capture[1]->SetDstAddr(val); return;
+ case 0x0400051C: Capture[1]->SetLength(val & 0xFFFF); return;
}
}
-
- printf("unknown SPU write32 %08X %08X\n", addr, val);
}
}
diff --git a/src/SPU.h b/src/SPU.h
index 0687b41..a00e094 100644
--- a/src/SPU.h
+++ b/src/SPU.h
@@ -103,6 +103,56 @@ public:
void NextSample_Noise();
template<u32 type> void Run(s32* buf, u32 samples);
+
+ void DoRun(s32* buf, u32 samples)
+ {
+ switch ((Cnt >> 29) & 0x3)
+ {
+ case 0: Run<0>(buf, samples); break;
+ case 1: Run<1>(buf, samples); break;
+ case 2: Run<2>(buf, samples); break;
+ case 3:
+ if (Num >= 14) Run<4>(buf, samples);
+ else if (Num >= 8) Run<3>(buf, samples);
+ break;
+ }
+ }
+};
+
+class CaptureUnit
+{
+public:
+ CaptureUnit(u32 num);
+ ~CaptureUnit();
+ void Reset();
+
+ u32 Num;
+
+ u8 Cnt;
+ u32 DstAddr;
+ u16 TimerReload;
+ u32 Length;
+
+ u32 Timer;
+ s32 Pos;
+
+ void SetCnt(u8 val)
+ {
+ if ((val & 0x80) && !(Cnt & 0x80))
+ Start();
+
+ val &= 0x8F;
+ if (!(val & 0x80)) val &= ~0x01;
+ Cnt = val;
+ }
+
+ void SetDstAddr(u32 val) { DstAddr = val & 0x07FFFFFC; }
+ void SetTimerReload(u32 val) { TimerReload = val & 0xFFFF; }
+ void SetLength(u32 val) { Length = val << 2; if (Length == 0) Length = 4; }
+
+ void Start() { Timer = TimerReload; }
+
+ void Run(s32 sample);
};
}