cleanup IAsyncSoundProvider hacks and consolidate code that is only used for PCE
This commit is contained in:
parent
b16684b4c7
commit
976ea4967b
|
@ -1,15 +1,16 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Components;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.PCEngine
|
||||
{
|
||||
public sealed class ADPCM : IMixedSoundProvider
|
||||
{
|
||||
ScsiCDBus SCSI;
|
||||
PCEngine pce;
|
||||
MetaspuSoundProvider SoundProvider = new MetaspuSoundProvider();
|
||||
private readonly ScsiCDBus _scsi;
|
||||
private readonly PCEngine _pce;
|
||||
private readonly VecnaSynchronizer _synchronizer = new VecnaSynchronizer();
|
||||
|
||||
// ***************************************************************************
|
||||
|
||||
|
@ -60,8 +61,8 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
|
||||
public ADPCM(PCEngine pcEngine, ScsiCDBus scsi)
|
||||
{
|
||||
pce = pcEngine;
|
||||
SCSI = scsi;
|
||||
_pce = pcEngine;
|
||||
_scsi = scsi;
|
||||
MaxVolume = 24576;
|
||||
}
|
||||
|
||||
|
@ -156,24 +157,24 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
|
||||
if (AdpcmCdDmaRequested)
|
||||
{
|
||||
if (SCSI.REQ && SCSI.IO && !SCSI.CD && !SCSI.ACK)
|
||||
if (_scsi.REQ && _scsi.IO && !_scsi.CD && !_scsi.ACK)
|
||||
{
|
||||
byte dmaByte = SCSI.DataBits;
|
||||
byte dmaByte = _scsi.DataBits;
|
||||
RAM[WriteAddress++] = dmaByte;
|
||||
AdpcmLength++;
|
||||
|
||||
SCSI.ACK = false;
|
||||
SCSI.REQ = false;
|
||||
SCSI.Think();
|
||||
_scsi.ACK = false;
|
||||
_scsi.REQ = false;
|
||||
_scsi.Think();
|
||||
}
|
||||
|
||||
if (SCSI.DataTransferInProgress == false)
|
||||
if (_scsi.DataTransferInProgress == false)
|
||||
Port180B = 0;
|
||||
}
|
||||
|
||||
pce.IntADPCM = HalfReached;
|
||||
pce.IntStop = EndReached;
|
||||
pce.RefreshIRQ2();
|
||||
_pce.IntADPCM = HalfReached;
|
||||
_pce.IntStop = EndReached;
|
||||
_pce.RefreshIRQ2();
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
|
@ -281,7 +282,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
private void AdpcmEmitSample()
|
||||
{
|
||||
if (AdpcmIsPlaying == false)
|
||||
SoundProvider.Buffer.EnqueueSample(0, 0);
|
||||
_synchronizer.EnqueueSample(0, 0);
|
||||
else
|
||||
{
|
||||
if (nextSampleTimer <= 0)
|
||||
|
@ -301,21 +302,37 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
}
|
||||
|
||||
short adjustedSample = (short)((playingSample - 2048) * MaxVolume / 2048);
|
||||
SoundProvider.Buffer.EnqueueSample(adjustedSample, adjustedSample);
|
||||
_synchronizer.EnqueueSample(adjustedSample, adjustedSample);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
SoundProvider.GetSamplesAsync(samples);
|
||||
_synchronizer.OutputSamples(samples, samples.Length / 2);
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
SoundProvider.DiscardSamples();
|
||||
_synchronizer.Clear();
|
||||
}
|
||||
|
||||
public int MaxVolume { get; set; }
|
||||
public bool CanProvideAsync => true;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode != SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotImplementedException("Only async currently supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Async;
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
throw new NotImplementedException("Sync sound not yet supported");
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
|
||||
|
@ -351,9 +368,9 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
if (ser.IsReader)
|
||||
{
|
||||
Port180E = port180E;
|
||||
pce.IntADPCM = HalfReached;
|
||||
pce.IntStop = EndReached;
|
||||
pce.RefreshIRQ2();
|
||||
_pce.IntADPCM = HalfReached;
|
||||
_pce.IntStop = EndReached;
|
||||
_pce.RefreshIRQ2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
Cpu = new HuC6280(MemoryCallbacks);
|
||||
VCE = new VCE();
|
||||
VDC1 = new VDC(this, Cpu, VCE);
|
||||
PSG = new HuC6280PSG();
|
||||
PSG = new HuC6280PSG(735);
|
||||
SCSI = new ScsiCDBus(this, disc);
|
||||
|
||||
Cpu.Logger = s => Tracer.Put(s);
|
||||
|
@ -166,7 +166,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
Cpu.ReadMemory21 = ReadMemory;
|
||||
Cpu.WriteMemory21 = WriteMemory;
|
||||
Cpu.WriteVDC = VDC1.WriteVDC;
|
||||
_soundProvider = new FakeSyncSound(PSG, 735);
|
||||
_soundProvider = PSG;
|
||||
CDAudio = new CDAudio(null, 0);
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
Cpu.ReadMemory21 = ReadMemorySGX;
|
||||
Cpu.WriteMemory21 = WriteMemorySGX;
|
||||
Cpu.WriteVDC = VDC1.WriteVDC;
|
||||
_soundProvider = new FakeSyncSound(PSG, 735);
|
||||
_soundProvider = PSG;
|
||||
CDAudio = new CDAudio(null, 0);
|
||||
}
|
||||
|
||||
|
@ -193,9 +193,9 @@ namespace BizHawk.Emulation.Cores.PCEngine
|
|||
CDAudio = new CDAudio(disc);
|
||||
SetCDAudioCallback();
|
||||
PSG.MaxVolume = short.MaxValue * 3 / 4;
|
||||
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM);
|
||||
_soundProvider = new FakeSyncSound(SoundMixer, 735);
|
||||
Cpu.ThinkAction = (cycles) => { SCSI.Think(); ADPCM.Think(cycles); };
|
||||
SoundMixer = new SoundMixer(735, PSG, CDAudio, ADPCM);
|
||||
_soundProvider = SoundMixer;
|
||||
Cpu.ThinkAction = cycles => { SCSI.Think(); ADPCM.Think(cycles); };
|
||||
}
|
||||
|
||||
if (rom.Length == 0x60000)
|
||||
|
|
|
@ -1,127 +1,114 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public interface ISynchronizingAudioBuffer
|
||||
{
|
||||
void EnqueueSample(short left, short right);
|
||||
void Clear();
|
||||
|
||||
// returns the number of samples actually supplied, which may not match the number requested
|
||||
// ^^ what the hell is that supposed to mean.
|
||||
// the entire point of an ISynchronizingAudioBuffer
|
||||
// is to provide exact amounts of output samples,
|
||||
// even when the input provided varies....
|
||||
int OutputSamples(short[] buf, int samplesRequested);
|
||||
}
|
||||
|
||||
public class VecnaSynchronizer : ISynchronizingAudioBuffer
|
||||
{
|
||||
// vecna's attempt at a fully synchronous sound provider.
|
||||
// It's similar in philosophy to my "BufferedAsync" provider, but BufferedAsync is not
|
||||
// fully synchronous.
|
||||
|
||||
// Like BufferedAsync, it tries to make most frames 100% correct and just suck it up
|
||||
// periodically and have a big bad-sounding mistake frame if it has to.
|
||||
|
||||
// It is significantly less ambitious and elaborate than the other methods.
|
||||
// We'll see if it works better or not!
|
||||
|
||||
// It has a min and maximum amount of excess buffer to deal with minor overflows.
|
||||
// When fast-forwarding, it will discard samples above the maximum excess buffer.
|
||||
|
||||
// When underflowing, it will attempt to resample to a certain thresh
|
||||
// old.
|
||||
// If it underflows beyond that threshold, it will give up and output silence.
|
||||
// Since it has done this, it will go ahead and generate some excess silence in order
|
||||
// to restock its excess buffer.
|
||||
private struct Sample
|
||||
{
|
||||
public readonly short Left;
|
||||
public readonly short Right;
|
||||
|
||||
public Sample(short left, short right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
}
|
||||
|
||||
private const int MaxExcessSamples = 2048;
|
||||
|
||||
private readonly Queue<Sample> _buffer;
|
||||
private readonly Sample[] _resampleBuffer;
|
||||
|
||||
public VecnaSynchronizer()
|
||||
{
|
||||
_buffer = new Queue<Sample>(2048);
|
||||
_resampleBuffer = new Sample[2730]; // 2048 * 1.25
|
||||
|
||||
// Give us a little buffer wiggle-room
|
||||
for (int i = 0; i < 367; i++)
|
||||
{
|
||||
_buffer.Enqueue(new Sample(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void EnqueueSample(short left, short right)
|
||||
{
|
||||
if (_buffer.Count >= MaxExcessSamples - 1)
|
||||
{
|
||||
// if buffer is overfull, dequeue old samples to make room for new samples.
|
||||
_buffer.Dequeue();
|
||||
}
|
||||
|
||||
_buffer.Enqueue(new Sample(left, right));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
public int OutputSamples(short[] buf, int samplesRequested)
|
||||
{
|
||||
if (samplesRequested > _buffer.Count)
|
||||
{
|
||||
// underflow!
|
||||
if (_buffer.Count > samplesRequested * 3 / 4)
|
||||
{
|
||||
// if we're within 75% of target, then I guess we suck it up and resample.
|
||||
// we sample in a goofy way, we could probably do it a bit smarter, if we cared more.
|
||||
int samplesAvailable = _buffer.Count;
|
||||
for (int i = 0; _buffer.Count > 0; i++)
|
||||
{
|
||||
_resampleBuffer[i] = _buffer.Dequeue();
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < samplesRequested; i++)
|
||||
{
|
||||
Sample sample = _resampleBuffer[i * samplesAvailable / samplesRequested];
|
||||
buf[index++] += sample.Left;
|
||||
buf[index++] += sample.Right;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're outside of a "reasonable" underflow. Give up and output silence.
|
||||
// Do nothing. The whole frame will be excess buffer.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal operation
|
||||
int index = 0;
|
||||
for (int i = 0; i < samplesRequested && _buffer.Count > 0; i++)
|
||||
{
|
||||
Sample sample = _buffer.Dequeue();
|
||||
buf[index++] += sample.Left;
|
||||
buf[index++] += sample.Right;
|
||||
}
|
||||
}
|
||||
|
||||
return samplesRequested;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components
|
||||
{
|
||||
public class VecnaSynchronizer
|
||||
{
|
||||
// vecna's attempt at a fully synchronous sound provider.
|
||||
// It's similar in philosophy to my "BufferedAsync" provider, but BufferedAsync is not
|
||||
// fully synchronous.
|
||||
|
||||
// Like BufferedAsync, it tries to make most frames 100% correct and just suck it up
|
||||
// periodically and have a big bad-sounding mistake frame if it has to.
|
||||
|
||||
// It is significantly less ambitious and elaborate than the other methods.
|
||||
// We'll see if it works better or not!
|
||||
|
||||
// It has a min and maximum amount of excess buffer to deal with minor overflows.
|
||||
// When fast-forwarding, it will discard samples above the maximum excess buffer.
|
||||
|
||||
// When underflowing, it will attempt to resample to a certain thresh
|
||||
// old.
|
||||
// If it underflows beyond that threshold, it will give up and output silence.
|
||||
// Since it has done this, it will go ahead and generate some excess silence in order
|
||||
// to restock its excess buffer.
|
||||
private struct Sample
|
||||
{
|
||||
public readonly short Left;
|
||||
public readonly short Right;
|
||||
|
||||
public Sample(short left, short right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
}
|
||||
|
||||
private const int MaxExcessSamples = 2048;
|
||||
|
||||
private readonly Queue<Sample> _buffer;
|
||||
private readonly Sample[] _resampleBuffer;
|
||||
|
||||
public VecnaSynchronizer()
|
||||
{
|
||||
_buffer = new Queue<Sample>(2048);
|
||||
_resampleBuffer = new Sample[2730]; // 2048 * 1.25
|
||||
|
||||
// Give us a little buffer wiggle-room
|
||||
for (int i = 0; i < 367; i++)
|
||||
{
|
||||
_buffer.Enqueue(new Sample(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void EnqueueSample(short left, short right)
|
||||
{
|
||||
if (_buffer.Count >= MaxExcessSamples - 1)
|
||||
{
|
||||
// if buffer is overfull, dequeue old samples to make room for new samples.
|
||||
_buffer.Dequeue();
|
||||
}
|
||||
|
||||
_buffer.Enqueue(new Sample(left, right));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
public int OutputSamples(short[] buf, int samplesRequested)
|
||||
{
|
||||
if (samplesRequested > _buffer.Count)
|
||||
{
|
||||
// underflow!
|
||||
if (_buffer.Count > samplesRequested * 3 / 4)
|
||||
{
|
||||
// if we're within 75% of target, then I guess we suck it up and resample.
|
||||
// we sample in a goofy way, we could probably do it a bit smarter, if we cared more.
|
||||
int samplesAvailable = _buffer.Count;
|
||||
for (int i = 0; _buffer.Count > 0; i++)
|
||||
{
|
||||
_resampleBuffer[i] = _buffer.Dequeue();
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < samplesRequested; i++)
|
||||
{
|
||||
Sample sample = _resampleBuffer[i * samplesAvailable / samplesRequested];
|
||||
buf[index++] += sample.Left;
|
||||
buf[index++] += sample.Right;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're outside of a "reasonable" underflow. Give up and output silence.
|
||||
// Do nothing. The whole frame will be excess buffer.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal operation
|
||||
int index = 0;
|
||||
for (int i = 0; i < samplesRequested && _buffer.Count > 0; i++)
|
||||
{
|
||||
Sample sample = _buffer.Dequeue();
|
||||
buf[index++] += sample.Left;
|
||||
buf[index++] += sample.Right;
|
||||
}
|
||||
}
|
||||
|
||||
return samplesRequested;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
||||
// The state of the cd player is quantized to the frame level.
|
||||
|
@ -139,7 +140,23 @@ namespace BizHawk.Emulation.Cores.Components
|
|||
}
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
public bool CanProvideAsync => true;
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode != SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotImplementedException("Only async currently supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Async;
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
throw new NotImplementedException("Sync sound not yet supported");
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
if (Mode != CDAudioMode_Playing)
|
||||
return;
|
||||
|
|
|
@ -11,8 +11,10 @@ namespace BizHawk.Emulation.Cores.Components
|
|||
// It is embedded on the CPU and doesn't have its own part number. None the less, it is emulated separately from the 6280 CPU.
|
||||
|
||||
// Sound refactor TODO: IMixedSoundProvider must inherit ISoundProvider
|
||||
public sealed class HuC6280PSG : IMixedSoundProvider
|
||||
// TODo: this provides "fake" sync sound by hardcoding the number of samples
|
||||
public sealed class HuC6280PSG : ISoundProvider, IMixedSoundProvider
|
||||
{
|
||||
private readonly int _spf;
|
||||
public class PSGChannel
|
||||
{
|
||||
public ushort Frequency;
|
||||
|
@ -44,11 +46,11 @@ namespace BizHawk.Emulation.Cores.Components
|
|||
|
||||
public byte MainVolumeLeft;
|
||||
public byte MainVolumeRight;
|
||||
public int MaxVolume { get; set; }
|
||||
public int MaxVolume { get; set; } = short.MaxValue;
|
||||
|
||||
public HuC6280PSG()
|
||||
public HuC6280PSG(int spf)
|
||||
{
|
||||
MaxVolume = short.MaxValue;
|
||||
_spf = spf;
|
||||
Waves.InitWaves();
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
|
@ -147,7 +149,25 @@ namespace BizHawk.Emulation.Cores.Components
|
|||
|
||||
public void DiscardSamples() { }
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
public bool CanProvideAsync => true;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode) => SyncMode = mode;
|
||||
public SyncSoundMode SyncMode { get; private set; }
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
if (SyncMode != SyncSoundMode.Sync)
|
||||
{
|
||||
throw new InvalidOperationException("Must be in sync mode to call a sync method");
|
||||
}
|
||||
|
||||
short[] ret = new short[_spf * 2];
|
||||
GetSamplesAsync(ret);
|
||||
samples = ret;
|
||||
nsamp = _spf;
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
int elapsedCycles = (int)(frameStopTime - frameStartTime);
|
||||
int start = 0;
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface is for legacy sound implementations in some older cores
|
||||
/// This needs to go away, but is provided here, for now
|
||||
/// </summary>
|
||||
internal interface IAsyncSoundProvider
|
||||
{
|
||||
void GetSamples(short[] samples);
|
||||
void DiscardSamples();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO: this is a shim for now, and needs to go away
|
||||
/// turns an <seealso cref="IAsyncSoundProvider"/> into a full ISoundProvider
|
||||
/// This is used in cores that have an async only sound implementation
|
||||
/// better is implement a sync sound option in those cores without the need for
|
||||
/// this class or an IAsyncSoundProvider interface
|
||||
/// </summary>
|
||||
internal class FakeSyncSound : ISoundProvider
|
||||
{
|
||||
private readonly IAsyncSoundProvider _source;
|
||||
private readonly int _spf;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FakeSyncSound"/> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The async sound provider</param>
|
||||
/// <param name="spf">number of sample pairs to request and provide on each GetSamples() call</param>
|
||||
public FakeSyncSound(IAsyncSoundProvider source, int spf)
|
||||
{
|
||||
_source = source;
|
||||
_spf = spf;
|
||||
SyncMode = SyncSoundMode.Sync;
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
if (SyncMode != SyncSoundMode.Sync)
|
||||
{
|
||||
throw new InvalidOperationException("Must be in sync mode to call a sync method");
|
||||
}
|
||||
|
||||
short[] ret = new short[_spf * 2];
|
||||
_source.GetSamples(ret);
|
||||
samples = ret;
|
||||
nsamp = _spf;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
_source.DiscardSamples();
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
if (SyncMode != SyncSoundMode.Async)
|
||||
{
|
||||
throw new InvalidOperationException("Must be in async mode to call an async method");
|
||||
}
|
||||
|
||||
_source.GetSamples(samples);
|
||||
}
|
||||
|
||||
public bool CanProvideAsync => true;
|
||||
|
||||
public SyncSoundMode SyncMode { get; private set; }
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
SyncMode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
// An async sound provider
|
||||
// This class needs to go away, it takes an IAsyncSoundProvider
|
||||
// and is only used by legacy sound implementations
|
||||
internal class MetaspuSoundProvider : ISoundProvider
|
||||
{
|
||||
public ISynchronizingAudioBuffer Buffer { get; } = new VecnaSynchronizer();
|
||||
|
||||
public bool CanProvideAsync => true;
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Async;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode != SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Only Async mode is supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
throw new InvalidOperationException("Sync mode is not supported.");
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
Buffer.OutputSamples(samples, samples.Length / 2);
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
Buffer.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components
|
||||
{
|
||||
// TODO: Sound mixer is a good concept, but it needs to be refactored to use an ISoundProvider, it perhaps can enforce only receiving providers in Async mode
|
||||
// TODO: Sound mixer is a good concept, but it needs to support sync mode
|
||||
/// <summary>
|
||||
/// An interface that extends a sound provider to provide mixing capabilities through the SoundMixer class
|
||||
/// </summary>
|
||||
internal interface IMixedSoundProvider : IAsyncSoundProvider
|
||||
internal interface IMixedSoundProvider : ISoundProvider
|
||||
{
|
||||
int MaxVolume { get; set; }
|
||||
}
|
||||
|
||||
// This is a straightforward class to mix/chain multiple ISoundProvider sources.
|
||||
internal sealed class SoundMixer : IAsyncSoundProvider
|
||||
// Relies on a hack of passing in the samples per frame for sync sound abilities
|
||||
internal sealed class SoundMixer : ISoundProvider
|
||||
{
|
||||
private readonly int _spf;
|
||||
private readonly List<IMixedSoundProvider> _soundProviders;
|
||||
|
||||
public SoundMixer(params IMixedSoundProvider[] soundProviders)
|
||||
public SoundMixer(int spf, params IMixedSoundProvider[] soundProviders)
|
||||
{
|
||||
if (soundProviders.Any(s => !s.CanProvideAsync))
|
||||
{
|
||||
throw new InvalidOperationException("Sound mixer only works with async sound currently");
|
||||
}
|
||||
|
||||
_soundProviders = new List<IMixedSoundProvider>(soundProviders);
|
||||
_spf = spf;
|
||||
}
|
||||
|
||||
public void DisableSource(IMixedSoundProvider source)
|
||||
|
@ -34,11 +45,29 @@ namespace BizHawk.Emulation.Cores.Components
|
|||
}
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
public bool CanProvideAsync => true;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode) => SyncMode = mode;
|
||||
public SyncSoundMode SyncMode { get; private set; }
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
if (SyncMode != SyncSoundMode.Sync)
|
||||
{
|
||||
throw new InvalidOperationException("Must be in sync mode to call a sync method");
|
||||
}
|
||||
|
||||
short[] ret = new short[_spf * 2];
|
||||
GetSamplesAsync(ret);
|
||||
samples = ret;
|
||||
nsamp = _spf;
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
foreach (var soundSource in _soundProviders)
|
||||
{
|
||||
soundSource.GetSamples(samples);
|
||||
soundSource.GetSamplesAsync(samples);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue