UNPKG

nes-emu

Version:

A NES emulator

93 lines (82 loc) 3.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _synthesis = require("../synthesis"); var _config = _interopRequireDefault(require("../../config")); var _helpers = require("../../helpers"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * The noise channel produces noise with a pseudo-random bit generator. It supports: * - note lengths * - volume envelope / constant volume */ class NoiseChannel { constructor() { _helpers.WithContext.apply(this); this.lengthCounter = new _synthesis.LengthCounter(); this.volumeEnvelope = new _synthesis.VolumeEnvelope(); this.shift = 0b1; // (the shift register is 15 bits wide) this.dividerCount = 0; } /** When a context is loaded. */ onLoad(context) { this.registers = context.apu.registers.noise; } /** Generates a new sample. */ sample() { if (!this.isEnabled) return 0; const volume = this.registers.control.constantVolume ? this.registers.control.volumeOrEnvelopePeriod : this.volumeEnvelope.volume; this._processNoise(); /* * The mixer receives **the volume** except when * - Bit 0 of the shift register is set, or * - The length counter is zero */ return !this.lengthCounter.didFinish && !_helpers.Byte.getBit(this.shift, 0) ? volume * _config.default.NOISE_CHANNEL_VOLUME : 0; } /** Updates the envelope. */ quarterBeat() { this.volumeEnvelope.clock(this.registers.control.volumeOrEnvelopePeriod, this.registers.control.envelopeLoopOrLengthCounterHalt); } /** Updates length counter. */ halfBeat() { this.lengthCounter.clock(this.isEnabled, this.registers.control.envelopeLoopOrLengthCounterHalt); } /** Returns a snapshot of the current state. */ getSaveState() { return { lengthCounter: this.lengthCounter.getSaveState(), volumeEnvelope: this.volumeEnvelope.getSaveState(), shift: this.shift, dividerCount: this.dividerCount }; } /** Restores state from a snapshot. */ setSaveState(saveState) { this.lengthCounter.setSaveState(saveState.lengthCounter); this.volumeEnvelope.setSaveState(saveState.volumeEnvelope); this.shift = saveState.shift; this.dividerCount = saveState.dividerCount; } /** Returns whether the channel is enabled or not. */ get isEnabled() { return this.context.apu.registers.apuControl.enableNoise; } _processNoise() { this.dividerCount++; if (this.dividerCount >= this.registers.form.period) this.dividerCount = 0;else return; /** * When the timer clocks the shift register, the following actions occur in order: * 1. Feedback is calculated as the exclusive-OR of bit 0 and one other bit: bit 6 if Mode flag is set, otherwise bit 1. * 2. The shift register is shifted right by one bit. * 3. Bit 14, the leftmost bit, is set to the feedback calculated earlier. */ const bitPosition = this.registers.form.mode ? 6 : 1; const bit = _helpers.Byte.getBit(this.shift, bitPosition); const feedback = _helpers.Byte.getBit(this.shift, 0) ^ bit; this.shift = this.shift >> 1 | feedback << 14; } } exports.default = NoiseChannel;