ZXHawk: Better SoundProviderMixer implementation

This commit is contained in:
Asnivor 2018-05-09 11:31:13 +01:00
parent 828dbba1a5
commit 2f513a3ed5
2 changed files with 43 additions and 117 deletions

View File

@ -8,13 +8,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary> /// <summary>
/// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider /// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider
/// Currently only supports SyncSoundMode.Sync /// Currently only supports SyncSoundMode.Sync
/// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length /// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882)
/// (if not, their buffer will be truncated/expanded)
/// </summary> /// </summary>
internal sealed class SoundProviderMixer : ISoundProvider internal sealed class SoundProviderMixer : ISoundProvider
{ {
private class Provider private class Provider
{ {
public ISoundProvider SoundProvider { get; set; } public ISoundProvider SoundProvider { get; set; }
public string ProviderDescription { get; set; }
public int MaxVolume { get; set; } public int MaxVolume { get; set; }
public short[] Buffer { get; set; } public short[] Buffer { get; set; }
public int NSamp { get; set; } public int NSamp { get; set; }
@ -45,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
EqualizeVolumes(); EqualizeVolumes();
} }
public SoundProviderMixer(short maxVolume, params ISoundProvider[] soundProviders) public SoundProviderMixer(short maxVolume, string description, params ISoundProvider[] soundProviders)
{ {
SoundProviders = new List<Provider>(); SoundProviders = new List<Provider>();
@ -55,29 +57,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
SoundProvider = s, SoundProvider = s,
MaxVolume = maxVolume, MaxVolume = maxVolume,
ProviderDescription = description
}); });
} }
EqualizeVolumes(); EqualizeVolumes();
} }
public void AddSource(ISoundProvider source) public void AddSource(ISoundProvider source, string description)
{ {
SoundProviders.Add(new Provider SoundProviders.Add(new Provider
{ {
SoundProvider = source, SoundProvider = source,
MaxVolume = short.MaxValue MaxVolume = short.MaxValue,
ProviderDescription = description
}); });
EqualizeVolumes(); EqualizeVolumes();
} }
public void AddSource(ISoundProvider source, short maxVolume) public void AddSource(ISoundProvider source, short maxVolume, string description)
{ {
SoundProviders.Add(new Provider SoundProviders.Add(new Provider
{ {
SoundProvider = source, SoundProvider = source,
MaxVolume = maxVolume MaxVolume = maxVolume,
ProviderDescription = description
}); });
EqualizeVolumes(); EqualizeVolumes();
@ -150,134 +155,55 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
var firstEntry = SoundProviders.First(); var firstEntry = SoundProviders.First();
bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp); bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp);
if (!sameCount)
if (sameCount)
{ {
nsamp = firstEntry.NSamp; // this is a bit hacky, really all ISoundProviders should be supplying 44100 with 882 samples per frame.
samples = new short[nsamp * 2]; // we will make sure this happens (no matter how it sounds)
if (SoundProviders.Count > 1)
if (_stereo)
{ {
for (int i = 0; i < samples.Length; i++) for (int i = 0; i < SoundProviders.Count; i++)
{ {
short sectorVal = 0; int ns = SoundProviders[i].NSamp;
foreach (var sp in SoundProviders) short[] buff = new short[882 * 2];
for (int b = 0; b < 882 * 2; b++)
{ {
if (sp.Buffer[i] > sp.MaxVolume) if (b == SoundProviders[i].Buffer.Length - 1)
sectorVal += (short)sp.MaxVolume;
else
{ {
sectorVal += sp.Buffer[i]; // end of source buffer
break;
} }
buff[b] = SoundProviders[i].Buffer[b];
} }
samples[i] = sectorVal; // save back to the soundprovider
SoundProviders[i].NSamp = 882;
SoundProviders[i].Buffer = buff;
} }
} }
else else
{ {
// convert to mono // just process what we have as-is
for (int i = 0; i < samples.Length; i += 2)
{
short s = 0;
foreach (var sp in SoundProviders)
{
s += (short)((sp.Buffer[i] + sp.Buffer[i + 1]) / 2);
}
samples[i] = s;
samples[i + 1] = s;
}
} }
} }
else if (!sameCount) // mix the soundproviders together
nsamp = 882;
samples = new short[nsamp * 2];
for (int i = 0; i < samples.Length; i++)
{ {
// this is a pretty poor implementation that doesnt work very well short sectorVal = 0;
// ideally soundproviders should ensure that their number of samples is identical
int divisor = 1;
int highestCount = 0;
// get the lowest divisor of all the soundprovider nsamps
for (int d = 2; d < 999; d++)
{
bool divFound = false;
foreach (var sp in SoundProviders)
{
if (sp.NSamp > highestCount)
highestCount = sp.NSamp;
if (sp.NSamp % d == 0)
divFound = true;
else
divFound = false;
}
if (divFound)
{
divisor = d;
break;
}
}
// now we have the largest current number of samples among the providers
// along with a common divisor for all of them
nsamp = highestCount * divisor;
samples = new short[nsamp * 2];
// take a pass at populating the samples array for each provider
foreach (var sp in SoundProviders) foreach (var sp in SoundProviders)
{ {
short sectorVal = 0; if (sp.Buffer[i] > sp.MaxVolume)
int pos = 0; sectorVal += (short)sp.MaxVolume;
for (int i = 0; i < sp.Buffer.Length; i++) else
{ sectorVal += sp.Buffer[i];
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal = (short)sp.MaxVolume;
else
sectorVal = sp.Buffer[i];
for (int s = 0; s < divisor; s++)
{
samples[pos++] += sectorVal;
}
}
} }
/* samples[i] = sectorVal;
// get the highest number of samples
int max = SoundProviders.Aggregate((i, j) => i.Buffer.Length > j.Buffer.Length ? i : j).Buffer.Length;
nsamp = max;
samples = new short[nsamp * 2];
// take a pass at populating the samples array for each provider
foreach (var sp in SoundProviders)
{
short sectorVal = 0;
int pos = 0;
for (int i = 0; i < sp.Buffer.Length; i++)
{
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal = (short)sp.MaxVolume;
else
{
if (sp.SoundProvider is AY38912)
{
// boost audio
sectorVal += (short)(sp.Buffer[i] * 2);
}
else
{
sectorVal += sp.Buffer[i];
}
}
samples[pos++] += sectorVal;
}
}
*/
} }
} }

View File

@ -97,10 +97,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ser.Register<IVideoProvider>(_machine.ULADevice); ser.Register<IVideoProvider>(_machine.ULADevice);
// initialize sound mixer and attach the various ISoundProvider devices // initialize sound mixer and attach the various ISoundProvider devices
SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); SoundMixer = new SoundProviderMixer((int)(32767 / 10), "System Beeper", (ISoundProvider)_machine.BuzzerDevice);
SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer, "Tape Audio");
if (_machine.AYDevice != null) if (_machine.AYDevice != null)
SoundMixer.AddSource(_machine.AYDevice); SoundMixer.AddSource(_machine.AYDevice, "AY-3-3912");
// set audio device settings // set audio device settings
if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912)) if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912))