192 lines
5.4 KiB
C#
192 lines
5.4 KiB
C#
namespace GarboDev
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
public class SoundManager
|
|
{
|
|
private Memory memory = null;
|
|
private Queue<byte>[] soundQueue = new Queue<byte>[2];
|
|
private byte latchedA, latchedB;
|
|
private int frequency, cyclesPerSample;
|
|
private int leftover = 0;
|
|
|
|
private short[] soundBuffer = new short[40000];
|
|
private int soundBufferPos = 0;
|
|
private int lastSoundBufferPos = 0;
|
|
|
|
public SoundManager(Memory memory, int frequency)
|
|
{
|
|
this.Frequency = frequency;
|
|
|
|
this.memory = memory;
|
|
this.memory.SoundManager = this;
|
|
|
|
this.soundQueue[0] = new Queue<byte>(32);
|
|
this.soundQueue[1] = new Queue<byte>(32);
|
|
}
|
|
|
|
#region Public Properties
|
|
public int Frequency
|
|
{
|
|
get { return this.frequency; }
|
|
set
|
|
{
|
|
this.frequency = value;
|
|
this.cyclesPerSample = (GbaManager.cpuFreq << 5) / this.frequency;
|
|
}
|
|
}
|
|
|
|
public int QueueSizeA
|
|
{
|
|
get { return this.soundQueue[0].Count; }
|
|
}
|
|
|
|
public int QueueSizeB
|
|
{
|
|
get { return this.soundQueue[1].Count; }
|
|
}
|
|
|
|
public int SamplesMixed
|
|
{
|
|
get
|
|
{
|
|
int value = this.soundBufferPos - this.lastSoundBufferPos;
|
|
if (value < 0) value += this.soundBuffer.Length;
|
|
return value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
public void GetSamples(short[] buffer, int length)
|
|
{
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
if (this.lastSoundBufferPos == this.soundBuffer.Length)
|
|
{
|
|
this.lastSoundBufferPos = 0;
|
|
}
|
|
buffer[i] = this.soundBuffer[this.lastSoundBufferPos++];
|
|
}
|
|
}
|
|
|
|
public void Mix(int cycles)
|
|
{
|
|
ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H);
|
|
ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X);
|
|
|
|
cycles <<= 5;
|
|
cycles += this.leftover;
|
|
|
|
if (cycles > 0)
|
|
{
|
|
// Precompute loop invariants
|
|
short directA = (short)(sbyte)(this.latchedA);
|
|
short directB = (short)(sbyte)(this.latchedB);
|
|
|
|
if ((soundCntH & (1 << 2)) == 0)
|
|
{
|
|
directA >>= 1;
|
|
}
|
|
if ((soundCntH & (1 << 3)) == 0)
|
|
{
|
|
directB >>= 1;
|
|
}
|
|
|
|
while (cycles > 0)
|
|
{
|
|
short l = 0, r = 0;
|
|
|
|
cycles -= this.cyclesPerSample;
|
|
|
|
// Mixing
|
|
if ((soundCntX & (1 << 7)) != 0)
|
|
{
|
|
if ((soundCntH & (1 << 8)) != 0)
|
|
{
|
|
r += directA;
|
|
}
|
|
if ((soundCntH & (1 << 9)) != 0)
|
|
{
|
|
l += directA;
|
|
}
|
|
if ((soundCntH & (1 << 12)) != 0)
|
|
{
|
|
r += directB;
|
|
}
|
|
if ((soundCntH & (1 << 13)) != 0)
|
|
{
|
|
l += directB;
|
|
}
|
|
}
|
|
|
|
if (this.soundBufferPos == this.soundBuffer.Length)
|
|
{
|
|
this.soundBufferPos = 0;
|
|
}
|
|
|
|
this.soundBuffer[this.soundBufferPos++] = (short)(l << 6);
|
|
this.soundBuffer[this.soundBufferPos++] = (short)(r << 6);
|
|
}
|
|
}
|
|
|
|
this.leftover = cycles;
|
|
}
|
|
|
|
public void ResetFifoA()
|
|
{
|
|
this.soundQueue[0].Clear();
|
|
this.latchedA = 0;
|
|
}
|
|
|
|
public void ResetFifoB()
|
|
{
|
|
this.soundQueue[1].Clear();
|
|
this.latchedB = 0;
|
|
}
|
|
|
|
public void IncrementFifoA()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
this.EnqueueDSoundSample(0, this.memory.IORam[Memory.FIFO_A_L + i]);
|
|
}
|
|
}
|
|
|
|
public void IncrementFifoB()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
this.EnqueueDSoundSample(1, this.memory.IORam[Memory.FIFO_B_L + i]);
|
|
}
|
|
}
|
|
|
|
public void DequeueA()
|
|
{
|
|
if (this.soundQueue[0].Count > 0)
|
|
{
|
|
this.latchedA = this.soundQueue[0].Dequeue();
|
|
}
|
|
}
|
|
|
|
public void DequeueB()
|
|
{
|
|
if (this.soundQueue[1].Count > 0)
|
|
{
|
|
this.latchedB = this.soundQueue[1].Dequeue();
|
|
}
|
|
}
|
|
#endregion Public Methods
|
|
|
|
private void EnqueueDSoundSample(int channel, byte sample)
|
|
{
|
|
if (this.soundQueue[channel].Count < 32)
|
|
{
|
|
this.soundQueue[channel].Enqueue(sample);
|
|
}
|
|
}
|
|
}
|
|
}
|