diff options
author | StapleButter <thetotalworm@gmail.com> | 2017-04-08 22:59:27 +0200 |
---|---|---|
committer | StapleButter <thetotalworm@gmail.com> | 2017-04-08 22:59:27 +0200 |
commit | 3f3b2977d7decf8f691f5f999ae9b08a6d214bc9 (patch) | |
tree | 26effef45222d0aec60d373147281ea432e2d0c9 | |
parent | a4f436f8279477e683e4155bd280e19dff9d3f91 (diff) |
* sound capture from left/right mixers
* support for appropriate output modes
-rw-r--r-- | src/GPU3D.cpp | 1 | ||||
-rw-r--r-- | src/SPU.cpp | 313 | ||||
-rw-r--r-- | src/SPU.h | 50 |
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); } } @@ -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); }; } |