Add sound output to sound card while dumping is occurring. Uses a proxy ISoundProvider (DualSound.cs) to guarantee emulation sound core and dumping routine are unaffected, while sending "best effort" sound to system output. Doesn't sound great, but simple and functional.
This commit is contained in:
parent
35a9edc018
commit
a313d3910d
|
@ -295,6 +295,7 @@
|
|||
<Compile Include="Properties\svnrev.cs" />
|
||||
<Compile Include="QuickCollections.cs" />
|
||||
<Compile Include="Sound\CDAudio.cs" />
|
||||
<Compile Include="Sound\Utilities\DualSound.cs" />
|
||||
<Compile Include="Sound\Utilities\Equalizer.cs" />
|
||||
<Compile Include="Sound\YM2612.IO.cs" />
|
||||
<Compile Include="Sound\YM2612.Channel.cs" />
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Sound.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// provides pass-through sound for the dumping tool to use, while making a "best effort"
|
||||
/// to have something available for audio output
|
||||
/// </summary>
|
||||
public class DualSound : ISoundProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of a "slave" ISoundProvider that recieves best effort audio
|
||||
/// </summary>
|
||||
class SecondPin : ISoundProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// the source to draw from
|
||||
/// </summary>
|
||||
DualSound master;
|
||||
|
||||
public SecondPin(DualSound master)
|
||||
{
|
||||
this.master = master;
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < Math.Min(samples.Length, master.ringbuffer.Count); i++)
|
||||
samples[i] = master.ringbuffer.Dequeue();
|
||||
for (; i < samples.Length; i++)
|
||||
// underflow
|
||||
samples[i] = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
master.ringbuffer.Clear();
|
||||
}
|
||||
|
||||
public int MaxVolume
|
||||
{
|
||||
// ignored
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// original input source
|
||||
/// </summary>
|
||||
ISoundProvider input;
|
||||
|
||||
/// <summary>
|
||||
/// threshold at which to discard samples
|
||||
/// </summary>
|
||||
int killsize;
|
||||
|
||||
/// <summary>
|
||||
/// storage of samples waiting to go to second pin
|
||||
/// </summary>
|
||||
Queue<short> ringbuffer;
|
||||
|
||||
/// <summary>
|
||||
/// get the slave pin
|
||||
/// </summary>
|
||||
public ISoundProvider secondpin
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// default constructor
|
||||
/// </summary>
|
||||
/// <param name="input">the ISoundProvider to use as input</param>
|
||||
/// <param name="buffsize">how many sample pairs to save for the second pin</param>
|
||||
public DualSound(ISoundProvider input, int buffsize)
|
||||
{
|
||||
this.input = input;
|
||||
killsize = buffsize * 2;
|
||||
ringbuffer = new Queue<short>(killsize);
|
||||
secondpin = new SecondPin(this);
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
input.GetSamples(samples);
|
||||
if (ringbuffer.Count >= killsize)
|
||||
ringbuffer.Clear();
|
||||
foreach (var sample in samples)
|
||||
ringbuffer.Enqueue(sample);
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
throw new Exception("Dumpers should never discard samples!");
|
||||
}
|
||||
|
||||
public int MaxVolume
|
||||
{
|
||||
get { return input.MaxVolume; }
|
||||
set { input.MaxVolume = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,11 @@ namespace BizHawk.MultiClient
|
|||
|
||||
//avi/wav state
|
||||
IVideoWriter CurrAviWriter = null;
|
||||
/// <summary>
|
||||
/// an audio proxy used for dumping
|
||||
/// </summary>
|
||||
Emulation.Sound.Utilities.DualSound DumpProxy = null;
|
||||
|
||||
|
||||
//runloop control
|
||||
bool exit;
|
||||
|
@ -1874,8 +1879,9 @@ namespace BizHawk.MultiClient
|
|||
//TODO - this will stray over time! have AviWriter keep an accumulation!
|
||||
int samples = (int)(44100 / Global.Emulator.CoreOutputComm.VsyncRate);
|
||||
short[] temp = new short[samples * 2];
|
||||
Global.Emulator.SoundProvider.GetSamples(temp);
|
||||
genSound = false;
|
||||
//Global.Emulator.SoundProvider.GetSamples(temp);
|
||||
DumpProxy.GetSamples(temp);
|
||||
//genSound = false;
|
||||
|
||||
CurrAviWriter.AddFrame(Global.Emulator.VideoProvider);
|
||||
CurrAviWriter.AddSamples(temp);
|
||||
|
@ -1891,10 +1897,16 @@ namespace BizHawk.MultiClient
|
|||
PressFrameAdvance = false;
|
||||
}
|
||||
|
||||
if (genSound)
|
||||
Global.Sound.UpdateSound(Global.Emulator.SoundProvider);
|
||||
else
|
||||
Global.Sound.UpdateSound(NullSound.SilenceProvider);
|
||||
if (genSound)
|
||||
{
|
||||
// change audio path if dumping is occuring
|
||||
if (DumpProxy != null)
|
||||
Global.Sound.UpdateSound(DumpProxy.secondpin);
|
||||
else
|
||||
Global.Sound.UpdateSound(Global.Emulator.SoundProvider);
|
||||
}
|
||||
else
|
||||
Global.Sound.UpdateSound(NullSound.SilenceProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -2704,16 +2716,23 @@ namespace BizHawk.MultiClient
|
|||
aw.Dispose();
|
||||
throw;
|
||||
}
|
||||
// buffersize here is entirely guess
|
||||
DumpProxy = new Emulation.Sound.Utilities.DualSound(Global.Emulator.SoundProvider, 8192);
|
||||
}
|
||||
|
||||
public void StopAVI()
|
||||
{
|
||||
if (CurrAviWriter == null) return;
|
||||
if (CurrAviWriter == null)
|
||||
{
|
||||
DumpProxy = null;
|
||||
return;
|
||||
}
|
||||
CurrAviWriter.CloseFile();
|
||||
CurrAviWriter = null;
|
||||
Global.OSD.AddMessage("AVI capture stopped");
|
||||
AVIStatusLabel.Image = BizHawk.MultiClient.Properties.Resources.Blank;
|
||||
AVIStatusLabel.ToolTipText = "";
|
||||
DumpProxy = null; // return to normal sound output
|
||||
}
|
||||
|
||||
private void SwapBackupSavestate(string path)
|
||||
|
|
Loading…
Reference in New Issue