mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Merge branch 'develop' into greenkeeper/expose-loader-0.7.4
This commit is contained in:
commit
51bd0349b0
24 changed files with 367 additions and 105 deletions
12
package.json
12
package.json
|
@ -33,9 +33,9 @@
|
|||
"copy-webpack-plugin": "4.0.1",
|
||||
"escape-html": "1.0.3",
|
||||
"eslint": "^4.5.0",
|
||||
"eslint-config-scratch": "^4.0.0",
|
||||
"eslint-config-scratch": "^5.0.0",
|
||||
"expose-loader": "0.7.4",
|
||||
"gh-pages": "^0.12.0",
|
||||
"gh-pages": "^1.1.0",
|
||||
"got": "5.7.1",
|
||||
"highlightjs": "^9.8.0",
|
||||
"htmlparser2": "3.9.2",
|
||||
|
@ -44,18 +44,18 @@
|
|||
"json": "^9.0.4",
|
||||
"lodash.defaultsdeep": "4.6.0",
|
||||
"minilog": "3.1.0",
|
||||
"promise": "7.1.1",
|
||||
"promise": "8.0.1",
|
||||
"scratch-audio": "latest",
|
||||
"scratch-blocks": "latest",
|
||||
"scratch-render": "latest",
|
||||
"scratch-storage": "^0.3.0",
|
||||
"script-loader": "0.7.0",
|
||||
"socket.io-client": "1.7.3",
|
||||
"script-loader": "0.7.2",
|
||||
"socket.io-client": "2.0.4",
|
||||
"stats.js": "^0.17.0",
|
||||
"tap": "^10.2.0",
|
||||
"tiny-worker": "^2.1.1",
|
||||
"webpack": "^2.4.1",
|
||||
"webpack-dev-server": "^2.4.1",
|
||||
"worker-loader": "0.8.1"
|
||||
"worker-loader": "1.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,30 @@ class Blocks {
|
|||
* @type {Array.<String>}
|
||||
*/
|
||||
this._scripts = [];
|
||||
|
||||
/**
|
||||
* Runtime Cache
|
||||
* @type {{inputs: {}, procedureParamNames: {}, procedureDefinitions: {}}}
|
||||
* @private
|
||||
*/
|
||||
this._cache = {
|
||||
/**
|
||||
* Cache block inputs by block id
|
||||
* @type {object.<string, !Array.<object>>}
|
||||
*/
|
||||
inputs: {},
|
||||
/**
|
||||
* Cache procedure Param Names by block id
|
||||
* @type {object.<string, ?Array.<string>>}
|
||||
*/
|
||||
procedureParamNames: {},
|
||||
/**
|
||||
* Cache procedure definitions by block id
|
||||
* @type {object.<string, ?string>}
|
||||
*/
|
||||
procedureDefinitions: {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,11 +129,16 @@ class Blocks {
|
|||
/**
|
||||
* Get all non-branch inputs for a block.
|
||||
* @param {?object} block the block to query.
|
||||
* @return {!object} All non-branch inputs and their associated blocks.
|
||||
* @return {?Array.<object>} All non-branch inputs and their associated blocks.
|
||||
*/
|
||||
getInputs (block) {
|
||||
if (typeof block === 'undefined') return null;
|
||||
const inputs = {};
|
||||
let inputs = this._cache.inputs[block.id];
|
||||
if (typeof inputs !== 'undefined') {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
inputs = {};
|
||||
for (const input in block.inputs) {
|
||||
// Ignore blocks prefixed with branch prefix.
|
||||
if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !==
|
||||
|
@ -117,6 +146,8 @@ class Blocks {
|
|||
inputs[input] = block.inputs[input];
|
||||
}
|
||||
}
|
||||
|
||||
this._cache.inputs[block.id] = inputs;
|
||||
return inputs;
|
||||
}
|
||||
|
||||
|
@ -149,16 +180,24 @@ class Blocks {
|
|||
* @return {?string} ID of procedure definition.
|
||||
*/
|
||||
getProcedureDefinition (name) {
|
||||
const blockID = this._cache.procedureDefinitions[name];
|
||||
if (typeof blockID !== 'undefined') {
|
||||
return blockID;
|
||||
}
|
||||
|
||||
for (const id in this._blocks) {
|
||||
if (!this._blocks.hasOwnProperty(id)) continue;
|
||||
const block = this._blocks[id];
|
||||
if (block.opcode === 'procedures_definition') {
|
||||
const internal = this._getCustomBlockInternal(block);
|
||||
if (internal && internal.mutation.proccode === name) {
|
||||
return id; // The outer define block id
|
||||
this._cache.procedureDefinitions[name] = id; // The outer define block id
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._cache.procedureDefinitions[name] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -168,14 +207,23 @@ class Blocks {
|
|||
* @return {?Array.<string>} List of param names for a procedure.
|
||||
*/
|
||||
getProcedureParamNames (name) {
|
||||
const cachedNames = this._cache.procedureParamNames[name];
|
||||
if (typeof cachedNames !== 'undefined') {
|
||||
return cachedNames;
|
||||
}
|
||||
|
||||
for (const id in this._blocks) {
|
||||
if (!this._blocks.hasOwnProperty(id)) continue;
|
||||
const block = this._blocks[id];
|
||||
if (block.opcode === 'procedures_prototype' &&
|
||||
block.mutation.proccode === name) {
|
||||
return JSON.parse(block.mutation.argumentnames);
|
||||
const paramNames = JSON.parse(block.mutation.argumentnames);
|
||||
this._cache.procedureParamNames[name] = paramNames;
|
||||
return paramNames;
|
||||
}
|
||||
}
|
||||
|
||||
this._cache.procedureParamNames[name] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -271,6 +319,15 @@ class Blocks {
|
|||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reset all runtime caches.
|
||||
*/
|
||||
resetCache () {
|
||||
this._cache.inputs = {};
|
||||
this._cache.procedureParamNames = {};
|
||||
this._cache.procedureDefinitions = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Block management: create blocks and scripts from a `create` event
|
||||
* @param {!object} block Blockly create event to be processed
|
||||
|
@ -289,6 +346,8 @@ class Blocks {
|
|||
if (block.topLevel) {
|
||||
this._addScript(block.id);
|
||||
}
|
||||
|
||||
this.resetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -301,7 +360,6 @@ class Blocks {
|
|||
if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return;
|
||||
const block = this._blocks[args.id];
|
||||
if (typeof block === 'undefined') return;
|
||||
|
||||
const wasMonitored = block.isMonitored;
|
||||
switch (args.element) {
|
||||
case 'field':
|
||||
|
@ -337,6 +395,8 @@ class Blocks {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.resetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,6 +453,7 @@ class Blocks {
|
|||
}
|
||||
this._blocks[e.id].parent = e.newParent;
|
||||
}
|
||||
this.resetCache();
|
||||
}
|
||||
|
||||
|
||||
|
@ -447,6 +508,8 @@ class Blocks {
|
|||
|
||||
// Delete block itself.
|
||||
delete this._blocks[blockId];
|
||||
|
||||
this.resetCache();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
|
@ -8,7 +8,7 @@ const BlockType = require('./block-type');
|
|||
// TODO: change extension spec so that library info, including extension ID, can be collected through static methods
|
||||
const Scratch3PenBlocks = require('../blocks/scratch3_pen');
|
||||
const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2');
|
||||
const Scratch3MusicBlocks = require('../blocks/scratch3_music');
|
||||
const Scratch3MusicBlocks = require('../extensions/scratch3_music');
|
||||
const builtinExtensions = {
|
||||
pen: Scratch3PenBlocks,
|
||||
wedo2: Scratch3WeDo2Blocks,
|
||||
|
|
BIN
src/extensions/scratch3_music/assets/1-snare.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/1-snare.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/10-wood-block.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/10-wood-block.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/11-cowbell.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/11-cowbell.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/12-triangle.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/12-triangle.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/13-bongo.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/13-bongo.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/14-conga.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/14-conga.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/15-cabasa.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/15-cabasa.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/16-guiro.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/16-guiro.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/17-vibraslap.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/17-vibraslap.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/18-cuica.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/18-cuica.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/2-bass-drum.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/2-bass-drum.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/3-side-stick.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/3-side-stick.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/4-crash-cymbal.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/4-crash-cymbal.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/5-open-hi-hat.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/5-open-hi-hat.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/6-closed-hi-hat.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/6-closed-hi-hat.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/7-tambourine.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/7-tambourine.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/8-hand-clap.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/8-hand-clap.mp3
Normal file
Binary file not shown.
BIN
src/extensions/scratch3_music/assets/9-claves.mp3
Normal file
BIN
src/extensions/scratch3_music/assets/9-claves.mp3
Normal file
Binary file not shown.
|
@ -1,62 +1,9 @@
|
|||
const ArgumentType = require('../extension-support/argument-type');
|
||||
const BlockType = require('../extension-support/block-type');
|
||||
const Clone = require('../util/clone');
|
||||
const Cast = require('../util/cast');
|
||||
const MathUtil = require('../util/math-util');
|
||||
const Timer = require('../util/timer');
|
||||
|
||||
/**
|
||||
* An array of drum names, used in the play drum block.
|
||||
* @type {string[]}
|
||||
*/
|
||||
const drumNames = [
|
||||
'Snare Drum',
|
||||
'Bass Drum',
|
||||
'Side Stick',
|
||||
'Crash Cymbal',
|
||||
'Open Hi-Hat',
|
||||
'Closed Hi-Hat',
|
||||
'Tambourine',
|
||||
'Hand Clap',
|
||||
'Claves',
|
||||
'Wood Block',
|
||||
'Cowbell',
|
||||
'Triangle',
|
||||
'Bongo',
|
||||
'Conga',
|
||||
'Cabasa',
|
||||
'Guiro',
|
||||
'Vibraslap',
|
||||
'Open Cuica'
|
||||
];
|
||||
|
||||
/**
|
||||
* An array of instrument names, used in the set instrument block.
|
||||
* @type {string[]}
|
||||
*/
|
||||
const instrumentNames = [
|
||||
'Piano',
|
||||
'Electric Piano',
|
||||
'Organ',
|
||||
'Guitar',
|
||||
'Electric Guitar',
|
||||
'Bass',
|
||||
'Pizzicato',
|
||||
'Cello',
|
||||
'Trombone',
|
||||
'Clarinet',
|
||||
'Saxophone',
|
||||
'Flute',
|
||||
'Wooden Flute',
|
||||
'Bassoon',
|
||||
'Choir',
|
||||
'Vibraphone',
|
||||
'Music Box',
|
||||
'Steel Drum',
|
||||
'Marimba',
|
||||
'Synth Lead',
|
||||
'Synth Pad'
|
||||
];
|
||||
const ArgumentType = require('../../extension-support/argument-type');
|
||||
const BlockType = require('../../extension-support/block-type');
|
||||
const Clone = require('../../util/clone');
|
||||
const Cast = require('../../util/cast');
|
||||
const MathUtil = require('../../util/math-util');
|
||||
const Timer = require('../../util/timer');
|
||||
|
||||
/**
|
||||
* Class for the music-related blocks in Scratch 3.0
|
||||
|
@ -78,27 +25,246 @@ class Scratch3MusicBlocks {
|
|||
*/
|
||||
this.tempo = 60;
|
||||
|
||||
this.drumMenu = this._buildMenu(drumNames);
|
||||
this.instrumentMenu = this._buildMenu(instrumentNames);
|
||||
/**
|
||||
* The number of drum sounds currently being played simultaneously.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this._drumConcurrencyCounter = 0;
|
||||
|
||||
/**
|
||||
* An array of audio buffers, one for each drum sound.
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this._drumBuffers = [];
|
||||
|
||||
this._loadAllDrumSounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a menu using an array of strings.
|
||||
* Used for creating the drum and instrument menus.
|
||||
* @param {string[]} names - An array of names.
|
||||
* @return {array} - An array of objects with text and value properties, for constructing a block menu.
|
||||
* Download and decode the full set of drum sounds, and store the audio buffers
|
||||
* in the drum buffers array.
|
||||
* @TODO: Also load the instrument sounds here (rename this fn)
|
||||
*/
|
||||
_loadAllDrumSounds () {
|
||||
const loadingPromises = [];
|
||||
this.DRUM_INFO.forEach((drumInfo, index) => {
|
||||
const promise = this._loadSound(drumInfo.fileName, index, this._drumBuffers);
|
||||
loadingPromises.push(promise);
|
||||
});
|
||||
Promise.all(loadingPromises).then(() => {
|
||||
// @TODO: Update the extension status indicator.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download and decode a sound, and store the buffer in an array.
|
||||
* @param {string} fileName - the audio file name.
|
||||
* @param {number} index - the index at which to store the audio buffer.
|
||||
* @param {array} bufferArray - the array of buffers in which to store it.
|
||||
* @return {Promise} - a promise which will resolve once the sound has loaded.
|
||||
*/
|
||||
_loadSound (fileName, index, bufferArray) {
|
||||
if (!this.runtime.storage) return;
|
||||
if (!this.runtime.audioEngine) return;
|
||||
return this.runtime.storage.load(this.runtime.storage.AssetType.Sound, fileName, 'mp3')
|
||||
.then(soundAsset =>
|
||||
this.runtime.audioEngine.audioContext.decodeAudioData(soundAsset.data.buffer)
|
||||
)
|
||||
.then(buffer => {
|
||||
bufferArray[index] = buffer;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create data for a menu in scratch-blocks format, consisting of an array of objects with text and
|
||||
* value properties. The text is a translated string, and the value is one-indexed.
|
||||
* @param {object[]} info - An array of info objects each having a name property.
|
||||
* @return {array} - An array of objects with text and value properties.
|
||||
* @private
|
||||
*/
|
||||
_buildMenu (names) {
|
||||
const menu = [];
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const entry = {};
|
||||
const num = i + 1; // Menu numbers are one-indexed
|
||||
entry.text = `(${num}) ${names[i]}`;
|
||||
entry.value = String(num);
|
||||
menu.push(entry);
|
||||
_buildMenu (info) {
|
||||
return info.map((entry, index) => {
|
||||
const obj = {};
|
||||
obj.text = entry.name;
|
||||
obj.value = index + 1;
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
return menu;
|
||||
|
||||
/**
|
||||
* An array of translatable drum names and corresponding audio file names.
|
||||
* @type {array}
|
||||
*/
|
||||
get DRUM_INFO () {
|
||||
return [
|
||||
{
|
||||
name: '(1) Snare Drum',
|
||||
fileName: '1-snare'
|
||||
},
|
||||
{
|
||||
name: '(2) Bass Drum',
|
||||
fileName: '2-bass-drum'
|
||||
},
|
||||
{
|
||||
name: '(3) Side Stick',
|
||||
fileName: '3-side-stick'
|
||||
},
|
||||
{
|
||||
name: '(4) Crash Cymbal',
|
||||
fileName: '4-crash-cymbal'
|
||||
},
|
||||
{
|
||||
name: '(5) Open Hi-Hat',
|
||||
fileName: '5-open-hi-hat'
|
||||
},
|
||||
{
|
||||
name: '(6) Closed Hi-Hat',
|
||||
fileName: '6-closed-hi-hat'
|
||||
},
|
||||
{
|
||||
name: '(7) Tambourine',
|
||||
fileName: '7-tambourine'
|
||||
},
|
||||
{
|
||||
name: '(8) Hand Clap',
|
||||
fileName: '8-hand-clap'
|
||||
},
|
||||
{
|
||||
name: '(9) Claves',
|
||||
fileName: '9-claves'
|
||||
},
|
||||
{
|
||||
name: '(10) Wood Block',
|
||||
fileName: '10-wood-block'
|
||||
},
|
||||
{
|
||||
name: '(11) Cowbell',
|
||||
fileName: '11-cowbell'
|
||||
},
|
||||
{
|
||||
name: '(12) Triangle',
|
||||
fileName: '12-triangle'
|
||||
},
|
||||
{
|
||||
name: '(13) Bongo',
|
||||
fileName: '13-bongo'
|
||||
},
|
||||
{
|
||||
name: '(14) Conga',
|
||||
fileName: '14-conga'
|
||||
},
|
||||
{
|
||||
name: '(15) Cabasa',
|
||||
fileName: '15-cabasa'
|
||||
},
|
||||
{
|
||||
name: '(16) Guiro',
|
||||
fileName: '16-guiro'
|
||||
},
|
||||
{
|
||||
name: '(17) Vibraslap',
|
||||
fileName: '17-vibraslap'
|
||||
},
|
||||
{
|
||||
name: '(18) Cuica',
|
||||
fileName: '18-cuica'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of translatable instrument names and corresponding audio file names.
|
||||
* @type {array}
|
||||
*/
|
||||
get INSTRUMENT_INFO () {
|
||||
return [
|
||||
{
|
||||
name: '(1) Piano',
|
||||
fileName: '1-piano'
|
||||
},
|
||||
{
|
||||
name: '(2) Electric Piano',
|
||||
fileName: '2-electric-piano'
|
||||
},
|
||||
{
|
||||
name: '(3) Organ',
|
||||
fileName: '3-organ'
|
||||
},
|
||||
{
|
||||
name: '(4) Guitar',
|
||||
fileName: '4-guitar'
|
||||
},
|
||||
{
|
||||
name: '(5) Electric Guitar',
|
||||
fileName: '5-electric-guitar'
|
||||
},
|
||||
{
|
||||
name: '(6) Bass',
|
||||
fileName: '6-bass'
|
||||
},
|
||||
{
|
||||
name: '(7) Pizzicato',
|
||||
fileName: '7-pizzicato'
|
||||
},
|
||||
{
|
||||
name: '(8) Cello',
|
||||
fileName: '8-cello'
|
||||
},
|
||||
{
|
||||
name: '(9) Trombone',
|
||||
fileName: '9-trombone'
|
||||
},
|
||||
{
|
||||
name: '(10) Clarinet',
|
||||
fileName: '10-clarinet'
|
||||
},
|
||||
{
|
||||
name: '(11) Saxophone',
|
||||
fileName: '11-saxophone'
|
||||
},
|
||||
{
|
||||
name: '(12) Flute',
|
||||
fileName: '12-flute'
|
||||
},
|
||||
{
|
||||
name: '(13) Wooden Flute',
|
||||
fileName: '13-wooden-flute'
|
||||
},
|
||||
{
|
||||
name: '(14) Bassoon',
|
||||
fileName: '14-bassoon'
|
||||
},
|
||||
{
|
||||
name: '(15) Choir',
|
||||
fileName: '15-choir'
|
||||
},
|
||||
{
|
||||
name: '(16) Vibraphone',
|
||||
fileName: '16-vibraphone'
|
||||
},
|
||||
{
|
||||
name: '(17) Music Box',
|
||||
fileName: '17-music-box'
|
||||
},
|
||||
{
|
||||
name: '(18) Steel Drum',
|
||||
fileName: '18-steel-drum'
|
||||
},
|
||||
{
|
||||
name: '(19) Marimba',
|
||||
fileName: '19-marimba'
|
||||
},
|
||||
{
|
||||
name: '(20) Synth Lead',
|
||||
fileName: '20-synth-lead'
|
||||
},
|
||||
{
|
||||
name: '(21) Synth Pad',
|
||||
fileName: '21-synth-pad'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,6 +309,14 @@ class Scratch3MusicBlocks {
|
|||
return {min: 20, max: 500};
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of sounds to allow to play simultaneously.
|
||||
* @type {number}
|
||||
*/
|
||||
static get CONCURRENCY_LIMIT () {
|
||||
return 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Target} target - collect music state for this target.
|
||||
* @returns {MusicState} the mutable music state associated with that target. This will be created if necessary.
|
||||
|
@ -248,8 +422,8 @@ class Scratch3MusicBlocks {
|
|||
}
|
||||
],
|
||||
menus: {
|
||||
drums: this.drumMenu,
|
||||
instruments: this.instrumentMenu
|
||||
drums: this._buildMenu(this.DRUM_INFO),
|
||||
instruments: this._buildMenu(this.INSTRUMENT_INFO)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -264,20 +438,41 @@ class Scratch3MusicBlocks {
|
|||
playDrumForBeats (args, util) {
|
||||
if (this._stackTimerNeedsInit(util)) {
|
||||
let drum = Cast.toNumber(args.DRUM);
|
||||
drum = Math.round(drum);
|
||||
drum -= 1; // drums are one-indexed
|
||||
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||
drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums - 1);
|
||||
drum = MathUtil.wrapClamp(drum, 0, this.DRUM_INFO.length - 1);
|
||||
let beats = Cast.toNumber(args.BEATS);
|
||||
beats = this._clampBeats(beats);
|
||||
if (util.target.audioPlayer !== null) {
|
||||
util.target.audioPlayer.playDrumForBeats(drum, beats);
|
||||
}
|
||||
this._playDrumNum(util, drum);
|
||||
this._startStackTimer(util, this._beatsToSec(beats));
|
||||
} else {
|
||||
this._checkStackTimer(util);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a drum sound using its 0-indexed number.
|
||||
* @param {object} util - utility object provided by the runtime.
|
||||
* @param {number} drumNum - the number of the drum to play.
|
||||
* @private
|
||||
*/
|
||||
_playDrumNum (util, drumNum) {
|
||||
if (util.target.audioPlayer === null) return;
|
||||
// If we're playing too many sounds, do not play the drum sound.
|
||||
if (this._drumConcurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) {
|
||||
return;
|
||||
}
|
||||
const outputNode = util.target.audioPlayer.getInputNode();
|
||||
const bufferSource = this.runtime.audioEngine.audioContext.createBufferSource();
|
||||
bufferSource.buffer = this._drumBuffers[drumNum];
|
||||
bufferSource.connect(outputNode);
|
||||
bufferSource.start();
|
||||
this._drumConcurrencyCounter++;
|
||||
bufferSource.onended = () => {
|
||||
this._drumConcurrencyCounter--;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest for some number of beats.
|
||||
* @param {object} args - the block arguments.
|
||||
|
@ -359,10 +554,8 @@ class Scratch3MusicBlocks {
|
|||
util.stackFrame.timer = new Timer();
|
||||
util.stackFrame.timer.start();
|
||||
util.stackFrame.duration = duration;
|
||||
if (util.stackFrame.duration > 0) {
|
||||
util.yield();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the stack timer, and if its time is not up yet, yield the thread.
|
|
@ -1,10 +1,9 @@
|
|||
const test = require('tap').test;
|
||||
const Music = require('../../src/blocks/scratch3_music');
|
||||
const Music = require('../../src/extensions/scratch3_music/index.js');
|
||||
let playedDrum;
|
||||
let playedInstrument;
|
||||
const runtime = {
|
||||
audioEngine: {
|
||||
numDrums: 3,
|
||||
numInstruments: 3,
|
||||
instrumentPlayer: {
|
||||
loadInstrument: instrument => (playedInstrument = instrument)
|
||||
|
@ -12,13 +11,14 @@ const runtime = {
|
|||
}
|
||||
};
|
||||
const blocks = new Music(runtime);
|
||||
blocks._playDrumNum = (util, drum) => (playedDrum = drum);
|
||||
|
||||
const util = {
|
||||
stackFrame: Object.create(null),
|
||||
target: {
|
||||
audioPlayer: {
|
||||
playDrumForBeats: drum => (playedDrum = drum)
|
||||
}
|
||||
audioPlayer: null
|
||||
},
|
||||
stackFrame: Object.create(null)
|
||||
yield: () => null
|
||||
};
|
||||
|
||||
test('playDrum uses 1-indexing and wrap clamps', t => {
|
||||
|
@ -26,7 +26,7 @@ test('playDrum uses 1-indexing and wrap clamps', t => {
|
|||
blocks.playDrumForBeats(args, util);
|
||||
t.strictEqual(playedDrum, 0);
|
||||
|
||||
args = {DRUM: runtime.audioEngine.numDrums + 1};
|
||||
args = {DRUM: blocks.DRUM_INFO.length + 1};
|
||||
blocks.playDrumForBeats(args, util);
|
||||
t.strictEqual(playedDrum, 0);
|
||||
|
|
@ -60,7 +60,13 @@ module.exports = [
|
|||
libraryTarget: 'commonjs2',
|
||||
path: path.resolve(__dirname, 'dist/node'),
|
||||
filename: '[name].js'
|
||||
}
|
||||
},
|
||||
plugins: base.plugins.concat([
|
||||
new CopyWebpackPlugin([{
|
||||
from: './src/extensions/scratch3_music/assets',
|
||||
to: 'assets/scratch3_music'
|
||||
}])
|
||||
])
|
||||
}),
|
||||
// Playground
|
||||
defaultsDeep({}, base, {
|
||||
|
|
Loading…
Reference in a new issue