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);
|
MetaspuSoundProvider metaspu = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
||||||
|
public int MaxVolume { get; set; } // not supported
|
||||||
|
|
||||||
void ISoundProvider.GetSamples(short[] samples)
|
void ISoundProvider.GetSamples(short[] samples)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,27 +3,31 @@ using BizHawk.Emulation.Sound;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
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_io_address;
|
||||||
public ushort adpcm_read_address;
|
public ushort adpcm_read_address;
|
||||||
public ushort adpcm_write_address;
|
public ushort adpcm_write_address;
|
||||||
public ushort adpcm_length;
|
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 byte adpcm_read_buffer, adpcm_write_buffer;
|
||||||
public bool adpcm_read_pending, adpcm_write_pending;
|
public bool adpcm_read_pending, adpcm_write_pending;
|
||||||
|
|
||||||
|
public byte[] RAM = new byte[0x10000];
|
||||||
|
public MetaspuSoundProvider SoundProvider = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
||||||
|
|
||||||
long LastThink;
|
long LastThink;
|
||||||
float adpcm_playback_timer;
|
float adpcm_playback_timer;
|
||||||
|
|
||||||
|
ScsiCDBus SCSI;
|
||||||
|
PCEngine pce;
|
||||||
|
|
||||||
public byte[] ADPCM_RAM;
|
public ADPCM(PCEngine pcEngine, ScsiCDBus scsi)
|
||||||
|
{
|
||||||
public MetaspuSoundProvider ADPCM_Provider;
|
pce = pcEngine;
|
||||||
|
SCSI = scsi;
|
||||||
|
}
|
||||||
|
|
||||||
static readonly int[] StepSize =
|
static readonly int[] StepSize =
|
||||||
{
|
{
|
||||||
|
@ -47,7 +51,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
public void AdpcmControlWrite(byte value)
|
public void AdpcmControlWrite(byte value)
|
||||||
{
|
{
|
||||||
Log.Error("CD","ADPCM CONTROL WRITE {0:X2}",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!");
|
Log.Note("CD", "Reset ADPCM!");
|
||||||
adpcm_read_address = 0;
|
adpcm_read_address = 0;
|
||||||
|
@ -67,7 +71,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
adpcm_read_address--;
|
adpcm_read_address--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((CdIoPorts[0x0D] & 2) == 0 && (value & 2) != 0)
|
if ((Port180D & 2) == 0 && (value & 2) != 0)
|
||||||
{
|
{
|
||||||
adpcm_write_address = adpcm_io_address;
|
adpcm_write_address = adpcm_io_address;
|
||||||
if ((value & 1) == 0)
|
if ((value & 1) == 0)
|
||||||
|
@ -92,23 +96,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
adpcm_playback_timer = 0;
|
adpcm_playback_timer = 0;
|
||||||
magnitude = 0;
|
magnitude = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void AdpcmDataWrite(byte value)
|
Port180D = value;
|
||||||
{
|
|
||||||
adpcm_write_buffer = value;
|
|
||||||
adpcm_write_timer = 24;
|
|
||||||
adpcm_write_pending = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte AdpcmDataRead()
|
|
||||||
{
|
|
||||||
adpcm_read_pending = true;
|
|
||||||
adpcm_read_timer = 24;
|
|
||||||
return adpcm_read_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public bool AdpcmIsPlaying { get; private set; }
|
public bool AdpcmIsPlaying { get; private set; }
|
||||||
public bool AdpcmBusyWriting { get { return AdpcmCdDmaRequested; } }
|
public bool AdpcmBusyWriting { get { return AdpcmCdDmaRequested; } }
|
||||||
public bool AdpcmBusyReading { get { return adpcm_read_pending; } }
|
public bool AdpcmBusyReading { get { return adpcm_read_pending; } }
|
||||||
|
@ -127,10 +118,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
byte sample;
|
byte sample;
|
||||||
if (nibble == false)
|
if (nibble == false)
|
||||||
{
|
{
|
||||||
sample = (byte) (ADPCM_RAM[adpcm_read_address] >> 4);
|
sample = (byte) (RAM[adpcm_read_address] >> 4);
|
||||||
nibble = true;
|
nibble = true;
|
||||||
} else {
|
} else {
|
||||||
sample = (byte)(ADPCM_RAM[adpcm_read_address] & 0xF);
|
sample = (byte)(RAM[adpcm_read_address] & 0xF);
|
||||||
nibble = false;
|
nibble = false;
|
||||||
adpcm_length--;
|
adpcm_length--;
|
||||||
adpcm_read_address++;
|
adpcm_read_address++;
|
||||||
|
@ -151,10 +142,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
void AdpcmEmitSample()
|
void AdpcmEmitSample()
|
||||||
{
|
{
|
||||||
if (AdpcmIsPlaying == false)
|
if (AdpcmIsPlaying == false)
|
||||||
ADPCM_Provider.buffer.enqueue_sample(0, 0);
|
SoundProvider.buffer.enqueue_sample(0, 0);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int rate = 16 - (CdIoPorts[0x0E] & 0x0F);
|
int rate = 16 - (Port180E & 0x0F);
|
||||||
float khz = 32 / rate;
|
float khz = 32 / rate;
|
||||||
|
|
||||||
if (nextSampleTimer == 0)
|
if (nextSampleTimer == 0)
|
||||||
|
@ -170,14 +161,14 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
}
|
}
|
||||||
|
|
||||||
short adjustedSample = (short)((playingSample - 2048) << 3);
|
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);
|
int cycles = (int) (pce.Cpu.TotalExecutedCycles - LastThink);
|
||||||
LastThink = Cpu.TotalExecutedCycles;
|
LastThink = pce.Cpu.TotalExecutedCycles;
|
||||||
|
|
||||||
adpcm_playback_timer -= cycles;
|
adpcm_playback_timer -= cycles;
|
||||||
if (adpcm_playback_timer < 0)
|
if (adpcm_playback_timer < 0)
|
||||||
|
@ -191,7 +182,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
|
|
||||||
if (adpcm_read_pending && adpcm_read_timer <= 0)
|
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;
|
adpcm_read_pending = false;
|
||||||
if (adpcm_length > ushort.MinValue)
|
if (adpcm_length > ushort.MinValue)
|
||||||
adpcm_length--;
|
adpcm_length--;
|
||||||
|
@ -199,7 +190,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
|
|
||||||
if (adpcm_write_pending && adpcm_write_timer <= 0)
|
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;
|
adpcm_write_pending = false;
|
||||||
if (adpcm_length < ushort.MaxValue)
|
if (adpcm_length < ushort.MaxValue)
|
||||||
adpcm_length++;
|
adpcm_length++;
|
||||||
|
@ -210,7 +201,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
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;
|
||||||
ADPCM_RAM[adpcm_write_address++] = dmaByte;
|
RAM[adpcm_write_address++] = dmaByte;
|
||||||
|
|
||||||
SCSI.ACK = false;
|
SCSI.ACK = false;
|
||||||
SCSI.REQ = false;
|
SCSI.REQ = false;
|
||||||
|
@ -219,16 +210,37 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
|
|
||||||
if (SCSI.DataTransferInProgress == false)
|
if (SCSI.DataTransferInProgress == false)
|
||||||
{
|
{
|
||||||
CdIoPorts[0x0B] = 0;
|
Port180B = 0;
|
||||||
Console.WriteLine(" ADPCM DMA COMPLETED");
|
Console.WriteLine(" ADPCM DMA COMPLETED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CdIoPorts[0x03] &= 0xF3;
|
pce.IRQ2Monitor &= 0xF3;
|
||||||
if (AdpcmIsPlaying == false) CdIoPorts[0x03] |= 0x08;
|
if (AdpcmIsPlaying == false) pce.IRQ2Monitor |= 0x08;
|
||||||
RefreshIRQ2();
|
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
|
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
{
|
{
|
||||||
public enum NecSystemType
|
public enum NecSystemType { TurboGrafx, TurboCD, SuperGrafx }
|
||||||
{
|
|
||||||
TurboGrafx,
|
|
||||||
TurboCD,
|
|
||||||
SuperGrafx
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed partial class PCEngine : IEmulator
|
public sealed partial class PCEngine : IEmulator
|
||||||
{
|
{
|
||||||
|
@ -29,10 +24,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
public VCE VCE;
|
public VCE VCE;
|
||||||
public VPC VPC;
|
public VPC VPC;
|
||||||
public ScsiCDBus SCSI;
|
public ScsiCDBus SCSI;
|
||||||
|
public ADPCM ADPCM;
|
||||||
|
|
||||||
public HuC6280PSG PSG;
|
public HuC6280PSG PSG;
|
||||||
public CDAudio CDAudio;
|
public CDAudio CDAudio;
|
||||||
// TODO ADPCM
|
|
||||||
public SoundMixer SoundMixer;
|
public SoundMixer SoundMixer;
|
||||||
public MetaspuSoundProvider SoundSynchronizer;
|
public MetaspuSoundProvider SoundSynchronizer;
|
||||||
|
|
||||||
|
@ -109,16 +104,16 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
{
|
{
|
||||||
Ram = new byte[0x2000];
|
Ram = new byte[0x2000];
|
||||||
CDRam = new byte[0x10000];
|
CDRam = new byte[0x10000];
|
||||||
ADPCM_RAM = new byte[0x10000];
|
ADPCM = new ADPCM(this, SCSI);
|
||||||
Cpu.ReadMemory21 = ReadMemoryCD;
|
Cpu.ReadMemory21 = ReadMemoryCD;
|
||||||
Cpu.WriteMemory21 = WriteMemoryCD;
|
Cpu.WriteMemory21 = WriteMemoryCD;
|
||||||
Cpu.WriteVDC = VDC1.WriteVDC;
|
Cpu.WriteVDC = VDC1.WriteVDC;
|
||||||
CDAudio = new CDAudio(disc, short.MaxValue);
|
CDAudio = new CDAudio(disc);
|
||||||
ADPCM_Provider = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
PSG.MaxVolume = short.MaxValue * 3 / 4;
|
||||||
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM_Provider);
|
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM.SoundProvider);
|
||||||
SoundSynchronizer = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
SoundSynchronizer = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
||||||
soundProvider = SoundSynchronizer;
|
soundProvider = SoundSynchronizer;
|
||||||
Cpu.ThinkAction = () => { SCSI.Think(); AdpcmThink(); };
|
Cpu.ThinkAction = () => { SCSI.Think(); ADPCM.Think(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rom.Length == 0x60000)
|
if (rom.Length == 0x60000)
|
||||||
|
@ -175,6 +170,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
VDC2.PerformSpriteLimit = true;
|
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.
|
// 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.
|
// 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
|
// 2) The games which have custom HBlankPeriods work without it, the override only
|
||||||
|
@ -365,7 +366,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
if (TurboCD)
|
if (TurboCD)
|
||||||
{
|
{
|
||||||
writer.Write(CDRam);
|
writer.Write(CDRam);
|
||||||
writer.Write(ADPCM_RAM);
|
writer.Write(ADPCM.RAM);
|
||||||
}
|
}
|
||||||
writer.Write(Frame);
|
writer.Write(Frame);
|
||||||
writer.Write(_lagcount);
|
writer.Write(_lagcount);
|
||||||
|
@ -407,7 +408,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
if (TurboCD)
|
if (TurboCD)
|
||||||
{
|
{
|
||||||
CDRam = reader.ReadBytes(0x10000);
|
CDRam = reader.ReadBytes(0x10000);
|
||||||
ADPCM_RAM = reader.ReadBytes(0x10000);
|
ADPCM.RAM = reader.ReadBytes(0x10000);
|
||||||
}
|
}
|
||||||
Frame = reader.ReadInt32();
|
Frame = reader.ReadInt32();
|
||||||
_lagcount = reader.ReadInt32();
|
_lagcount = reader.ReadInt32();
|
||||||
|
@ -482,9 +483,9 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
(addr, value) => CDRam[addr & 0xFFFF] = value);
|
(addr, value) => CDRam[addr & 0xFFFF] = value);
|
||||||
domains.Add(CDRamMemoryDomain);
|
domains.Add(CDRamMemoryDomain);
|
||||||
|
|
||||||
var AdpcmMemoryDomain = new MemoryDomain("ADPCM RAM", ADPCM_RAM.Length, Endian.Little,
|
var AdpcmMemoryDomain = new MemoryDomain("ADPCM RAM", ADPCM.RAM.Length, Endian.Little,
|
||||||
addr => ADPCM_RAM[addr & 0xFFFF],
|
addr => ADPCM.RAM[addr & 0xFFFF],
|
||||||
(addr, value) => ADPCM_RAM[addr & 0xFFFF] = value);
|
(addr, value) => ADPCM.RAM[addr & 0xFFFF] = value);
|
||||||
domains.Add(AdpcmMemoryDomain);
|
domains.Add(AdpcmMemoryDomain);
|
||||||
|
|
||||||
if (SuperRam != null)
|
if (SuperRam != null)
|
||||||
|
|
|
@ -11,9 +11,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
{
|
{
|
||||||
public partial class PCEngine
|
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);
|
SCSI = new ScsiCDBus(this, disc);
|
||||||
// this is kind of stupid
|
// 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);
|
//Log.Error("CD","Write: {0:X4} {1:X2} (PC={2:X4})", addr & 0x1FFF, value, Cpu.PC);
|
||||||
switch (addr & 0x1FFF)
|
switch (addr & 0x1FFF)
|
||||||
|
@ -103,45 +106,44 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1808: // ADPCM address LSB
|
case 0x1808: // ADPCM address LSB
|
||||||
adpcm_io_address &= 0xFF00;
|
ADPCM.adpcm_io_address &= 0xFF00;
|
||||||
adpcm_io_address |= value;
|
ADPCM.adpcm_io_address |= value;
|
||||||
if ((CdIoPorts[0x0D] & 0x10) != 0)
|
if ((CdIoPorts[0x0D] & 0x10) != 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine("doing silly thing");
|
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;
|
break;
|
||||||
|
|
||||||
case 0x1809: // ADPCM address MSB
|
case 0x1809: // ADPCM address MSB
|
||||||
adpcm_io_address &= 0x00FF;
|
ADPCM.adpcm_io_address &= 0x00FF;
|
||||||
adpcm_io_address |= (ushort)(value << 8);
|
ADPCM.adpcm_io_address |= (ushort)(value << 8);
|
||||||
if ((CdIoPorts[0x0D] & 0x10) != 0)
|
if ((CdIoPorts[0x0D] & 0x10) != 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine("doing silly thing");
|
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;
|
break;
|
||||||
|
|
||||||
case 0x180A: // ADPCM Memory Read/Write Port
|
case 0x180A: // ADPCM Memory Read/Write Port
|
||||||
AdpcmDataWrite(value);
|
ADPCM.Port180A = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x180B: // ADPCM DMA Control
|
case 0x180B: // ADPCM DMA Control
|
||||||
CdIoPorts[0x0B] = value;
|
ADPCM.Port180B = value;
|
||||||
Log.Error("CD", "Write to ADPCM DMA Control [B] {0:X2}", value);
|
Log.Error("CD", "Write to ADPCM DMA Control [B] {0:X2}", value);
|
||||||
if (AdpcmCdDmaRequested)
|
if (ADPCM.AdpcmCdDmaRequested)
|
||||||
Console.WriteLine(" ADPCM DMA REQUESTED");
|
Console.WriteLine(" ADPCM DMA REQUESTED");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x180D: // ADPCM Address Control
|
case 0x180D: // ADPCM Address Control
|
||||||
AdpcmControlWrite(value);
|
ADPCM.AdpcmControlWrite(value);
|
||||||
CdIoPorts[0x0D] = value;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x180E: // ADPCM Playback Rate
|
case 0x180E: // ADPCM Playback Rate
|
||||||
CdIoPorts[0x0E] = value;
|
ADPCM.Port180E = value;
|
||||||
Log.Error("CD", "Write to ADPCM Sample Rate [E] {0:X2}", value);
|
Log.Error("CD", "Write to ADPCM Sample Rate [E] {0:X2}", value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -238,21 +240,21 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
|
||||||
return returnValue;
|
return returnValue;
|
||||||
|
|
||||||
case 0x180A: // ADPCM Memory Read/Write Port
|
case 0x180A: // ADPCM Memory Read/Write Port
|
||||||
return AdpcmDataRead();
|
return ADPCM.Port180A;
|
||||||
|
|
||||||
case 0x180B: // ADPCM Data Transfer Control
|
case 0x180B: // ADPCM Data Transfer Control
|
||||||
//Log.Error("CD", "Read ADPCM Data Transfer Control");
|
//Log.Error("CD", "Read ADPCM Data Transfer Control");
|
||||||
return CdIoPorts[0x0B];
|
return ADPCM.Port180B;
|
||||||
|
|
||||||
case 0x180C: // ADPCM Status
|
case 0x180C: // ADPCM Status
|
||||||
returnValue = 0;
|
returnValue = 0;
|
||||||
if (AdpcmIsPlaying)
|
if (ADPCM.AdpcmIsPlaying)
|
||||||
returnValue |= 0x08;
|
returnValue |= 0x08;
|
||||||
else
|
else
|
||||||
returnValue |= 0x01;
|
returnValue |= 0x01;
|
||||||
if (AdpcmBusyWriting)
|
if (ADPCM.AdpcmBusyWriting)
|
||||||
returnValue |= 0x04;
|
returnValue |= 0x04;
|
||||||
if (AdpcmBusyReading)
|
if (ADPCM.AdpcmBusyReading)
|
||||||
returnValue |= 0x80;
|
returnValue |= 0x80;
|
||||||
|
|
||||||
//Log.Error("CD", "Read ADPCM Status {0:X2}", returnValue);
|
//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;
|
int mask = CdIoPorts[2] & CdIoPorts[3] & 0x7C;
|
||||||
Cpu.IRQ2Assert = (mask != 0);
|
Cpu.IRQ2Assert = (mask != 0);
|
||||||
|
|
|
@ -92,6 +92,7 @@ namespace BizHawk
|
||||||
public byte[] SaveStateBinary() { return new byte[1]; }
|
public byte[] SaveStateBinary() { return new byte[1]; }
|
||||||
public void GetSamples(short[] samples) { }
|
public void GetSamples(short[] samples) { }
|
||||||
public void DiscardSamples() { }
|
public void DiscardSamples() { }
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
private IList<MemoryDomain> memoryDomains;
|
private IList<MemoryDomain> memoryDomains;
|
||||||
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
||||||
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace BizHawk
|
||||||
public int BackgroundColor { get { return 0; } }
|
public int BackgroundColor { get { return 0; } }
|
||||||
public void GetSamples(short[] samples) { }
|
public void GetSamples(short[] samples) { }
|
||||||
public void DiscardSamples() { }
|
public void DiscardSamples() { }
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
private IList<MemoryDomain> memoryDomains;
|
private IList<MemoryDomain> memoryDomains;
|
||||||
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
||||||
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
||||||
|
@ -67,5 +68,6 @@ namespace BizHawk
|
||||||
|
|
||||||
public void GetSamples(short[] samples) { }
|
public void GetSamples(short[] samples) { }
|
||||||
public void DiscardSamples() { }
|
public void DiscardSamples() { }
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
{
|
{
|
||||||
void GetSamples(short[] samples);
|
void GetSamples(short[] samples);
|
||||||
void DiscardSamples();
|
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.
|
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.
|
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
|
Emulation Notes
|
||||||
---------------
|
---------------
|
||||||
|
@ -43,8 +46,6 @@ Emulation Notes
|
||||||
Feedback is appreciated.
|
Feedback is appreciated.
|
||||||
|
|
||||||
ISoundProvider:
|
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_
|
* 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
|
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
|
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.
|
* I suppose NTSC/PAL (ie: target fps) could be useful also.
|
||||||
|
|
||||||
IEmulator:
|
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.
|
* 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
|
* 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 CDAudioMode Mode = CDAudioMode.Stopped;
|
||||||
public PlaybackMode PlayMode = PlaybackMode.LoopOnCompletion;
|
public PlaybackMode PlayMode = PlaybackMode.LoopOnCompletion;
|
||||||
|
|
||||||
public int MaxVolume;
|
public int MaxVolume { get; set; }
|
||||||
public int LogicalVolume = 100;
|
public int LogicalVolume = 100;
|
||||||
|
|
||||||
public int StartLBA, EndLBA;
|
public int StartLBA, EndLBA;
|
||||||
|
@ -45,7 +45,7 @@ namespace BizHawk.Emulation.Sound
|
||||||
public int FadeOutOverFrames = 0;
|
public int FadeOutOverFrames = 0;
|
||||||
public int FadeOutFramesRemaining = 0;
|
public int FadeOutFramesRemaining = 0;
|
||||||
|
|
||||||
public CDAudio(Disc disc, int maxVolume)
|
public CDAudio(Disc disc, int maxVolume = short.MaxValue)
|
||||||
{
|
{
|
||||||
Disc = disc;
|
Disc = disc;
|
||||||
MaxVolume = maxVolume;
|
MaxVolume = maxVolume;
|
||||||
|
@ -150,10 +150,8 @@ namespace BizHawk.Emulation.Sound
|
||||||
short left = (short)((SectorCache[sectorOffset + 1] << 8) | (SectorCache[sectorOffset + 0]));
|
short left = (short)((SectorCache[sectorOffset + 1] << 8) | (SectorCache[sectorOffset + 0]));
|
||||||
short right = (short)((SectorCache[sectorOffset + 3] << 8) | (SectorCache[sectorOffset + 2]));
|
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 * MaxVolume / short.MaxValue);
|
||||||
|
samples[offset++] += (short) (right * LogicalVolume / 100 * MaxVolume / short.MaxValue);
|
||||||
samples[offset++] += (short) (left * LogicalVolume / 100);
|
|
||||||
samples[offset++] += (short) (right * LogicalVolume / 100);
|
|
||||||
SectorOffset++;
|
SectorOffset++;
|
||||||
|
|
||||||
if (SectorOffset == 588)
|
if (SectorOffset == 588)
|
||||||
|
@ -207,7 +205,6 @@ namespace BizHawk.Emulation.Sound
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// TODO apply the damn volume
|
|
||||||
if (Mode != CDAudioMode.Playing)
|
if (Mode != CDAudioMode.Playing)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -27,21 +27,23 @@ namespace BizHawk.Emulation.Sound
|
||||||
public PSGChannel[] Channels = new PSGChannel[8];
|
public PSGChannel[] Channels = new PSGChannel[8];
|
||||||
|
|
||||||
public byte VoiceLatch;
|
public byte VoiceLatch;
|
||||||
private byte WaveTableWriteOffset;
|
byte WaveTableWriteOffset;
|
||||||
|
|
||||||
private Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
|
Queue<QueuedCommand> commands = new Queue<QueuedCommand>(256);
|
||||||
private long frameStartTime, frameStopTime;
|
long frameStartTime, frameStopTime;
|
||||||
|
|
||||||
private const int SampleRate = 44100;
|
const int SampleRate = 44100;
|
||||||
private const int PsgBase = 3580000;
|
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 };
|
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 };
|
static byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 };
|
||||||
|
|
||||||
public byte MainVolumeLeft;
|
public byte MainVolumeLeft;
|
||||||
public byte MainVolumeRight;
|
public byte MainVolumeRight;
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
|
|
||||||
public HuC6280PSG()
|
public HuC6280PSG()
|
||||||
{
|
{
|
||||||
|
MaxVolume = short.MaxValue;
|
||||||
Waves.InitWaves();
|
Waves.InitWaves();
|
||||||
for (int i=0; i<8; i++)
|
for (int i=0; i<8; i++)
|
||||||
Channels[i] = new PSGChannel();
|
Channels[i] = new PSGChannel();
|
||||||
|
@ -129,7 +131,7 @@ namespace BizHawk.Emulation.Sound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DiscardSamples() { /*TBD*/ }
|
public void DiscardSamples() { }
|
||||||
public void GetSamples(short[] samples)
|
public void GetSamples(short[] samples)
|
||||||
{
|
{
|
||||||
int elapsedCycles = (int) (frameStopTime - frameStartTime);
|
int elapsedCycles = (int) (frameStopTime - frameStartTime);
|
||||||
|
@ -193,8 +195,8 @@ namespace BizHawk.Emulation.Sound
|
||||||
channel.SampleOffset %= wave.Length;
|
channel.SampleOffset %= wave.Length;
|
||||||
short value = channel.DDA ? channel.DDAValue : wave[(int) channel.SampleOffset];
|
short value = channel.DDA ? channel.DDAValue : wave[(int) channel.SampleOffset];
|
||||||
|
|
||||||
samples[i++] += (short)(value * LogScale[volumeLeft] / 255f / 6f);
|
samples[i++] += (short)(value * LogScale[volumeLeft] / 255f / 6f * MaxVolume / short.MaxValue);
|
||||||
samples[i++] += (short)(value * LogScale[volumeRight] / 255f / 6f);
|
samples[i++] += (short)(value * LogScale[volumeRight] / 255f / 6f * MaxVolume / short.MaxValue);
|
||||||
|
|
||||||
channel.SampleOffset += moveThroughWaveRate;
|
channel.SampleOffset += moveThroughWaveRate;
|
||||||
channel.SampleOffset %= wave.Length;
|
channel.SampleOffset %= wave.Length;
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace BizHawk.Emulation.Sound
|
||||||
private const int SampleRate = 44100;
|
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 };
|
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;
|
if (Volume == 0) return;
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ namespace BizHawk.Emulation.Sound
|
||||||
{
|
{
|
||||||
short value = Wave[(int)WaveOffset];
|
short value = Wave[(int)WaveOffset];
|
||||||
|
|
||||||
samples[i++] += (short)(Left ? (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] / 0x1FF) : 0);
|
samples[i++] += (short)(Right ? (value / 4 * LogScale[Volume] / 0xFF * maxVolume / short.MaxValue) : 0);
|
||||||
WaveOffset += moveThroughWaveRate;
|
WaveOffset += moveThroughWaveRate;
|
||||||
if (WaveOffset >= Wave.Length)
|
if (WaveOffset >= Wave.Length)
|
||||||
WaveOffset %= Wave.Length;
|
WaveOffset %= Wave.Length;
|
||||||
|
@ -55,6 +55,7 @@ namespace BizHawk.Emulation.Sound
|
||||||
|
|
||||||
public SN76489()
|
public SN76489()
|
||||||
{
|
{
|
||||||
|
MaxVolume = short.MaxValue * 2 / 3;
|
||||||
Waves.InitWaves();
|
Waves.InitWaves();
|
||||||
for (int i=0; i<4; i++)
|
for (int i=0; i<4; i++)
|
||||||
{
|
{
|
||||||
|
@ -416,7 +417,8 @@ namespace BizHawk.Emulation.Sound
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void DiscardSamples() { /* todo */ }
|
public int MaxVolume { get; set; }
|
||||||
|
public void DiscardSamples() { commands.Clear(); }
|
||||||
public void GetSamples(short[] samples)
|
public void GetSamples(short[] samples)
|
||||||
{
|
{
|
||||||
int elapsedCycles = frameStopTime - frameStartTime;
|
int elapsedCycles = frameStopTime - frameStartTime;
|
||||||
|
@ -435,7 +437,7 @@ namespace BizHawk.Emulation.Sound
|
||||||
public void GetSamplesImmediate(short[] samples, int start, int len)
|
public void GetSamplesImmediate(short[] samples, int start, int len)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
Channels[i].Mix(samples, start, len);
|
Channels[i].Mix(samples, start, len, MaxVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueuedCommand
|
class QueuedCommand
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace BizHawk.Emulation.Sound
|
||||||
BaseSoundProvider.DiscardSamples();
|
BaseSoundProvider.DiscardSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
|
|
||||||
public void GetSamples(short[] samples)
|
public void GetSamples(short[] samples)
|
||||||
{
|
{
|
||||||
int samplesToGenerate = SamplesInOneFrame;
|
int samplesToGenerate = SamplesInOneFrame;
|
||||||
|
|
|
@ -32,7 +32,9 @@ namespace BizHawk.Emulation.Sound
|
||||||
{
|
{
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public interface ISynchronizingAudioBuffer
|
public interface ISynchronizingAudioBuffer
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
namespace BizHawk.Emulation.Sound
|
namespace BizHawk.Emulation.Sound
|
||||||
{
|
{
|
||||||
// This is a straightforward class to mix/chain multiple ISoundProvider sources.
|
// 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
|
public sealed class SoundMixer : ISoundProvider
|
||||||
{
|
{
|
||||||
|
@ -35,5 +34,16 @@ namespace BizHawk.Emulation.Sound
|
||||||
foreach (var soundSource in SoundProviders)
|
foreach (var soundSource in SoundProviders)
|
||||||
soundSource.GetSamples(samples);
|
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()
|
public YM2413()
|
||||||
{
|
{
|
||||||
|
MaxVolume = short.MaxValue;
|
||||||
opll = OPLL_new(3579545, 44100);
|
opll = OPLL_new(3579545, 44100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +40,12 @@ namespace BizHawk.Emulation.Sound
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DiscardSamples() { }
|
public void DiscardSamples() { }
|
||||||
|
public int MaxVolume { get; set; }
|
||||||
public void GetSamples(short[] samples)
|
public void GetSamples(short[] samples)
|
||||||
{
|
{
|
||||||
for (int i=0; i<samples.Length;)
|
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;
|
||||||
samples[i++] = val;
|
samples[i++] = val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,8 @@
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DiscardSamples() { }
|
public void DiscardSamples() {}
|
||||||
public void GetSamples(short[] samples)
|
public void GetSamples(short[] samples) {}
|
||||||
{
|
public int MaxVolume { get; set; }
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue