Max volume is settable per sound source to enable mixing many sound sources without clipping. Potentially can be used to disable individual sound sources.

This commit is contained in:
beirich 2011-09-04 04:38:11 +00:00
parent ed7cc01d9f
commit 647cae698e
16 changed files with 151 additions and 117 deletions

View File

@ -923,6 +923,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
MetaspuSoundProvider metaspu = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
public int MaxVolume { get; set; } // not supported
void ISoundProvider.GetSamples(short[] samples)
{

View File

@ -3,27 +3,31 @@ using BizHawk.Emulation.Sound;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
public partial class PCEngine
public sealed class ADPCM
{
// herein I begin to realize the downside of prodigous use of partial class.
// too much stuff in one namespace.
// partial class still rocks though. you rock, partial class.
public ushort adpcm_io_address;
public ushort adpcm_read_address;
public ushort adpcm_write_address;
public ushort adpcm_length;
public int adpcm_read_timer, adpcm_write_timer;
public int adpcm_read_timer, adpcm_write_timer;
public byte adpcm_read_buffer, adpcm_write_buffer;
public bool adpcm_read_pending, adpcm_write_pending;
public byte[] RAM = new byte[0x10000];
public MetaspuSoundProvider SoundProvider = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
long LastThink;
float adpcm_playback_timer;
ScsiCDBus SCSI;
PCEngine pce;
public byte[] ADPCM_RAM;
public MetaspuSoundProvider ADPCM_Provider;
public ADPCM(PCEngine pcEngine, ScsiCDBus scsi)
{
pce = pcEngine;
SCSI = scsi;
}
static readonly int[] StepSize =
{
@ -47,7 +51,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public void AdpcmControlWrite(byte value)
{
Log.Error("CD","ADPCM CONTROL WRITE {0:X2}",value);
if ((CdIoPorts[0x0D] & 0x80) != 0 && (value & 0x80) == 0)
if ((Port180D & 0x80) != 0 && (value & 0x80) == 0)
{
Log.Note("CD", "Reset ADPCM!");
adpcm_read_address = 0;
@ -67,7 +71,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
adpcm_read_address--;
}
if ((CdIoPorts[0x0D] & 2) == 0 && (value & 2) != 0)
if ((Port180D & 2) == 0 && (value & 2) != 0)
{
adpcm_write_address = adpcm_io_address;
if ((value & 1) == 0)
@ -92,23 +96,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
adpcm_playback_timer = 0;
magnitude = 0;
}
}
public void AdpcmDataWrite(byte value)
{
adpcm_write_buffer = value;
adpcm_write_timer = 24;
adpcm_write_pending = true;
Port180D = value;
}
public byte AdpcmDataRead()
{
adpcm_read_pending = true;
adpcm_read_timer = 24;
return adpcm_read_buffer;
}
public bool AdpcmIsPlaying { get; private set; }
public bool AdpcmBusyWriting { get { return AdpcmCdDmaRequested; } }
public bool AdpcmBusyReading { get { return adpcm_read_pending; } }
@ -127,10 +118,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
byte sample;
if (nibble == false)
{
sample = (byte) (ADPCM_RAM[adpcm_read_address] >> 4);
sample = (byte) (RAM[adpcm_read_address] >> 4);
nibble = true;
} else {
sample = (byte)(ADPCM_RAM[adpcm_read_address] & 0xF);
sample = (byte)(RAM[adpcm_read_address] & 0xF);
nibble = false;
adpcm_length--;
adpcm_read_address++;
@ -151,10 +142,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
void AdpcmEmitSample()
{
if (AdpcmIsPlaying == false)
ADPCM_Provider.buffer.enqueue_sample(0, 0);
SoundProvider.buffer.enqueue_sample(0, 0);
else
{
int rate = 16 - (CdIoPorts[0x0E] & 0x0F);
int rate = 16 - (Port180E & 0x0F);
float khz = 32 / rate;
if (nextSampleTimer == 0)
@ -170,14 +161,14 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
}
short adjustedSample = (short)((playingSample - 2048) << 3);
ADPCM_Provider.buffer.enqueue_sample(adjustedSample, adjustedSample);
SoundProvider.buffer.enqueue_sample(adjustedSample, adjustedSample);
}
}
public void AdpcmThink()
public void Think()
{
int cycles = (int) (Cpu.TotalExecutedCycles - LastThink);
LastThink = Cpu.TotalExecutedCycles;
int cycles = (int) (pce.Cpu.TotalExecutedCycles - LastThink);
LastThink = pce.Cpu.TotalExecutedCycles;
adpcm_playback_timer -= cycles;
if (adpcm_playback_timer < 0)
@ -191,7 +182,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (adpcm_read_pending && adpcm_read_timer <= 0)
{
adpcm_read_buffer = ADPCM_RAM[adpcm_read_address++];
adpcm_read_buffer = RAM[adpcm_read_address++];
adpcm_read_pending = false;
if (adpcm_length > ushort.MinValue)
adpcm_length--;
@ -199,7 +190,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (adpcm_write_pending && adpcm_write_timer <= 0)
{
ADPCM_RAM[adpcm_write_address++] = adpcm_write_buffer;
RAM[adpcm_write_address++] = adpcm_write_buffer;
adpcm_write_pending = false;
if (adpcm_length < ushort.MaxValue)
adpcm_length++;
@ -210,7 +201,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (SCSI.REQ && SCSI.IO && !SCSI.CD && !SCSI.ACK)
{
byte dmaByte = SCSI.DataBits;
ADPCM_RAM[adpcm_write_address++] = dmaByte;
RAM[adpcm_write_address++] = dmaByte;
SCSI.ACK = false;
SCSI.REQ = false;
@ -219,16 +210,37 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (SCSI.DataTransferInProgress == false)
{
CdIoPorts[0x0B] = 0;
Port180B = 0;
Console.WriteLine(" ADPCM DMA COMPLETED");
}
}
CdIoPorts[0x03] &= 0xF3;
if (AdpcmIsPlaying == false) CdIoPorts[0x03] |= 0x08;
RefreshIRQ2();
pce.IRQ2Monitor &= 0xF3;
if (AdpcmIsPlaying == false) pce.IRQ2Monitor |= 0x08;
pce.RefreshIRQ2();
}
bool AdpcmCdDmaRequested { get { return (CdIoPorts[0x0B] & 3) != 0; } }
public bool AdpcmCdDmaRequested { get { return (Port180B & 3) != 0; } }
public byte Port180A
{
set
{
adpcm_write_buffer = value;
adpcm_write_timer = 24;
adpcm_write_pending = true;
}
get
{
adpcm_read_pending = true;
adpcm_read_timer = 24;
return adpcm_read_buffer;
}
}
public byte Port180B;
public byte Port180D;
public byte Port180E;
}
}

View File

@ -8,12 +8,7 @@ using BizHawk.DiscSystem;
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
public enum NecSystemType
{
TurboGrafx,
TurboCD,
SuperGrafx
}
public enum NecSystemType { TurboGrafx, TurboCD, SuperGrafx }
public sealed partial class PCEngine : IEmulator
{
@ -29,10 +24,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public VCE VCE;
public VPC VPC;
public ScsiCDBus SCSI;
public ADPCM ADPCM;
public HuC6280PSG PSG;
public CDAudio CDAudio;
// TODO ADPCM
public SoundMixer SoundMixer;
public MetaspuSoundProvider SoundSynchronizer;
@ -109,16 +104,16 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
{
Ram = new byte[0x2000];
CDRam = new byte[0x10000];
ADPCM_RAM = new byte[0x10000];
ADPCM = new ADPCM(this, SCSI);
Cpu.ReadMemory21 = ReadMemoryCD;
Cpu.WriteMemory21 = WriteMemoryCD;
Cpu.WriteVDC = VDC1.WriteVDC;
CDAudio = new CDAudio(disc, short.MaxValue);
ADPCM_Provider = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM_Provider);
CDAudio = new CDAudio(disc);
PSG.MaxVolume = short.MaxValue * 3 / 4;
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM.SoundProvider);
SoundSynchronizer = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
soundProvider = SoundSynchronizer;
Cpu.ThinkAction = () => { SCSI.Think(); AdpcmThink(); };
Cpu.ThinkAction = () => { SCSI.Think(); ADPCM.Think(); };
}
if (rom.Length == 0x60000)
@ -175,6 +170,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VDC2.PerformSpriteLimit = true;
}
if (game["CdVol"])
CDAudio.MaxVolume = int.Parse(game.OptionValue("CdVol"));
if (game["PsgVol"])
PSG.MaxVolume = int.Parse(game.OptionValue("PsgVol"));
// TODO ADPCM
// Ok, yes, HBlankPeriod's only purpose is game-specific hax.
// 1) At least they're not coded directly into the emulator, but instead data-driven.
// 2) The games which have custom HBlankPeriods work without it, the override only
@ -365,7 +366,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (TurboCD)
{
writer.Write(CDRam);
writer.Write(ADPCM_RAM);
writer.Write(ADPCM.RAM);
}
writer.Write(Frame);
writer.Write(_lagcount);
@ -407,7 +408,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (TurboCD)
{
CDRam = reader.ReadBytes(0x10000);
ADPCM_RAM = reader.ReadBytes(0x10000);
ADPCM.RAM = reader.ReadBytes(0x10000);
}
Frame = reader.ReadInt32();
_lagcount = reader.ReadInt32();
@ -482,9 +483,9 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
(addr, value) => CDRam[addr & 0xFFFF] = value);
domains.Add(CDRamMemoryDomain);
var AdpcmMemoryDomain = new MemoryDomain("ADPCM RAM", ADPCM_RAM.Length, Endian.Little,
addr => ADPCM_RAM[addr & 0xFFFF],
(addr, value) => ADPCM_RAM[addr & 0xFFFF] = value);
var AdpcmMemoryDomain = new MemoryDomain("ADPCM RAM", ADPCM.RAM.Length, Endian.Little,
addr => ADPCM.RAM[addr & 0xFFFF],
(addr, value) => ADPCM.RAM[addr & 0xFFFF] = value);
domains.Add(AdpcmMemoryDomain);
if (SuperRam != null)

View File

@ -11,9 +11,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
{
public partial class PCEngine
{
private byte[] CdIoPorts = new byte[16];
byte[] CdIoPorts = new byte[16];
private void InitScsiBus()
public byte IRQ2Control { get { return CdIoPorts[2]; } set { CdIoPorts[2] = value; } }
public byte IRQ2Monitor { get { return CdIoPorts[3]; } set { CdIoPorts[3] = value; } }
void InitScsiBus()
{
SCSI = new ScsiCDBus(this, disc);
// this is kind of stupid
@ -38,7 +41,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
};
}
private void WriteCD(int addr, byte value)
void WriteCD(int addr, byte value)
{
//Log.Error("CD","Write: {0:X4} {1:X2} (PC={2:X4})", addr & 0x1FFF, value, Cpu.PC);
switch (addr & 0x1FFF)
@ -103,45 +106,44 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
break;
case 0x1808: // ADPCM address LSB
adpcm_io_address &= 0xFF00;
adpcm_io_address |= value;
ADPCM.adpcm_io_address &= 0xFF00;
ADPCM.adpcm_io_address |= value;
if ((CdIoPorts[0x0D] & 0x10) != 0)
{
Console.WriteLine("doing silly thing");
adpcm_length = adpcm_io_address;
ADPCM.adpcm_length = ADPCM.adpcm_io_address;
}
Log.Error("CD", "adpcm address = {0:X4}", adpcm_io_address);
Log.Error("CD", "adpcm address = {0:X4}", ADPCM.adpcm_io_address);
break;
case 0x1809: // ADPCM address MSB
adpcm_io_address &= 0x00FF;
adpcm_io_address |= (ushort)(value << 8);
ADPCM.adpcm_io_address &= 0x00FF;
ADPCM.adpcm_io_address |= (ushort)(value << 8);
if ((CdIoPorts[0x0D] & 0x10) != 0)
{
Console.WriteLine("doing silly thing");
adpcm_length = adpcm_io_address;
ADPCM.adpcm_length = ADPCM.adpcm_io_address;
}
Log.Error("CD", "adpcm address = {0:X4}", adpcm_io_address);
Log.Error("CD", "adpcm address = {0:X4}", ADPCM.adpcm_io_address);
break;
case 0x180A: // ADPCM Memory Read/Write Port
AdpcmDataWrite(value);
ADPCM.Port180A = value;
break;
case 0x180B: // ADPCM DMA Control
CdIoPorts[0x0B] = value;
ADPCM.Port180B = value;
Log.Error("CD", "Write to ADPCM DMA Control [B] {0:X2}", value);
if (AdpcmCdDmaRequested)
if (ADPCM.AdpcmCdDmaRequested)
Console.WriteLine(" ADPCM DMA REQUESTED");
break;
case 0x180D: // ADPCM Address Control
AdpcmControlWrite(value);
CdIoPorts[0x0D] = value;
ADPCM.AdpcmControlWrite(value);
break;
case 0x180E: // ADPCM Playback Rate
CdIoPorts[0x0E] = value;
ADPCM.Port180E = value;
Log.Error("CD", "Write to ADPCM Sample Rate [E] {0:X2}", value);
break;
@ -238,21 +240,21 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
return returnValue;
case 0x180A: // ADPCM Memory Read/Write Port
return AdpcmDataRead();
return ADPCM.Port180A;
case 0x180B: // ADPCM Data Transfer Control
//Log.Error("CD", "Read ADPCM Data Transfer Control");
return CdIoPorts[0x0B];
return ADPCM.Port180B;
case 0x180C: // ADPCM Status
returnValue = 0;
if (AdpcmIsPlaying)
if (ADPCM.AdpcmIsPlaying)
returnValue |= 0x08;
else
returnValue |= 0x01;
if (AdpcmBusyWriting)
if (ADPCM.AdpcmBusyWriting)
returnValue |= 0x04;
if (AdpcmBusyReading)
if (ADPCM.AdpcmBusyReading)
returnValue |= 0x80;
//Log.Error("CD", "Read ADPCM Status {0:X2}", returnValue);
@ -281,7 +283,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
}
}
private void RefreshIRQ2()
public void RefreshIRQ2()
{
int mask = CdIoPorts[2] & CdIoPorts[3] & 0x7C;
Cpu.IRQ2Assert = (mask != 0);

View File

@ -92,6 +92,7 @@ namespace BizHawk
public byte[] SaveStateBinary() { return new byte[1]; }
public void GetSamples(short[] samples) { }
public void DiscardSamples() { }
public int MaxVolume { get; set; }
private IList<MemoryDomain> memoryDomains;
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }

View File

@ -55,6 +55,7 @@ namespace BizHawk
public int BackgroundColor { get { return 0; } }
public void GetSamples(short[] samples) { }
public void DiscardSamples() { }
public int MaxVolume { get; set; }
private IList<MemoryDomain> memoryDomains;
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
@ -67,5 +68,6 @@ namespace BizHawk
public void GetSamples(short[] samples) { }
public void DiscardSamples() { }
public int MaxVolume { get; set; }
}
}

View File

@ -4,5 +4,6 @@
{
void GetSamples(short[] samples);
void DiscardSamples();
int MaxVolume { get; set; }
}
}

View File

@ -31,6 +31,9 @@ BizHawk.Emulation Project Notes
Open the CpuCoreGenerator solution and make your changes there. Run the program to regenerate your cpus.
CpuCoreGenerator is a separate solution and it may not be obvious that it is there.
7) The new new plan is really for cores to be native. Which was actually the original plan.
Note that native doesn't _necessarily_ mean C++.
---------------
Emulation Notes
---------------
@ -43,8 +46,6 @@ Emulation Notes
Feedback is appreciated.
ISoundProvider:
* A means of controlling overall volume for the SoundProvider would be beneficial
when it comes to mixing multiple ISoundProviders.
* Current implementations generate garbage on each frame, although the API does not _require_
garbage to be created, it encourages it because you cannot provide a large buffer and specify
a smaller number of samples to fill. Also the BufferAsync generates garbage and probably the
@ -57,8 +58,6 @@ Emulation Notes
* I suppose NTSC/PAL (ie: target fps) could be useful also.
IEmulator:
* The LoadGame(IGame) is potentially problematic. Perhaps we simply remove this. Most likely
the constructor would then take an IGame as a requirement (or ICDImage or IRomSet or IFileSystem...)
* IEmulator should provide metadata about what Options it recognizes.
* Possibly, a lot of the metadata type functions should be removed from IEmulator and added to new
interface (such as IConsole, IArcade, IComputer, whatever)
interface (such as IConsole, IArcade, IComputer, whatever)

View File

@ -32,7 +32,7 @@ namespace BizHawk.Emulation.Sound
public CDAudioMode Mode = CDAudioMode.Stopped;
public PlaybackMode PlayMode = PlaybackMode.LoopOnCompletion;
public int MaxVolume;
public int MaxVolume { get; set; }
public int LogicalVolume = 100;
public int StartLBA, EndLBA;
@ -45,7 +45,7 @@ namespace BizHawk.Emulation.Sound
public int FadeOutOverFrames = 0;
public int FadeOutFramesRemaining = 0;
public CDAudio(Disc disc, int maxVolume)
public CDAudio(Disc disc, int maxVolume = short.MaxValue)
{
Disc = disc;
MaxVolume = maxVolume;
@ -150,10 +150,8 @@ namespace BizHawk.Emulation.Sound
short left = (short)((SectorCache[sectorOffset + 1] << 8) | (SectorCache[sectorOffset + 0]));
short right = (short)((SectorCache[sectorOffset + 3] << 8) | (SectorCache[sectorOffset + 2]));
// TODO absolute volume (for setting relative volumes between sound sources)
samples[offset++] += (short) (left * LogicalVolume / 100);
samples[offset++] += (short) (right * LogicalVolume / 100);
samples[offset++] += (short) (left * LogicalVolume / 100 * MaxVolume / short.MaxValue);
samples[offset++] += (short) (right * LogicalVolume / 100 * MaxVolume / short.MaxValue);
SectorOffset++;
if (SectorOffset == 588)
@ -207,7 +205,6 @@ namespace BizHawk.Emulation.Sound
{
get
{
// TODO apply the damn volume
if (Mode != CDAudioMode.Playing)
return 0;

View File

@ -27,21 +27,23 @@ namespace BizHawk.Emulation.Sound
public PSGChannel[] Channels = new PSGChannel[8];
public byte VoiceLatch;
private byte WaveTableWriteOffset;
byte WaveTableWriteOffset;
private Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
private long frameStartTime, frameStopTime;
Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
long frameStartTime, frameStopTime;
private const int SampleRate = 44100;
private const int PsgBase = 3580000;
private static byte[] LogScale = { 0, 0, 10, 10, 13, 13, 16, 16, 20, 20, 26, 26, 32, 32, 40, 40, 51, 51, 64, 64, 81, 81, 102, 102, 128, 128, 161, 161, 203, 203, 255, 255 };
private static byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 };
const int SampleRate = 44100;
const int PsgBase = 3580000;
static byte[] LogScale = { 0, 0, 10, 10, 13, 13, 16, 16, 20, 20, 26, 26, 32, 32, 40, 40, 51, 51, 64, 64, 81, 81, 102, 102, 128, 128, 161, 161, 203, 203, 255, 255 };
static byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 };
public byte MainVolumeLeft;
public byte MainVolumeRight;
public int MaxVolume { get; set; }
public HuC6280PSG()
{
MaxVolume = short.MaxValue;
Waves.InitWaves();
for (int i=0; i<8; i++)
Channels[i] = new PSGChannel();
@ -129,7 +131,7 @@ namespace BizHawk.Emulation.Sound
}
}
public void DiscardSamples() { /*TBD*/ }
public void DiscardSamples() { }
public void GetSamples(short[] samples)
{
int elapsedCycles = (int) (frameStopTime - frameStartTime);
@ -193,8 +195,8 @@ namespace BizHawk.Emulation.Sound
channel.SampleOffset %= wave.Length;
short value = channel.DDA ? channel.DDAValue : wave[(int) channel.SampleOffset];
samples[i++] += (short)(value * LogScale[volumeLeft] / 255f / 6f);
samples[i++] += (short)(value * LogScale[volumeRight] / 255f / 6f);
samples[i++] += (short)(value * LogScale[volumeLeft] / 255f / 6f * MaxVolume / short.MaxValue);
samples[i++] += (short)(value * LogScale[volumeRight] / 255f / 6f * MaxVolume / short.MaxValue);
channel.SampleOffset += moveThroughWaveRate;
channel.SampleOffset %= wave.Length;

View File

@ -24,7 +24,7 @@ namespace BizHawk.Emulation.Sound
private const int SampleRate = 44100;
private static byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 };
public void Mix(short[] samples, int start, int len)
public void Mix(short[] samples, int start, int len, int maxVolume)
{
if (Volume == 0) return;
@ -36,8 +36,8 @@ namespace BizHawk.Emulation.Sound
{
short value = Wave[(int)WaveOffset];
samples[i++] += (short)(Left ? (value / 4 * LogScale[Volume] / 0x1FF) : 0);
samples[i++] += (short)(Right ? (value / 4 * LogScale[Volume] / 0x1FF) : 0);
samples[i++] += (short)(Left ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
samples[i++] += (short)(Right ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
WaveOffset += moveThroughWaveRate;
if (WaveOffset >= Wave.Length)
WaveOffset %= Wave.Length;
@ -55,6 +55,7 @@ namespace BizHawk.Emulation.Sound
public SN76489()
{
MaxVolume = short.MaxValue * 2 / 3;
Waves.InitWaves();
for (int i=0; i<4; i++)
{
@ -416,7 +417,8 @@ namespace BizHawk.Emulation.Sound
#endregion
public void DiscardSamples() { /* todo */ }
public int MaxVolume { get; set; }
public void DiscardSamples() { commands.Clear(); }
public void GetSamples(short[] samples)
{
int elapsedCycles = frameStopTime - frameStartTime;
@ -435,7 +437,7 @@ namespace BizHawk.Emulation.Sound
public void GetSamplesImmediate(short[] samples, int start, int len)
{
for (int i = 0; i < 4; i++)
Channels[i].Mix(samples, start, len);
Channels[i].Mix(samples, start, len, MaxVolume);
}
class QueuedCommand

View File

@ -29,6 +29,8 @@ namespace BizHawk.Emulation.Sound
BaseSoundProvider.DiscardSamples();
}
public int MaxVolume { get; set; }
public void GetSamples(short[] samples)
{
int samplesToGenerate = SamplesInOneFrame;

View File

@ -32,7 +32,9 @@ namespace BizHawk.Emulation.Sound
{
buffer.clear();
}
}
public int MaxVolume { get; set; }
}
public interface ISynchronizingAudioBuffer
{

View File

@ -3,7 +3,6 @@
namespace BizHawk.Emulation.Sound
{
// This is a straightforward class to mix/chain multiple ISoundProvider sources.
// TODO: Fine-tuned volume control would be a good thing.
public sealed class SoundMixer : ISoundProvider
{
@ -35,5 +34,16 @@ namespace BizHawk.Emulation.Sound
foreach (var soundSource in SoundProviders)
soundSource.GetSamples(samples);
}
// Splits the volume space equally between available sources.
public void EqualizeVolumes()
{
int eachVolume = short.MaxValue / SoundProviders.Count;
foreach (var source in SoundProviders)
source.MaxVolume = eachVolume;
}
// Not actually supported on mixer.
public int MaxVolume { get; set; }
}
}

View File

@ -18,6 +18,7 @@ namespace BizHawk.Emulation.Sound
public YM2413()
{
MaxVolume = short.MaxValue;
opll = OPLL_new(3579545, 44100);
}
@ -39,11 +40,12 @@ namespace BizHawk.Emulation.Sound
}
public void DiscardSamples() { }
public int MaxVolume { get; set; }
public void GetSamples(short[] samples)
{
for (int i=0; i<samples.Length;)
{
short val = calc(opll);
short val = (short)(calc(opll) * MaxVolume / short.MaxValue);
samples[i++] = val;
samples[i++] = val;
}

View File

@ -17,10 +17,8 @@
{
}
public void DiscardSamples() { }
public void GetSamples(short[] samples)
{
// TODO
}
public void DiscardSamples() {}
public void GetSamples(short[] samples) {}
public int MaxVolume { get; set; }
}
}