Skip to content

Commit

Permalink
keep working on triangle
Browse files Browse the repository at this point in the history
  • Loading branch information
shicks committed Jan 12, 2016
1 parent 2cd72ae commit 503821e
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 22 deletions.
34 changes: 26 additions & 8 deletions apu/apu.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ import Triangle from './triangle';
// 4-step mode. We never need to worry about IRQ.


class Enabler {
constructor(id) {
const element = document.getElementById(id);
this.enabled = true;
if (element) {
element.addEventListener('click', () => {
this.enabled = element.checked;
console.log(id + ' => ' + this.enabled);
});
}
}
}

export default class Apu {
/**
* @param {!Memory} mem
Expand All @@ -39,14 +52,20 @@ export default class Apu {
this.clock_ = clock;
this.pulse1_ = new Pulse(mem, 0x4000);
this.pulse2_ = new Pulse(mem, 0x4004);
//this.triangle_ = new Triangle(mem);
this.triangle_ = new Triangle(mem);
this.noise_ = new Noise(mem);
this.steps_ = [];
this.last_ = 0;
this.wait_ = 2;

this.frameCounter_ = 0;

this.pulse1Enabled = new Enabler('pulse1_enabled');
this.pulse2Enabled = new Enabler('pulse2_enabled');
this.triangleEnabled = new Enabler('triangle_enabled');
this.noiseEnabled = new Enabler('noise_enabled');


// TODO - add a callback when volume changes, so we don't
// need to keep recomputing the mixer every single time!

Expand Down Expand Up @@ -75,12 +94,11 @@ export default class Apu {
// TODO - distinguish half from quarter frames.
this.pulse1_.clockFrame();
this.pulse2_.clockFrame();
//this.triangle_.clockFrame();
this.triangle_.clockFrame();
this.noise_.clockFrame();
}

//this.triangle_.clockSequencer(); // clocks every cycle

this.triangle_.clockSequencer(); // clocks every cycle
if (!--this.wait_) {
this.pulse1_.clockSequencer();
this.pulse2_.clockSequencer();
Expand Down Expand Up @@ -110,13 +128,13 @@ export default class Apu {
}

volume() {
const pulse1 = this.pulse1_.volume();
const pulse2 = this.pulse2_.volume();
const pulse1 = this.pulse1Enabled.enabled ? this.pulse1_.volume() : 0;
const pulse2 = this.pulse2Enabled.enabled ? this.pulse2_.volume() : 0;
const pulseOut = (pulse1 || pulse2) &&
95.88 / (8128 / (pulse1 + pulse2) + 100);

const triangle = 0; // this.triangle_.volume();
const noise = this.noise_.volume();
const triangle = this.triangleEnabled.enabled ? this.triangle_.volume() : 0;
const noise = this.noiseEnabled.enabled ? this.noise_.volume() : 0;
const dmc = 0; // this.dmc_.volume();
const tndOut = (triangle || noise || dmc) &&
159.79 / (1 / (triangle / 8227 + noise / 12241 + dmc / 22638) + 100);
Expand Down
2 changes: 1 addition & 1 deletion apu/lengthcounter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default class LengthCounter {
/** @private @const {!Memory.Register<number>} */
this.reload_ = mem.int(base + 3, 3, 5);
/** @private {number} */
this.counter_ = 0;
this.counter_ = 1; // TODO - so that it's nonzero if it's never clocked...?

/** @private {function()} */
this.disableCallback_ = function() {};
Expand Down
37 changes: 26 additions & 11 deletions apu/triangle.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/**
* @fileoverview NES APU Emulator.
*/

import Memory from '../mem';
import LengthCounter from './lengthcounter';

// TODO - this pops a lot - can we smooth it out?!?

export default class Triangle {
/**
* @param {!Memory} mem
Expand Down Expand Up @@ -35,11 +33,22 @@ export default class Triangle {
/** @private {number} */
this.sequence_ = 0;


for (let i = 0x4008; i < 0x400C; i++) mem.listen(i, () => this.print());

mem.listen(0x400B, () => this.linearCounterReloadFlag_ = true);
this.lengthCounter_.onDisable(() => this.volume_ = this.computeVolume_());
this.lengthCounter_.onDisable(() => this.volume_ = 0);
}

print() {}
print() {
return;
console.log(`TRIANGLE
linear ${this.linearCounter_} of ${this.linearCounterReloadValue_.get()}
control ${this.control_.get()}
sequence ${this.sequenceTimer_} of ${this.sequenceTimerPeriod_.get()} => ${this.sequence_}
length ${this.lengthCounter_.enabled_.get()} ${this.lengthCounter_.counter_} ${this.lengthCounter_.reload_.get()}
volume ${this.volume_}`);
}

/**
* @return {number} The value of the waveform, from 0 to 15 (?)
Expand All @@ -52,8 +61,10 @@ export default class Triangle {
* @return {number} Computes the volume.
*/
computeVolume_() {
return this.lengthCounter_.enabled() && this.linearCounter_ > 0 ?
WAVEFORM[this.linearCounter_] : 0;
const v = this.lengthCounter_.enabled() && this.linearCounter_ > 0 ?
WAVEFORM[this.sequence_] : 0;
//if (v == 15) console.log('TRIANGLE 15');
return v;
}

/**
Expand All @@ -63,21 +74,25 @@ export default class Triangle {
clockFrame(quarter) {
if (this.linearCounterReloadFlag_) {
this.linearCounter_ = this.linearCounterReloadValue_.get();
} else if (this.linearCounter_ >= 0) {
} else if (this.linearCounter_ > 0) {
this.linearCounter_--;
if (!this.linearCounter_) this.volume_ = this.computeVolume_();
if (!this.linearCounter_) this.volume_ = 0;
}

if (this.control_.get()) {
this.linearCounterReloadFlag_ = false;
}

if (quarter % 2 && !this.control_.get()) {
this.lengthCounter_.clock();
}
}

/** Clocks the sequencer. */
clockSequencer() {
if (this.sequenceTimer_ == 0) {
this.sequence_ = (this.sequence_ + 1) % 32;
this.sequence_ = this.sequenceTimerPeriod_.get();
this.sequenceTimer_ = this.sequenceTimerPeriod_.get();
this.volume_ = this.computeVolume_();
} else {
this.sequenceTimer_--;
Expand Down
10 changes: 10 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@
<input type="submit" id="stop" value="stop" style="display:none;">
<input type="submit" id="next" value="next" style="display:none;">

<div>
<input type="checkbox" id="pulse1_enabled" checked> Pulse 1: <span id="pulse1"></span>
<br>
<input type="checkbox" id="pulse2_enabled" checked> Pulse 2: <span id="pulse2"></span>
<br>
<input type="checkbox" id="triangle_enabled" checked> Triangle: <span id="triangle"></span>
<br>
<input type="checkbox" id="noise_enabled" checked> Noise: <span id="noise"></span>
</div>

<div id="log" style="white-space: pre; font-family: monospace;"></div>
10 changes: 8 additions & 2 deletions nsfplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export default class NsfPlayer {

if (this.promise != promise) return;
if (this.node.bufferTime() == 0) console.log('buffer underrun!');
for (let i = 0; i < 100; i++) {
// TODO - use i < 100 and requestAnimationFrame to be smoother?!?
for (let i = 0; i < 10; i++) {
for (let frameCycle = this.cyclesPerFrame; frameCycle >= 0; frameCycle--) {
if (frameCycle != frameCycle) throw new Error('NaN');
if (this.cpu.PC != 0xFFFF) this.cpu.clock();
Expand All @@ -59,7 +60,12 @@ export default class NsfPlayer {
this.writer.write(this.apu.steps(), this.clock.time)
//.then(() => this.play(promise));
// .then(() => { setTimeout(() => this.play(promise), 0); });
.then(() => { requestAnimationFrame(() => this.play(promise)); });
// TODO(sdh): for some reason, requestAnimationFrame (and/or i<100)
// causes a weird glitch on the 2nd and later song...?!?
//.then(() => { requestAnimationFrame(() => this.play(promise)); });
.then(() => {
setTimeout(() => this.play(promise), 1000 * (this.node.bufferTime()) - 60);
});
// console.log('Yield data', data);
}

Expand Down

0 comments on commit 503821e

Please sign in to comment.