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:
parent
ed7cc01d9f
commit
647cae698e
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]; } }
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
{
|
||||
void GetSamples(short[] samples);
|
||||
void DiscardSamples();
|
||||
int MaxVolume { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace BizHawk.Emulation.Sound
|
|||
BaseSoundProvider.DiscardSamples();
|
||||
}
|
||||
|
||||
public int MaxVolume { get; set; }
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
int samplesToGenerate = SamplesInOneFrame;
|
||||
|
|
|
@ -32,7 +32,9 @@ namespace BizHawk.Emulation.Sound
|
|||
{
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxVolume { get; set; }
|
||||
}
|
||||
|
||||
public interface ISynchronizingAudioBuffer
|
||||
{
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue