mirror of
https://github.com/doublespeakgames/adarkroom.git
synced 2026-05-28 00:01:54 +08:00
We have audio! Thanks @orsi!
This commit is contained in:
+236
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Module that takes care of audio playback
|
||||
*/
|
||||
var AudioEngine = {
|
||||
FADE_TIME: 1,
|
||||
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();
|
||||
},
|
||||
_createMasterChannel: function () {
|
||||
// create master
|
||||
AudioEngine._master = AudioEngine._audioContext.createGain();
|
||||
AudioEngine._master.gain.setValueAtTime(1.0, AudioEngine._audioContext.currentTime);
|
||||
AudioEngine._master.connect(AudioEngine._audioContext.destination);
|
||||
},
|
||||
_getMissingAudioBuffer: function () {
|
||||
// plays beeping sound to indicate missing audio
|
||||
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 * 0.05) / 4; // max .25 gain value
|
||||
}
|
||||
return buffer;
|
||||
},
|
||||
_playSound: function (buffer) {
|
||||
if (AudioEngine._currentSoundEffectAudio &&
|
||||
AudioEngine._currentSoundEffectAudio.source.buffer == buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var source = AudioEngine._audioContext.createBufferSource();
|
||||
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
|
||||
};
|
||||
},
|
||||
_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
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
};
|
||||
},
|
||||
_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
|
||||
if (AudioEngine._currentBackgroundMusic) {
|
||||
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) {
|
||||
AudioEngine.loadAudioFile(src)
|
||||
.then(function (buffer) {
|
||||
AudioEngine._playBackgroundMusic(buffer);
|
||||
});
|
||||
},
|
||||
playEventMusic: function (src) {
|
||||
AudioEngine.loadAudioFile(src)
|
||||
.then(function (buffer) {
|
||||
AudioEngine._playEventMusic(buffer);
|
||||
});
|
||||
},
|
||||
stopEventMusic: function () {
|
||||
AudioEngine._stopEventMusic();
|
||||
},
|
||||
playSound: function (src) {
|
||||
AudioEngine.loadAudioFile(src)
|
||||
.then(function (buffer) {
|
||||
AudioEngine._playSound(buffer);
|
||||
});
|
||||
},
|
||||
loadAudioFile: function (src) {
|
||||
if (src.indexOf('http') === -1) {
|
||||
src = window.location + src;
|
||||
}
|
||||
if (AudioEngine.AUDIO_BUFFER_CACHE[src]) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
resolve(AudioEngine.AUDIO_BUFFER_CACHE[src]);
|
||||
});
|
||||
} else {
|
||||
var request = new Request(src);
|
||||
return fetch(request).then(function (response) {
|
||||
return response.arrayBuffer();
|
||||
}).then(function (buffer) {
|
||||
if (buffer.byteLength === 0) {
|
||||
console.error('cannot load audio from ' + src);
|
||||
return AudioEngine._getMissingAudioBuffer();
|
||||
}
|
||||
|
||||
return AudioEngine._audioContext.decodeAudioData(buffer, function (decodedData) {
|
||||
AudioEngine.AUDIO_BUFFER_CACHE[src] = decodedData;
|
||||
return AudioEngine.AUDIO_BUFFER_CACHE[src];
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
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) {
|
||||
volume = 1.0;
|
||||
}
|
||||
if (s === undefined) {
|
||||
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(
|
||||
volume,
|
||||
AudioEngine._audioContext.currentTime + s
|
||||
);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user