mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 05:53:43 -05:00
Use uid instead of md5 for keying audio buffer cache
This commit is contained in:
parent
6863ad25a2
commit
0555407406
2 changed files with 48 additions and 18 deletions
37
src/index.js
37
src/index.js
|
@ -1,7 +1,8 @@
|
||||||
const log = require('./log');
|
|
||||||
|
|
||||||
const AudioContext = require('audio-context');
|
const AudioContext = require('audio-context');
|
||||||
|
|
||||||
|
const log = require('./log');
|
||||||
|
const uid = require('./uid');
|
||||||
|
|
||||||
const PitchEffect = require('./effects/PitchEffect');
|
const PitchEffect = require('./effects/PitchEffect');
|
||||||
const PanEffect = require('./effects/PanEffect');
|
const PanEffect = require('./effects/PanEffect');
|
||||||
|
|
||||||
|
@ -39,35 +40,35 @@ class AudioPlayer {
|
||||||
// reset effects to their default parameters
|
// reset effects to their default parameters
|
||||||
this.clearEffects();
|
this.clearEffects();
|
||||||
|
|
||||||
// sound players that are currently playing, indexed by the sound's md5
|
// sound players that are currently playing, indexed by the sound's soundId
|
||||||
this.activeSoundPlayers = {};
|
this.activeSoundPlayers = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play a sound
|
* Play a sound
|
||||||
* @param {string} md5 - the md5 id of a sound file
|
* @param {string} soundId - the soundId id of a sound file
|
||||||
* @return {Promise} a Promise that resolves when the sound finishes playing
|
* @return {Promise} a Promise that resolves when the sound finishes playing
|
||||||
*/
|
*/
|
||||||
playSound (md5) {
|
playSound (soundId) {
|
||||||
// if this sound is not in the audio engine, return
|
// if this sound is not in the audio engine, return
|
||||||
if (!this.audioEngine.audioBuffers[md5]) {
|
if (!this.audioEngine.audioBuffers[soundId]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this sprite or clone is already playing this sound, stop it first
|
// if this sprite or clone is already playing this sound, stop it first
|
||||||
if (this.activeSoundPlayers[md5]) {
|
if (this.activeSoundPlayers[soundId]) {
|
||||||
this.activeSoundPlayers[md5].stop();
|
this.activeSoundPlayers[soundId].stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new soundplayer to play the sound
|
// create a new soundplayer to play the sound
|
||||||
const player = new SoundPlayer(this.audioEngine.audioContext);
|
const player = new SoundPlayer(this.audioEngine.audioContext);
|
||||||
player.setBuffer(this.audioEngine.audioBuffers[md5]);
|
player.setBuffer(this.audioEngine.audioBuffers[soundId]);
|
||||||
player.connect(this.effectsNode);
|
player.connect(this.effectsNode);
|
||||||
this.pitchEffect.updatePlayer(player);
|
this.pitchEffect.updatePlayer(player);
|
||||||
player.start();
|
player.start();
|
||||||
|
|
||||||
// add it to the list of active sound players
|
// add it to the list of active sound players
|
||||||
this.activeSoundPlayers[md5] = player;
|
this.activeSoundPlayers[soundId] = player;
|
||||||
|
|
||||||
// remove sounds that are not playing from the active sound players array
|
// remove sounds that are not playing from the active sound players array
|
||||||
for (const id in this.activeSoundPlayers) {
|
for (const id in this.activeSoundPlayers) {
|
||||||
|
@ -98,8 +99,8 @@ class AudioPlayer {
|
||||||
*/
|
*/
|
||||||
stopAllSounds () {
|
stopAllSounds () {
|
||||||
// stop all active sound players
|
// stop all active sound players
|
||||||
for (const md5 in this.activeSoundPlayers) {
|
for (const soundId in this.activeSoundPlayers) {
|
||||||
this.activeSoundPlayers[md5].stop();
|
this.activeSoundPlayers[soundId].stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop all instruments
|
// stop all instruments
|
||||||
|
@ -168,7 +169,7 @@ class AudioEngine {
|
||||||
this.drumPlayer = new DrumPlayer(this.audioContext);
|
this.drumPlayer = new DrumPlayer(this.audioContext);
|
||||||
this.numDrums = this.drumPlayer.drumSounds.length;
|
this.numDrums = this.drumPlayer.drumSounds.length;
|
||||||
|
|
||||||
// a map of md5s to audio buffers, holding sounds for all sprites
|
// a map of soundIds to audio buffers, holding sounds for all sprites
|
||||||
this.audioBuffers = {};
|
this.audioBuffers = {};
|
||||||
|
|
||||||
// microphone, for measuring loudness, with a level meter analyzer
|
// microphone, for measuring loudness, with a level meter analyzer
|
||||||
|
@ -188,15 +189,14 @@ class AudioEngine {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a sound, decompressing it into audio samples.
|
* Decode a sound, decompressing it into audio samples.
|
||||||
* Store a reference to it the sound in the audioBuffers dictionary, indexed by md5
|
* Store a reference to it the sound in the audioBuffers dictionary, indexed by soundId
|
||||||
* @param {object} sound - an object containing audio data and metadata for a sound
|
* @param {object} sound - an object containing audio data and metadata for a sound
|
||||||
* @property {Buffer} data - sound data loaded from scratch-storage.
|
* @property {Buffer} data - sound data loaded from scratch-storage.
|
||||||
* @property {string} format - format type, either empty or adpcm.
|
* @property {string} format - format type, either empty or adpcm.
|
||||||
* @property {string} md5 - the MD5 and extension of the sound.
|
* @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored.
|
||||||
* @returns {?Promise} - a promise which will resolve after the audio buffer is stored, or null on error.
|
|
||||||
*/
|
*/
|
||||||
decodeSound (sound) {
|
decodeSound (sound) {
|
||||||
|
const soundId = uid();
|
||||||
let loaderPromise = null;
|
let loaderPromise = null;
|
||||||
|
|
||||||
// Make a copy of the buffer because decoding detaches the original buffer
|
// Make a copy of the buffer because decoding detaches the original buffer
|
||||||
|
@ -216,7 +216,8 @@ class AudioEngine {
|
||||||
const storedContext = this;
|
const storedContext = this;
|
||||||
return loaderPromise.then(
|
return loaderPromise.then(
|
||||||
decodedAudio => {
|
decodedAudio => {
|
||||||
storedContext.audioBuffers[sound.md5] = decodedAudio;
|
storedContext.audioBuffers[soundId] = decodedAudio;
|
||||||
|
return soundId;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
log.warn('audio data could not be decoded', error);
|
log.warn('audio data could not be decoded', error);
|
||||||
|
|
29
src/uid.js
Normal file
29
src/uid.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* @fileoverview UID generator, from Blockly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legal characters for the unique ID.
|
||||||
|
* Should be all on a US keyboard. No XML special characters or control codes.
|
||||||
|
* Removed $ due to issue 251.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
const soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' +
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique ID, from Blockly. This should be globally unique.
|
||||||
|
* 87 characters ^ 20 length > 128 bits (better than a UUID).
|
||||||
|
* @return {string} A globally unique ID string.
|
||||||
|
*/
|
||||||
|
const uid = function () {
|
||||||
|
const length = 20;
|
||||||
|
const soupLength = soup_.length;
|
||||||
|
const id = [];
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
id[i] = soup_.charAt(Math.random() * soupLength);
|
||||||
|
}
|
||||||
|
return id.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = uid;
|
Loading…
Reference in a new issue