mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 06:52:40 -05:00
Move musical timing into music extension
and use the stackframe timer, which improves timing accuracy.
This commit is contained in:
parent
3fa1599b90
commit
db3b0ca204
1 changed files with 46 additions and 9 deletions
|
@ -3,6 +3,7 @@ const BlockType = require('../extension-support/block-type');
|
||||||
const Clone = require('../util/clone');
|
const Clone = require('../util/clone');
|
||||||
const Cast = require('../util/cast');
|
const Cast = require('../util/cast');
|
||||||
const MathUtil = require('../util/math-util');
|
const MathUtil = require('../util/math-util');
|
||||||
|
const Timer = require('../util/timer');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of drum names, used in the play drum block.
|
* An array of drum names, used in the play drum block.
|
||||||
|
@ -70,6 +71,13 @@ class Scratch3MusicBlocks {
|
||||||
*/
|
*/
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current tempo in beats per minute. The tempo is a global property of the project,
|
||||||
|
* not a property of each sprite, so it is not stored in the MusicState object.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.tempo = 60;
|
||||||
|
|
||||||
this.drumMenu = this._buildMenu(drumNames);
|
this.drumMenu = this._buildMenu(drumNames);
|
||||||
this.instrumentMenu = this._buildMenu(instrumentNames);
|
this.instrumentMenu = this._buildMenu(instrumentNames);
|
||||||
}
|
}
|
||||||
|
@ -272,11 +280,10 @@ class Scratch3MusicBlocks {
|
||||||
* @property {number} BEATS - the duration in beats of the rest.
|
* @property {number} BEATS - the duration in beats of the rest.
|
||||||
* @return {Promise} - a promise which will resolve at the end of the duration.
|
* @return {Promise} - a promise which will resolve at the end of the duration.
|
||||||
*/
|
*/
|
||||||
restForBeats (args) {
|
restForBeats (args, util) {
|
||||||
let beats = Cast.toNumber(args.BEATS);
|
let beats = Cast.toNumber(args.BEATS);
|
||||||
beats = this._clampBeats(beats);
|
beats = this._clampBeats(beats);
|
||||||
if (typeof this.runtime.audioEngine === 'undefined') return;
|
this._waitForBeats(beats, util);
|
||||||
return this.runtime.audioEngine.waitForBeats(beats);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,6 +322,39 @@ class Scratch3MusicBlocks {
|
||||||
return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument);
|
return this.runtime.audioEngine.instrumentPlayer.loadInstrument(musicState.currentInstrument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_beatsToSec (beats) {
|
||||||
|
return (60 / this.tempo) * beats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for some number of beats.
|
||||||
|
* @param {number} beats - number of beats to wait for.
|
||||||
|
* @param {object} util - utility object provided by the runtime.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_waitForBeats (beats, util) {
|
||||||
|
if (util.stackFrame.timer) {
|
||||||
|
const timeElapsed = util.stackFrame.timer.timeElapsed();
|
||||||
|
if (timeElapsed < util.stackFrame.duration * 1000) {
|
||||||
|
util.yield();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
util.stackFrame.timer = new Timer();
|
||||||
|
util.stackFrame.timer.start();
|
||||||
|
util.stackFrame.duration = this._beatsToSec(beats);
|
||||||
|
if (util.stackFrame.duration <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
util.yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clamp a duration in beats to the allowed min and max duration.
|
* Clamp a duration in beats to the allowed min and max duration.
|
||||||
* @param {number} beats - a duration in beats.
|
* @param {number} beats - a duration in beats.
|
||||||
|
@ -342,8 +382,7 @@ class Scratch3MusicBlocks {
|
||||||
*/
|
*/
|
||||||
changeTempo (args) {
|
changeTempo (args) {
|
||||||
const change = Cast.toNumber(args.TEMPO);
|
const change = Cast.toNumber(args.TEMPO);
|
||||||
if (typeof this.runtime.audioEngine === 'undefined') return;
|
const tempo = change + this.tempo;
|
||||||
const tempo = change + this.runtime.audioEngine.currentTempo;
|
|
||||||
this._updateTempo(tempo);
|
this._updateTempo(tempo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,8 +393,7 @@ class Scratch3MusicBlocks {
|
||||||
*/
|
*/
|
||||||
_updateTempo (tempo) {
|
_updateTempo (tempo) {
|
||||||
tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max);
|
tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max);
|
||||||
if (typeof this.runtime.audioEngine === 'undefined') return;
|
this.tempo = tempo;
|
||||||
this.runtime.audioEngine.setTempo(tempo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -363,8 +401,7 @@ class Scratch3MusicBlocks {
|
||||||
* @return {number} - the current tempo, in beats per minute.
|
* @return {number} - the current tempo, in beats per minute.
|
||||||
*/
|
*/
|
||||||
getTempo () {
|
getTempo () {
|
||||||
if (typeof this.runtime.audioEngine === 'undefined') return;
|
return this.tempo;
|
||||||
return this.runtime.audioEngine.currentTempo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue