Files
adarkroom/script/audio.js
T

212 lines
8.1 KiB
JavaScript
Raw Normal View History

2020-05-28 19:10:09 -04:00
/**
* Module that takes care of audio playback
*/
var AudioEngine = {
FADE_TIME: 1,
2020-05-28 19:10:09 -04:00
AUDIO_BUFFER_CACHE: {},
audioContext: null,
master: null,
tracks: {
'bg1': null,
'bg2': null,
'events': null,
'sfx': null
},
currentBackgroundChannel: 'bg1',
currentBackgroundAudio: null,
currentEventAudio: null,
2020-05-30 22:20:29 -04:00
init: function (options) {
2020-05-28 19:10:09 -04:00
// for legacy browsers
2020-05-30 17:20:01 -04:00
AudioEngine.audioContext = new (window.AudioContext || window.webkitAudioContext);
2020-05-30 17:20:01 -04:00
if (AudioEngine.audioContext.state === 'suspended') {
AudioEngine.audioContext.resume().then(function () {
AudioEngine.createChannels();
});
} else {
AudioEngine.createChannels();
}
},
createChannels() {
// create master
2020-05-30 17:20:01 -04:00
AudioEngine.master = AudioEngine.audioContext.createGain();
AudioEngine.master.gain.setValueAtTime(1.0, AudioEngine.audioContext.currentTime);
2020-05-30 17:20:01 -04:00
AudioEngine.master.connect(AudioEngine.audioContext.destination);
// create 4 tracks to output to master
AudioEngine.tracks['bg1'] = AudioEngine.audioContext.createGain();
AudioEngine.tracks['bg1'].connect(AudioEngine.master);
AudioEngine.tracks['bg1'].gain.setValueAtTime(1.0, AudioEngine.audioContext.currentTime);
AudioEngine.tracks['bg2'] = AudioEngine.audioContext.createGain();
AudioEngine.tracks['bg2'].connect(AudioEngine.master);
AudioEngine.tracks['bg2'].gain.setValueAtTime(1.0, AudioEngine.audioContext.currentTime);
AudioEngine.tracks['events'] = AudioEngine.audioContext.createGain();
AudioEngine.tracks['events'].connect(AudioEngine.master);
AudioEngine.tracks['events'].gain.setValueAtTime(1.0, AudioEngine.audioContext.currentTime);
AudioEngine.tracks['sfx'] = AudioEngine.audioContext.createGain();
AudioEngine.tracks['sfx'].connect(AudioEngine.master);
AudioEngine.tracks['sfx'].gain.setValueAtTime(1.0, AudioEngine.audioContext.currentTime);
},
options: {}, // Nothing for now,
2020-05-30 22:20:29 -04:00
_canPlayAudio: function () {
if (AudioEngine.audioContext.state === 'suspended') {
return false;
}
return true;
2020-05-30 17:20:01 -04:00
},
2020-06-01 11:12:00 -04:00
_getMissingAudioBuffer: function () {
var buffer = AudioEngine.audioContext.createBuffer(
1,
AudioEngine.audioContext.sampleRate,
AudioEngine.audioContext.sampleRate
);
// Fill the buffer
var bufferData = buffer.getChannelData(0);
for (var i = 0; i < buffer.length / 2; i++) {
bufferData[i] = Math.sin(i * .05) / 2;
}
return buffer;
},
2020-05-30 22:20:29 -04:00
_playSound: function (buffer) {
if (!AudioEngine._canPlayAudio()) return;
2020-05-30 17:20:01 -04:00
var source = AudioEngine.audioContext.createBufferSource();
2020-05-28 19:10:09 -04:00
source.buffer = buffer;
source.connect(AudioEngine.tracks['sfx']);
2020-05-30 17:20:01 -04:00
source.start(AudioEngine.audioContext.currentTime);
2020-05-28 19:10:09 -04:00
},
2020-05-30 22:20:29 -04:00
_fadeTrack: function (buffer) {
if (!AudioEngine._canPlayAudio()) return;
2020-05-30 17:20:01 -04:00
var bufferSource = AudioEngine.audioContext.createBufferSource();
bufferSource.buffer = buffer;
bufferSource.loop = true;
// figure out which background track to start on
// in order to do crossfade
var nextBackgroundChannel;
if (AudioEngine.currentBackgroundChannel === 'bg1') {
nextBackgroundChannel = 'bg2';
} else {
nextBackgroundChannel = 'bg1';
}
2020-05-30 22:20:29 -04:00
// fade in new track
var fadeTime = AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME;
bufferSource.connect(AudioEngine.tracks[nextBackgroundChannel]);
bufferSource.start(AudioEngine.audioContext.currentTime);
2020-05-30 17:20:01 -04:00
AudioEngine.tracks[nextBackgroundChannel].gain.setValueAtTime(0.0, AudioEngine.audioContext.currentTime);
AudioEngine.tracks[nextBackgroundChannel].gain.linearRampToValueAtTime(1.0, fadeTime);
// fade out old track
2020-05-30 17:20:01 -04:00
AudioEngine.tracks[AudioEngine.currentBackgroundChannel].gain.linearRampToValueAtTime(0.0, fadeTime);
if (AudioEngine.currentBackgroundAudio) {
AudioEngine.currentBackgroundAudio.stop(fadeTime + 0.3); // make sure fade has completed
}
// switch background track
2020-05-30 17:20:01 -04:00
AudioEngine.currentBackgroundChannel = nextBackgroundChannel;
AudioEngine.currentBackgroundAudio = bufferSource;
},
2020-05-30 22:20:29 -04:00
_playEvent: function (buffer) {
if (!AudioEngine._canPlayAudio()) return;
var bufferSource = AudioEngine.audioContext.createBufferSource();
bufferSource.buffer = buffer;
bufferSource.loop = true;
2020-05-30 22:20:29 -04:00
var fadeTime = AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME * 2;
// turn down background music
AudioEngine.tracks['bg1'].gain.linearRampToValueAtTime(0.2, fadeTime);
AudioEngine.tracks['bg2'].gain.linearRampToValueAtTime(0.2, fadeTime);
2020-05-30 22:20:29 -04:00
// fade in event music
bufferSource.connect(AudioEngine.tracks['events']);
bufferSource.start(0);
AudioEngine.currentEventAudio = bufferSource;
AudioEngine.tracks['events'].gain.setValueAtTime(0.0, AudioEngine.audioContext.currentTime);
AudioEngine.tracks['events'].gain.linearRampToValueAtTime(0.1, fadeTime);
},
2020-05-30 22:20:29 -04:00
_stopEventMusic: function () {
var fadeTime = AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME * 2;
// fade out event music and stop
AudioEngine.tracks['events'].gain.linearRampToValueAtTime(0.0, fadeTime);
if (AudioEngine.currentEventAudio) {
AudioEngine.currentEventAudio.stop(fadeTime + 1); // make sure fade has completed
AudioEngine.currentEventAudio = null;
}
// turn up background music
AudioEngine.tracks[AudioEngine.currentBackgroundChannel].gain.linearRampToValueAtTime(1.0, fadeTime);
},
2020-05-30 22:20:29 -04:00
changeMusic: function (src) {
2020-05-31 19:19:36 -04:00
console.log('changeMusic ', src);
2020-05-30 17:20:01 -04:00
AudioEngine.loadAudioFile(src)
.then(function (buffer) {
2020-05-30 17:20:01 -04:00
AudioEngine._fadeTrack(buffer);
});
},
2020-05-30 22:20:29 -04:00
playEventMusic: function (src) {
AudioEngine.loadAudioFile(src)
.then(function (buffer) {
AudioEngine._playEvent(buffer);
});
},
2020-05-30 22:20:29 -04:00
stopEventMusic: function () {
AudioEngine._stopEventMusic();
},
2020-05-30 22:20:29 -04:00
playSound: function (src) {
2020-05-30 17:20:01 -04:00
AudioEngine.loadAudioFile(src)
2020-05-28 19:10:09 -04:00
.then(function (buffer) {
2020-05-30 17:20:01 -04:00
AudioEngine._playSound(buffer);
2020-05-28 19:10:09 -04:00
});
},
loadAudioFile(src) {
if (src.indexOf('http') === -1) {
src = window.location + src;
}
2020-05-30 17:20:01 -04:00
if (AudioEngine.AUDIO_BUFFER_CACHE[src]) {
2020-05-28 19:10:09 -04:00
return new Promise(function (resolve, reject) {
2020-05-30 17:20:01 -04:00
resolve(AudioEngine.AUDIO_BUFFER_CACHE[src]);
2020-05-28 19:10:09 -04:00
});
} else {
var request = new Request(src);
2020-05-30 22:20:29 -04:00
return fetch(request).then(function (response) {
2020-05-28 19:10:09 -04:00
return response.arrayBuffer();
2020-05-30 22:20:29 -04:00
}).then(function (buffer) {
2020-06-01 11:12:00 -04:00
if (buffer.byteLength === 0) {
console.error('cannot load audio from ' + src);
return AudioEngine._getMissingAudioBuffer();
}
2020-05-30 22:20:29 -04:00
return AudioEngine.audioContext.decodeAudioData(buffer, function (decodedData) {
2020-05-30 17:20:01 -04:00
AudioEngine.AUDIO_BUFFER_CACHE[src] = decodedData;
return AudioEngine.AUDIO_BUFFER_CACHE[src];
2020-05-28 19:10:09 -04:00
});
2020-05-30 22:20:29 -04:00
});
}
},
mute: function () {
AudioEngine.master.gain.linearRampToValueAtTime(
0.0,
AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME
);
},
setVolume: function (volume) {
if (!AudioEngine.master) return; // master may not be ready yet
if (!volume) {
volume = 1.0;
2020-05-28 19:10:09 -04:00
}
2020-05-30 22:20:29 -04:00
AudioEngine.master.gain.setValueAtTime(
AudioEngine.master.gain.value,
AudioEngine.audioContext.currentTime
);
AudioEngine.master.gain.linearRampToValueAtTime(
volume,
AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME / 2
);
}
2020-05-30 22:27:38 -04:00
};