* sound capture from left/right mixers

* support for appropriate output modes
This commit is contained in:
StapleButter 2017-04-08 22:59:27 +02:00
parent a4f436f827
commit 3f3b2977d7
3 changed files with 321 additions and 43 deletions

View File

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

View File

@ -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;
u32 mixermask = 0xFFFF;
if (Cnt & (1<<12)) mixermask &= ~(1<<1);
if (Cnt & (1<<13)) mixermask &= ~(1<<3);
// TODO: what happens if we use type 3 on channels 0-7??
switch ((chan->Cnt >> 29) & 0x3)
for (int i = 0; i < 16; i++)
{
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);
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;
}
}
// sound capture
// TODO: other sound capture sources, along with their bugs
if (Capture[0]->Cnt & (1<<7))
{
for (u32 s = 0; s < samples; s++)
{
s32 val = leftbuf[s];
val >>= 8;
if (val < -0x8000) val = -0x8000;
else if (val > 0x7FFF) val = 0x7FFF;
Capture[0]->Run(val);
if (!((Capture[0]->Cnt & (1<<7)))) break;
}
}
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;
}
for (u32 s = 0; s < samples; s++)
switch (Cnt & 0x0C00)
{
s32 val = (s32)channelbuf[s];
val <<= chan->VolumeShift;
val *= chan->Volume;
s32 l = ((s64)val * (128-chan->Pan)) >> 10;
s32 r = ((s64)val * chan->Pan) >> 10;
leftbuf[s] += l;
rightbuf[s] += r;
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);
}
}

View File

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