new "play frequency" commands in the Sounds category

pull/89/head
jmoenig 2019-04-04 07:08:40 +02:00
rodzic 9d2628274f
commit 0da085bc59
4 zmienionych plików z 115 dodań i 13 usunięć

Wyświetl plik

@ -16,6 +16,7 @@
* selectors for changing and querying "draggable" and "rotation style" settings
* new sound + music "volume" feature + blocks
* new sound + music stereo "panning" feature + blocks
* new "play frequency" commands in the Sounds category
* added "neg" selector to monadic function reporter in "Operators" category
* added "log2" selector to monadic function reporter in "Operators" category
* added "^" reporter (power of) in the Operators category
@ -23,7 +24,6 @@
* special context-aware drop-downs for custom blocks
* new "stick to" submenu in the sprite context menu where applicable
* multi-line and monospaced "code" input slots for custom blocks
* new "play frequency" commands in the AudioComp libary's Sound category
* new "string" library, thanks, Brian
* new "text costumes" library for generating costumes from letters or words of text
* enhanced support for embedding Snap in other website, thanks, Bernat!
@ -56,9 +56,12 @@
* German
* French
### 2019-04-04
* Objects, Threads: new "play frequency" commands in the Sounds category
### 2019-04-03
* Objects: Threads: Safari compatibility tweaks (only use StereoPanner if available)
* Objects. Store: new feature: volume blocks
* Objects, Threads: Safari compatibility tweaks (only use StereoPanner if available)
* Objects, Store: new feature: volume blocks
* Objects: added relabelling information for the new volume blocks
* Objects, Store: new feature: audio stereo-panning blocks
* Objects: added relabelling information for the new stereo-panning blocks

Wyświetl plik

@ -7,8 +7,8 @@
<script type="text/javascript" src="src/morphic.js?version=2019-02-07"></script>
<script type="text/javascript" src="src/widgets.js?version=2018-10-02"></script>
<script type="text/javascript" src="src/blocks.js?version=2019-04-02"></script>
<script type="text/javascript" src="src/threads.js?version=2019-04-03"></script>
<script type="text/javascript" src="src/objects.js?version=2019-04-03"></script>
<script type="text/javascript" src="src/threads.js?version=2019-04-04"></script>
<script type="text/javascript" src="src/objects.js?version=2019-04-04"></script>
<script type="text/javascript" src="src/gui.js?version=2019-03-25"></script>
<script type="text/javascript" src="src/paint.js?version=2019-02-22"></script>
<script type="text/javascript" src="src/lists.js?version=2019-02-07"></script>

Wyświetl plik

@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph, HandleMorph,
AlignmentMorph, Process, XML_Element, VectorPaintEditorMorph*/
modules.objects = '2019-April-03';
modules.objects = '2019-April-04';
var SpriteMorph;
var StageMorph;
@ -513,6 +513,17 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sound',
spec: 'pan left/right'
},
playFreq: {
type: 'command',
category: 'sound',
spec: 'play frequency %n hz',
defaults: [440]
},
stopFreq: {
type: 'command',
category: 'sound',
spec: 'stop frequency'
},
// Sound - Debugging primitives for development mode
reportSounds: {
@ -1471,6 +1482,9 @@ SpriteMorph.prototype.init = function (globals) {
this.pan = 0;
this.pannerNode = null; // must be lazily initialized in Chrome, sigh...
// frequency player, experimental
this.freqPlayer = null; // Note, to be lazily initialized
// pen hsv color support
this.cachedHSV = [0, 0, 0]; // not serialized
@ -1534,6 +1548,7 @@ SpriteMorph.prototype.fullCopy = function (forClone) {
c.color = this.color.copy();
c.gainNode = null;
c.pannerNode = null;
c.freqPlayer = null;
c.blocksCache = {};
c.paletteCache = {};
c.cachedHSV = c.color.hsv();
@ -2030,6 +2045,9 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('setPan'));
blocks.push(watcherToggle('getPan'));
blocks.push(block('getPan'));
blocks.push('-');
blocks.push(block('playFreq'));
blocks.push(block('stopFreq'));
// for debugging: ///////////////
@ -3384,6 +3402,57 @@ SpriteMorph.prototype.getPannerNode = function () {
return this.pannerNode;
};
// SpriteMorph frequency player
SpriteMorph.prototype.playFreq = function (hz) {
// start playing the given frequency until stopped
var note,
ctx = this.audioContext(),
gain = this.getGainNode(),
pan = this.getPannerNode(),
stage = this.parentThatIsA(StageMorph);
if (!this.freqPlayer) {
this.freqPlayer = new Note();
}
note = this.freqPlayer;
if (note.oscillator) {
note.oscillator.frequency.value = hz;
} else {
note.oscillator = ctx.createOscillator();
if (!note.oscillator.start) {
note.oscillator.start = note.oscillator.noteOn;
}
if (!note.oscillator.stop) {
note.oscillator.stop = note.oscillator.noteOff;
}
note.setInstrument(this.instrument);
note.oscillator.frequency.value = hz;
this.setVolume(this.volume);
note.oscillator.connect(gain);
if (pan) {
gain.connect(pan);
pan.connect(ctx.destination);
this.setPan(this.pan);
} else {
gain.connect(ctx.destination);
}
note.ended = false;
if (stage) {
stage.activeSounds.push(note);
stage.activeSounds = stage.activeSounds.filter(function (snd) {
return !snd.ended && !snd.terminated;
});
}
note.oscillator.start(0);
}
};
SpriteMorph.prototype.stopFreq = function () {
if (this.freqPlayer) {
this.freqPlayer.stop();
}
};
// SpriteMorph user menu
SpriteMorph.prototype.userMenu = function () {
@ -6568,6 +6637,9 @@ StageMorph.prototype.init = function (globals) {
this.pan = 0;
this.pannerNode = null; // must be lazily initialized in Chrome, sigh...
// frequency player, experimental
this.freqPlayer = null; // Note, to be lazily initialized
this.watcherUpdateFrequency = 2;
this.lastWatcherUpdate = Date.now();
@ -7400,6 +7472,9 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('setPan'));
blocks.push(watcherToggle('getPan'));
blocks.push(block('getPan'));
blocks.push('-');
blocks.push(block('playFreq'));
blocks.push(block('stopFreq'));
// for debugging: ///////////////
@ -8038,6 +8113,14 @@ StageMorph.prototype.getPan
StageMorph.prototype.getPannerNode
= SpriteMorph.prototype.getPannerNode;
// StageMorph frequency player
StageMorph.prototype.playFreq
= SpriteMorph.prototype.playFreq;
StageMorph.prototype.stopFreq
= SpriteMorph.prototype.stopFreq;
// StageMorph non-variable watchers
StageMorph.prototype.toggleWatcher
@ -9037,6 +9120,7 @@ function Note(pitch) {
this.frequency = null; // alternative for playing a non-note frequency
this.setupContext();
this.oscillator = null;
this.ended = false; // for active sounds management
}
// Note shared properties
@ -9085,12 +9169,7 @@ Note.prototype.play = function (type, gainNode, pannerNode) {
if (!this.oscillator.stop) {
this.oscillator.stop = this.oscillator.noteOff;
}
this.oscillator.type = [
'sine',
'square',
'sawtooth',
'triangle'
][(type || 1) - 1];
this.setInstrument(type);
this.oscillator.frequency.value = isNil(this.frequency) ?
Math.pow(2, (this.pitch - 69) / 12) * 440 : this.frequency;
this.oscillator.connect(gainNode);
@ -9100,14 +9179,31 @@ Note.prototype.play = function (type, gainNode, pannerNode) {
} else {
gainNode.connect(this.audioContext.destination);
}
this.ended = false;
this.oscillator.start(0);
};
Note.prototype.setInstrument = function (type) {
// private - make sure the oscillator node has been initialized before
this.oscillator.type = [
'sine',
'square',
'sawtooth',
'triangle'
][(type || 1) - 1];
};
Note.prototype.stop = function () {
if (this.oscillator) {
this.oscillator.stop(0);
this.oscillator = null;
}
this.ended = true;
};
Note.prototype.pause = function () {
// emulate a sound for active sounds mngmt
this.stop();
};
// Microphone /////////////////////////////////////////////////////////

Wyświetl plik

@ -62,7 +62,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, Color,
TableFrameMorph, ColorSlotMorph, isSnapObject, Map*/
modules.threads = '2019-April-03';
modules.threads = '2019-April-04';
var ThreadManager;
var Process;
@ -4147,6 +4147,9 @@ Process.prototype.doPlayFrequencyForSecs = function (hz, secs) {
Process.prototype.doSetInstrument = function (num) {
this.instrument = +num;
this.receiver.instrument = +num;
if (this.receiver.freqPlayer) {
this.receiver.freqPlayer.setInstrument(+num);
}
};
// Process constant input options