UNPKG

nes-emu

Version:

A NES emulator

80 lines (70 loc) 2.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _synthesis = require("../synthesis"); var _constants = _interopRequireDefault(require("../../constants")); var _helpers = require("../../helpers"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * The triangle channel produces a quantized triangle wave. It supports: * - note lengths * - an extra high-resolution ("linear") length counter */ class TriangleChannel { constructor() { _helpers.WithContext.apply(this); this.oscillator = new _synthesis.TriangleOscillator(); this.lengthCounter = new _synthesis.LengthCounter(); this.linearLengthCounter = new _synthesis.LinearLengthCounter(); this.outputSample = 0; } /** When a context is loaded. */ onLoad(context) { this.registers = context.apu.registers.triangle; } /** Generates a new sample. */ sample(isNewSample) { if (!this.isEnabled) return this.outputSample; const timer = _helpers.Byte.to16Bit(this.registers.lclTimerHigh.timerHigh, this.registers.timerLow.value); // this channel only outputs a sample if the timer is between [2, 0x7ff] if (!(timer >= 2 && timer <= 0x7ff)) return 0; this.oscillator.frequency = _constants.default.FREQ_CPU_HZ / (16 * (timer + 1)) / 2; // from nesdev: f = fCPU / (16 * (t + 1)) // (the pitch is one octave below the pulse channels with an equivalent timer value) // (i.e. use the formula above but divide the resulting frequency by two). const isActive = !this.lengthCounter.didFinish && !this.linearLengthCounter.didFinish; if (isActive) this.outputSample = this.oscillator.sample(isNewSample); return this.outputSample; } /** Updates linear length counter. */ quarterBeat() { this.linearLengthCounter.clock(this.isEnabled, this.registers.linearLCL.halt); } /** Updates length counter. */ halfBeat() { this.lengthCounter.clock(this.isEnabled, this.registers.linearLCL.halt); } /** Returns a snapshot of the current state. */ getSaveState() { return { outputSample: this.outputSample, oscillator: this.oscillator.getSaveState(), lengthCounter: this.lengthCounter.getSaveState(), linearLengthCounter: this.linearLengthCounter.getSaveState() }; } /** Restores state from a snapshot. */ setSaveState(saveState) { this.outputSample = saveState.outputSample; this.oscillator.setSaveState(saveState.oscillator); this.lengthCounter.setSaveState(saveState.lengthCounter); this.linearLengthCounter.setSaveState(saveState.linearLengthCounter); } /** Returns whether the channel is enabled or not. */ get isEnabled() { return this.context.apu.registers.apuControl.enableTriangle; } } exports.default = TriangleChannel;