gba/IodineGBA/core/Sound.js

848 lines
32 KiB
JavaScript
Raw Normal View History

2015-07-11 23:11:25 -04:00
"use strict";
/*
Copyright (C) 2012-2015 Grant Galitz
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function GameBoyAdvanceSound(IOCore) {
//Build references:
this.IOCore = IOCore;
}
GameBoyAdvanceSound.prototype.initialize = function () {
this.coreExposed = this.IOCore.coreExposed;
this.dmaChannel1 = this.IOCore.dmaChannel1;
this.dmaChannel2 = this.IOCore.dmaChannel2;
//Did the emulator core initialize us for output yet?
this.preprocessInitialization(false);
//Initialize start:
this.audioTicks = 0;
this.initializeAudioStartState();
}
GameBoyAdvanceSound.prototype.initializeOutput = function (enabled, audioResamplerFirstPassFactor) {
this.preprocessInitialization(enabled);
this.audioIndex = 0;
this.downsampleInputLeft = 0;
this.downsampleInputRight = 0;
this.audioResamplerFirstPassFactor = audioResamplerFirstPassFactor | 0;
}
GameBoyAdvanceSound.prototype.initializeAudioStartState = function () {
//NOTE: NR 60-63 never get reset in audio halting:
this.nr60 = 0;
this.nr61 = 0;
this.nr62 = (this.IOCore.BIOSFound && !this.IOCore.settings.SKIPBoot) ? 0 : 0xFF;
this.nr63 = (this.IOCore.BIOSFound && !this.IOCore.settings.SKIPBoot) ? 0 : 0x2;
this.soundMasterEnabled = (!this.IOCore.BIOSFound || this.IOCore.settings.SKIPBoot);
this.mixerSoundBIAS = (this.IOCore.BIOSFound && !this.IOCore.settings.SKIPBoot) ? 0 : 0x200;
this.channel1 = new GameBoyAdvanceChannel1Synth(this);
this.channel2 = new GameBoyAdvanceChannel2Synth(this);
this.channel3 = new GameBoyAdvanceChannel3Synth(this);
this.channel4 = new GameBoyAdvanceChannel4Synth(this);
this.CGBMixerOutputCacheLeft = 0;
this.CGBMixerOutputCacheLeftFolded = 0;
this.CGBMixerOutputCacheRight = 0;
this.CGBMixerOutputCacheRightFolded = 0;
this.AGBDirectSoundATimer = 0;
this.AGBDirectSoundBTimer = 0;
this.AGBDirectSoundA = 0;
this.AGBDirectSoundAFolded = 0;
this.AGBDirectSoundB = 0;
this.AGBDirectSoundBFolded = 0;
this.AGBDirectSoundAShifter = 0;
this.AGBDirectSoundBShifter = 0;
this.AGBDirectSoundALeftCanPlay = false;
this.AGBDirectSoundBLeftCanPlay = false;
this.AGBDirectSoundARightCanPlay = false;
this.AGBDirectSoundBRightCanPlay = false;
this.CGBOutputRatio = 2;
this.FIFOABuffer = new GameBoyAdvanceFIFO();
this.FIFOBBuffer = new GameBoyAdvanceFIFO();
this.audioDisabled(); //Clear legacy PAPU registers:
}
GameBoyAdvanceSound.prototype.audioDisabled = function () {
this.channel1.disabled();
this.channel2.disabled();
this.channel3.disabled();
this.channel4.disabled();
//Clear FIFO:
this.AGBDirectSoundAFIFOClear();
this.AGBDirectSoundBFIFOClear();
//Clear NR50:
this.nr50 = 0;
this.VinLeftChannelMasterVolume = 1;
this.VinRightChannelMasterVolume = 1;
//Clear NR51:
this.nr51 = 0;
this.rightChannel1 = false;
this.rightChannel2 = false;
this.rightChannel3 = false;
this.rightChannel4 = false;
this.leftChannel1 = false;
this.leftChannel2 = false;
this.leftChannel3 = false;
this.leftChannel4 = false;
//Clear NR52:
this.nr52 = 0;
this.soundMasterEnabled = false;
this.mixerOutputCacheLeft = this.mixerSoundBIAS | 0;
this.mixerOutputCacheRight = this.mixerSoundBIAS | 0;
this.audioClocksUntilNextEventCounter = 0;
this.audioClocksUntilNextEvent = 0;
this.sequencePosition = 0;
this.sequencerClocks = 0x8000;
this.PWMWidth = 0x200;
this.PWMWidthOld = 0x200;
this.PWMWidthShadow = 0x200;
this.PWMBitDepthMask = 0x3FE;
this.PWMBitDepthMaskShadow = 0x3FE;
this.channel1.outputLevelCache();
this.channel2.outputLevelCache();
this.channel3.updateCache();
this.channel4.updateCache();
}
GameBoyAdvanceSound.prototype.audioEnabled = function () {
//Set NR52:
this.nr52 = 0x80;
this.soundMasterEnabled = true;
}
GameBoyAdvanceSound.prototype.addClocks = function (clocks) {
clocks = clocks | 0;
this.audioTicks = ((this.audioTicks | 0) + (clocks | 0)) | 0;
}
if (typeof Math.imul == "function") {
//Math.imul found, insert the optimized path in:
GameBoyAdvanceSound.prototype.generateAudioReal = function (numSamples) {
numSamples = numSamples | 0;
var multiplier = 0;
if (this.soundMasterEnabled && !this.IOCore.isStopped()) {
for (var clockUpTo = 0; (numSamples | 0) > 0;) {
clockUpTo = Math.min(this.PWMWidth | 0, numSamples | 0) | 0;
this.PWMWidth = ((this.PWMWidth | 0) - (clockUpTo | 0)) | 0;
numSamples = ((numSamples | 0) - (clockUpTo | 0)) | 0;
while ((clockUpTo | 0) > 0) {
multiplier = Math.min(clockUpTo | 0, ((this.audioResamplerFirstPassFactor | 0) - (this.audioIndex | 0)) | 0) | 0;
clockUpTo = ((clockUpTo | 0) - (multiplier | 0)) | 0;
this.audioIndex = ((this.audioIndex | 0) + (multiplier | 0)) | 0;
this.downsampleInputLeft = ((this.downsampleInputLeft | 0) + Math.imul(this.mixerOutputCacheLeft | 0, multiplier | 0)) | 0;
this.downsampleInputRight = ((this.downsampleInputRight | 0) + Math.imul(this.mixerOutputCacheRight | 0, multiplier | 0)) | 0;
if ((this.audioIndex | 0) == (this.audioResamplerFirstPassFactor | 0)) {
this.audioIndex = 0;
this.coreExposed.outputAudio(this.downsampleInputLeft | 0, this.downsampleInputRight | 0);
this.downsampleInputLeft = 0;
this.downsampleInputRight = 0;
}
}
if ((this.PWMWidth | 0) == 0) {
this.computeNextPWMInterval();
this.PWMWidthOld = this.PWMWidthShadow | 0;
this.PWMWidth = this.PWMWidthShadow | 0;
}
}
}
else {
//SILENT OUTPUT:
while ((numSamples | 0) > 0) {
multiplier = Math.min(numSamples | 0, ((this.audioResamplerFirstPassFactor | 0) - (this.audioIndex | 0)) | 0) | 0;
numSamples = ((numSamples | 0) - (multiplier | 0)) | 0;
this.audioIndex = ((this.audioIndex | 0) + (multiplier | 0)) | 0;
if ((this.audioIndex | 0) == (this.audioResamplerFirstPassFactor | 0)) {
this.audioIndex = 0;
this.coreExposed.outputAudio(this.downsampleInputLeft | 0, this.downsampleInputRight | 0);
this.downsampleInputLeft = 0;
this.downsampleInputRight = 0;
}
}
}
}
}
else {
//Math.imul not found, use the compatibility method:
GameBoyAdvanceSound.prototype.generateAudioReal = function (numSamples) {
var multiplier = 0;
if (this.soundMasterEnabled && !this.IOCore.isStopped()) {
for (var clockUpTo = 0; numSamples > 0;) {
clockUpTo = Math.min(this.PWMWidth, numSamples);
this.PWMWidth = this.PWMWidth - clockUpTo;
numSamples -= clockUpTo;
while (clockUpTo > 0) {
multiplier = Math.min(clockUpTo, this.audioResamplerFirstPassFactor - this.audioIndex);
clockUpTo -= multiplier;
this.audioIndex += multiplier;
this.downsampleInputLeft += this.mixerOutputCacheLeft * multiplier;
this.downsampleInputRight += this.mixerOutputCacheRight * multiplier;
if (this.audioIndex == this.audioResamplerFirstPassFactor) {
this.audioIndex = 0;
this.coreExposed.outputAudio(this.downsampleInputLeft, this.downsampleInputRight);
this.downsampleInputLeft = 0;
this.downsampleInputRight = 0;
}
}
if (this.PWMWidth == 0) {
this.computeNextPWMInterval();
this.PWMWidthOld = this.PWMWidthShadow;
this.PWMWidth = this.PWMWidthShadow;
}
}
}
else {
//SILENT OUTPUT:
while (numSamples > 0) {
multiplier = Math.min(numSamples, this.audioResamplerFirstPassFactor - this.audioIndex);
numSamples -= multiplier;
this.audioIndex += multiplier;
if (this.audioIndex == this.audioResamplerFirstPassFactor) {
this.audioIndex = 0;
this.coreExposed.outputAudio(this.downsampleInputLeft, this.downsampleInputRight);
this.downsampleInputLeft = 0;
this.downsampleInputRight = 0;
}
}
}
}
}
//Generate audio, but don't actually output it (Used for when sound is disabled by user/browser):
GameBoyAdvanceSound.prototype.generateAudioFake = function (numSamples) {
numSamples = numSamples | 0;
if (this.soundMasterEnabled && !this.IOCore.isStopped()) {
for (var clockUpTo = 0; (numSamples | 0) > 0;) {
clockUpTo = Math.min(this.PWMWidth | 0, numSamples | 0) | 0;
this.PWMWidth = ((this.PWMWidth | 0) - (clockUpTo | 0)) | 0;
numSamples = ((numSamples | 0) - (clockUpTo | 0)) | 0;
if ((this.PWMWidth | 0) == 0) {
this.computeNextPWMInterval();
this.PWMWidthOld = this.PWMWidthShadow | 0;
this.PWMWidth = this.PWMWidthShadow | 0;
}
}
}
}
GameBoyAdvanceSound.prototype.preprocessInitialization = function (audioInitialized) {
if (audioInitialized) {
this.generateAudio = this.generateAudioReal;
this.audioInitialized = true;
}
else {
this.generateAudio = this.generateAudioFake;
this.audioInitialized = false;
}
}
GameBoyAdvanceSound.prototype.audioJIT = function () {
//Audio Sample Generation Timing:
this.generateAudio(this.audioTicks | 0);
this.audioTicks = 0;
}
GameBoyAdvanceSound.prototype.computeNextPWMInterval = function () {
//Clock down the PSG system:
for (var numSamples = this.PWMWidthOld | 0, clockUpTo = 0; (numSamples | 0) > 0; numSamples = ((numSamples | 0) - 1) | 0) {
clockUpTo = Math.min(this.audioClocksUntilNextEventCounter | 0, this.sequencerClocks | 0, numSamples | 0) | 0;
this.audioClocksUntilNextEventCounter = ((this.audioClocksUntilNextEventCounter | 0) - (clockUpTo | 0)) | 0;
this.sequencerClocks = ((this.sequencerClocks | 0) - (clockUpTo | 0)) | 0;
numSamples = ((numSamples | 0) - (clockUpTo | 0)) | 0;
if ((this.sequencerClocks | 0) == 0) {
this.audioComputeSequencer();
this.sequencerClocks = 0x8000;
}
if ((this.audioClocksUntilNextEventCounter | 0) == 0) {
this.computeAudioChannels();
}
}
//Copy the new bit-depth mask for the next counter interval:
this.PWMBitDepthMask = this.PWMBitDepthMaskShadow | 0;
//Compute next sample for the PWM output:
this.channel1.outputLevelCache();
this.channel2.outputLevelCache();
this.channel3.updateCache();
this.channel4.updateCache();
this.CGBMixerOutputLevelCache();
this.mixerOutputLevelCache();
}
GameBoyAdvanceSound.prototype.audioComputeSequencer = function () {
switch (this.sequencePosition++) {
case 0:
this.clockAudioLength();
break;
case 2:
this.clockAudioLength();
this.channel1.clockAudioSweep();
break;
case 4:
this.clockAudioLength();
break;
case 6:
this.clockAudioLength();
this.channel1.clockAudioSweep();
break;
case 7:
this.clockAudioEnvelope();
this.sequencePosition = 0;
}
}
GameBoyAdvanceSound.prototype.clockAudioLength = function () {
//Channel 1:
this.channel1.clockAudioLength();
//Channel 2:
this.channel2.clockAudioLength();
//Channel 3:
this.channel3.clockAudioLength();
//Channel 4:
this.channel4.clockAudioLength();
}
GameBoyAdvanceSound.prototype.clockAudioEnvelope = function () {
//Channel 1:
this.channel1.clockAudioEnvelope();
//Channel 2:
this.channel2.clockAudioEnvelope();
//Channel 4:
this.channel4.clockAudioEnvelope();
}
GameBoyAdvanceSound.prototype.computeAudioChannels = function () {
//Clock down the four audio channels to the next closest audio event:
this.channel1.FrequencyCounter = ((this.channel1.FrequencyCounter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0;
this.channel2.FrequencyCounter = ((this.channel2.FrequencyCounter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0;
this.channel3.counter = ((this.channel3.counter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0;
this.channel4.counter = ((this.channel4.counter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0;
//Channel 1 counter:
this.channel1.computeAudioChannel();
//Channel 2 counter:
this.channel2.computeAudioChannel();
//Channel 3 counter:
this.channel3.computeAudioChannel();
//Channel 4 counter:
this.channel4.computeAudioChannel();
//Find the number of clocks to next closest counter event:
this.audioClocksUntilNextEventCounter = this.audioClocksUntilNextEvent = Math.min(this.channel1.FrequencyCounter | 0, this.channel2.FrequencyCounter | 0, this.channel3.counter | 0, this.channel4.counter | 0) | 0;
}
if (typeof Math.imul == "function") {
//Math.imul found, insert the optimized path in:
GameBoyAdvanceSound.prototype.CGBMixerOutputLevelCache = function () {
this.CGBMixerOutputCacheLeft = Math.imul(((this.channel1.currentSampleLeftTrimary | 0) + (this.channel2.currentSampleLeftTrimary | 0) + (this.channel3.currentSampleLeftSecondary | 0) + (this.channel4.currentSampleLeftSecondary | 0)) | 0, this.VinLeftChannelMasterVolume | 0) | 0;
this.CGBMixerOutputCacheRight = Math.imul(((this.channel1.currentSampleRightTrimary | 0) + (this.channel2.currentSampleRightTrimary | 0) + (this.channel3.currentSampleRightSecondary | 0) + (this.channel4.currentSampleRightSecondary | 0)) | 0, this.VinRightChannelMasterVolume | 0) | 0;
this.CGBFolder();
}
}
else {
//Math.imul not found, use the compatibility method:
GameBoyAdvanceSound.prototype.CGBMixerOutputLevelCache = function () {
this.CGBMixerOutputCacheLeft = (this.channel1.currentSampleLeftTrimary + this.channel2.currentSampleLeftTrimary + this.channel3.currentSampleLeftSecondary + this.channel4.currentSampleLeftSecondary) * this.VinLeftChannelMasterVolume;
this.CGBMixerOutputCacheRight = (this.channel1.currentSampleRightTrimary + this.channel2.currentSampleRightTrimary + this.channel3.currentSampleRightSecondary + this.channel4.currentSampleRightSecondary) * this.VinRightChannelMasterVolume;
this.CGBFolder();
}
}
GameBoyAdvanceSound.prototype.writeWAVE8 = function (address, data) {
address = address | 0;
data = data | 0;
this.IOCore.updateTimerClocking();
this.channel3.writeWAVE8(address | 0, data | 0);
}
GameBoyAdvanceSound.prototype.readWAVE8 = function (address) {
address = address | 0;
this.IOCore.updateTimerClocking();
return this.channel3.readWAVE8(address | 0) | 0;
}
GameBoyAdvanceSound.prototype.writeWAVE16 = function (address, data) {
address = address | 0;
data = data | 0;
this.IOCore.updateTimerClocking();
this.channel3.writeWAVE16(address >> 1, data | 0);
}
GameBoyAdvanceSound.prototype.readWAVE16 = function (address) {
address = address | 0;
this.IOCore.updateTimerClocking();
return this.channel3.readWAVE16(address >> 1) | 0;
}
GameBoyAdvanceSound.prototype.writeWAVE32 = function (address, data) {
address = address | 0;
data = data | 0;
this.IOCore.updateTimerClocking();
this.channel3.writeWAVE32(address >> 2, data | 0);
}
GameBoyAdvanceSound.prototype.readWAVE32 = function (address) {
address = address | 0;
this.IOCore.updateTimerClocking();
return this.channel3.readWAVE32(address >> 2) | 0;
}
GameBoyAdvanceSound.prototype.writeFIFOA8 = function (data) {
data = data | 0;
this.IOCore.updateTimerClocking();
this.FIFOABuffer.push8(data | 0);
this.checkFIFOAPendingSignal();
}
GameBoyAdvanceSound.prototype.writeFIFOB8 = function (data) {
data = data | 0;
this.IOCore.updateTimerClocking();
this.FIFOBBuffer.push8(data | 0);
this.checkFIFOBPendingSignal();
}
GameBoyAdvanceSound.prototype.writeFIFOA16 = function (data) {
data = data | 0;
this.IOCore.updateTimerClocking();
this.FIFOABuffer.push16(data | 0);
this.checkFIFOAPendingSignal();
}
GameBoyAdvanceSound.prototype.writeFIFOB16 = function (data) {
data = data | 0;
this.IOCore.updateTimerClocking();
this.FIFOBBuffer.push16(data | 0);
this.checkFIFOBPendingSignal();
}
GameBoyAdvanceSound.prototype.writeFIFOA32 = function (data) {
data = data | 0;
this.IOCore.updateTimerClocking();
this.FIFOABuffer.push32(data | 0);
this.checkFIFOAPendingSignal();
}
GameBoyAdvanceSound.prototype.writeFIFOB32 = function (data) {
data = data | 0;
this.IOCore.updateTimerClocking();
this.FIFOBBuffer.push32(data | 0);
this.checkFIFOBPendingSignal();
}
GameBoyAdvanceSound.prototype.checkFIFOAPendingSignal = function () {
if (this.FIFOABuffer.requestingDMA()) {
this.dmaChannel1.soundFIFOARequest();
}
}
GameBoyAdvanceSound.prototype.checkFIFOBPendingSignal = function () {
if (this.FIFOBBuffer.requestingDMA()) {
this.dmaChannel2.soundFIFOBRequest();
}
}
GameBoyAdvanceSound.prototype.AGBDirectSoundAFIFOClear = function () {
this.FIFOABuffer.clear();
this.AGBDirectSoundATimerIncrement();
}
GameBoyAdvanceSound.prototype.AGBDirectSoundBFIFOClear = function () {
this.FIFOBBuffer.clear();
this.AGBDirectSoundBTimerIncrement();
}
GameBoyAdvanceSound.prototype.AGBDirectSoundTimer0ClockTick = function () {
this.audioJIT();
if (this.soundMasterEnabled) {
if ((this.AGBDirectSoundATimer | 0) == 0) {
this.AGBDirectSoundATimerIncrement();
}
if ((this.AGBDirectSoundBTimer | 0) == 0) {
this.AGBDirectSoundBTimerIncrement();
}
}
}
GameBoyAdvanceSound.prototype.AGBDirectSoundTimer1ClockTick = function () {
this.audioJIT();
if (this.soundMasterEnabled) {
if ((this.AGBDirectSoundATimer | 0) == 1) {
this.AGBDirectSoundATimerIncrement();
}
if ((this.AGBDirectSoundBTimer | 0) == 1) {
this.AGBDirectSoundBTimerIncrement();
}
}
}
GameBoyAdvanceSound.prototype.nextFIFOAEventTime = function () {
var nextEventTime = 0x7FFFFFFF;
if (this.soundMasterEnabled) {
if (!this.FIFOABuffer.requestingDMA()) {
var samplesUntilDMA = this.FIFOABuffer.samplesUntilDMATrigger() | 0;
if ((this.AGBDirectSoundATimer | 0) == 0) {
nextEventTime = this.IOCore.timer.nextTimer0Overflow(samplesUntilDMA | 0) | 0;
}
else {
nextEventTime = this.IOCore.timer.nextTimer1Overflow(samplesUntilDMA | 0) | 0;
}
}
else {
nextEventTime = 0;
}
}
return nextEventTime | 0;
}
GameBoyAdvanceSound.prototype.nextFIFOBEventTime = function () {
var nextEventTime = 0x7FFFFFFF;
if (this.soundMasterEnabled) {
if (!this.FIFOBBuffer.requestingDMA()) {
var samplesUntilDMA = this.FIFOBBuffer.samplesUntilDMATrigger() | 0;
if ((this.AGBDirectSoundBTimer | 0) == 0) {
nextEventTime = this.IOCore.timer.nextTimer0Overflow(samplesUntilDMA | 0) | 0;
}
else {
nextEventTime = this.IOCore.timer.nextTimer1Overflow(samplesUntilDMA | 0) | 0;
}
}
else {
nextEventTime = 0;
}
}
return nextEventTime | 0;
}
GameBoyAdvanceSound.prototype.AGBDirectSoundATimerIncrement = function () {
this.AGBDirectSoundA = this.FIFOABuffer.shift() | 0;
this.checkFIFOAPendingSignal();
this.AGBFIFOAFolder();
}
GameBoyAdvanceSound.prototype.AGBDirectSoundBTimerIncrement = function () {
this.AGBDirectSoundB = this.FIFOBBuffer.shift() | 0;
this.checkFIFOBPendingSignal();
this.AGBFIFOBFolder();
}
GameBoyAdvanceSound.prototype.AGBFIFOAFolder = function () {
this.AGBDirectSoundAFolded = this.AGBDirectSoundA >> (this.AGBDirectSoundAShifter | 0);
}
GameBoyAdvanceSound.prototype.AGBFIFOBFolder = function () {
this.AGBDirectSoundBFolded = this.AGBDirectSoundB >> (this.AGBDirectSoundBShifter | 0);
}
GameBoyAdvanceSound.prototype.CGBFolder = function () {
this.CGBMixerOutputCacheLeftFolded = (this.CGBMixerOutputCacheLeft << (this.CGBOutputRatio | 0)) >> 1;
this.CGBMixerOutputCacheRightFolded = (this.CGBMixerOutputCacheRight << (this.CGBOutputRatio | 0)) >> 1;
}
GameBoyAdvanceSound.prototype.mixerOutputLevelCache = function () {
this.mixerOutputCacheLeft = Math.min(Math.max((((this.AGBDirectSoundALeftCanPlay) ? (this.AGBDirectSoundAFolded | 0) : 0) +
((this.AGBDirectSoundBLeftCanPlay) ? (this.AGBDirectSoundBFolded | 0) : 0) +
(this.CGBMixerOutputCacheLeftFolded | 0) + (this.mixerSoundBIAS | 0)) | 0, 0) | 0, 0x3FF) & this.PWMBitDepthMask;
this.mixerOutputCacheRight = Math.min(Math.max((((this.AGBDirectSoundARightCanPlay) ? (this.AGBDirectSoundAFolded | 0) : 0) +
((this.AGBDirectSoundBRightCanPlay) ? (this.AGBDirectSoundBFolded | 0) : 0) +
(this.CGBMixerOutputCacheRightFolded | 0) + (this.mixerSoundBIAS | 0)) | 0, 0) | 0, 0x3FF) & this.PWMBitDepthMask;
}
GameBoyAdvanceSound.prototype.setNR52 = function (data) {
data = data | 0;
this.nr52 = data | this.nr52;
}
GameBoyAdvanceSound.prototype.unsetNR52 = function (data) {
data = data | 0;
this.nr52 = data & this.nr52;
}
GameBoyAdvanceSound.prototype.readSOUND1CNT8_0 = function () {
//NR10:
return this.channel1.readSOUND1CNT8_0() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT8_0 = function (data) {
//NR10:
data = data | 0;
if (this.soundMasterEnabled) {
this.IOCore.updateTimerClocking();
this.audioJIT();
this.channel1.writeSOUND1CNT8_0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND1CNT8_2 = function () {
//NR11:
return this.channel1.readSOUND1CNT8_2() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT8_2 = function (data) {
//NR11:
data = data | 0;
if (this.soundMasterEnabled) {
this.IOCore.updateTimerClocking();
this.audioJIT();
this.channel1.writeSOUND1CNT8_2(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND1CNT8_3 = function () {
//NR12:
return this.channel1.readSOUND1CNT8_3() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT8_3 = function (data) {
//NR12:
data = data | 0;
if (this.soundMasterEnabled) {
this.IOCore.updateTimerClocking();
this.audioJIT();
this.channel1.writeSOUND1CNT8_3(data | 0);
}
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT16 = function (data) {
data = data | 0;
if (this.soundMasterEnabled) {
this.IOCore.updateTimerClocking();
this.audioJIT();
//NR11:
this.channel1.writeSOUND1CNT8_2(data | 0);
//NR12:
this.channel1.writeSOUND1CNT8_3(data >> 8);
}
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT32 = function (data) {
data = data | 0;
if (this.soundMasterEnabled) {
this.IOCore.updateTimerClocking();
this.audioJIT();
//NR10:
this.channel1.writeSOUND1CNT8_0(data | 0);
//NR11:
this.channel1.writeSOUND1CNT8_2(data >> 16);
//NR12:
this.channel1.writeSOUND1CNT8_3(data >> 24);
}
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT_X0 = function (data) {
//NR13:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel1.writeSOUND1CNT_X0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND1CNT_X = function () {
//NR14:
return this.channel1.readSOUND1CNT_X() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND1CNT_X1 = function (data) {
//NR14:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel1.writeSOUND1CNT_X1(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND2CNT_L0 = function () {
//NR21:
return this.channel2.readSOUND2CNT_L0() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND2CNT_L0 = function (data) {
data = data | 0;
//NR21:
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel2.writeSOUND2CNT_L0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND2CNT_L1 = function () {
//NR22:
return this.channel2.readSOUND2CNT_L1() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND2CNT_L1 = function (data) {
data = data | 0;
//NR22:
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel2.writeSOUND2CNT_L1(data | 0);
}
}
GameBoyAdvanceSound.prototype.writeSOUND2CNT_H0 = function (data) {
data = data | 0;
//NR23:
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel2.writeSOUND2CNT_H0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND2CNT_H = function () {
//NR24:
return this.channel2.readSOUND2CNT_H() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND2CNT_H1 = function (data) {
data = data | 0;
//NR24:
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel2.writeSOUND2CNT_H1(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND3CNT_L = function () {
//NR30:
return this.channel3.readSOUND3CNT_L() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND3CNT_L = function (data) {
//NR30:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel3.writeSOUND3CNT_L(data | 0);
}
}
GameBoyAdvanceSound.prototype.writeSOUND3CNT_H0 = function (data) {
//NR31:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel3.writeSOUND3CNT_H0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND3CNT_H = function () {
//NR32:
return this.channel3.readSOUND3CNT_H() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND3CNT_H1 = function (data) {
//NR32:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel3.writeSOUND3CNT_H1(data | 0);
}
}
GameBoyAdvanceSound.prototype.writeSOUND3CNT_X0 = function (data) {
//NR33:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel3.writeSOUND3CNT_X0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND3CNT_X = function () {
//NR34:
return this.channel3.readSOUND3CNT_X() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND3CNT_X1 = function (data) {
//NR34:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel3.writeSOUND3CNT_X1(data | 0);
}
}
GameBoyAdvanceSound.prototype.writeSOUND4CNT_L0 = function (data) {
//NR41:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel4.writeSOUND4CNT_L0(data | 0);
}
}
GameBoyAdvanceSound.prototype.writeSOUND4CNT_L1 = function (data) {
//NR42:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel4.writeSOUND4CNT_L1(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND4CNT_L = function () {
//NR42:
return this.channel4.readSOUND4CNT_L() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND4CNT_H0 = function (data) {
//NR43:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel4.writeSOUND4CNT_H0(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND4CNT_H0 = function () {
//NR43:
return this.channel4.readSOUND4CNT_H0() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUND4CNT_H1 = function (data) {
//NR44:
data = data | 0;
if (this.soundMasterEnabled) {
this.audioJIT();
this.channel4.writeSOUND4CNT_H1(data | 0);
}
}
GameBoyAdvanceSound.prototype.readSOUND4CNT_H1 = function () {
//NR44:
return this.channel4.readSOUND4CNT_H1() | 0;
}
GameBoyAdvanceSound.prototype.writeSOUNDCNT_L0 = function (data) {
//NR50:
data = data | 0;
if (this.soundMasterEnabled && (this.nr50 | 0) != (data | 0)) {
this.audioJIT();
this.nr50 = data | 0;
this.VinLeftChannelMasterVolume = (((data >> 4) & 0x07) + 1) | 0;
this.VinRightChannelMasterVolume = ((data & 0x07) + 1) | 0;
}
}
GameBoyAdvanceSound.prototype.readSOUNDCNT_L0 = function () {
//NR50:
return 0x88 | this.nr50;
}
GameBoyAdvanceSound.prototype.writeSOUNDCNT_L1 = function (data) {
//NR51:
data = data | 0;
if (this.soundMasterEnabled && (this.nr51 | 0) != (data | 0)) {
this.audioJIT();
this.nr51 = data | 0;
this.rightChannel1 = ((data & 0x01) == 0x01);
this.rightChannel2 = ((data & 0x02) == 0x02);
this.rightChannel3 = ((data & 0x04) == 0x04);
this.rightChannel4 = ((data & 0x08) == 0x08);
this.leftChannel1 = ((data & 0x10) == 0x10);
this.leftChannel2 = ((data & 0x20) == 0x20);
this.leftChannel3 = ((data & 0x40) == 0x40);
this.leftChannel4 = (data > 0x7F);
}
}
GameBoyAdvanceSound.prototype.readSOUNDCNT_L1 = function () {
//NR51:
return this.nr51 | 0;
}
GameBoyAdvanceSound.prototype.writeSOUNDCNT_H0 = function (data) {
//NR60:
data = data | 0;
this.audioJIT();
this.CGBOutputRatio = data & 0x3;
this.AGBDirectSoundAShifter = (data & 0x04) >> 2;
this.AGBDirectSoundBShifter = (data & 0x08) >> 3;
this.nr60 = data | 0;
}
GameBoyAdvanceSound.prototype.readSOUNDCNT_H0 = function () {
//NR60:
return this.nr60 | 0;
}
GameBoyAdvanceSound.prototype.writeSOUNDCNT_H1 = function (data) {
//NR61:
data = data | 0;
this.audioJIT();
this.AGBDirectSoundARightCanPlay = ((data & 0x1) != 0);
this.AGBDirectSoundALeftCanPlay = ((data & 0x2) != 0);
this.AGBDirectSoundATimer = (data & 0x4) >> 2;
if ((data & 0x08) != 0) {
this.AGBDirectSoundAFIFOClear();
}
this.AGBDirectSoundBRightCanPlay = ((data & 0x10) != 0);
this.AGBDirectSoundBLeftCanPlay = ((data & 0x20) != 0);
this.AGBDirectSoundBTimer = (data & 0x40) >> 6;
if ((data & 0x80) != 0) {
this.AGBDirectSoundBFIFOClear();
}
this.nr61 = data | 0;
}
GameBoyAdvanceSound.prototype.readSOUNDCNT_H1 = function () {
//NR61:
return this.nr61 | 0;
}
GameBoyAdvanceSound.prototype.writeSOUNDCNT_X = function (data) {
//NR52:
data = data | 0;
if (!this.soundMasterEnabled && (data | 0) > 0x7F) {
this.audioJIT();
this.audioEnabled();
}
else if (this.soundMasterEnabled && (data | 0) < 0x80) {
this.audioJIT();
this.audioDisabled();
}
}
GameBoyAdvanceSound.prototype.readSOUNDCNT_X = function () {
//NR52:
return this.nr52 | 0;
}
GameBoyAdvanceSound.prototype.writeSOUNDBIAS0 = function (data) {
//NR62:
data = data | 0;
this.audioJIT();
this.mixerSoundBIAS = this.mixerSoundBIAS & 0x300;
this.mixerSoundBIAS = this.mixerSoundBIAS | data;
this.nr62 = data | 0;
}
GameBoyAdvanceSound.prototype.readSOUNDBIAS0 = function () {
//NR62:
return this.nr62 | 0;
}
GameBoyAdvanceSound.prototype.writeSOUNDBIAS1 = function (data) {
//NR63:
data = data | 0;
this.audioJIT();
this.mixerSoundBIAS = this.mixerSoundBIAS & 0xFF;
this.mixerSoundBIAS = this.mixerSoundBIAS | ((data & 0x3) << 8);
this.PWMWidthShadow = 0x200 >> ((data & 0xC0) >> 6);
this.PWMBitDepthMaskShadow = ((this.PWMWidthShadow | 0) - 1) << (1 + ((data & 0xC0) >> 6));
this.nr63 = data | 0;
}
GameBoyAdvanceSound.prototype.readSOUNDBIAS1 = function () {
//NR63:
return this.nr63 | 0;
}