BizHawk/BizHawk.Emulation.Cores/Computers/AmstradCPC/SoundProviderMixer.cs

218 lines
6.7 KiB
C#

using BizHawk.Emulation.Common;
using System;
using System.Collections.Generic;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider
/// Currently only supports SyncSoundMode.Sync
/// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882)
/// (if not, only 882 samples of their buffer will be used)
/// </summary>
internal sealed class SoundProviderMixer : ISoundProvider
{
private class Provider
{
public ISoundProvider SoundProvider { get; set; }
public string ProviderDescription { get; set; }
public int MaxVolume { get; set; }
public short[] Buffer { get; set; }
public int NSamp { get; set; }
}
private bool _stereo = true;
public bool Stereo
{
get { return _stereo; }
set { _stereo = value; }
}
private readonly List<Provider> SoundProviders;
public SoundProviderMixer(params ISoundProvider[] soundProviders)
{
SoundProviders = new List<Provider>();
foreach (var s in soundProviders)
{
SoundProviders.Add(new Provider
{
SoundProvider = s,
MaxVolume = short.MaxValue,
});
}
EqualizeVolumes();
}
public SoundProviderMixer(short maxVolume, string description, params ISoundProvider[] soundProviders)
{
SoundProviders = new List<Provider>();
foreach (var s in soundProviders)
{
SoundProviders.Add(new Provider
{
SoundProvider = s,
MaxVolume = maxVolume,
ProviderDescription = description
});
}
EqualizeVolumes();
}
public void AddSource(ISoundProvider source, string description)
{
SoundProviders.Add(new Provider
{
SoundProvider = source,
MaxVolume = short.MaxValue,
ProviderDescription = description
});
EqualizeVolumes();
}
public void AddSource(ISoundProvider source, short maxVolume, string description)
{
SoundProviders.Add(new Provider
{
SoundProvider = source,
MaxVolume = maxVolume,
ProviderDescription = description
});
EqualizeVolumes();
}
public void DisableSource(ISoundProvider source)
{
var sp = SoundProviders.Where(a => a.SoundProvider == source);
if (sp.Count() == 1)
SoundProviders.Remove(sp.First());
else if (sp.Count() > 1)
foreach (var s in sp)
SoundProviders.Remove(s);
EqualizeVolumes();
}
public void EqualizeVolumes()
{
if (SoundProviders.Count < 1)
return;
int eachVolume = short.MaxValue / SoundProviders.Count;
foreach (var source in SoundProviders)
{
source.MaxVolume = eachVolume;
}
}
#region ISoundProvider
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
foreach (var soundSource in SoundProviders)
{
soundSource.SoundProvider.DiscardSamples();
}
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = null;
nsamp = 0;
// get samples from all the providers
foreach (var sp in SoundProviders)
{
int sampCount;
short[] samp;
sp.SoundProvider.GetSamplesSync(out samp, out sampCount);
sp.NSamp = sampCount;
sp.Buffer = samp;
}
// are all the sample lengths the same?
var firstEntry = SoundProviders.First();
bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp);
if (!sameCount)
{
// this is a bit hacky, really all ISoundProviders should be supplying 44100 with 882 samples per frame.
// we will make sure this happens (no matter how it sounds)
if (SoundProviders.Count > 1)
{
for (int i = 0; i < SoundProviders.Count; i++)
{
int ns = SoundProviders[i].NSamp;
short[] buff = new short[882 * 2];
for (int b = 0; b < 882 * 2; b++)
{
if (b == SoundProviders[i].Buffer.Length - 1)
{
// end of source buffer
break;
}
buff[b] = SoundProviders[i].Buffer[b];
}
// save back to the soundprovider
SoundProviders[i].NSamp = 882;
SoundProviders[i].Buffer = buff;
}
}
else
{
// just process what we have as-is
}
}
// mix the soundproviders together
nsamp = 882;
samples = new short[nsamp * 2];
for (int i = 0; i < samples.Length; i++)
{
short sectorVal = 0;
foreach (var sp in SoundProviders)
{
if (i < sp.Buffer.Length)
{
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal += (short)sp.MaxVolume;
else
sectorVal += sp.Buffer[i];
}
}
samples[i] = sectorVal;
}
}
#endregion
}
}