From 730c57a9762c7b66d79269f8d3667e5e163c181c Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Tue, 21 Nov 2017 10:44:50 -0500 Subject: [PATCH] Remove instruments and drums --- src/DrumPlayer.js | 90 ----------------------------------------- src/InstrumentPlayer.js | 82 ------------------------------------- src/index.js | 84 ++------------------------------------ 3 files changed, 3 insertions(+), 253 deletions(-) delete mode 100644 src/DrumPlayer.js delete mode 100644 src/InstrumentPlayer.js diff --git a/src/DrumPlayer.js b/src/DrumPlayer.js deleted file mode 100644 index 9b021f6..0000000 --- a/src/DrumPlayer.js +++ /dev/null @@ -1,90 +0,0 @@ -const SoundPlayer = require('./SoundPlayer'); - -class DrumPlayer { - /** - * A prototype for the drum sound functionality that can load drum sounds, play, and stop them. - * @param {AudioContext} audioContext - a webAudio context - * @constructor - */ - constructor (audioContext) { - this.audioContext = audioContext; - - const baseUrl = 'https://raw.githubusercontent.com/LLK/scratch-audio/develop/sound-files/drums/'; - const fileNames = [ - 'SnareDrum(1)', - 'BassDrum(1b)', - 'SideStick(1)', - 'Crash(2)', - 'HiHatOpen(2)', - 'HiHatClosed(1)', - 'Tambourine(3)', - 'Clap(1)', - 'Claves(1)', - 'WoodBlock(1)', - 'Cowbell(3)', - 'Triangle(1)', - 'Bongo', - 'Conga(1)', - 'Cabasa(1)', - 'GuiroLong(1)', - 'Vibraslap(1)', - 'Cuica(2)' - ]; - - this.drumSounds = []; - - for (let i = 0; i < fileNames.length; i++) { - this.drumSounds[i] = new SoundPlayer(this.audioContext); - - // download and decode the drum sounds - // @todo: use scratch-storage to manage these sound files - const url = `${baseUrl}${fileNames[i]}_22k.wav`; - const request = new XMLHttpRequest(); - request.open('GET', url, true); - request.responseType = 'arraybuffer'; - request.onload = () => { - const audioData = request.response; - // Check for newer promise-based API - let loaderPromise; - if (this.audioContext.decodeAudioData.length === 1) { - loaderPromise = this.audioContext.decodeAudioData(audioData); - } else { - // Fall back to callback API - loaderPromise = new Promise((resolve, reject) => { - this.audioContext.decodeAudioData(audioData, - decodedAudio => resolve(decodedAudio), - error => reject(error) - ); - }); - } - loaderPromise.then(buffer => { - this.drumSounds[i].setBuffer(buffer); - }); - }; - request.send(); - } - } - - /** - * Play a drum sound. - * The parameter for output node allows sprites or clones to send the drum sound - * to their individual audio effect chains. - * @param {number} drum - the drum number to play (0-indexed) - * @param {AudioNode} outputNode - a node to send the output to - */ - play (drum, outputNode) { - this.drumSounds[drum].connect(outputNode); - this.drumSounds[drum].start(); - } - - /** - * Stop all drum sounds. - */ - stopAll () { - for (let i = 0; i < this.drumSounds.length; i++) { - this.drumSounds[i].stop(); - } - } -} - -module.exports = DrumPlayer; diff --git a/src/InstrumentPlayer.js b/src/InstrumentPlayer.js deleted file mode 100644 index 87c900f..0000000 --- a/src/InstrumentPlayer.js +++ /dev/null @@ -1,82 +0,0 @@ -const Soundfont = require('soundfont-player'); - -class InstrumentPlayer { - /** - * A prototype for the instrument sound functionality that can play notes. - * This prototype version (which will be replaced at some point) uses an - * existing soundfont library that creates several limitations: - * The sound files are high quality but large, so they are loaded 'on demand,' at the time the - * play note or set instrument block runs, causing a delay of a few seconds. - * Using this library we don't have a way to set the volume, sustain the note beyond the sample - * duration, or run it through the sprite-specific audio effects. - * @param {AudioContext} audioContext - a webAudio context - * @constructor - */ - constructor (audioContext) { - this.audioContext = audioContext; - this.outputNode = null; - - // Instrument names used by Musyng Kite soundfont, in order to - // match scratch instruments - this.instrumentNames = ['acoustic_grand_piano', 'electric_piano_1', - 'drawbar_organ', 'acoustic_guitar_nylon', 'electric_guitar_clean', - 'acoustic_bass', 'pizzicato_strings', 'cello', 'trombone', 'clarinet', - 'tenor_sax', 'flute', 'pan_flute', 'bassoon', 'choir_aahs', 'vibraphone', - 'music_box', 'steel_drums', 'marimba', 'lead_1_square', 'fx_4_atmosphere']; - - this.instruments = []; - } - - /** - * Play a note for some number of seconds with a particular instrument. - * Load the instrument first, if it has not already been loaded. - * The duration is in seconds because the AudioEngine manages the tempo, - * and converts beats to seconds. - * @param {number} note - a MIDI note number - * @param {number} sec - a duration in seconds - * @param {number} instrumentNum - an instrument number (0-indexed) - * @param {number} vol - a volume level (0-100%) - */ - playNoteForSecWithInstAndVol (note, sec, instrumentNum, vol) { - const gain = vol / 100; - this.loadInstrument(instrumentNum) - .then(() => { - this.instruments[instrumentNum].play( - note, this.audioContext.currentTime, { - duration: sec, - gain: gain - } - ); - }); - } - - /** - * Load an instrument by number - * @param {number} instrumentNum - an instrument number (0-indexed) - * @return {Promise} a Promise that resolves once the instrument audio data has been loaded - */ - loadInstrument (instrumentNum) { - if (this.instruments[instrumentNum]) { - return Promise.resolve(); - } - return Soundfont.instrument(this.audioContext, this.instrumentNames[instrumentNum]) - .then(inst => { - inst.connect(this.outputNode); - this.instruments[instrumentNum] = inst; - }); - - } - - /** - * Stop all notes being played on all instruments - */ - stopAll () { - for (let i = 0; i < this.instruments.length; i++) { - if (this.instruments[i]) { - this.instruments[i].stop(); - } - } - } -} - -module.exports = InstrumentPlayer; diff --git a/src/index.js b/src/index.js index c7a38e9..33eaf19 100644 --- a/src/index.js +++ b/src/index.js @@ -9,8 +9,6 @@ const PanEffect = require('./effects/PanEffect'); const SoundPlayer = require('./SoundPlayer'); const ADPCMSoundDecoder = require('./ADPCMSoundDecoder'); -const InstrumentPlayer = require('./InstrumentPlayer'); -const DrumPlayer = require('./DrumPlayer'); /** * @fileOverview Scratch Audio is divided into a single AudioEngine, @@ -43,6 +41,8 @@ class AudioPlayer { // sound players that are currently playing, indexed by the sound's soundId this.activeSoundPlayers = {}; + + console.log('updated audio engine!'); } /** @@ -91,18 +91,6 @@ class AudioPlayer { return player.finished(); } - /** - * Play a drum sound. The AudioEngine contains the DrumPlayer, but the AudioPlayer - * calls this function so that it can pass a reference to its own effects node. - * @param {number} drum - a drum number (0-indexed) - * @param {number} beats - a duration in beats - * @return {Promise} a Promise that resolves after the duration has elapsed - */ - playDrumForBeats (drum, beats) { - this.audioEngine.drumPlayer.play(drum, this.effectsNode); - return this.audioEngine.waitForBeats(beats); - } - /** * Stop all sounds, notes and drums that are playing */ @@ -156,8 +144,7 @@ class AudioPlayer { /** * There is a single instance of the AudioEngine. It handles global audio properties and effects, - * loads all the audio buffers for sounds belonging to sprites, and creates a single instrument player - * and a drum player, used by all play note and play drum blocks. + * loads all the audio buffers for sounds belonging to sprites. */ class AudioEngine { constructor () { @@ -167,18 +154,6 @@ class AudioEngine { this.input = this.audioContext.createGain(); this.input.connect(this.audioContext.destination); - // global tempo in bpm (beats per minute) - this.currentTempo = 60; - - // instrument player for play note blocks - this.instrumentPlayer = new InstrumentPlayer(this.audioContext); - this.instrumentPlayer.outputNode = this.input; - this.numInstruments = this.instrumentPlayer.instrumentNames.length; - - // drum player for play drum blocks - this.drumPlayer = new DrumPlayer(this.audioContext); - this.numDrums = this.drumPlayer.drumSounds.length; - // a map of soundIds to audio buffers, holding sounds for all sprites this.audioBuffers = {}; @@ -273,59 +248,6 @@ class AudioEngine { log.warn('The loadSounds function is no longer available. Please use Scratch Storage.'); } - /** - * Play a note for a duration on an instrument with a volume - * @param {number} note - a MIDI note number - * @param {number} beats - a duration in beats - * @param {number} inst - an instrument number (0-indexed) - * @param {number} vol - a volume level (0-100%) - * @return {Promise} a Promise that resolves after the duration has elapsed - */ - playNoteForBeatsWithInstAndVol (note, beats, inst, vol) { - const sec = this.beatsToSec(beats); - this.instrumentPlayer.playNoteForSecWithInstAndVol(note, sec, inst, vol); - return this.waitForBeats(beats); - } - - /** - * Convert a number of beats to a number of seconds, using the current tempo - * @param {number} beats number of beats to convert to secs - * @return {number} seconds number of seconds `beats` will last - */ - beatsToSec (beats) { - return (60 / this.currentTempo) * beats; - } - - /** - * Wait for some number of beats - * @param {number} beats number of beats to wait for - * @return {Promise} a Promise that resolves after the duration has elapsed - */ - waitForBeats (beats) { - const storedContext = this; - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, storedContext.beatsToSec(beats) * 1000); - }); - } - - /** - * Set the global tempo in bpm (beats per minute) - * @param {number} value - the new tempo to set - */ - setTempo (value) { - this.currentTempo = value; - } - - /** - * Change the tempo by some number of bpm (beats per minute) - * @param {number} value - the number of bpm to change the tempo by - */ - changeTempo (value) { - this.setTempo(this.currentTempo + value); - } - /** * Get the current loudness of sound received by the microphone. * Sound is measured in RMS and smoothed.