Permalink
Browse files

Adding audio support.

  • Loading branch information...
1 parent cb7bdc2 commit 2f1427d09e59fe329093d6d8444b65107b32a9ae @reu committed Mar 24, 2013
Showing with 122 additions and 0 deletions.
  1. +3 −0 chip8.js
  2. +2 −0 index.html
  3. +16 −0 src/cpu.js
  4. +61 −0 src/speaker.js
  5. +19 −0 test/cpu.test.js
  6. +19 −0 test/speaker.test.js
  7. +2 −0 test/tests.html
View
@@ -7,6 +7,7 @@
*
* @property {Chip8.CPU} vm the CHIP8 CPU
* @property {Chip8.Screen} screen where the emulator will render the graphics
+ * @property {Chip8.Speaker} speaker the audio engine used to play the sound
* @property {Chip8.Input} input the input handler
*/
var Chip8 = function Chip8() {
@@ -52,6 +53,7 @@
if (request.response) {
cpu.screen = self.screen;
cpu.input = self.input;
+ cpu.speaker = self.speaker;
self.stop();
cpu.reset();
cpu.loadProgram(new Uint8Array(request.response));
@@ -102,6 +104,7 @@
Chip8.CPU = require("./src/cpu.js");
Chip8.Keyboard = require("./src/keyboard.js");
Chip8.Screen = require("./src/screen.js");
+ Chip8.Speaker = require("./src/speaker.js");
module.exports = Chip8;
} else {
View
@@ -12,11 +12,13 @@
<script src="src/cpu.js"></script>
<script src="src/screen.js"></script>
<script src="src/keyboard.js"></script>
+ <script src="src/speaker.js"></script>
<script>
(function() {
var emulator = new Chip8;
emulator.input = new Chip8.Keyboard;
+ emulator.speaker = new Chip8.Speaker;
var canvas = document.querySelector("canvas");
emulator.screen = new Chip8.Screen.CanvasScreen(canvas.getContext("2d"));
View
@@ -10,6 +10,7 @@
this.stack = new Array;
this.screen = { clear: function() {}, render: function() {}, setPixel: function() {} };
this.input = { isKeyPressed: function(key) {}, clear: function() {} };
+ this.speaker = { clear: function() {}, play: function() {}, stop: function() {} };
this.v = new Uint8Array(16);
this.i = 0;
this.memory = new Uint8Array(4096);
@@ -63,6 +64,7 @@
this.soundTimer = 0;
this.screen.clear();
this.input.clear();
+ this.speaker.clear();
this.loadFonts();
this.paused = false;
}
@@ -93,6 +95,8 @@
if (!this.paused) {
this.updateTimers();
}
+
+ this.playSound();
this.render();
}
@@ -453,6 +457,18 @@
this.screen.render();
}
+ /**
+ * Play the speaker until the sound timer reaches zero.
+ * @method playSound
+ */
+ this.playSound = function() {
+ if (this.soundTimer > 0) {
+ this.speaker.play();
+ } else {
+ this.speaker.stop();
+ }
+ }
+
/**
* Updates the CPU delay and sound timers.
* More info at: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.5
View
@@ -0,0 +1,61 @@
+(function() {
+ /**
+ * A speaker used to emit sounds.
+ *
+ * @class Chip8.Speaker
+ * @constructor
+ */
+ var Speaker = function Speaker() {
+ var contextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext)
+ , context
+ , oscillator
+ , gain;
+
+ if (contextClass) {
+ context = new contextClass();
+ gain = context.createGain();
+ gain.connect(context.destination);
+ }
+
+ /**
+ * Play a triangle wave sound.
+ * @method play
+ * @param {Integer} frequency the frequency of the sound wave
+ */
+ this.play = function(frequency) {
+ if (context && !oscillator) {
+ oscillator = context.createOscillator();
+ oscillator.frequency.value = frequency || 440;
+ // Originally it would be a square wave, but
+ // a triangle one sounds much better...
+ oscillator.type = oscillator.TRIANGLE;
+ oscillator.connect(gain);
+ oscillator.start(0);
+ }
+ }
+
+ /**
+ * Stop the speaker.
+ * @method stop
+ */
+ this.stop = function() {
+ if (oscillator) {
+ oscillator.stop(0);
+ oscillator.disconnect(0);
+ oscillator = null;
+ }
+ }
+
+ /**
+ * Clear the speaker state.
+ * @method clear
+ */
+ this.clear = this.stop;
+ }
+
+ if (typeof module != "undefined") {
+ module.exports = Speaker;
+ } else {
+ window.Chip8.Speaker = Speaker;
+ }
+})();
View
@@ -76,6 +76,11 @@ describe("Chip8.CPU", function() {
cpu.cycle();
});
+ it("play sounds", function(done) {
+ cpu.playSound = done;
+ cpu.cycle();
+ });
+
it("update the timers", function(done) {
cpu.updateTimers = done;
cpu.cycle();
@@ -135,6 +140,20 @@ describe("Chip8.CPU", function() {
});
});
+ describe("#playSound", function() {
+ it("plays the speaker if the soundTimer is greater than zero", function(done) {
+ cpu.soundTimer = 1;
+ cpu.speaker.play = done;
+ cpu.playSound();
+ });
+
+ it("stops the speaker if the soundTimer is zero", function(done) {
+ cpu.soundTimer = 0;
+ cpu.speaker.stop = done;
+ cpu.playSound();
+ });
+ });
+
describe("#perform(opcode)", function() {
// Shared examples
var shouldIncrementProgramCounter = function(opcode) {
View
@@ -0,0 +1,19 @@
+describe("Chip8.Speaker", function() {
+ var speaker;
+
+ beforeEach(function() {
+ speaker = new Chip8.Speaker;
+ });
+
+ describe("#play", function() {
+ it("is a method", function() {
+ expect(speaker.play).to.be.a("function");
+ });
+ });
+
+ describe("#stop", function() {
+ it("is a method", function() {
+ expect(speaker.stop).to.be.a("function");
+ });
+ });
+});
View
@@ -15,8 +15,10 @@
<script src="../src/cpu.js"></script>
<script src="../src/screen.js"></script>
<script src="../src/keyboard.js"></script>
+ <script src="../src/speaker.js"></script>
<script src="cpu.test.js"></script>
<script src="keyboard.test.js"></script>
+ <script src="speaker.test.js"></script>
<script>
mocha.run();
</script>

0 comments on commit 2f1427d

Please sign in to comment.