diff --git a/games/masterplan/src/screens/battle/game/objects/object-soldier.ts b/games/masterplan/src/screens/battle/game/objects/object-soldier.ts index c673910a..f3aca5e2 100644 --- a/games/masterplan/src/screens/battle/game/objects/object-soldier.ts +++ b/games/masterplan/src/screens/battle/game/objects/object-soldier.ts @@ -1,7 +1,7 @@ import { GameObject } from './game-object'; import { VMath } from '../../util/vmath'; import { EVENT_DAMAGE, EVENT_DAMAGE_ARROW } from '../../events'; -import { aa } from '../../lib/sound'; +import { aa } from '../../util/arcade-audio'; import { MAX_LIFE, SOLDIER_WIDTH, diff --git a/games/masterplan/src/screens/battle/lib/jsfxr.js b/games/masterplan/src/screens/battle/lib/jsfxr.js new file mode 100644 index 00000000..82e96f0b --- /dev/null +++ b/games/masterplan/src/screens/battle/lib/jsfxr.js @@ -0,0 +1,41 @@ +import { SfxrSynth } from './sfxr-synth'; + +// Adapted from http://codebase.es/riffwave/ +var synth = new SfxrSynth(); +// Export for the Closure Compiler +export const jsfxr = function (settings) { + // Initialize SfxrParams + synth._params.setSettings(settings); + // Synthesize Wave + var envelopeFullLength = synth.totalReset(); + var data = new Uint8Array((((envelopeFullLength + 1) / 2) | 0) * 4 + 44); + var used = synth.synthWave(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; + var dv = new Uint32Array(data.buffer, 0, 44); + // Initialize header + dv[0] = 0x46464952; // "RIFF" + dv[1] = used + 36; // put total size here + dv[2] = 0x45564157; // "WAVE" + dv[3] = 0x20746d66; // "fmt " + dv[4] = 0x00000010; // size of the following + dv[5] = 0x00010001; // Mono: 1 channel, PCM format + dv[6] = 0x0000ac44; // 44,100 samples per second + dv[7] = 0x00015888; // byte rate: two bytes per sample + dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes + dv[9] = 0x61746164; // "data" + dv[10] = used; // put number of samples here + + // Base64 encoding written by me, @maettig + used += 44; + var i = 0, + base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + output = 'data:audio/wav;base64,'; + for (; i < used; i += 3) { + var a = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2]; + output += + base64Characters[a >> 18] + + base64Characters[(a >> 12) & 63] + + base64Characters[(a >> 6) & 63] + + base64Characters[a & 63]; + } + return output; +}; diff --git a/games/masterplan/src/screens/battle/lib/sfxr-synth.js b/games/masterplan/src/screens/battle/lib/sfxr-synth.js new file mode 100644 index 00000000..566de778 --- /dev/null +++ b/games/masterplan/src/screens/battle/lib/sfxr-synth.js @@ -0,0 +1,410 @@ +/** + * SfxrParams + * + * Copyright 2010 Thomas Vian + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Thomas Vian + */ +/** @constructor */ +export function SfxrParams() { + //-------------------------------------------------------------------------- + // + // Settings String Methods + // + //-------------------------------------------------------------------------- + + this.setSettings = function (values) { + for (var i = 0; i < 24; i++) { + this[String.fromCharCode(97 + i)] = values[i] || 0; + } + + // I moved this here from the reset(true) function + if (this['c'] < 0.01) { + this['c'] = 0.01; + } + + var totalTime = this['b'] + this['c'] + this['e']; + if (totalTime < 0.18) { + var multiplier = 0.18 / totalTime; + this['b'] *= multiplier; + this['c'] *= multiplier; + this['e'] *= multiplier; + } + }; +} + +/** + * SfxrSynth + * + * Copyright 2010 Thomas Vian + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Thomas Vian + */ +/** @constructor */ +export function SfxrSynth() { + // All variables are kept alive through function closures + + //-------------------------------------------------------------------------- + // + // Sound Parameters + // + //-------------------------------------------------------------------------- + + this._params = new SfxrParams(); // Params instance + + //-------------------------------------------------------------------------- + // + // Synth Variables + // + //-------------------------------------------------------------------------- + + var _envelopeLength0, // Length of the attack stage + _envelopeLength1, // Length of the sustain stage + _envelopeLength2, // Length of the decay stage + _period, // Period of the wave + _maxPeriod, // Maximum period before sound stops (from minFrequency) + _slide, // Note slide + _deltaSlide, // Change in slide + _changeAmount, // Amount to change the note by + _changeTime, // Counter for the note change + _changeLimit, // Once the time reaches this limit, the note changes + _squareDuty, // Offset of center switching point in the square wave + _dutySweep; // Amount to change the duty by + + //-------------------------------------------------------------------------- + // + // Synth Methods + // + //-------------------------------------------------------------------------- + + /** + * Resets the runing variables from the params + * Used once at the start (total reset) and for the repeat effect (partial reset) + */ + this.reset = function () { + // Shorter reference + var p = this._params; + + _period = 100 / (p['f'] * p['f'] + 0.001); + _maxPeriod = 100 / (p['g'] * p['g'] + 0.001); + + _slide = 1 - p['h'] * p['h'] * p['h'] * 0.01; + _deltaSlide = -p['i'] * p['i'] * p['i'] * 0.000001; + + if (!p['a']) { + _squareDuty = 0.5 - p['n'] / 2; + _dutySweep = -p['o'] * 0.00005; + } + + _changeAmount = 1 + p['l'] * p['l'] * (p['l'] > 0 ? -0.9 : 10); + _changeTime = 0; + _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; + }; + + // I split the reset() function into two functions for better readability + this.totalReset = function () { + this.reset(); + + // Shorter reference + var p = this._params; + + // Calculating the length is all that remained here, everything else moved somewhere + _envelopeLength0 = p['b'] * p['b'] * 100000; + _envelopeLength1 = p['c'] * p['c'] * 100000; + _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; + // Full length of the volume envelop (and therefore sound) + // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode + return (((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3) | 0) * 3; + }; + + /** + * Writes the wave to the supplied buffer ByteArray + * @param buffer A ByteArray to write the wave to + * @return If the wave is finished + */ + this.synthWave = function (buffer, length) { + // Shorter reference + var p = this._params; + + // If the filters are active + var _filters = p['s'] != 1 || p['v'], + // Cutoff multiplier which adjusts the amount the wave position can move + _hpFilterCutoff = p['v'] * p['v'] * 0.1, + // Speed of the high-pass cutoff multiplier + _hpFilterDeltaCutoff = 1 + p['w'] * 0.0003, + // Cutoff multiplier which adjusts the amount the wave position can move + _lpFilterCutoff = p['s'] * p['s'] * p['s'] * 0.1, + // Speed of the low-pass cutoff multiplier + _lpFilterDeltaCutoff = 1 + p['t'] * 0.0001, + // If the low pass filter is active + _lpFilterOn = p['s'] != 1, + // masterVolume * masterVolume (for quick calculations) + _masterVolume = p['x'] * p['x'], + // Minimum frequency before stopping + _minFreqency = p['g'], + // If the phaser is active + _phaser = p['q'] || p['r'], + // Change in phase offset + _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * 0.2, + // Phase offset for phaser effect + _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), + // Once the time reaches this limit, some of the iables are reset + _repeatLimit = p['p'] ? (((1 - p['p']) * (1 - p['p']) * 20000) | 0) + 32 : 0, + // The punch factor (louder at begining of sustain) + _sustainPunch = p['d'], + // Amount to change the period of the wave by at the peak of the vibrato wave + _vibratoAmplitude = p['j'] / 2, + // Speed at which the vibrato phase moves + _vibratoSpeed = p['k'] * p['k'] * 0.01, + // The type of wave to generate + _waveType = p['a']; + + var _envelopeLength = _envelopeLength0, // Length of the current envelope stage + _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) + _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) + _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) + + // Damping muliplier which restricts how fast the wave position can move + var _lpFilterDamping = (5 / (1 + p['u'] * p['u'] * 20)) * (0.01 + _lpFilterCutoff); + if (_lpFilterDamping > 0.8) { + _lpFilterDamping = 0.8; + } + _lpFilterDamping = 1 - _lpFilterDamping; + + var _finished = false, // If the sound has finished + _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) + _envelopeTime = 0, // Current time through current enelope stage + _envelopeVolume = 0, // Current volume of the envelope + _hpFilterPos = 0, // Adjusted wave position after high-pass filter + _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping + _lpFilterOldPos, // Previous low-pass wave position + _lpFilterPos = 0, // Adjusted wave position after low-pass filter + _periodTemp, // Period modified by vibrato + _phase = 0, // Phase through the wave + _phaserInt, // Integer phaser offset, for bit maths + _phaserPos = 0, // Position through the phaser buffer + _pos, // Phase expresed as a Number from 0-1, used for fast sin approx + _repeatTime = 0, // Counter for the repeats + _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample + _superSample, // Actual sample writen to the wave + _vibratoPhase = 0; // Phase through the vibrato sine wave + + // Buffer of wave values used to create the out of phase second wave + var _phaserBuffer = new Array(1024), + // Buffer of random values used to generate noise + _noiseBuffer = new Array(32); + for (let i = _phaserBuffer.length; i--; ) { + _phaserBuffer[i] = 0; + } + for (let j = _noiseBuffer.length; j--; ) { + _noiseBuffer[j] = Math.random() * 2 - 1; + } + + for (var i = 0; i < length; i++) { + if (_finished) { + return i; + } + + // Repeats every _repeatLimit times, partially resetting the sound parameters + if (_repeatLimit) { + if (++_repeatTime >= _repeatLimit) { + _repeatTime = 0; + this.reset(); + } + } + + // If _changeLimit is reached, shifts the pitch + if (_changeLimit) { + if (++_changeTime >= _changeLimit) { + _changeLimit = 0; + _period *= _changeAmount; + } + } + + // Acccelerate and apply slide + _slide += _deltaSlide; + _period *= _slide; + + // Checks for frequency getting too low, and stops the sound if a minFrequency was set + if (_period > _maxPeriod) { + _period = _maxPeriod; + if (_minFreqency > 0) { + _finished = true; + } + } + + _periodTemp = _period; + + // Applies the vibrato effect + if (_vibratoAmplitude > 0) { + _vibratoPhase += _vibratoSpeed; + _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; + } + + _periodTemp |= 0; + if (_periodTemp < 8) { + _periodTemp = 8; + } + + // Sweeps the square duty + if (!_waveType) { + _squareDuty += _dutySweep; + if (_squareDuty < 0) { + _squareDuty = 0; + } else if (_squareDuty > 0.5) { + _squareDuty = 0.5; + } + } + + // Moves through the different stages of the volume envelope + if (++_envelopeTime > _envelopeLength) { + _envelopeTime = 0; + + switch (++_envelopeStage) { + case 1: + _envelopeLength = _envelopeLength1; + break; + case 2: + _envelopeLength = _envelopeLength2; + } + } + + // Sets the volume based on the position in the envelope + switch (_envelopeStage) { + case 0: + _envelopeVolume = _envelopeTime * _envelopeOverLength0; + break; + case 1: + _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; + break; + case 2: + _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; + break; + case 3: + _envelopeVolume = 0; + _finished = true; + } + + // Moves the phaser offset + if (_phaser) { + _phaserOffset += _phaserDeltaOffset; + _phaserInt = _phaserOffset | 0; + if (_phaserInt < 0) { + _phaserInt = -_phaserInt; + } else if (_phaserInt > 1023) { + _phaserInt = 1023; + } + } + + // Moves the high-pass filter cutoff + if (_filters && _hpFilterDeltaCutoff) { + _hpFilterCutoff *= _hpFilterDeltaCutoff; + if (_hpFilterCutoff < 0.00001) { + _hpFilterCutoff = 0.00001; + } else if (_hpFilterCutoff > 0.1) { + _hpFilterCutoff = 0.1; + } + } + + _superSample = 0; + for (var j = 8; j--; ) { + // Cycles through the period + _phase++; + if (_phase >= _periodTemp) { + _phase %= _periodTemp; + + // Generates new random noise for this period + if (_waveType == 3) { + for (var n = _noiseBuffer.length; n--; ) { + _noiseBuffer[n] = Math.random() * 2 - 1; + } + } + } + + // Gets the sample from the oscillator + switch (_waveType) { + case 0: // Square wave + _sample = _phase / _periodTemp < _squareDuty ? 0.5 : -0.5; + break; + case 1: // Saw wave + _sample = 1 - (_phase / _periodTemp) * 2; + break; + case 2: // Sine wave (fast and accurate approx) + _pos = _phase / _periodTemp; + _pos = (_pos > 0.5 ? _pos - 1 : _pos) * 6.28318531; + _sample = 1.27323954 * _pos + 0.405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); + _sample = 0.225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; + break; + case 3: // Noise + _sample = _noiseBuffer[Math.abs(((_phase * 32) / _periodTemp) | 0)]; + } + + // Applies the low and high pass filters + if (_filters) { + _lpFilterOldPos = _lpFilterPos; + _lpFilterCutoff *= _lpFilterDeltaCutoff; + if (_lpFilterCutoff < 0) { + _lpFilterCutoff = 0; + } else if (_lpFilterCutoff > 0.1) { + _lpFilterCutoff = 0.1; + } + + if (_lpFilterOn) { + _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; + _lpFilterDeltaPos *= _lpFilterDamping; + } else { + _lpFilterPos = _sample; + _lpFilterDeltaPos = 0; + } + + _lpFilterPos += _lpFilterDeltaPos; + + _hpFilterPos += _lpFilterPos - _lpFilterOldPos; + _hpFilterPos *= 1 - _hpFilterCutoff; + _sample = _hpFilterPos; + } + + // Applies the phaser effect + if (_phaser) { + _phaserBuffer[_phaserPos % 1024] = _sample; + _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; + _phaserPos++; + } + + _superSample += _sample; + } + + // Averages out the super samples and applies volumes + _superSample *= 0.125 * _envelopeVolume * _masterVolume; + + // Clipping if too loud + buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : (_superSample * 32767) | 0; + } + + return length; + }; +} diff --git a/games/masterplan/src/screens/battle/lib/sound.js b/games/masterplan/src/screens/battle/lib/sound.js deleted file mode 100644 index 413a9506..00000000 --- a/games/masterplan/src/screens/battle/lib/sound.js +++ /dev/null @@ -1,488 +0,0 @@ -/** - * SfxrParams - * - * Copyright 2010 Thomas Vian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Thomas Vian - */ -/** @constructor */ -export function SfxrParams() { - //-------------------------------------------------------------------------- - // - // Settings String Methods - // - //-------------------------------------------------------------------------- - - this.setSettings = function (values) { - for (var i = 0; i < 24; i++) { - this[String.fromCharCode(97 + i)] = values[i] || 0; - } - - // I moved this here from the reset(true) function - if (this['c'] < .01) { - this['c'] = .01; - } - - var totalTime = this['b'] + this['c'] + this['e']; - if (totalTime < .18) { - var multiplier = .18 / totalTime; - this['b'] *= multiplier; - this['c'] *= multiplier; - this['e'] *= multiplier; - } - } -} - -/** - * SfxrSynth - * - * Copyright 2010 Thomas Vian - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Thomas Vian - */ -/** @constructor */ -export function SfxrSynth() { - // All variables are kept alive through function closures - - //-------------------------------------------------------------------------- - // - // Sound Parameters - // - //-------------------------------------------------------------------------- - - this._params = new SfxrParams(); // Params instance - - //-------------------------------------------------------------------------- - // - // Synth Variables - // - //-------------------------------------------------------------------------- - - var _envelopeLength0, // Length of the attack stage - _envelopeLength1, // Length of the sustain stage - _envelopeLength2, // Length of the decay stage - - _period, // Period of the wave - _maxPeriod, // Maximum period before sound stops (from minFrequency) - - _slide, // Note slide - _deltaSlide, // Change in slide - - _changeAmount, // Amount to change the note by - _changeTime, // Counter for the note change - _changeLimit, // Once the time reaches this limit, the note changes - - _squareDuty, // Offset of center switching point in the square wave - _dutySweep; // Amount to change the duty by - - //-------------------------------------------------------------------------- - // - // Synth Methods - // - //-------------------------------------------------------------------------- - - /** - * Resets the runing variables from the params - * Used once at the start (total reset) and for the repeat effect (partial reset) - */ - this.reset = function () { - // Shorter reference - var p = this._params; - - _period = 100 / (p['f'] * p['f'] + .001); - _maxPeriod = 100 / (p['g'] * p['g'] + .001); - - _slide = 1 - p['h'] * p['h'] * p['h'] * .01; - _deltaSlide = -p['i'] * p['i'] * p['i'] * .000001; - - if (!p['a']) { - _squareDuty = .5 - p['n'] / 2; - _dutySweep = -p['o'] * .00005; - } - - _changeAmount = 1 + p['l'] * p['l'] * (p['l'] > 0 ? -.9 : 10); - _changeTime = 0; - _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; - } - - // I split the reset() function into two functions for better readability - this.totalReset = function () { - this.reset(); - - // Shorter reference - var p = this._params; - - // Calculating the length is all that remained here, everything else moved somewhere - _envelopeLength0 = p['b'] * p['b'] * 100000; - _envelopeLength1 = p['c'] * p['c'] * 100000; - _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; - // Full length of the volume envelop (and therefore sound) - // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode - return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; - } - - /** - * Writes the wave to the supplied buffer ByteArray - * @param buffer A ByteArray to write the wave to - * @return If the wave is finished - */ - this.synthWave = function (buffer, length) { - // Shorter reference - var p = this._params; - - // If the filters are active - var _filters = p['s'] != 1 || p['v'], - // Cutoff multiplier which adjusts the amount the wave position can move - _hpFilterCutoff = p['v'] * p['v'] * .1, - // Speed of the high-pass cutoff multiplier - _hpFilterDeltaCutoff = 1 + p['w'] * .0003, - // Cutoff multiplier which adjusts the amount the wave position can move - _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, - // Speed of the low-pass cutoff multiplier - _lpFilterDeltaCutoff = 1 + p['t'] * .0001, - // If the low pass filter is active - _lpFilterOn = p['s'] != 1, - // masterVolume * masterVolume (for quick calculations) - _masterVolume = p['x'] * p['x'], - // Minimum frequency before stopping - _minFreqency = p['g'], - // If the phaser is active - _phaser = p['q'] || p['r'], - // Change in phase offset - _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, - // Phase offset for phaser effect - _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), - // Once the time reaches this limit, some of the iables are reset - _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, - // The punch factor (louder at begining of sustain) - _sustainPunch = p['d'], - // Amount to change the period of the wave by at the peak of the vibrato wave - _vibratoAmplitude = p['j'] / 2, - // Speed at which the vibrato phase moves - _vibratoSpeed = p['k'] * p['k'] * .01, - // The type of wave to generate - _waveType = p['a']; - - var _envelopeLength = _envelopeLength0, // Length of the current envelope stage - _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) - _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) - _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) - - // Damping muliplier which restricts how fast the wave position can move - var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); - if (_lpFilterDamping > .8) { - _lpFilterDamping = .8; - } - _lpFilterDamping = 1 - _lpFilterDamping; - - var _finished = false, // If the sound has finished - _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) - _envelopeTime = 0, // Current time through current enelope stage - _envelopeVolume = 0, // Current volume of the envelope - _hpFilterPos = 0, // Adjusted wave position after high-pass filter - _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping - _lpFilterOldPos, // Previous low-pass wave position - _lpFilterPos = 0, // Adjusted wave position after low-pass filter - _periodTemp, // Period modified by vibrato - _phase = 0, // Phase through the wave - _phaserInt, // Integer phaser offset, for bit maths - _phaserPos = 0, // Position through the phaser buffer - _pos, // Phase expresed as a Number from 0-1, used for fast sin approx - _repeatTime = 0, // Counter for the repeats - _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample - _superSample, // Actual sample writen to the wave - _vibratoPhase = 0; // Phase through the vibrato sine wave - - // Buffer of wave values used to create the out of phase second wave - var _phaserBuffer = new Array(1024), - // Buffer of random values used to generate noise - _noiseBuffer = new Array(32); - for (let i = _phaserBuffer.length; i--;) { - _phaserBuffer[i] = 0; - } - for (let j = _noiseBuffer.length; j--;) { - _noiseBuffer[j] = Math.random() * 2 - 1; - } - - for (var i = 0; i < length; i++) { - if (_finished) { - return i; - } - - // Repeats every _repeatLimit times, partially resetting the sound parameters - if (_repeatLimit) { - if (++_repeatTime >= _repeatLimit) { - _repeatTime = 0; - this.reset(); - } - } - - // If _changeLimit is reached, shifts the pitch - if (_changeLimit) { - if (++_changeTime >= _changeLimit) { - _changeLimit = 0; - _period *= _changeAmount; - } - } - - // Acccelerate and apply slide - _slide += _deltaSlide; - _period *= _slide; - - // Checks for frequency getting too low, and stops the sound if a minFrequency was set - if (_period > _maxPeriod) { - _period = _maxPeriod; - if (_minFreqency > 0) { - _finished = true; - } - } - - _periodTemp = _period; - - // Applies the vibrato effect - if (_vibratoAmplitude > 0) { - _vibratoPhase += _vibratoSpeed; - _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; - } - - _periodTemp |= 0; - if (_periodTemp < 8) { - _periodTemp = 8; - } - - // Sweeps the square duty - if (!_waveType) { - _squareDuty += _dutySweep; - if (_squareDuty < 0) { - _squareDuty = 0; - } else if (_squareDuty > .5) { - _squareDuty = .5; - } - } - - // Moves through the different stages of the volume envelope - if (++_envelopeTime > _envelopeLength) { - _envelopeTime = 0; - - switch (++_envelopeStage) { - case 1: - _envelopeLength = _envelopeLength1; - break; - case 2: - _envelopeLength = _envelopeLength2; - } - } - - // Sets the volume based on the position in the envelope - switch (_envelopeStage) { - case 0: - _envelopeVolume = _envelopeTime * _envelopeOverLength0; - break; - case 1: - _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; - break; - case 2: - _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; - break; - case 3: - _envelopeVolume = 0; - _finished = true; - } - - // Moves the phaser offset - if (_phaser) { - _phaserOffset += _phaserDeltaOffset; - _phaserInt = _phaserOffset | 0; - if (_phaserInt < 0) { - _phaserInt = -_phaserInt; - } else if (_phaserInt > 1023) { - _phaserInt = 1023; - } - } - - // Moves the high-pass filter cutoff - if (_filters && _hpFilterDeltaCutoff) { - _hpFilterCutoff *= _hpFilterDeltaCutoff; - if (_hpFilterCutoff < .00001) { - _hpFilterCutoff = .00001; - } else if (_hpFilterCutoff > .1) { - _hpFilterCutoff = .1; - } - } - - _superSample = 0; - for (var j = 8; j--;) { - // Cycles through the period - _phase++; - if (_phase >= _periodTemp) { - _phase %= _periodTemp; - - // Generates new random noise for this period - if (_waveType == 3) { - for (var n = _noiseBuffer.length; n--;) { - _noiseBuffer[n] = Math.random() * 2 - 1; - } - } - } - - // Gets the sample from the oscillator - switch (_waveType) { - case 0: // Square wave - _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; - break; - case 1: // Saw wave - _sample = 1 - _phase / _periodTemp * 2; - break; - case 2: // Sine wave (fast and accurate approx) - _pos = _phase / _periodTemp; - _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; - _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); - _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; - break; - case 3: // Noise - _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; - } - - // Applies the low and high pass filters - if (_filters) { - _lpFilterOldPos = _lpFilterPos; - _lpFilterCutoff *= _lpFilterDeltaCutoff; - if (_lpFilterCutoff < 0) { - _lpFilterCutoff = 0; - } else if (_lpFilterCutoff > .1) { - _lpFilterCutoff = .1; - } - - if (_lpFilterOn) { - _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; - _lpFilterDeltaPos *= _lpFilterDamping; - } else { - _lpFilterPos = _sample; - _lpFilterDeltaPos = 0; - } - - _lpFilterPos += _lpFilterDeltaPos; - - _hpFilterPos += _lpFilterPos - _lpFilterOldPos; - _hpFilterPos *= 1 - _hpFilterCutoff; - _sample = _hpFilterPos; - } - - // Applies the phaser effect - if (_phaser) { - _phaserBuffer[_phaserPos % 1024] = _sample; - _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; - _phaserPos++; - } - - _superSample += _sample; - } - - // Averages out the super samples and applies volumes - _superSample *= .125 * _envelopeVolume * _masterVolume; - - // Clipping if too loud - buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; - } - - return length; - } -} - -// Adapted from http://codebase.es/riffwave/ -var synth = new SfxrSynth(); -// Export for the Closure Compiler -export const jsfxr = function (settings) { - // Initialize SfxrParams - synth._params.setSettings(settings); - // Synthesize Wave - var envelopeFullLength = synth.totalReset(); - var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); - var used = synth.synthWave(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; - var dv = new Uint32Array(data.buffer, 0, 44); - // Initialize header - dv[0] = 0x46464952; // "RIFF" - dv[1] = used + 36; // put total size here - dv[2] = 0x45564157; // "WAVE" - dv[3] = 0x20746D66; // "fmt " - dv[4] = 0x00000010; // size of the following - dv[5] = 0x00010001; // Mono: 1 channel, PCM format - dv[6] = 0x0000AC44; // 44,100 samples per second - dv[7] = 0x00015888; // byte rate: two bytes per sample - dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes - dv[9] = 0x61746164; // "data" - dv[10] = used; // put number of samples here - - // Base64 encoding written by me, @maettig - used += 44; - var i = 0, - base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', - output = 'data:audio/wav;base64,'; - for (; i < used; i += 3) { - var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; - output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; - } - return output; -} - -/** @constructor */ -export function ArcadeAudio() { - this.sounds = {}; -} - -ArcadeAudio.prototype.add = function (key, count, settings) { - this.sounds[key] = []; - settings.forEach(function (elem, index) { - this.sounds[key].push({ - tick: 0, - count: count, - pool: [] - }); - for (var i = 0; i < count; i++) { - var audio = new Audio(); - audio.src = jsfxr(elem); - this.sounds[key][index].pool.push(audio); - } - }, this); -}; - -ArcadeAudio.prototype.play = function (key) { - var sound = this.sounds[key]; - var soundData = sound.length > 1 ? sound[Math.floor(Math.random() * sound.length)] : sound[0]; - soundData.pool[soundData.tick].play(); - soundData.tick < soundData.count - 1 ? soundData.tick++ : soundData.tick = 0; -}; - -export const aa = new ArcadeAudio(); - -aa.add('arrow', 10, [[0,0.13,0.12,0.21,0.28,0.7097,0.11,0.4399,,,,0.2845,0.6608,,,,,,1,,,,,0.32]]); -aa.add('hitarrow', 10, [[2,,0.0664,,0.1176,0.7984,,-0.5791,,,,,,,,,,,1,,,0.0922,,0.47]]); -aa.add('damage', 10, - [ - [3,,0.0421,,0.1467,0.7815,,-0.4846,,,,,,0.4464,,,,,1,,,0.2247,,0.47] - ] -); \ No newline at end of file diff --git a/games/masterplan/src/screens/battle/util/arcade-audio.js b/games/masterplan/src/screens/battle/util/arcade-audio.js new file mode 100644 index 00000000..d1f6ab5b --- /dev/null +++ b/games/masterplan/src/screens/battle/util/arcade-audio.js @@ -0,0 +1,37 @@ +import { jsfxr } from '../lib/jsfxr'; + +/** @constructor */ +export function ArcadeAudio() { + this.sounds = {}; +} + +ArcadeAudio.prototype.add = function (key, count, settings) { + this.sounds[key] = []; + settings.forEach(function (elem, index) { + this.sounds[key].push({ + tick: 0, + count: count, + pool: [], + }); + for (var i = 0; i < count; i++) { + var audio = new Audio(); + audio.src = jsfxr(elem); + this.sounds[key][index].pool.push(audio); + } + }, this); +}; + +ArcadeAudio.prototype.play = function (key) { + var sound = this.sounds[key]; + var soundData = sound.length > 1 ? sound[Math.floor(Math.random() * sound.length)] : sound[0]; + soundData.pool[soundData.tick].play(); + soundData.tick < soundData.count - 1 ? soundData.tick++ : (soundData.tick = 0); +}; + +export const aa = new ArcadeAudio(); + +aa.add('arrow', 10, [ + [0, 0.13, 0.12, 0.21, 0.28, 0.7097, 0.11, 0.4399, , , , 0.2845, 0.6608, , , , , , 1, , , , , 0.32], +]); +aa.add('hitarrow', 10, [[2, , 0.0664, , 0.1176, 0.7984, , -0.5791, , , , , , , , , , , 1, , , 0.0922, , 0.47]]); +aa.add('damage', 10, [[3, , 0.0421, , 0.1467, 0.7815, , -0.4846, , , , , , 0.4464, , , , , 1, , , 0.2247, , 0.47]]);