mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 22:12:48 -05:00
Merge pull request #65 from LLK/feature/remove-music-stuff
Remove music functionality
This commit is contained in:
commit
d31494fed6
3 changed files with 1 additions and 253 deletions
|
@ -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;
|
|
@ -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;
|
82
src/index.js
82
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,
|
||||
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue