Files
adarkroom/script/audio.js
T

234 lines
9.7 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,
_currentBackgroundMusic: null,
_currentEventAudio: null,
_currentSoundEffectAudio: null,
init: function () {
AudioEngine._initAudioContext();
},
_initAudioContext: function () {
AudioEngine._audioContext = new (window.AudioContext || window.webkitAudioContext);
AudioEngine._createMasterChannel();
2020-05-30 17:20:01 -04:00
},
_createMasterChannel: function () {
// create master
AudioEngine._master = AudioEngine._audioContext.createGain();
AudioEngine._master.gain.setValueAtTime(1.0, AudioEngine._audioContext.currentTime);
AudioEngine._master.connect(AudioEngine._audioContext.destination);
},
2020-06-01 11:12:00 -04:00
_getMissingAudioBuffer: function () {
// plays beeping sound to indicate missing audio
var buffer = AudioEngine._audioContext.createBuffer(
2020-06-03 16:39:12 -04:00
1,
AudioEngine._audioContext.sampleRate,
AudioEngine._audioContext.sampleRate
2020-06-01 11:12:00 -04:00
);
// Fill the buffer
var bufferData = buffer.getChannelData(0);
for (var i = 0; i < buffer.length / 2; i++) {
bufferData[i] = Math.sin(i * 0.05) / 4; // max .25 gain value
2020-06-01 11:12:00 -04:00
}
return buffer;
},
2020-05-30 22:20:29 -04:00
_playSound: function (buffer) {
if (AudioEngine._currentSoundEffectAudio &&
AudioEngine._currentSoundEffectAudio.source.buffer == buffer) {
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.onended = function(event) {
// dereference current sound effect when finished
if (AudioEngine._currentSoundEffectAudio &&
AudioEngine._currentSoundEffectAudio.source.buffer == buffer) {
AudioEngine._currentSoundEffectAudio = null;
}
};
source.connect(AudioEngine._master);
source.start();
AudioEngine._currentSoundEffectAudio = {
source: source
};
2020-05-28 19:10:09 -04:00
},
_playBackgroundMusic: function (buffer) {
var source = AudioEngine._audioContext.createBufferSource();
source.buffer = buffer;
source.loop = true;
var envelope = AudioEngine._audioContext.createGain();
envelope.gain.setValueAtTime(0.0, AudioEngine._audioContext.currentTime);
var fadeTime = AudioEngine._audioContext.currentTime + AudioEngine.FADE_TIME;
// fade out current background music
if (AudioEngine._currentBackgroundMusic &&
AudioEngine._currentBackgroundMusic.source &&
AudioEngine._currentBackgroundMusic.source.playbackState !== 0) {
var currentBackgroundGainValue = AudioEngine._currentBackgroundMusic.envelope.gain.value;
AudioEngine._currentBackgroundMusic.envelope.gain.cancelScheduledValues(AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.setValueAtTime(currentBackgroundGainValue, AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.linearRampToValueAtTime(0.0, fadeTime);
AudioEngine._currentBackgroundMusic.source.stop(fadeTime + 0.3); // make sure fade has completed
}
2020-05-30 22:20:29 -04:00
// fade in new backgorund music
source.connect(envelope);
envelope.connect(AudioEngine._master);
source.start();
envelope.gain.linearRampToValueAtTime(1.0, fadeTime);
// update current background music
AudioEngine._currentBackgroundMusic = {
source: source,
envelope: envelope
};
},
_playEventMusic: function (buffer) {
var source = AudioEngine._audioContext.createBufferSource();
source.buffer = buffer;
source.loop = true;
var envelope = AudioEngine._audioContext.createGain();
envelope.gain.setValueAtTime(0.0, AudioEngine._audioContext.currentTime);
2020-05-30 22:20:29 -04:00
var fadeTime = AudioEngine._audioContext.currentTime + AudioEngine.FADE_TIME * 2;
// turn down current background music
if (AudioEngine._currentBackgroundMusic != null) {
var currentBackgroundGainValue = AudioEngine._currentBackgroundMusic.envelope.gain.value;
AudioEngine._currentBackgroundMusic.envelope.gain.cancelScheduledValues(AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.setValueAtTime(currentBackgroundGainValue, AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.linearRampToValueAtTime(0.2, fadeTime);
}
2020-05-30 22:20:29 -04:00
// fade in event music
source.connect(envelope);
envelope.connect(AudioEngine._master);
source.start();
envelope.gain.linearRampToValueAtTime(1.0, fadeTime);
// update reference
AudioEngine._currentEventAudio = {
source: source,
envelope: envelope
};
},
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
if (AudioEngine._currentEventAudio) {
var currentEventGainValue = AudioEngine._currentEventAudio.envelope.gain.value;
AudioEngine._currentEventAudio.envelope.gain.cancelScheduledValues(AudioEngine._audioContext.currentTime);
AudioEngine._currentEventAudio.envelope.gain.setValueAtTime(currentEventGainValue, AudioEngine._audioContext.currentTime);
AudioEngine._currentEventAudio.envelope.gain.linearRampToValueAtTime(0.0, fadeTime);
AudioEngine._currentEventAudio.source.stop(fadeTime + 1); // make sure fade has completed
AudioEngine._currentEventAudio = null;
}
// turn up background music
var currentBackgroundGainValue = AudioEngine._currentBackgroundMusic.envelope.gain.value;
AudioEngine._currentBackgroundMusic.envelope.gain.cancelScheduledValues(AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.setValueAtTime(currentBackgroundGainValue, AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.linearRampToValueAtTime(1.0, fadeTime);
},
isAudioContextRunning: function () {
return AudioEngine._audioContext.state !== 'suspended';
},
tryResumingAudioContext: function() {
if (AudioEngine._audioContext.state === 'suspended') {
AudioEngine._audioContext.resume();
}
},
playBackgroundMusic: function (src) {
2020-05-30 17:20:01 -04:00
AudioEngine.loadAudioFile(src)
.then(function (buffer) {
AudioEngine._playBackgroundMusic(buffer);
});
},
2020-05-30 22:20:29 -04:00
playEventMusic: function (src) {
AudioEngine.loadAudioFile(src)
.then(function (buffer) {
AudioEngine._playEventMusic(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
});
},
2020-06-03 16:39:12 -04:00
loadAudioFile: function (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();
}
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
});
}
},
setBackgroundMusicVolume: function (volume, s) {
if (AudioEngine._master == null) return; // master may not be ready yet
if (volume === undefined) {
volume = 1.0;
}
if (s === undefined) {
s = 1.0;
}
// cancel any current schedules and then ramp
var currentBackgroundGainValue = AudioEngine._currentBackgroundMusic.envelope.gain.value;
AudioEngine._currentBackgroundMusic.envelope.gain.cancelScheduledValues(AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.setValueAtTime(currentBackgroundGainValue, AudioEngine._audioContext.currentTime);
AudioEngine._currentBackgroundMusic.envelope.gain.linearRampToValueAtTime(
volume,
AudioEngine._audioContext.currentTime + s
);
},
setMasterVolume: function (volume, s) {
if (AudioEngine._master == null) return; // master may not be ready yet
if (volume === undefined) {
2020-05-30 22:20:29 -04:00
volume = 1.0;
2020-05-28 19:10:09 -04:00
}
if (s === undefined) {
2020-06-02 16:02:12 -04:00
s = 1.0;
}
// cancel any current schedules and then ramp
var currentGainValue = AudioEngine._master.gain.value;
AudioEngine._master.gain.cancelScheduledValues(AudioEngine._audioContext.currentTime);
AudioEngine._master.gain.setValueAtTime(currentGainValue, AudioEngine._audioContext.currentTime);
AudioEngine._master.gain.linearRampToValueAtTime(
2020-05-30 22:20:29 -04:00
volume,
AudioEngine._audioContext.currentTime + s
2020-05-30 22:20:29 -04:00
);
}
2020-05-30 22:27:38 -04:00
};