First commit

This commit is contained in:
Rex
2014-11-17 15:49:32 -05:00
parent b13fec66e3
commit 8a29151b1b
7 changed files with 1013 additions and 3 deletions
+12 -3
View File
@@ -1,4 +1,13 @@
soundForGames Sound for games
============= ===============
A micro-library to load, play and generate sound effects and music for games and interactive applications "Sound for Games" is micro-library to load, play and generate sound effects and music for
games and interactive applications. At it's heart it's composed of
just two, short functions. The `makeSound` function helps you
load and play sound files (mp3, wav, ogg, and webm). The `soundEffect`
function helps you generate a wide range of sound and music effects.
These two functions are completely modular free of dependencies, so
you can
copy and paste whichever parts of them you need into your own
projects. All the code is in the `sound.js` file. Take a look at the
`index.html` file for a working example of all the features.
+203
View File
@@ -0,0 +1,203 @@
<!doctype html>
<meta charset="utf-8">
<title>Sound for games</title>
<script src="sound.js"></script>
<body>
<p>Press the number keys to control the sounds
<br>
<br>Sounds from loaded sound files:
<br>
<br>a - Shoot sound
<br>b - Play music
<br>c - Pause music
<br>d - Restart music
<br>e - Play music from the 10 second point
<br>f - Play the bounce sound with an echo effect
<br>
<br>Generated sound effects
<br>
<br>g - Shoot
<br>h - Jump
<br>i - Explosion
<br>j - Bonus
</p>
<script>
/*
Part 1 - Working with sound files
=================================
*/
//Load the sounds
sounds.load([
"sounds/shoot.wav",
"sounds/music.wav",
"sounds/bounce.mp3",
]);
sounds.whenLoaded = setup;
function setup() {
console.log("sounds loaded");
//Create the sounds
var shoot = sounds["sounds/shoot.wav"],
music = sounds["sounds/music.wav"],
bounce = sounds["sounds/bounce.mp3"];
//Pan the shoot sound to the right
shoot.pan = 0.8;
//Make the music loop
music.loop = true;
//Set the pan to the left
music.pan = -0.8;
//Set the music volume
music.volume = 0.7;
//Set a reverb effect on the bounce sound
//arguments: duration, decay, reverse?
//music.setReverb(2, 2, false);
//Set the sound's `reverb` property to `false` to turn it off
//music.reverb = false;
//Add an echo effect to the bounce sound
//arguments: delay time, feedback time, optional frequency filtering
bounce.setEcho(0.2, 0.3, 1000);
//Set `echo` to false to turn it off
//bounce.echo = false;
//Optionally set the music playback rate to half speed
//music.playbackRate = 0.5;
//Capture the keyboard events
var a = keyboard(65),
b = keyboard(66),
c = keyboard(67),
d = keyboard(68),
e = keyboard(69),
f = keyboard(70);
//Control the sounds based on which keys are pressed
//Play the loaded shoot sound
a.press = function() { shoot.play() };
//Play the loaded music sound
b.press = function() {
if (!music.isPlaying) music.play();
console.log("music playing");
};
//Pause the music
c.press = function() {
music.pause();
console.log("music paused");
};
//Restart the music
d.press = function() {
music.restart();
console.log("music restarted");
};
//Play the music from the 10 second mark
e.press = function() {
music.playFrom(10);
console.log("music start point changed");
};
//Play the bounce sound
f.press = function() { bounce.play() };
}
/*
Part 2 - Working with sound effects
===================================
*/
var g = keyboard(71),
h = keyboard(72),
i = keyboard(73),
j = keyboard(74);
g.press = function(){ shootSound() };
h.press = function(){ jumpSound() };
i.press = function(){ explosionSound() };
j.press = function(){ bonusSound() };
//The sound effect functions
//The shoot sound
function shootSound() {
soundEffect(
1046.5, //frequency
0, //attack
0.3, //decay
"sawtooth", //waveform
1, //Volume
-0.8, //pan
0, //wait before playing
1200, //pitch bend amount
false, //reverse bend
0, //random pitch range
25, //dissonance
[0.2, 0.2, 2000], //echo: [delay, feedback, filter]
undefined //reverb: [duration, decay, reverse?]
);
}
//The jump sound
function jumpSound() {
soundEffect(
523.25, //frequency
0.05, //attack
0.2, //decay
"sine", //waveform
3, //volume
0.8, //pan
0, //wait before playing
600, //pitch bend amount
true, //reverse
100, //random pitch range
0, //dissonance
undefined, //echo: [delay, feedback, filter]
undefined //reverb: [duration, decay, reverse?]
);
}
//The explosion sound
function explosionSound() {
soundEffect(
16, //frequency
0, //attack
1, //decay
"sawtooth", //waveform
1, //volume
0, //pan
0, //wait before playing
0, //pitch bend amount
false, //reverse
0, //random pitch range
50, //dissonance
undefined, //echo: [delay, feedback, filter]
undefined //reverb: [duration, decay, reverse?]
);
}
//The bonus points sound
function bonusSound() {
//D
soundEffect(587.33, 0, 0.2, "square", 1, 0, 0);
//A
soundEffect(880, 0, 0.2, "square", 1, 0, 0.1);
//High D
soundEffect(1174.66, 0, 0.3, "square", 1, 0, 0.2);
}
</script>
</body>
+798
View File
@@ -0,0 +1,798 @@
/*
Prologue: Fixing the WebAudio API
--------------------------
The WebAudio API is so new that it's API is not consistently implemented properly across
all modern browsers. Thankfully, Chris Wilson's Audio Context Monkey Patch script
normalizes the API for maximum compatibility.
https://github.com/cwilso/AudioContext-MonkeyPatch/blob/gh-pages/AudioContextMonkeyPatch.js
It's included here.
Thank you, Chris!
*/
(function (global, exports, perf) {
'use strict';
function fixSetTarget(param) {
if (!param) // if NYI, just return
return;
if (!param.setTargetAtTime)
param.setTargetAtTime = param.setTargetValueAtTime;
}
if (window.hasOwnProperty('webkitAudioContext') &&
!window.hasOwnProperty('AudioContext')) {
window.AudioContext = webkitAudioContext;
if (!AudioContext.prototype.hasOwnProperty('createGain'))
AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
if (!AudioContext.prototype.hasOwnProperty('createDelay'))
AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
if (!AudioContext.prototype.hasOwnProperty('createScriptProcessor'))
AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createJavaScriptNode;
AudioContext.prototype.internal_createGain = AudioContext.prototype.createGain;
AudioContext.prototype.createGain = function() {
var node = this.internal_createGain();
fixSetTarget(node.gain);
return node;
};
AudioContext.prototype.internal_createDelay = AudioContext.prototype.createDelay;
AudioContext.prototype.createDelay = function(maxDelayTime) {
var node = maxDelayTime ? this.internal_createDelay(maxDelayTime) : this.internal_createDelay();
fixSetTarget(node.delayTime);
return node;
};
AudioContext.prototype.internal_createBufferSource = AudioContext.prototype.createBufferSource;
AudioContext.prototype.createBufferSource = function() {
var node = this.internal_createBufferSource();
if (!node.start) {
node.start = function ( when, offset, duration ) {
if ( offset || duration )
this.noteGrainOn( when, offset, duration );
else
this.noteOn( when );
}
}
if (!node.stop)
node.stop = node.noteOff;
fixSetTarget(node.playbackRate);
return node;
};
AudioContext.prototype.internal_createDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor;
AudioContext.prototype.createDynamicsCompressor = function() {
var node = this.internal_createDynamicsCompressor();
fixSetTarget(node.threshold);
fixSetTarget(node.knee);
fixSetTarget(node.ratio);
fixSetTarget(node.reduction);
fixSetTarget(node.attack);
fixSetTarget(node.release);
return node;
};
AudioContext.prototype.internal_createBiquadFilter = AudioContext.prototype.createBiquadFilter;
AudioContext.prototype.createBiquadFilter = function() {
var node = this.internal_createBiquadFilter();
fixSetTarget(node.frequency);
fixSetTarget(node.detune);
fixSetTarget(node.Q);
fixSetTarget(node.gain);
return node;
};
if (AudioContext.prototype.hasOwnProperty( 'createOscillator' )) {
AudioContext.prototype.internal_createOscillator = AudioContext.prototype.createOscillator;
AudioContext.prototype.createOscillator = function() {
var node = this.internal_createOscillator();
if (!node.start)
node.start = node.noteOn;
if (!node.stop)
node.stop = node.noteOff;
fixSetTarget(node.frequency);
fixSetTarget(node.detune);
return node;
};
}
}
}(window));
//Define the audio context
var actx = new AudioContext();
/*
# sounds
All the loaded sound files are stored in this object. It has
a `load` method that manages asset loading. You can load sounds at
any time during the game by using the `sounds.load` method.
Here's how to load three sound files from the `sounds` folder and
call a `setup` method when they're finished loading:
sounds.load([
"sounds/shoot.wav",
"sounds/music.wav",
"sounds/bounce.mp3"
]);
sounds.whenLoaded = setup;
You can now acess these loaded sounds like this:
var shoot = sounds["sounds/shoot.wav"],
music = sounds["sounds/music.wav"],
bounce = sounds["sounds/bounce.mp3"];
*/
var sounds = {
//Properties to help track the assets being loaded.
toLoad: 0,
loaded: 0,
//File extensions for different types of sounds.
audioExtensions: ["mp3", "ogg", "wav", "webm"],
//The callback function that should run when all assets have loaded.
//Assign this when you load the fonts, like this: `assets.whenLoaded = makeSprites;`.
whenLoaded: undefined,
//The load method creates and loads all the assets. Use it like this:
//`assets.load(["images/anyImage.png", "fonts/anyFont.otf"]);`.
load: function(sources) {
console.log("Loading sounds..");
//Get a reference to this asset object so we can
//refer to it in the `forEach` loop ahead.
var self = this;
//Find the number of files that need to be loaded.
self.toLoad = sources.length;
sources.forEach(function(source){
//Find the file extension of the asset.
var extension = source.split('.').pop();
//#### Sounds
//Load audio files that have file extensions that match
//the `audioExtensions` array.
if (self.audioExtensions.indexOf(extension) !== -1) {
//Create a sound sprite.
var soundSprite = makeSound(source, self.loadHandler.bind(self));
//Get the sound file name.
soundSprite.name = source;
//If you just want to extract the file name with the
//extension, you can do it like this:
//soundSprite.name = source.split("/").pop();
//Assign the sound as a property of the assets object so
//we can access it like this: `assets["sounds/sound.mp3"]`.
self[soundSprite.name] = soundSprite;
}
//Display a message if the file type isn't recognized.
else {
console.log("File type not recognized: " + source);
}
});
},
//#### loadHandler
//The `loadHandler` will be called each time an asset finishes loading.
loadHandler: function () {
var self = this;
self.loaded += 1;
console.log(self.loaded);
//Check whether everything has loaded.
if (self.toLoad === self.loaded) {
//If it has, run the callback function that was assigned to the `whenLoaded` property
console.log("Sounds finished loading");
//Reset `loaded` and `toLoaded` so we can load more assets
//later if we want to.
self.toLoad = 0;
self.loaded = 0;
self.whenLoaded();
}
}
};
/*
#makeSound
`makeSound` creates and returns and WebAudio sound sprite.
You can use it to load a sound like this:
var anySound = makeSound("sounds/anySound.mp3", loadHandler);
(However, it's more convenient to load the sound file using
the `sounds.load` method described above.)
After the sound has been loaded you can access and use it like this:
function loadHandler() {
anySound.loop = true;
anySound.pan = 0.8;
anySound.volume = 0.5;
anySound.play();
anySound.pause();
anySound.playFrom(second);
anySound.restart();
}
*/
function makeSound(source, loadHandler) {
//The sound object that this function returns.
var o = {};
//Set the default properties.
o.volumeNode = actx.createGain();
o.panNode = actx.createPanner();
o.delayNode = actx.createDelay();
o.feedbackNode = actx.createGain();
o.filterNode = actx.createBiquadFilter();
o.convolverNode = actx.createConvolver();
o.soundNode = null;
o.buffer = null;
o.source = null;
o.loop = false;
o.isPlaying = false;
//The function that should run when the sound is loaded.
o.loadHandler = undefined;
//Values for the `pan` and `volume` getters/setters.
o.panValue = 0;
o.volumeValue = 1;
//Values to help track and set the start and pause times.
o.startTime = 0;
o.startOffset = 0;
//Set the playback rate.
o.playbackRate = 1;
//Echo properties.
o.echo = false;
o.delayValue = 0.3;
o.feebackValue = 0.3;
o.filterValue = 0;
//Reverb properties
o.reverb = false;
o.reverbImpulse = null;
//The sound object's methods.
o.play = function() {
//Set the start time (it will be `0` when the sound
//first starts.
o.startTime = actx.currentTime;
//Create a sound node.
o.soundNode = actx.createBufferSource();
//Set the sound node's buffer property to the loaded sound.
o.soundNode.buffer = o.buffer;
//Set the playback rate
o.soundNode.playbackRate.value = this.playbackRate;
//Connect the sound to the pan, connect the pan to the
//volume, and connect the volume to the destination.
o.soundNode.connect(o.volumeNode);
//If there's no reverb, bypass the convolverNode
if (o.reverb === false) {
o.volumeNode.connect(o.panNode);
}
//If there is reverb, connect the `convolverNode` and apply
//the impulse response
else {
o.volumeNode.connect(o.convolverNode);
o.convolverNode.connect(o.panNode);
o.convolverNode.buffer = o.reverbImpulse;
}
//Connect the `panNode` to the destination to complete the chain.
o.panNode.connect(actx.destination);
//Add optional echo.
if (o.echo) {
//Set the values.
o.feedbackNode.gain.value = o.feebackValue;
o.delayNode.delayTime.value = o.delayValue;
o.filterNode.frequency.value = o.filterValue;
//Create the delay loop, with optional filtering.
o.delayNode.connect(o.feedbackNode);
if (o.filterValue > 0) {
o.feedbackNode.connect(o.filterNode);
o.filterNode.connect(o.delayNode);
} else {
o.feedbackNode.connect(o.delayNode);
}
//Capture the sound from the main node chain, send it to the
//delay loop, and send the final echo effect to the `panNode` which
//will then route it to the destination.
o.volumeNode.connect(o.delayNode);
o.delayNode.connect(o.panNode);
}
//Will the sound loop? This can be `true` or `false`.
o.soundNode.loop = o.loop;
//Finally, use the `start` method to play the sound.
//The start time will either be `0`,
//or a later time if the sound was paused.
o.soundNode.start(
0, o.startOffset % o.buffer.duration
);
//Set `isPlaying` to `true` to help control the
//`pause` and `restart` methods.
o.isPlaying = true;
};
o.pause = function() {
//Pause the sound if it's playing, and calculate the
//`startOffset` to save the current position.
if (o.isPlaying) {
o.soundNode.stop(0);
o.startOffset += actx.currentTime - o.startTime;
o.isPlaying = false;
}
};
o.restart = function() {
//Stop the sound if it's playing, reset the start and offset times,
//then call the `play` method again.
if (o.isPlaying) {
o.soundNode.stop(0);
}
o.startOffset = 0;
o.play();
};
o.playFrom = function(value) {
if (o.isPlaying) {
o.soundNode.stop(0);
}
o.startOffset = value;
o.play();
};
o.setEcho = function(delayValue, feedbackValue, filterValue) {
if (delayValue === undefined) delayValue = 0.3;
if (feedbackValue === undefined) feedbackValue = 0.3;
if (filterValue === undefined) filterValue = 0;
o.delayValue = delayValue;
o.feebackValue = feedbackValue;
o.filterValue = filterValue;
o.echo = true;
};
o.setReverb = function(duration, decay, reverse) {
if (duration === undefined) duration = 2;
if (decay === undefined) decay = 2;
if (reverse === undefined) reverse = false;
o.reverbImpulse = impulseResponse(duration, decay, reverse, actx);
o.reverb = true;
};
//Volume and pan getters/setters.
Object.defineProperties(o, {
volume: {
get: function() {
return o.volumeValue;
},
set: function(value) {
o.volumeNode.gain.value = value;
o.volumeValue = value;
},
enumerable: true, configurable: true
},
pan: {
get: function() {
return o.panValue;
},
set: function(value) {
//Panner objects accept x, y and z coordinates for 3D
//sound. However, because we're only doing 2D left/right
//panning we're only interested in the x coordinate,
//the first one. However, for a natural effect, the z
//value also has to be set proportionately.
var x = value,
y = 0,
z = 1 - Math.abs(x);
o.panNode.setPosition(x, y, z);
o.panValue = value;
},
enumerable: true, configurable: true
}
});
//The `load` method. It will call the `loadHandler` passed
//that was passed as an argument when the sound has loaded.
o.load = function() {
var xhr = new XMLHttpRequest();
//Use xhr to load the sound file.
xhr.open("GET", source, true);
xhr.responseType = "arraybuffer";
xhr.addEventListener("load", function() {
//Decode the sound and store a reference to the buffer.
actx.decodeAudioData(
xhr.response,
function(buffer) {
o.buffer = buffer;
o.hasLoaded = true;
//This next bit is optional, but important.
//If you have a load manager in your game, call it here so that
//the sound is registered as having loaded.
if (loadHandler) {
loadHandler();
}
},
//Throw an error if the sound can't be decoded.
function(error) {
throw new Error("Audio could not be decoded: " + error);
}
);
});
//Send the request to load the file.
xhr.send();
};
//Load the sound.
o.load();
//Return the sound object.
return o;
};
//The `soundEffect` function that makes all these sounds
function soundEffect(
frequencyValue, //The sound's fequency pitch in Hertz
attack, //The time, in seconds, to fade the sound in
decay, //The time, in seconds, to fade the sound out
type, //waveform type: "sine", "triangle", "square", "sawtooth"
volumeValue, //The sound's maximum volume
panValue, //The speaker pan. left: -1, middle: 0, right: 1
wait, //The time, in seconds, to wait before playing the sound
pitchBendAmount, //The number of Hz in which to bend the sound's pitch down
reverse, //If `reverse` is true the pitch will bend up
randomValue, //A range, in Hz, within which to randomize the pitch
dissonance, //A value in Hz. It creates 2 dissonant frequencies above below the target pitch
echo, //An array: [delayTimeInSeconds, feedbackTimeInSeconds, filterValueInHz]
reverb //An array: [durationInSeconds, decayRateInSeconds, reverse]
) {
//Set the default values
if (frequencyValue === undefined) frequencyValue = 200;
if (attack === undefined) attack = 0;
if (decay === undefined) decay = 1;
if (type === undefined) type = "sine";
if (volumeValue === undefined) volumeValue = 1;
if (panValue === undefined) panValue = 0;
if (wait === undefined) wait = 0;
if (pitchBendAmount === undefined) pitchBendAmount = 0;
if (reverse === undefined) reverse = false;
if (randomValue === undefined) randomValue = 0;
if (dissonance === undefined) dissonance = 0;
if (echo === undefined) echo = undefined;
if (reverb === undefined) reverb = undefined;
//Create an oscillator, gain and pan nodes, and connect them
//together to the destination
var oscillator = actx.createOscillator(),
volume = actx.createGain(),
pan = actx.createPanner();
oscillator.connect(volume);
volume.connect(pan);
pan.connect(actx.destination);
//Set the supplied values
volume.gain.value = volumeValue;
pan.setPosition(panValue, 0, 1 - Math.abs(panValue));
oscillator.type = type;
//Optionally randomize the pitch. If the `randomValue` is greater
//than zero, a random pitch is selected that's within the range
//specified by `frequencyValue`. The random pitch will be either
//above or below the target frequency.
var frequency;
var randomInt = function(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min
};
if (randomValue > 0) {
frequency = randomInt(
frequencyValue - randomValue / 2,
frequencyValue + randomValue / 2
);
} else {
frequency = frequencyValue;
}
oscillator.frequency.value = frequency;
//Apply effects
if (attack > 0) fadeIn(volume);
fadeOut(volume);
if (pitchBendAmount > 0) pitchBend(oscillator);
if (echo) addEcho(volume);
if (reverb) addReverb(volume);
if (dissonance > 0) addDissonance();
//Play the sound
play(oscillator);
//The helper functions:
function addReverb(volumeNode) {
var convolver = actx.createConvolver();
convolver.buffer = impulseResponse(reverb[0], reverb[1], reverb[2], actx);
volumeNode.connect(convolver);
convolver.connect(pan);
}
function addEcho(volumeNode) {
//Create the nodes
var feedback = actx.createGain(),
delay = actx.createDelay(),
filter = actx.createBiquadFilter();
//Set their values (delay time, feedback time and filter frequency)
delay.delayTime.value = echo[0];
feedback.gain.value = echo[1];
if (echo[2]) filter.frequency.value = echo[2];
//Create the delay feedback loop, with
//optional filtering
delay.connect(feedback);
if (echo[2]) {
feedback.connect(filter);
filter.connect(delay);
} else {
feedback.connect(delay);
}
//Connect the delay loop to the oscillator's volume
//node, and then to the destination
volumeNode.connect(delay);
//Connect the delay loop to the main sound chain's
//pan node, so that the echo effect is directed to
//the correct speaker
delay.connect(pan);
}
//The `fadeIn` function
function fadeIn(volumeNode) {
//Set the volume to 0 so that you can fade
//in from silence
volumeNode.gain.value = 0;
volumeNode.gain.linearRampToValueAtTime(
0, actx.currentTime + wait
);
volumeNode.gain.linearRampToValueAtTime(
volumeValue, actx.currentTime + wait + attack
);
}
//The `fadeOut` function
function fadeOut(volumeNode) {
volumeNode.gain.linearRampToValueAtTime(
volumeValue, actx.currentTime + attack + wait
);
volumeNode.gain.linearRampToValueAtTime(
0, actx.currentTime + wait + attack + decay
);
}
//The `pitchBend` function
function pitchBend(oscillatorNode) {
//If `reverse` is true, make the note drop in frequency. Useful for
//shooting sounds
//Get the frequency of the current oscillator
var frequency = oscillatorNode.frequency.value;
//If `reverse` is true, make the sound drop in pitch
if (!reverse) {
oscillatorNode.frequency.linearRampToValueAtTime(
frequency,
actx.currentTime + wait
);
oscillatorNode.frequency.linearRampToValueAtTime(
frequency - pitchBendAmount,
actx.currentTime + wait + attack + decay
);
}
//If `reverse` is false, make the note rise in pitch. Useful for
//jumping sounds
else {
oscillatorNode.frequency.linearRampToValueAtTime(
frequency,
actx.currentTime + wait
);
oscillatorNode.frequency.linearRampToValueAtTime(
frequency + pitchBendAmount,
actx.currentTime + wait + attack + decay
);
}
}
//The `addDissonance` function
function addDissonance() {
//Create two more oscillators and gain nodes
var d1 = actx.createOscillator(),
d2 = actx.createOscillator(),
d1Volume = actx.createGain(),
d2Volume = actx.createGain();
//Set the volume to the `volumeValue`
d1Volume.gain.value = volumeValue;
d2Volume.gain.value = volumeValue;
//Connect the oscillators to the gain and destination nodes
d1.connect(d1Volume);
d1Volume.connect(actx.destination);
d2.connect(d2Volume);
d2Volume.connect(actx.destination);
//Set the waveform to "sawtooth" for a harsh effect
d1.type = "sawtooth";
d2.type = "sawtooth";
//Make the two oscillators play at frequencies above and
//below the main sound's frequency. Use whatever value was
//supplied by the `dissonance` argument
d1.frequency.value = frequency + dissonance;
d2.frequency.value = frequency - dissonance;
//Fade in/out, pitch bend and play the oscillators
//to match the main sound
if (attack > 0) {
fadeIn(d1Volume);
fadeIn(d2Volume);
}
if (decay > 0) {
fadeOut(d1Volume);
fadeOut(d2Volume);
}
if (pitchBendAmount > 0) {
pitchBend(d1);
pitchBend(d2);
}
if (echo) {
addEcho(d1Volume);
addEcho(d2Volume);
}
if (reverb) {
addReverb(d1Volume);
addReverb(d2Volume);
}
play(d1);
play(d2);
}
//The `play` function
function play(node) {
node.start(actx.currentTime + wait);
}
}
/*
# impulseResponse
The `makeSound` and `soundEffect` functions uses `impulseResponse` to help create an optional reverb effect.
It simulates a model of sound reverberation in an acoustic space which
a convolver node can blend with the source sound.
*/
function impulseResponse(duration, decay, reverse, actx) {
//The length of the buffer.
var length = actx.sampleRate * duration;
//Create an audio buffer (an empty sound container) to store the reverb effect.
var impulse = actx.createBuffer(2, length, actx.sampleRate);
//Use `getChannelData` to initialize empty arrays to store sound data for
//the left and right channels.
var left = impulse.getChannelData(0),
right = impulse.getChannelData(1);
//Loop through each sample-frame and fill the channel
//data with random noise.
for (var i = 0; i < length; i++){
//Apply the reverse effect, if `reverse` is `true`.
var n;
if (reverse) {
n = length - i;
} else {
n = i;
}
//Fill the left and right channels with random white noise which
//decays exponentially.
left[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
right[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
}
//Return the `impulse`.
return impulse;
}
/*
# keyboard
The `keyboard` helper function creates `key` objects
that listen for keyboard events. Create a new key object like
this:
var keyObject = g.keyboard(asciiKeyCodeNumber);
Then assign `press` and `release` methods like this:
keyObject.press = function() {
//key object pressed
};
keyObject.release = function() {
//key object released
};
Keyboard objects also have `isDown` and `isUp` Booleans that you can check.
*/
function keyboard(keyCode) {
var key = {};
key.code = keyCode;
key.isDown = false;
key.isUp = true;
key.press = undefined;
key.release = undefined;
//The `downHandler`
key.downHandler = function(event) {
if (event.keyCode === key.code) {
if (key.isUp && key.press) key.press();
key.isDown = true;
key.isUp = false;
}
event.preventDefault();
};
//The `upHandler`
key.upHandler = function(event) {
if (event.keyCode === key.code) {
if (key.isDown && key.release) key.release();
key.isDown = false;
key.isUp = true;
}
event.preventDefault();
};
//Attach event listeners
window.addEventListener(
"keydown", key.downHandler.bind(key), false
);
window.addEventListener(
"keyup", key.upHandler.bind(key), false
);
return key;
}
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.