using BizHawk.Emulation.Common; using System; using System.Collections.Generic; using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// 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, their buffer will be truncated/expanded) /// 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 SoundProviders; public SoundProviderMixer(params ISoundProvider[] soundProviders) { SoundProviders = new List(); 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(); 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 (sp.Buffer[i] > sp.MaxVolume) sectorVal += (short)sp.MaxVolume; else sectorVal += sp.Buffer[i]; } samples[i] = sectorVal; } } #endregion } }