Files
junisaber/src/components/beat-hit-sound.js
2018-12-01 19:27:31 -08:00

143 lines
4.1 KiB
JavaScript

var audioContext = new window.AudioContext();
var sourceCreatedCallback;
const LAYER_BOTTOM = 'bottom';
const LAYER_MIDDLE = 'middle';
const LAYER_TOP = 'top';
const VOLUME = 0.25;
// Allows for modifying detune. PR has been sent to three.js.
THREE.Audio.prototype.play = function () {
if (this.isPlaying === true) {
console.warn('THREE.Audio: Audio is already playing.');
return;
}
if (this.hasPlaybackControl === false) {
console.warn('THREE.Audio: this Audio has no playback control.');
return;
}
var source = this.context.createBufferSource();
source.buffer = this.buffer;
source.detune.value = this.detune;
source.loop = this.loop;
source.onended = this.onEnded.bind(this);
source.playbackRate.setValueAtTime(this.playbackRate, this.startTime);
this.startTime = this.context.currentTime;
if (sourceCreatedCallback) { sourceCreatedCallback(source); }
source.start(this.startTime, this.offset);
this.isPlaying = true;
this.source = source;
return this.connect();
};
/**
* Beat hit sound using positional audio and audio buffer source.
*/
AFRAME.registerComponent('beat-hit-sound', {
directionsToSounds: {
up: '',
down: '',
upleft: 'left',
upright: 'right',
downleft: 'left',
downright: 'right',
left: 'left',
right: 'right'
},
init: function () {
this.currentBeatEl = null;
this.currentCutDirection = '';
this.processSound = this.processSound.bind(this);
sourceCreatedCallback = this.sourceCreatedCallback.bind(this);
// Sound pools.
for (let i = 1; i <= 10; i++) {
this.el.setAttribute(`sound__beathit${i}`, {
poolSize: 4,
src: `#hitSound${i}`,
volume: VOLUME
});
this.el.setAttribute(`sound__beathit${i}left`, {
poolSize: 4,
src: `#hitSound${i}left`,
volume: VOLUME
});
this.el.setAttribute(`sound__beathit${i}right`, {
poolSize: 4,
src: `#hitSound${i}right`,
volume: VOLUME
});
}
},
play: function () {
// Kick three.js loader...Don't know why sometimes doesn't load.
for (let i = 1; i <= 10; i++) {
if (!this.el.components[`sound__beathit${i}`].loaded) {
this.el.setAttribute(`sound__beathit${i}`, 'src', '');
this.el.setAttribute(`sound__beathit${i}`, 'src', `#hitSound${i}`);
}
if (!this.el.components[`sound__beathit${i}left`].loaded) {
this.el.setAttribute(`sound__beathit${i}left`, 'src', '');
this.el.setAttribute(`sound__beathit${i}left`, 'src', `#hitSound${i}left`);
}
if (!this.el.components[`sound__beathit${i}right`].loaded) {
this.el.setAttribute(`sound__beathit${i}right`, 'src', '');
this.el.setAttribute(`sound__beathit${i}right`, 'src', `#hitSound${i}right`);
}
}
},
playSound: function (beatEl, cutDirection) {
const rand = 1 + Math.floor(Math.random() * 10);
const dir = this.directionsToSounds[cutDirection || 'up'];
const soundPool = this.el.components[`sound__beathit${rand}${dir}`];
this.currentBeatEl = beatEl;
soundPool.playSound(this.processSound);
},
/**
* Set audio stuff before playing.
*/
processSound: function (audio) {
audio.detune = 0;
this.currentBeatEl.object3D.getWorldPosition(audio.position);
},
/**
* Function callback to process source buffer once created.
* Set detune for pitch and inflections.
*/
sourceCreatedCallback: function (source) {
// Pitch based on layer.
const layer = this.getLayer(this.currentBeatEl.object3D.position.y);
if (layer === LAYER_BOTTOM) {
source.detune.setValueAtTime(-400, 0);
} else if (layer === LAYER_TOP) {
source.detune.setValueAtTime(200, 0);
}
// Inflection on strike down or up.
if (this.currentCutDirection === 'down') {
source.detune.linearRampToValueAtTime(-400, 1);
}
if (this.currentCutDirection === 'up') {
source.detune.linearRampToValueAtTime(400, 1);
}
},
/**
* Get whether beat is on bottom, middle, or top layer.
*/
getLayer: function (y) {
if (y === 1) { return LAYER_BOTTOM; }
if (y === 1.70) { return LAYER_TOP; }
return LAYER_MIDDLE;
}
});