diff --git a/lib/addons/p5.sound.js b/lib/addons/p5.sound.js index a8a7e3f450..25681ab89d 100644 --- a/lib/addons/p5.sound.js +++ b/lib/addons/p5.sound.js @@ -1,4 +1,4 @@ -/** [p5.sound] Version: 1.0.1 - 2021-05-25 */ +/** [p5.sound] Version: 1.0.1 - 2022-11-22 */ /** *

p5.sound extends p5 with Web Audio functionality including audio input, @@ -218,7 +218,7 @@ function getAudioContext() { * example below. This method utilizes * StartAudioContext * , a library by Yotam Mann (MIT Licence, 2016).

- * @param {Element|Array} [elements] This argument can be an Element, + * @param {Element|Array} [element(s)] This argument can be an Element, * Selector String, NodeList, p5.Element, * jQuery Element, or an Array of any of those. * @param {Function} [callback] Callback to invoke when the AudioContext @@ -895,8 +895,8 @@ function noteToFreq(note) { } var wholeNotes = { - A: 21, - B: 23, + A: 33, + B: 35, C: 24, D: 26, E: 28, @@ -968,7 +968,7 @@ function soundFormats() { } function disposeSound() { - for (var i = 0; i < main.soundArray.length; i++) { + for (var i = main.soundArray.length - 1; i >= 0; i--) { main.soundArray[i].dispose(); } } @@ -1828,10 +1828,10 @@ function () { }, { key: "playMode", value: function playMode(str) { - var s = str.toLowerCase(); + var s = str.toLowerCase().trim(); if (s === 'restart' && this.buffer && this.bufferSourceNode) { - for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) { + for (var i = 0; i < this.bufferSourceNodes.length; i++) { var now = main.audiocontext.currentTime; this.bufferSourceNodes[i].stop(now); } @@ -2043,11 +2043,10 @@ function () { this._paused = false; } else if (this.buffer && this.bufferSourceNode) { var now = main.audiocontext.currentTime; - var t = time || 0; this.pauseTime = 0; - this.bufferSourceNode.stop(now + t); + this.bufferSourceNode.stop(now + time); - this._counterNode.stop(now + t); + this._counterNode.stop(now + time); this._playing = false; this._paused = false; @@ -2349,7 +2348,7 @@ function () { }, { key: "channels", value: function channels() { - return this.buffer.numberOfChannels; + if (this.buffer) return this.buffer.numberOfChannels; } /** * Return the sample rate of the sound file. @@ -2362,7 +2361,7 @@ function () { }, { key: "sampleRate", value: function sampleRate() { - return this.buffer.sampleRate; + if (this.buffer) return this.buffer.sampleRate; } /** * Return the number of samples in a sound file. @@ -2376,7 +2375,7 @@ function () { }, { key: "frames", value: function frames() { - return this.buffer.length; + if (this.buffer) return this.buffer.length; } /** * Returns an array of amplitude peaks in a p5.SoundFile that can be @@ -2400,7 +2399,7 @@ function () { value: function getPeaks(length) { if (this.buffer) { if (!length) { - length = window.width * 5; + length = window.innerWidth * 5; } if (this.buffer) { @@ -3195,10 +3194,48 @@ function () { } else { this.output.connect(unit); } - } else { - this.output.connect(this.panner.connect(main.input)); - } + } + } + /** + * Disconnects the output of this p5.Amplitude object. + * + * @method disconnect + * @for p5.Amplitude + * @example + *
+ * let sound, amplitude; + * function preload(){ + * sound = loadSound('assets/beat.mp3'); + * } + * + * function setup() { + * let cnv = createCanvas(100, 100); + * cnv.mouseClicked(togglePlay); + * amplitude = new p5.Amplitude(); + * sound.loop(); + * } + * + * function draw() { + * background(220); + * text('Disconnect:', 20, 20); + * let level = amplitude.getLevel(); + * let size = map(level, 0, 1, 0, 200); + * ellipse(width/2, height/2, size, size); + * } + * + * function togglePlay() { + * if (sound.isPlaying()){ + * sound.pause(); + * amplitude.disconnect(); + * } + * else{ + * sound.play(); + * } + * } + *
+ */ + }, { key: "disconnect", value: function disconnect() { @@ -3519,13 +3556,12 @@ function () { }, { key: "waveform", value: function waveform() { - var bins, mode; + var mode; var normalArray = new Array(); for (var i = 0; i < arguments.length; i++) { if (typeof arguments[i] === 'number') { - bins = arguments[i]; - this.analyser.fftSize = bins * 2; + this.bins = arguments[i]; } if (typeof arguments[i] === 'string') { @@ -3628,7 +3664,6 @@ function () { for (var i = 0; i < arguments.length; i++) { if (typeof arguments[i] === 'number') { this.bins = arguments[i]; - this.analyser.fftSize = this.bins * 2; } if (typeof arguments[i] === 'string') { @@ -4850,7 +4885,7 @@ p5.Envelope.prototype._init = function () { * let l1 = 0.7; // attack level 0.0 to 1.0 * let t2 = 0.3; // decay time in seconds * let l2 = 0.1; // decay level 0.0 to 1.0 - * let l3 = 0.2; // release time in seconds + * let t3 = 0.2; // release time in seconds * * let env, triOsc; * @@ -4872,7 +4907,7 @@ p5.Envelope.prototype._init = function () { * * // mouseClick triggers envelope if over canvas * function playSound() { - * env.set(attackTime, l1, t2, l2, l3); + * env.set(attackTime, l1, t2, l2, t3); * * triOsc.start(); * env.play(triOsc); @@ -6249,7 +6284,7 @@ function () { key: "getLevel", value: function getLevel(smoothing) { if (smoothing) { - this.amplitude.smoothing = smoothing; + this.amplitude.smooth(smoothing); } return this.amplitude.getLevel(); @@ -7281,7 +7316,7 @@ function (_Effect) { this.bands[i / 2].gain(arguments[i + 1]); } } else { - console.error('Argument mismatch. .set() should be called with ' + this.bands.length * 2 + ' arguments. (one frequency and gain value pair for each band of the eq)'); + throw new Error('Argument mismatch. .set() should be called with ' + this.bands.length * 2 + ' arguments. (one frequency and gain value pair for each band of the eq)'); } } /** @@ -8393,6 +8428,7 @@ function (_Effect) { key: "_teardownConvolverNode", value: function _teardownConvolverNode() { if (this.convolverNode) { + this.input.disconnect(this.convolverNode); this.convolverNode.disconnect(); delete this.convolverNode; } @@ -8424,24 +8460,7 @@ function (_Effect) { key: "process", value: function process(src, seconds, decayRate, reverse) { src.connect(this.input); - var rebuild = false; - - if (seconds) { - this._seconds = seconds; - rebuild = true; - } - - if (decayRate) { - this._decay = decayRate; - } - - if (reverse) { - this._reverse = reverse; - } - - if (rebuild) { - this._buildImpulse(); - } + this.set(seconds, decayRate, reverse); } /** * Set the reverb settings. Similar to .process(), but without @@ -9027,7 +9046,7 @@ function () { }, { key: "getBPM", value: function getBPM() { - return this.clock.getRate() / this.tatums * 60; + return this.bpm; } }, { key: "_init", @@ -9045,7 +9064,8 @@ function () { key: "pushSync", value: function pushSync(part) { this.syncedParts.push(part); - } + } + }, { key: "start", value: function start(timeFromNow) { @@ -9508,13 +9528,15 @@ function () { looper_classCallCheck(this, Score); this.parts = []; - this.currentPart = new Array(arguments.length); - ; + this.currentPart = 0; var thisScore = this; for (var i in arguments) { this.parts[i] = arguments[i]; - this.parts[i].nextPart = this.parts[i + 1]; + + if (i > 0) { + this.parts[i - 1].nextPart = this.parts[i]; + } this.parts[i].onended = function () { thisScore.resetPart(i); @@ -9831,7 +9853,7 @@ function () { * The callback should only be called until maxIterations is reached */ - if (timeFromNow > 0 && self.iterations <= self.maxIterations) { + if (timeFromNow > 0 && self.iterations <= self.maxIterations && self.callback) { self.callback(timeFromNow); } }, @@ -10570,7 +10592,10 @@ var soundRecorder_ac = main.audiocontext; * @example *
* let mic, recorder, soundFile; - * let state = 0; + * // keeps record if recording is started + * let isRecordingStarted = false; + * // keeps record if the recorded result is played + * let isResultPlayed = false; * * function setup() { * let cnv = createCanvas(100, 100); @@ -10581,9 +10606,6 @@ var soundRecorder_ac = main.audiocontext; * // create an audio in * mic = new p5.AudioIn(); * - * // prompts user to enable their browser mic - * mic.start(); - * * // create a sound recorder * recorder = new p5.SoundRecorder(); * @@ -10601,31 +10623,34 @@ var soundRecorder_ac = main.audiocontext; * // ensure audio is enabled * userStartAudio(); * - * // make sure user enabled the mic - * if (state === 0 && mic.enabled) { - * - * // record to our p5.SoundFile - * recorder.record(soundFile); - * - * background(255,0,0); - * text('Recording!', width/2, height/2); - * state++; + * if (!isRecordingStarted && !isResultPlayed) { + * // make sure user enabled the mic by prompting to enable their browser mic + * // start recording after the mic is enabled + * mic.start(function() { + * // record to our p5.SoundFile + * recorder.record(soundFile); + * + * background(255,0,0); + * text('Recording!', width/2, height/2); + * isRecordingStarted = true; + * }); * } - * else if (state === 1) { + * else if (isRecordingStarted && !isResultPlayed) { * background(0,255,0); * * // stop recorder and * // send result to soundFile * recorder.stop(); + * // stop browser from accessing the mic + * mic.dispose(); * * text('Done! Tap to play and download', width/2, height/2, width - 20); - * state++; + * isResultPlayed = true; * } * - * else if (state === 2) { + * else if (isRecordingStarted && isResultPlayed) { * soundFile.play(); // play the result! * save(soundFile, 'mySound.wav'); - * state++; * } * } *
@@ -10641,7 +10666,7 @@ function () { this._inputChannels = 2; this._outputChannels = 2; - var workletBufferSize = safeBufferSize(1024); + var workletBufferSize = this.bufferSize = safeBufferSize(1024); this._workletNode = new AudioWorkletNode(soundRecorder_ac, processorNames_default.a.recorderProcessor, { outputChannelCount: [this._outputChannels], processorOptions: { @@ -11841,7 +11866,9 @@ function () { }, { key: "play", - value: function play(note, velocity, secondsFromNow) { + value: function play(note) { + var velocity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.1; + var secondsFromNow = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var susTime = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1; this.noteAttack(note, velocity, secondsFromNow); this.noteRelease(note, secondsFromNow + susTime); @@ -11966,7 +11993,9 @@ function () { } else { currentVoice = this._oldest; - oldestNote = freqToMidi(this.audiovoices[this._oldest].oscillator.freq().value); + + var oldestNote = this.audiovoices[this._oldest].oscillator.freq().value; + this.noteRelease(oldestNote); this._oldest = (this._oldest + 1) % (this.maxVoices - 1); } @@ -12078,6 +12107,8 @@ function () { delete this.notes[n]; } + this._newest = 0; + this._oldest = 0; return; } @@ -12087,7 +12118,7 @@ function () { if (!this.notes[note] || this.notes[note].getValueAtTime(t) === null) { console.warn('Cannot release a note that is not already playing'); } else { - var previousVal = Math.max(~~this._voicesInUse.getValueAtTime(t).value, 1); + var previousVal = Math.max(~~this._voicesInUse.getValueAtTime(t), 1); this._voicesInUse.setValueAtTime(previousVal - 1, t); @@ -12222,7 +12253,7 @@ p5.BandPass = BandPass; p5.EQ = eq; -p5.listener3D = listener3d; +p5.Listener3D = listener3d; p5.Panner3D = panner3d;