From 3b43a2754a9e1213f2f2eb3eea52bea54994e56f Mon Sep 17 00:00:00 2001 From: jorsi Date: Wed, 3 Jun 2020 17:44:27 -0400 Subject: [PATCH] refactor audio engine: simplified gain channels, improve fading between audio, rename functions --- script/audio.js | 221 ++++++++++++++++++++++------------------------ script/engine.js | 2 +- script/outside.js | 12 +-- script/path.js | 2 +- script/room.js | 10 +-- script/ship.js | 2 +- script/space.js | 6 +- script/world.js | 2 +- 8 files changed, 124 insertions(+), 133 deletions(-) diff --git a/script/audio.js b/script/audio.js index d861ba3..97c39e3 100644 --- a/script/audio.js +++ b/script/audio.js @@ -4,158 +4,157 @@ var AudioEngine = { FADE_TIME: 1, AUDIO_BUFFER_CACHE: {}, - _audioPreloaded: false, - audioContext: null, - master: null, - tracks: { - 'bg1': null, - 'bg2': null, - 'events': null, - 'sfx': null + _audioContext: null, + _master: null, + _currentBackgroundMusic: null, + _currentEventAudio: null, + _currentSoundEffectAudio: null, + init: function () { + AudioEngine._initAudioContext(); }, - currentBackgroundChannel: 'bg1', - currentBackgroundAudio: null, - currentEventAudio: null, - init: function (options) { - AudioEngine.initAudioContext(); - }, - initAudioContext: function () { + _initAudioContext: function () { // for legacy browsers - AudioEngine.audioContext = new (window.AudioContext || window.webkitAudioContext); - - if (AudioEngine.audioContext.state === 'suspended') { - AudioEngine.audioContext.resume().then(function () { - AudioEngine.createChannels(); + AudioEngine._audioContext = new (window.AudioContext || window.webkitAudioContext); + if (AudioEngine._audioContext.state === 'suspended') { + AudioEngine._audioContext.resume().then(function () { + AudioEngine._createMasterChannel(); }); } else { - AudioEngine.createChannels(); + AudioEngine._createMasterChannel(); } }, - createChannels: function () { + _createMasterChannel: function () { // create master - AudioEngine.master = AudioEngine.audioContext.createGain(); - AudioEngine.master.gain.setValueAtTime(1.0, AudioEngine.audioContext.currentTime); - 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); + AudioEngine._master = AudioEngine._audioContext.createGain(); + AudioEngine._master.gain.setValueAtTime(1.0, AudioEngine._audioContext.currentTime); + AudioEngine._master.connect(AudioEngine._audioContext.destination); }, - options: {}, // Nothing for now, _canPlayAudio: function () { - if (AudioEngine.audioContext.state === 'suspended') { + if (AudioEngine._audioContext.state === 'suspended') { return false; } return true; }, _getMissingAudioBuffer: function () { - var buffer = AudioEngine.audioContext.createBuffer( + // plays beeping sound to indicate missing audio + var buffer = AudioEngine._audioContext.createBuffer( 1, - AudioEngine.audioContext.sampleRate, - AudioEngine.audioContext.sampleRate + 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; + bufferData[i] = Math.sin(i * .05) / 4; // max .25 gain value } return buffer; }, _playSound: function (buffer) { if (!AudioEngine._canPlayAudio()) return; - var source = AudioEngine.audioContext.createBufferSource(); + var source = AudioEngine._audioContext.createBufferSource(); source.buffer = buffer; - source.connect(AudioEngine.tracks['sfx']); - source.start(AudioEngine.audioContext.currentTime); + source.connect(AudioEngine._master); + source.start(); + + AudioEngine._currentSoundEffectAudio = { + source: source + }; }, - _fadeTrack: function (buffer) { + _playBackgroundMusic: function (buffer) { if (!AudioEngine._canPlayAudio()) return; - var bufferSource = AudioEngine.audioContext.createBufferSource(); - bufferSource.buffer = buffer; - bufferSource.loop = true; + var source = AudioEngine._audioContext.createBufferSource(); + source.buffer = buffer; + source.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'; + 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) { + 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 track - var fadeTime = AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME; - bufferSource.connect(AudioEngine.tracks[nextBackgroundChannel]); - bufferSource.start(AudioEngine.audioContext.currentTime); - AudioEngine.tracks[nextBackgroundChannel].gain.setValueAtTime(0.0, AudioEngine.audioContext.currentTime); - AudioEngine.tracks[nextBackgroundChannel].gain.linearRampToValueAtTime(1.0, fadeTime); + // fade in new backgorund music + source.connect(envelope); + envelope.connect(AudioEngine._master); + source.start(); + envelope.gain.linearRampToValueAtTime(1.0, fadeTime); - // fade out old track - 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 - AudioEngine.currentBackgroundChannel = nextBackgroundChannel; - AudioEngine.currentBackgroundAudio = bufferSource; + // update current background music + AudioEngine._currentBackgroundMusic = { + source: source, + envelope: envelope + }; }, - _playEvent: function (buffer) { + _playEventMusic: function (buffer) { if (!AudioEngine._canPlayAudio()) return; - var bufferSource = AudioEngine.audioContext.createBufferSource(); - bufferSource.buffer = buffer; - bufferSource.loop = true; + var source = AudioEngine._audioContext.createBufferSource(); + source.buffer = buffer; + source.loop = true; - var fadeTime = AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME * 2; + var envelope = AudioEngine._audioContext.createGain(); + envelope.gain.setValueAtTime(0.0, AudioEngine._audioContext.currentTime); - // turn down background music - AudioEngine.tracks['bg1'].gain.linearRampToValueAtTime(0.2, fadeTime); - AudioEngine.tracks['bg2'].gain.linearRampToValueAtTime(0.2, fadeTime); + 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 - bufferSource.connect(AudioEngine.tracks['events']); - bufferSource.start(0); - AudioEngine.currentEventAudio = bufferSource; + source.connect(envelope); + envelope.connect(AudioEngine._master); + source.start(); + envelope.gain.linearRampToValueAtTime(1.0, fadeTime); - AudioEngine.tracks['events'].gain.setValueAtTime(0.0, AudioEngine.audioContext.currentTime); - AudioEngine.tracks['events'].gain.linearRampToValueAtTime(1.0, fadeTime); + // update reference + AudioEngine._currentEventAudio = { + source: source, + envelope: envelope + }; }, _stopEventMusic: function () { - var fadeTime = AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME * 2; + 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; + 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 - AudioEngine.tracks[AudioEngine.currentBackgroundChannel].gain.linearRampToValueAtTime(1.0, fadeTime); + 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); }, - changeMusic: function (src) { + playBackgroundMusic: function (src) { AudioEngine.loadAudioFile(src) .then(function (buffer) { - AudioEngine._fadeTrack(buffer); + AudioEngine._playBackgroundMusic(buffer); }); }, playEventMusic: function (src) { AudioEngine.loadAudioFile(src) .then(function (buffer) { - AudioEngine._playEvent(buffer); + AudioEngine._playEventMusic(buffer); }); }, stopEventMusic: function () { @@ -185,37 +184,29 @@ var AudioEngine = { return AudioEngine._getMissingAudioBuffer(); } - return AudioEngine.audioContext.decodeAudioData(buffer, function (decodedData) { + return AudioEngine._audioContext.decodeAudioData(buffer, function (decodedData) { AudioEngine.AUDIO_BUFFER_CACHE[src] = decodedData; return AudioEngine.AUDIO_BUFFER_CACHE[src]; }); }); } }, - mute: function () { - AudioEngine.master.gain.linearRampToValueAtTime( - 0.0, - AudioEngine.audioContext.currentTime + AudioEngine.FADE_TIME - ); - }, - getVolume: function () { - return AudioEngine.master.gain.value; - }, setVolume: function (volume, s) { - if (!AudioEngine.master) return; // master may not be ready yet - if (!volume) { + if (AudioEngine._master == null) return; // master may not be ready yet + if (volume === undefined) { volume = 1.0; } - if (!s) { + if (s === undefined) { s = 1.0; } - AudioEngine.master.gain.setValueAtTime( - AudioEngine.master.gain.value, - AudioEngine.audioContext.currentTime - ); - AudioEngine.master.gain.linearRampToValueAtTime( + + // 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 + AudioEngine._audioContext.currentTime + s ); } }; \ No newline at end of file diff --git a/script/engine.js b/script/engine.js index 142127a..3a077d8 100644 --- a/script/engine.js +++ b/script/engine.js @@ -805,7 +805,7 @@ if ($SM.get('config.soundOn')) { $('.volume').text(_('sound on.')); $SM.set('config.soundOn', false); - AudioEngine.mute(); + AudioEngine.setVolume(0.0); } else { $('.volume').text(_('sound off.')); $SM.set('config.soundOn', true); diff --git a/script/outside.js b/script/outside.js index 8a009a5..ef3f1a8 100644 --- a/script/outside.js +++ b/script/outside.js @@ -591,17 +591,17 @@ var Outside = { // set music var numberOfHuts = $SM.get('game.buildings["hut"]', true); if(numberOfHuts === 0) { - AudioEngine.changeMusic(AudioLibrary.MUSIC_SILENT_FOREST); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_SILENT_FOREST); } else if(numberOfHuts == 1) { - AudioEngine.changeMusic(AudioLibrary.MUSIC_LONELY_HUT); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_LONELY_HUT); } else if(numberOfHuts <= 4) { - AudioEngine.changeMusic(AudioLibrary.MUSIC_TINY_VILLAGE); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_TINY_VILLAGE); } else if(numberOfHuts <= 8) { - AudioEngine.changeMusic(AudioLibrary.MUSIC_MODEST_VILLAGE); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_MODEST_VILLAGE); } else if(numberOfHuts <= 14) { - AudioEngine.changeMusic(AudioLibrary.MUSIC_LARGE_VILLAGE); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_LARGE_VILLAGE); } else { - AudioEngine.changeMusic(AudioLibrary.MUSIC_RAUCOUS_VILLAGE); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_RAUCOUS_VILLAGE); } }, diff --git a/script/path.js b/script/path.js index 97c8840..cdf824b 100644 --- a/script/path.js +++ b/script/path.js @@ -304,7 +304,7 @@ var Path = { Path.updateOutfitting(); Path.updatePerks(true); - AudioEngine.changeMusic(AudioLibrary.MUSIC_DUSTY_PATH); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_DUSTY_PATH); Engine.moveStoresView($('#perks'), transition_diff); }, diff --git a/script/room.js b/script/room.js index 0a04515..1c10920 100644 --- a/script/room.js +++ b/script/room.js @@ -1232,19 +1232,19 @@ var Room = { var fireValue = $SM.get('game.fire.value'); switch (fireValue) { case 0: - AudioEngine.changeMusic(AudioLibrary.MUSIC_FIRE_DEAD); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_FIRE_DEAD); break; case 1: - AudioEngine.changeMusic(AudioLibrary.MUSIC_FIRE_SMOLDERING); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_FIRE_SMOLDERING); break; case 2: - AudioEngine.changeMusic(AudioLibrary.MUSIC_FIRE_FLICKERING); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_FIRE_FLICKERING); break; case 3: - AudioEngine.changeMusic(AudioLibrary.MUSIC_FIRE_BURNING); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_FIRE_BURNING); break; case 4: - AudioEngine.changeMusic(AudioLibrary.MUSIC_FIRE_ROARING); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_FIRE_ROARING); break; } } diff --git a/script/ship.js b/script/ship.js index 4187d41..3cfd1bc 100644 --- a/script/ship.js +++ b/script/ship.js @@ -90,7 +90,7 @@ var Ship = { Notifications.notify(Ship, _('somewhere above the debris cloud, the wanderer fleet hovers. been on this rock too long.')); $SM.set('game.spaceShip.seenShip', true); } - AudioEngine.changeMusic(AudioLibrary.MUSIC_SHIP); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_SHIP); Engine.moveStoresView(null, transition_diff); }, diff --git a/script/space.js b/script/space.js index f658b34..b889f25 100644 --- a/script/space.js +++ b/script/space.js @@ -53,7 +53,7 @@ var Space = { Space.hull = Ship.getMaxHull(); Space.altitude = 0; Space.setTitle(); - AudioEngine.changeMusic(AudioLibrary.MUSIC_SPACE); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_SPACE); Space.updateHull(); Space.up = @@ -68,7 +68,7 @@ var Space = { Space.startAscent(); Space._shipTimer = setInterval(Space.moveShip, 33); Space._volumeTimer = setInterval(Space.lowerVolume, 1000); - AudioEngine.changeMusic(AudioLibrary.MUSIC_SPACE); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_SPACE); }, setTitle: function() { @@ -401,7 +401,7 @@ var Space = { delete Outside._popTimeout; AudioEngine.setVolume(1.0); - AudioEngine.changeMusic(AudioLibrary.MUSIC_ENDING); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_ENDING); $('#hullRemaining', Space.panel).animate({opacity: 0}, 500, 'linear'); Space.ship.animate({ top: '350px', diff --git a/script/world.js b/script/world.js index 28be68e..15ad4b9 100644 --- a/script/world.js +++ b/script/world.js @@ -1012,7 +1012,7 @@ var World = { World.curPos = World.copyPos(World.VILLAGE_POS); World.drawMap(); World.setTitle(); - AudioEngine.changeMusic(AudioLibrary.MUSIC_WORLD); + AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_WORLD); World.dead = false; $('div#bagspace-world > div').empty(); World.updateSupplies();