Merge pull request #65 from LLK/feature/remove-music-stuff

Remove music functionality
This commit is contained in:
Eric Rosenbaum 2017-11-21 13:45:25 -05:00 committed by GitHub
commit d31494fed6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 1 additions and 253 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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,
@ -91,18 +89,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 +142,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 +152,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 +246,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.