1- /*! p5.sound.js v0.3.9 2018-09 -10 */
1+ /*! p5.sound.js v0.3.10 2019-01 -10 */
22/**
33 * p5.sound extends p5 with <a href="http://caniuse.com/audio-api"
44 * target="_blank">Web Audio</a> functionality including audio input,
@@ -254,9 +254,111 @@ shims = function () {
254254 }
255255 } ;
256256} ( ) ;
257+ var StartAudioContext ;
258+ ( function ( root , factory ) {
259+ if ( true ) {
260+ StartAudioContext = function ( ) {
261+ return factory ( ) ;
262+ } ( ) ;
263+ } else if ( typeof module === 'object' && module . exports ) {
264+ module . exports = factory ( ) ;
265+ } else {
266+ root . StartAudioContext = factory ( ) ;
267+ }
268+ } ( this , function ( ) {
269+ var TapListener = function ( element , context ) {
270+ this . _dragged = false ;
271+ this . _element = element ;
272+ this . _bindedMove = this . _moved . bind ( this ) ;
273+ this . _bindedEnd = this . _ended . bind ( this , context ) ;
274+ } ;
275+ TapListener . prototype . _moved = function ( e ) {
276+ this . _dragged = true ;
277+ } ;
278+ TapListener . prototype . _ended = function ( context ) {
279+ if ( ! this . _dragged ) {
280+ startContext ( context ) ;
281+ }
282+ this . _dragged = false ;
283+ } ;
284+ TapListener . prototype . dispose = function ( ) {
285+ this . _element . removeEventListener ( 'touchstart' , this . _bindedEnd ) ;
286+ this . _element . removeEventListener ( 'touchmove' , this . _bindedMove ) ;
287+ this . _element . removeEventListener ( 'touchend' , this . _bindedEnd ) ;
288+ this . _element . removeEventListener ( 'mouseup' , this . _bindedEnd ) ;
289+ this . _bindedMove = null ;
290+ this . _bindedEnd = null ;
291+ this . _element = null ;
292+ } ;
293+ function startContext ( context ) {
294+ var buffer = context . createBuffer ( 1 , 1 , context . sampleRate ) ;
295+ var source = context . createBufferSource ( ) ;
296+ source . buffer = buffer ;
297+ source . connect ( context . destination ) ;
298+ source . start ( 0 ) ;
299+ if ( context . resume ) {
300+ context . resume ( ) ;
301+ }
302+ }
303+ function isStarted ( context ) {
304+ return context . state === 'running' ;
305+ }
306+ function onStarted ( context , callback ) {
307+ function checkLoop ( ) {
308+ if ( isStarted ( context ) ) {
309+ callback ( ) ;
310+ } else {
311+ requestAnimationFrame ( checkLoop ) ;
312+ if ( context . resume ) {
313+ context . resume ( ) ;
314+ }
315+ }
316+ }
317+ if ( isStarted ( context ) ) {
318+ callback ( ) ;
319+ } else {
320+ checkLoop ( ) ;
321+ }
322+ }
323+ function bindTapListener ( element , tapListeners , context ) {
324+ if ( Array . isArray ( element ) || NodeList && element instanceof NodeList ) {
325+ for ( var i = 0 ; i < element . length ; i ++ ) {
326+ bindTapListener ( element [ i ] , tapListeners , context ) ;
327+ }
328+ } else if ( typeof element === 'string' ) {
329+ bindTapListener ( document . querySelectorAll ( element ) , tapListeners , context ) ;
330+ } else if ( element . jquery && typeof element . toArray === 'function' ) {
331+ bindTapListener ( element . toArray ( ) , tapListeners , context ) ;
332+ } else if ( Element && element instanceof Element ) {
333+ var tap = new TapListener ( element , context ) ;
334+ tapListeners . push ( tap ) ;
335+ }
336+ }
337+ function StartAudioContext ( context , elements , callback ) {
338+ var promise = new Promise ( function ( success ) {
339+ onStarted ( context , success ) ;
340+ } ) ;
341+ var tapListeners = [ ] ;
342+ if ( ! elements ) {
343+ elements = document . body ;
344+ }
345+ bindTapListener ( elements , tapListeners , context ) ;
346+ promise . then ( function ( ) {
347+ for ( var i = 0 ; i < tapListeners . length ; i ++ ) {
348+ tapListeners [ i ] . dispose ( ) ;
349+ }
350+ tapListeners = null ;
351+ if ( callback ) {
352+ callback ( ) ;
353+ }
354+ } ) ;
355+ return promise ;
356+ }
357+ return StartAudioContext ;
358+ } ) ) ;
257359var audiocontext ;
258360'use strict' ;
259- audiocontext = function ( ) {
361+ audiocontext = function ( StartAudioContext ) {
260362 // Create the Audio Context
261363 var audiocontext = new window . AudioContext ( ) ;
262364 /**
@@ -296,32 +398,60 @@ audiocontext = function () {
296398 p5 . prototype . getAudioContext = function ( ) {
297399 return audiocontext ;
298400 } ;
299- // if it is iOS, we have to have a user interaction to start Web Audio
300- // http://paulbakaus.com/tutorials/html5/web-audio-on-ios/
301- var iOS = navigator . userAgent . match ( / ( i P a d | i P h o n e | i P o d ) / g) ? true : false ;
302- if ( iOS ) {
303- var iosStarted = false ;
304- var startIOS = function ( ) {
305- if ( iosStarted )
306- return ;
307- // create empty buffer
308- var buffer = audiocontext . createBuffer ( 1 , 1 , 22050 ) ;
309- var source = audiocontext . createBufferSource ( ) ;
310- source . buffer = buffer ;
311- // connect to output (your speakers)
312- source . connect ( audiocontext . destination ) ;
313- // play the file
314- source . start ( 0 ) ;
315- console . log ( 'start ios!' ) ;
316- if ( audiocontext . state === 'running' ) {
317- iosStarted = true ;
318- }
319- } ;
320- document . addEventListener ( 'touchend' , startIOS , false ) ;
321- document . addEventListener ( 'touchstart' , startIOS , false ) ;
322- }
401+ /**
402+ * <p>It is a good practice to give users control over starting audio playback.
403+ * This practice is enforced by Google Chrome's autoplay policy as of r70
404+ * (<a href="https://goo.gl/7K7WLu">info</a>), iOS Safari, and other browsers.
405+ * </p>
406+ *
407+ * <p>
408+ * userStartAudio() starts the <a href="https://developer.mozilla.org/en-US/docs/Web/API/AudioContext"
409+ * target="_blank" title="Audio Context @ MDN">Audio Context</a> on a user gesture. It utilizes
410+ * the <a href="https://github.com/tambien/StartAudioContext">StartAudioContext</a> library by
411+ * Yotam Mann (MIT Licence, 2016). Read more at https://github.com/tambien/StartAudioContext.
412+ * </p>
413+ *
414+ * <p>Starting the audio context on a user gesture can be as simple as <code>userStartAudio()</code>.
415+ * Optional parameters let you decide on a specific element that will start the audio context,
416+ * and/or call a function once the audio context is started.</p>
417+ * @param {Element|Array } [element(s)] This argument can be an Element,
418+ * Selector String, NodeList, p5.Element,
419+ * jQuery Element, or an Array of any of those.
420+ * @param {Function } [callback] Callback to invoke when the AudioContext has started
421+ * @return {Promise } Returns a Promise which is resolved when
422+ * the AudioContext state is 'running'
423+ * @method userStartAudio
424+ * @example
425+ * <div><code>
426+ * function setup() {
427+ * var myDiv = createDiv('click to start audio');
428+ * myDiv.position(0, 0);
429+ *
430+ * var mySynth = new p5.MonoSynth();
431+ *
432+ * // This won't play until the context has started
433+ * mySynth.play('A6');
434+ *
435+ * // Start the audio context on a click/touch event
436+ * userStartAudio().then(function() {
437+ * myDiv.remove();
438+ * });
439+ * }
440+ * </code></div>
441+ */
442+ p5 . prototype . userStartAudio = function ( elements , callback ) {
443+ var elt = elements ;
444+ if ( elements instanceof p5 . Element ) {
445+ elt = elements . elt ;
446+ } else if ( elements instanceof Array && elements [ 0 ] instanceof p5 . Element ) {
447+ elt = elements . map ( function ( e ) {
448+ return e . elt ;
449+ } ) ;
450+ }
451+ return StartAudioContext ( audiocontext , elt , callback ) ;
452+ } ;
323453 return audiocontext ;
324- } ( ) ;
454+ } ( StartAudioContext ) ;
325455var master ;
326456'use strict' ;
327457master = function ( ) {
@@ -6326,13 +6456,13 @@ envelope = function () {
63266456 *
63276457 * env = new p5.Envelope(t1, l1, t2, l2, t3, l3);
63286458 * triOsc = new p5.Oscillator('triangle');
6329- * triOsc.amp(env); // give the envelope control of the oscillator 's amplitude
6459+ * triOsc.amp(env); // give the env control of the triOsc 's amp
63306460 * triOsc.start();
63316461 * }
63326462 *
63336463 * // mouseClick triggers envelope if over canvas
63346464 * function mouseClicked() {
6335- * // is the mouse over the canvas?
6465+ * // is mouse over canvas?
63366466 * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
63376467 * env.play(triOsc);
63386468 * }
@@ -8505,7 +8635,6 @@ filter = function () {
85058635 freq = 1 ;
85068636 }
85078637 if ( typeof freq === 'number' ) {
8508- this . biquad . frequency . value = freq ;
85098638 this . biquad . frequency . cancelScheduledValues ( this . ac . currentTime + 0.01 + t ) ;
85108639 this . biquad . frequency . exponentialRampToValueAtTime ( freq , this . ac . currentTime + 0.02 + t ) ;
85118640 } else if ( freq ) {
@@ -10425,14 +10554,14 @@ looper = function () {
1042510554 } ;
1042610555 /**
1042710556 * <p>A p5.Part plays back one or more p5.Phrases. Instantiate a part
10428- * with steps and tatums. By default, each step represents 1/16th note.</p>
10557+ * with steps and tatums. By default, each step represents a 1/16th note.</p>
1042910558 *
1043010559 * <p>See p5.Phrase for more about musical timing.</p>
1043110560 *
1043210561 * @class p5.Part
1043310562 * @constructor
1043410563 * @param {Number } [steps] Steps in the part
10435- * @param {Number } [tatums] Divisions of a beat (default is 1/16, a quarter note)
10564+ * @param {Number } [tatums] Divisions of a beat, e.g. use 1/4, or 0.25 for a quater note (default is 1/16, a sixteenth note)
1043610565 * @example
1043710566 * <div><code>
1043810567 * var box, drum, myPart;
@@ -10511,7 +10640,7 @@ looper = function () {
1051110640 this . metro . setBPM ( tempo , rampTime ) ;
1051210641 } ;
1051310642 /**
10514- * Returns the Beats Per Minute of this currently part.
10643+ * Returns the tempo, in Beats Per Minute, of this part.
1051510644 *
1051610645 * @method getBPM
1051710646 * @return {Number }
@@ -10565,7 +10694,7 @@ looper = function () {
1056510694 } ;
1056610695 } ;
1056710696 /**
10568- * Stop the part and cue it to step 0.
10697+ * Stop the part and cue it to step 0. Playback will resume from the begining of the Part when it is played again.
1056910698 *
1057010699 * @method stop
1057110700 * @param {Number } [time] seconds from now
@@ -10636,8 +10765,7 @@ looper = function () {
1063610765 }
1063710766 } ;
1063810767 /**
10639- * Get a phrase from this part, based on the name it was
10640- * given when it was created. Now you can modify its array.
10768+ * Find all sequences with the specified name, and replace their patterns with the specified array.
1064110769 *
1064210770 * @method replaceSequence
1064310771 * @param {String } phraseName
@@ -10664,7 +10792,7 @@ looper = function () {
1066410792 }
1066510793 } ;
1066610794 /**
10667- * Fire a callback function at every step.
10795+ * Set the function that will be called at every step. This will clear the previous function .
1066810796 *
1066910797 * @method onStep
1067010798 * @param {Function } callback The name of the callback
@@ -11546,7 +11674,8 @@ peakdetect = function () {
1154611674 *
1154711675 * <p>
1154811676 * Based on example contribtued by @b2renger, and a simple beat detection
11549- * explanation by <a href="http://www.airtightinteractive.com/2013/10/making-audio-reactive-visuals/"
11677+ * explanation by <a
11678+ * href="http://www.airtightinteractive.com/2013/10/making-audio-reactive-visuals/"
1155011679 * target="_blank">Felix Turner</a>.
1155111680 * </p>
1155211681 *
@@ -12741,4 +12870,4 @@ src_app = function () {
1274112870 var p5SOUND = master ;
1274212871 return p5SOUND ;
1274312872} ( shims , audiocontext , master , helpers , errorHandler , panner , soundfile , amplitude , fft , signal , oscillator , envelope , pulse , noise , audioin , filter , eq , panner3d , listener3d , delay , reverb , metro , looper , soundloop , compressor , soundRecorder , peakdetect , gain , monosynth , polysynth , distortion , audioVoice , monosynth , polysynth ) ;
12744- } ) ) ;
12873+ } ) ) ;
0 commit comments