mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-10 15:02:06 -05:00
play sound, play drum and audio effects experiments
This commit is contained in:
parent
951d7771f3
commit
8c40e9c383
17 changed files with 280 additions and 32 deletions
|
@ -248,17 +248,43 @@
|
|||
<block type="looks_size"></block>
|
||||
</category>
|
||||
<category name="Sound" colour="#D65CD6">
|
||||
|
||||
<block type="sound_playsound">
|
||||
<value name="SOUND_MENU">
|
||||
<shadow type="sound_sounds_option"></shadow>
|
||||
<value name="SOUND_NUM">
|
||||
<shadow type="sound_sounds_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playuntildone">
|
||||
<value name="SOUND_MENU">
|
||||
<shadow type="sound_sounds_option"></shadow>
|
||||
<value name="SOUND_NUM">
|
||||
<shadow type="sound_sounds_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playwithpitch">
|
||||
<value name="SOUND_NUM">
|
||||
<shadow type="sound_sounds_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="PITCH">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_stopallsounds"></block>
|
||||
|
||||
<block type="sound_playdrum">
|
||||
<value name="DRUMTYPE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playdrumforbeats">
|
||||
<value name="DRUMTYPE">
|
||||
<shadow type="math_number">
|
||||
|
@ -266,11 +292,11 @@
|
|||
</shadow>
|
||||
</value>
|
||||
<value name="BEATS">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0.25</field>
|
||||
<shadow type="sound_beats_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_restforbeats">
|
||||
<value name="BEATS">
|
||||
<shadow type="math_number">
|
||||
|
@ -279,6 +305,14 @@
|
|||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playnote">
|
||||
<value name="NOTE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playnoteforbeats">
|
||||
<value name="NOTE">
|
||||
<shadow type="math_number">
|
||||
|
@ -291,17 +325,41 @@
|
|||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_setkey">
|
||||
<value name="ROOT">
|
||||
<shadow type="sound_roots_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SCALE">
|
||||
<shadow type="sound_scales_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_setkey">
|
||||
<value name="ROOT">
|
||||
<shadow type="sound_roots_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SCALE">
|
||||
<shadow type="sound_scales_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_seteffectto">
|
||||
<value name="EFFECT">
|
||||
<shadow type="sound_effects_menu"></shadow>
|
||||
</value>
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">100</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_changeeffectby">
|
||||
<value name="EFFECT">
|
||||
<shadow type="sound_effects_menu"></shadow>
|
||||
</value>
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_cleareffects"></block>
|
||||
|
||||
<block type="sound_setinstrumentto">
|
||||
<value name="INSTRUMENT">
|
||||
|
|
BIN
playground/sounds/boing.mp3
Normal file
BIN
playground/sounds/boing.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/cave.mp3
Normal file
BIN
playground/sounds/cave.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/drip_drop.mp3
Normal file
BIN
playground/sounds/drip_drop.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/drum_machine.mp3
Normal file
BIN
playground/sounds/drum_machine.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/eggs.mp3
Normal file
BIN
playground/sounds/eggs.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/hi_tun_tabla.mp3
Normal file
BIN
playground/sounds/hi_tun_tabla.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/high_conga.mp3
Normal file
BIN
playground/sounds/high_conga.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/lo_gliss_tabla.mp3
Normal file
BIN
playground/sounds/lo_gliss_tabla.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/meow.mp3
Normal file
BIN
playground/sounds/meow.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/sidestick_snare.mp3
Normal file
BIN
playground/sounds/sidestick_snare.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/small_cowbell.mp3
Normal file
BIN
playground/sounds/small_cowbell.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/snare_drum.mp3
Normal file
BIN
playground/sounds/snare_drum.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/splash cymbal.mp3
Normal file
BIN
playground/sounds/splash cymbal.mp3
Normal file
Binary file not shown.
BIN
playground/sounds/zoop.mp3
Normal file
BIN
playground/sounds/zoop.mp3
Normal file
Binary file not shown.
|
@ -13,20 +13,38 @@ function Scratch3SoundBlocks(runtime) {
|
|||
Scratch3SoundBlocks.prototype.getPrimitives = function() {
|
||||
return {
|
||||
'sound_playsound': this.playSound,
|
||||
'sound_playnoteforbeats': this.playNote,
|
||||
'sound_playwithpitch': this.playSoundWithPitch,
|
||||
'sound_stopallsounds': this.stopAllSounds,
|
||||
'sound_playnote': this.playNote,
|
||||
'sound_playnoteforbeats': this.playNoteForBeats,
|
||||
'sound_playdrum': this.playDrum,
|
||||
'sound_playdrumforbeats': this.playDrumForBeats,
|
||||
'sound_setkey' : this.setKey,
|
||||
'sound_seteffectto' : this.setEffect,
|
||||
'sound_changeeffectby' : this.changeEffect,
|
||||
'sound_cleareffects' : this.clearEffects,
|
||||
'sound_scales_menu' : this.scalesMenu,
|
||||
'sound_sounds_menu' : this.soundsMenu,
|
||||
'sound_roots_menu' : this.rootsMenu,
|
||||
'sound_beats_menu' : this.beatsMenu,
|
||||
'sound_effects_menu' : this.effectsMenu,
|
||||
};
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.playSound = function (args, util) {
|
||||
self.postMessage({method: 'beep'});
|
||||
self.postMessage({method: 'playsound', soundnum:args.SOUND_NUM});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.playNote = function (args, util) {
|
||||
self.postMessage({method: 'playnote', note:args.NOTE, beats:args.BEATS});
|
||||
Scratch3SoundBlocks.prototype.playSoundWithPitch = function (args, util) {
|
||||
self.postMessage({method: 'playsoundwithpitch', soundnum:args.SOUND_NUM, pitch:args.PITCH});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) {
|
||||
self.postMessage({method: 'stopallsounds'});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) {
|
||||
self.postMessage({method: 'playnoteforbeats', note:args.NOTE, beats:args.BEATS});
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(function() {
|
||||
resolve();
|
||||
|
@ -34,10 +52,43 @@ Scratch3SoundBlocks.prototype.playNote = function (args, util) {
|
|||
});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.playNote = function (args, util) {
|
||||
self.postMessage({method: 'playnote', note:args.NOTE});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) {
|
||||
self.postMessage({method: 'playdrumforbeats', drum:args.DRUMTYPE, beats:args.BEATS});
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(function() {
|
||||
resolve();
|
||||
}, 1000 * args.BEATS);
|
||||
});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.playDrum = function (args, util) {
|
||||
self.postMessage({method: 'playdrum', drum:args.DRUMTYPE});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.setKey = function (args, util) {
|
||||
self.postMessage({method: 'setkey', root:args.ROOT, scale:args.SCALE});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.setEffect = function (args, util) {
|
||||
self.postMessage({method: 'seteffect', effect:args.EFFECT, value:args.VALUE});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.changeEffect = function (args, util) {
|
||||
self.postMessage({method: 'changeeffect', effect:args.EFFECT, value:args.VALUE});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.clearEffects = function (args, util) {
|
||||
self.postMessage({method: 'cleareffects'});
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.soundsMenu = function (args, util) {
|
||||
return args.SOUND_MENU;
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.scalesMenu = function (args, util) {
|
||||
return args.SCALE;
|
||||
};
|
||||
|
@ -50,4 +101,8 @@ Scratch3SoundBlocks.prototype.beatsMenu = function (args, util) {
|
|||
return args.BEATS;
|
||||
};
|
||||
|
||||
Scratch3SoundBlocks.prototype.effectsMenu = function (args, util) {
|
||||
return args.EFFECT;
|
||||
};
|
||||
|
||||
module.exports = Scratch3SoundBlocks;
|
||||
|
|
159
src/worker.js
159
src/worker.js
|
@ -13,10 +13,72 @@ function VirtualMachine () {
|
|||
instance.vmWorker = new Worker('../vm.js');
|
||||
|
||||
// MUSIC STUFF by ericr
|
||||
var options = {modulationEnvelope:{attack:0.1}};
|
||||
|
||||
// tone setup
|
||||
|
||||
var tone = new Tone();
|
||||
|
||||
// effects
|
||||
|
||||
var delay = new Tone.FeedbackDelay(0.25, 0.5);
|
||||
delay.wet.value = 0;
|
||||
|
||||
var pitchShift = new Tone.PitchShift();
|
||||
|
||||
var panner = new Tone.Panner(0);
|
||||
|
||||
var reverb = new Tone.Freeverb();
|
||||
reverb.wet.value = 0;
|
||||
|
||||
Tone.Master.chain(delay, pitchShift, panner, reverb);
|
||||
|
||||
// synth setup for play note block
|
||||
|
||||
var synth = new Tone.PolySynth(6, Tone.Synth).toMaster();
|
||||
var tone = new Tone();
|
||||
|
||||
// drum sounds
|
||||
|
||||
var drumFileNames = ['high_conga', 'small_cowbell', 'snare_drum', 'splash cymbal'];
|
||||
var drumSamplers = loadSoundFiles(drumFileNames);
|
||||
|
||||
// sounds
|
||||
|
||||
var soundFileNames = ['meow', 'boing', 'cave', 'drip_drop', 'drum_machine', 'eggs', 'zoop'];
|
||||
var soundSamplers = loadSoundFiles(soundFileNames);
|
||||
|
||||
// polyphonic samplers
|
||||
|
||||
function loadSoundFiles(filenames) {
|
||||
var samplers = [];
|
||||
|
||||
for (var name of filenames) {
|
||||
|
||||
var myVoices = [];
|
||||
for (var i=0; i<6; i++) {
|
||||
var p = new Tone.Sampler('sounds/' + name + '.mp3').toMaster();
|
||||
myVoices.push(p);
|
||||
}
|
||||
|
||||
var polySampler = {
|
||||
voices : myVoices,
|
||||
currentVoice : 0,
|
||||
nextVoice : function() {return this.voices[this.currentVoice++ % this.voices.length];},
|
||||
stopAllVoices : function() {for (var i=0;i<this.voices.length;i++) {this.voices[i].triggerRelease()}},
|
||||
};
|
||||
|
||||
samplers.push(polySampler);
|
||||
}
|
||||
|
||||
return samplers;
|
||||
}
|
||||
|
||||
// should fire once all sounds are loaded - seems to not work
|
||||
Tone.Buffer.onload = function() {
|
||||
console.log('loaded audio samples');
|
||||
};
|
||||
|
||||
// scales and keys data
|
||||
|
||||
var scales = {
|
||||
'MAJOR' : [0,2,4,5,7,9,11],
|
||||
'MINOR' : [0,2,3,5,7,8,10],
|
||||
|
@ -27,7 +89,8 @@ function VirtualMachine () {
|
|||
var currentScale = scales['MAJOR'];
|
||||
var rootNote = 60;
|
||||
|
||||
function scaleNoteToMidiNote(scaleNote, scale, root) {
|
||||
|
||||
function scaleNoteToMidiNote(scaleNote, scale, root) {
|
||||
var scaleIndex = (Math.round(scaleNote) - 1) % scale.length;
|
||||
if (scaleIndex < 0) {
|
||||
scaleIndex += scale.length;
|
||||
|
@ -42,17 +105,89 @@ function VirtualMachine () {
|
|||
return freq;
|
||||
}
|
||||
|
||||
function clamp(input, min, max) {
|
||||
return Math.min(Math.max(input, min), max);
|
||||
};
|
||||
|
||||
function playNoteForBeats(note, beats) {
|
||||
var midiNote = scaleNoteToMidiNote(note, currentScale, rootNote);
|
||||
var freq = midiToFreq(midiNote);
|
||||
synth.triggerAttackRelease(freq, beats);
|
||||
}
|
||||
|
||||
// onmessage calls are converted into emitted events.
|
||||
instance.vmWorker.onmessage = function (e) {
|
||||
if (e.data.method == 'playnote') {
|
||||
var midiNote = scaleNoteToMidiNote(e.data.note, currentScale, rootNote);
|
||||
var freq = midiToFreq(midiNote);
|
||||
synth.triggerAttackRelease(freq, e.data.beats);
|
||||
}
|
||||
if (e.data.method == 'setkey') {
|
||||
rootNote = e.data.root + 60;
|
||||
currentScale = scales[e.data.scale];
|
||||
}
|
||||
switch (e.data.method) {
|
||||
case 'playsound':
|
||||
soundSamplers[e.data.soundnum].nextVoice().triggerAttack();
|
||||
break;
|
||||
case 'playsoundwithpitch':
|
||||
soundSamplers[e.data.soundnum].nextVoice().triggerAttack(e.data.pitch);
|
||||
break;
|
||||
case 'playnoteforbeats':
|
||||
playNoteForBeats(e.data.note, e.data.beats);
|
||||
break;
|
||||
case 'playnote':
|
||||
playNoteForBeats(e.data.note, 0.25);
|
||||
break;
|
||||
case 'setkey':
|
||||
rootNote = parseInt(e.data.root) + 60;
|
||||
currentScale = scales[e.data.scale];
|
||||
break;
|
||||
case 'playdrumforbeats':
|
||||
case 'playdrum':
|
||||
var drumNum = e.data.drum - 1; // one-indexing
|
||||
drumSamplers[drumNum].nextVoice().triggerAttack();
|
||||
break;
|
||||
case 'seteffect' :
|
||||
switch (e.data.effect) {
|
||||
case 'ECHO':
|
||||
delay.wet.value = (e.data.value / 100) / 2; // max 50% wet (need dry signal too)
|
||||
break;
|
||||
case 'PANNER':
|
||||
panner.pan.value = e.data.value / 100;
|
||||
break;
|
||||
case 'REVERB':
|
||||
reverb.wet.value = e.data.value / 100;
|
||||
break;
|
||||
case 'PITCH':
|
||||
pitchShift.pitch = e.data.value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'changeeffect' :
|
||||
switch (e.data.effect) {
|
||||
case 'ECHO':
|
||||
delay.wet.value += (e.data.value / 100) / 2; // max 50% wet (need dry signal too)
|
||||
delay.wet.value = clamp(delay.wet.value, 0, 0.5);
|
||||
break;
|
||||
case 'PANNER':
|
||||
panner.pan.value += e.data.value / 100;
|
||||
panner.pan.value = clamp(panner.pan.value, -1, 1);
|
||||
break;
|
||||
case 'REVERB':
|
||||
reverb.wet.value += e.data.value / 100;
|
||||
reverb.wet.value = clamp(reverb.wet.value, 0, 1);
|
||||
break;
|
||||
case 'PITCH':
|
||||
pitchShift.pitch += e.data.value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'cleareffects' :
|
||||
delay.wet.value = 0;
|
||||
panner.pan.value = 0;
|
||||
reverb.wet.value = 0;
|
||||
pitchShift.pitch = 0;
|
||||
break;
|
||||
case 'stopallsounds' :
|
||||
// stop sounds from the play sound block
|
||||
// to do: stop sounds from play note and play drum blocks
|
||||
for (var i=0; i<soundSamplers.length; i++) {
|
||||
soundSamplers[i].stopAllVoices();
|
||||
}
|
||||
break;
|
||||
}
|
||||
instance.emit(e.data.method, e.data);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue