Convenient open place on the web for collaboration https://coalmedia.eu/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

p5.sound.js 312KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336
  1. /*! p5.sound.js v0.3.2 2016-11-01 */
  2. (function (root, factory) {
  3. if (typeof define === 'function' && define.amd)
  4. define('p5.sound', ['p5'], function (p5) { (factory(p5));});
  5. else if (typeof exports === 'object')
  6. factory(require('../p5'));
  7. else
  8. factory(root['p5']);
  9. }(this, function (p5) {
  10. /**
  11. * p5.sound extends p5 with <a href="http://caniuse.com/audio-api"
  12. * target="_blank">Web Audio</a> functionality including audio input,
  13. * playback, analysis and synthesis.
  14. * <br/><br/>
  15. * <a href="#/p5.SoundFile"><b>p5.SoundFile</b></a>: Load and play sound files.<br/>
  16. * <a href="#/p5.Amplitude"><b>p5.Amplitude</b></a>: Get the current volume of a sound.<br/>
  17. * <a href="#/p5.AudioIn"><b>p5.AudioIn</b></a>: Get sound from an input source, typically
  18. * a computer microphone.<br/>
  19. * <a href="#/p5.FFT"><b>p5.FFT</b></a>: Analyze the frequency of sound. Returns
  20. * results from the frequency spectrum or time domain (waveform).<br/>
  21. * <a href="#/p5.Oscillator"><b>p5.Oscillator</b></a>: Generate Sine,
  22. * Triangle, Square and Sawtooth waveforms. Base class of
  23. * <a href="#/p5.Noise">p5.Noise</a> and <a href="#/p5.Pulse">p5.Pulse</a>.
  24. * <br/>
  25. * <a href="#/p5.Env"><b>p5.Env</b></a>: An Envelope is a series
  26. * of fades over time. Often used to control an object's
  27. * output gain level as an "ADSR Envelope" (Attack, Decay,
  28. * Sustain, Release). Can also modulate other parameters.<br/>
  29. * <a href="#/p5.Delay"><b>p5.Delay</b></a>: A delay effect with
  30. * parameters for feedback, delayTime, and lowpass filter.<br/>
  31. * <a href="#/p5.Filter"><b>p5.Filter</b></a>: Filter the frequency range of a
  32. * sound.
  33. * <br/>
  34. * <a href="#/p5.Reverb"><b>p5.Reverb</b></a>: Add reverb to a sound by specifying
  35. * duration and decay. <br/>
  36. * <b><a href="#/p5.Convolver">p5.Convolver</a>:</b> Extends
  37. * <a href="#/p5.Reverb">p5.Reverb</a> to simulate the sound of real
  38. * physical spaces through convolution.<br/>
  39. * <b><a href="#/p5.SoundRecorder">p5.SoundRecorder</a></b>: Record sound for playback
  40. * / save the .wav file.
  41. * <b><a href="#/p5.Phrase">p5.Phrase</a></b>, <b><a href="#/p5.Part">p5.Part</a></b> and
  42. * <b><a href="#/p5.Score">p5.Score</a></b>: Compose musical sequences.
  43. * <br/><br/>
  44. * p5.sound is on <a href="https://github.com/therewasaguy/p5.sound/">GitHub</a>.
  45. * Download the latest version
  46. * <a href="https://github.com/therewasaguy/p5.sound/blob/master/lib/p5.sound.js">here</a>.
  47. *
  48. * @module p5.sound
  49. * @submodule p5.sound
  50. * @for p5.sound
  51. * @main
  52. */
  53. /**
  54. * p5.sound developed by Jason Sigal for the Processing Foundation, Google Summer of Code 2014. The MIT License (MIT).
  55. *
  56. * http://github.com/therewasaguy/p5.sound
  57. *
  58. * Some of the many audio libraries & resources that inspire p5.sound:
  59. * - TONE.js (c) Yotam Mann, 2014. Licensed under The MIT License (MIT). https://github.com/TONEnoTONE/Tone.js
  60. * - buzz.js (c) Jay Salvat, 2013. Licensed under The MIT License (MIT). http://buzz.jaysalvat.com/
  61. * - Boris Smus Web Audio API book, 2013. Licensed under the Apache License http://www.apache.org/licenses/LICENSE-2.0
  62. * - wavesurfer.js https://github.com/katspaugh/wavesurfer.js
  63. * - Web Audio Components by Jordan Santell https://github.com/web-audio-components
  64. * - Wilm Thoben's Sound library for Processing https://github.com/processing/processing/tree/master/java/libraries/sound
  65. *
  66. * Web Audio API: http://w3.org/TR/webaudio/
  67. */
  68. var sndcore;
  69. sndcore = function () {
  70. 'use strict';
  71. /* AudioContext Monkeypatch
  72. Copyright 2013 Chris Wilson
  73. Licensed under the Apache License, Version 2.0 (the "License");
  74. you may not use this file except in compliance with the License.
  75. You may obtain a copy of the License at
  76. http://www.apache.org/licenses/LICENSE-2.0
  77. Unless required by applicable law or agreed to in writing, software
  78. distributed under the License is distributed on an "AS IS" BASIS,
  79. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  80. See the License for the specific language governing permissions and
  81. limitations under the License.
  82. */
  83. (function (global, exports, perf) {
  84. exports = exports || {};
  85. 'use strict';
  86. function fixSetTarget(param) {
  87. if (!param)
  88. // if NYI, just return
  89. return;
  90. if (!param.setTargetAtTime)
  91. param.setTargetAtTime = param.setTargetValueAtTime;
  92. }
  93. if (window.hasOwnProperty('webkitAudioContext') && !window.hasOwnProperty('AudioContext')) {
  94. window.AudioContext = webkitAudioContext;
  95. if (typeof AudioContext.prototype.createGain !== 'function')
  96. AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
  97. if (typeof AudioContext.prototype.createDelay !== 'function')
  98. AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
  99. if (typeof AudioContext.prototype.createScriptProcessor !== 'function')
  100. AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createScriptProcessor;
  101. if (typeof AudioContext.prototype.createPeriodicWave !== 'function')
  102. AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
  103. AudioContext.prototype.internal_createGain = AudioContext.prototype.createGain;
  104. AudioContext.prototype.createGain = function () {
  105. var node = this.internal_createGain();
  106. fixSetTarget(node.gain);
  107. return node;
  108. };
  109. AudioContext.prototype.internal_createDelay = AudioContext.prototype.createDelay;
  110. AudioContext.prototype.createDelay = function (maxDelayTime) {
  111. var node = maxDelayTime ? this.internal_createDelay(maxDelayTime) : this.internal_createDelay();
  112. fixSetTarget(node.delayTime);
  113. return node;
  114. };
  115. AudioContext.prototype.internal_createBufferSource = AudioContext.prototype.createBufferSource;
  116. AudioContext.prototype.createBufferSource = function () {
  117. var node = this.internal_createBufferSource();
  118. if (!node.start) {
  119. node.start = function (when, offset, duration) {
  120. if (offset || duration)
  121. this.noteGrainOn(when || 0, offset, duration);
  122. else
  123. this.noteOn(when || 0);
  124. };
  125. } else {
  126. node.internal_start = node.start;
  127. node.start = function (when, offset, duration) {
  128. if (typeof duration !== 'undefined')
  129. node.internal_start(when || 0, offset, duration);
  130. else
  131. node.internal_start(when || 0, offset || 0);
  132. };
  133. }
  134. if (!node.stop) {
  135. node.stop = function (when) {
  136. this.noteOff(when || 0);
  137. };
  138. } else {
  139. node.internal_stop = node.stop;
  140. node.stop = function (when) {
  141. node.internal_stop(when || 0);
  142. };
  143. }
  144. fixSetTarget(node.playbackRate);
  145. return node;
  146. };
  147. AudioContext.prototype.internal_createDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor;
  148. AudioContext.prototype.createDynamicsCompressor = function () {
  149. var node = this.internal_createDynamicsCompressor();
  150. fixSetTarget(node.threshold);
  151. fixSetTarget(node.knee);
  152. fixSetTarget(node.ratio);
  153. fixSetTarget(node.reduction);
  154. fixSetTarget(node.attack);
  155. fixSetTarget(node.release);
  156. return node;
  157. };
  158. AudioContext.prototype.internal_createBiquadFilter = AudioContext.prototype.createBiquadFilter;
  159. AudioContext.prototype.createBiquadFilter = function () {
  160. var node = this.internal_createBiquadFilter();
  161. fixSetTarget(node.frequency);
  162. fixSetTarget(node.detune);
  163. fixSetTarget(node.Q);
  164. fixSetTarget(node.gain);
  165. return node;
  166. };
  167. if (typeof AudioContext.prototype.createOscillator !== 'function') {
  168. AudioContext.prototype.internal_createOscillator = AudioContext.prototype.createOscillator;
  169. AudioContext.prototype.createOscillator = function () {
  170. var node = this.internal_createOscillator();
  171. if (!node.start) {
  172. node.start = function (when) {
  173. this.noteOn(when || 0);
  174. };
  175. } else {
  176. node.internal_start = node.start;
  177. node.start = function (when) {
  178. node.internal_start(when || 0);
  179. };
  180. }
  181. if (!node.stop) {
  182. node.stop = function (when) {
  183. this.noteOff(when || 0);
  184. };
  185. } else {
  186. node.internal_stop = node.stop;
  187. node.stop = function (when) {
  188. node.internal_stop(when || 0);
  189. };
  190. }
  191. if (!node.setPeriodicWave)
  192. node.setPeriodicWave = node.setWaveTable;
  193. fixSetTarget(node.frequency);
  194. fixSetTarget(node.detune);
  195. return node;
  196. };
  197. }
  198. }
  199. if (window.hasOwnProperty('webkitOfflineAudioContext') && !window.hasOwnProperty('OfflineAudioContext')) {
  200. window.OfflineAudioContext = webkitOfflineAudioContext;
  201. }
  202. return exports;
  203. }(window));
  204. // <-- end MonkeyPatch.
  205. // Create the Audio Context
  206. var audiocontext = new window.AudioContext();
  207. /**
  208. * <p>Returns the Audio Context for this sketch. Useful for users
  209. * who would like to dig deeper into the <a target='_blank' href=
  210. * 'http://webaudio.github.io/web-audio-api/'>Web Audio API
  211. * </a>.</p>
  212. *
  213. * @method getAudioContext
  214. * @return {Object} AudioContext for this sketch
  215. */
  216. p5.prototype.getAudioContext = function () {
  217. return audiocontext;
  218. };
  219. // Polyfill for AudioIn, also handled by p5.dom createCapture
  220. navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
  221. /**
  222. * Determine which filetypes are supported (inspired by buzz.js)
  223. * The audio element (el) will only be used to test browser support for various audio formats
  224. */
  225. var el = document.createElement('audio');
  226. p5.prototype.isSupported = function () {
  227. return !!el.canPlayType;
  228. };
  229. var isOGGSupported = function () {
  230. return !!el.canPlayType && el.canPlayType('audio/ogg; codecs="vorbis"');
  231. };
  232. var isMP3Supported = function () {
  233. return !!el.canPlayType && el.canPlayType('audio/mpeg;');
  234. };
  235. var isWAVSupported = function () {
  236. return !!el.canPlayType && el.canPlayType('audio/wav; codecs="1"');
  237. };
  238. var isAACSupported = function () {
  239. return !!el.canPlayType && (el.canPlayType('audio/x-m4a;') || el.canPlayType('audio/aac;'));
  240. };
  241. var isAIFSupported = function () {
  242. return !!el.canPlayType && el.canPlayType('audio/x-aiff;');
  243. };
  244. p5.prototype.isFileSupported = function (extension) {
  245. switch (extension.toLowerCase()) {
  246. case 'mp3':
  247. return isMP3Supported();
  248. case 'wav':
  249. return isWAVSupported();
  250. case 'ogg':
  251. return isOGGSupported();
  252. case 'aac', 'm4a', 'mp4':
  253. return isAACSupported();
  254. case 'aif', 'aiff':
  255. return isAIFSupported();
  256. default:
  257. return false;
  258. }
  259. };
  260. // if it is iOS, we have to have a user interaction to start Web Audio
  261. // http://paulbakaus.com/tutorials/html5/web-audio-on-ios/
  262. var iOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false;
  263. if (iOS) {
  264. var iosStarted = false;
  265. var startIOS = function () {
  266. if (iosStarted)
  267. return;
  268. // create empty buffer
  269. var buffer = audiocontext.createBuffer(1, 1, 22050);
  270. var source = audiocontext.createBufferSource();
  271. source.buffer = buffer;
  272. // connect to output (your speakers)
  273. source.connect(audiocontext.destination);
  274. // play the file
  275. source.start(0);
  276. console.log('start ios!');
  277. if (audiocontext.state === 'running') {
  278. iosStarted = true;
  279. }
  280. };
  281. document.addEventListener('touchend', startIOS, false);
  282. document.addEventListener('touchstart', startIOS, false);
  283. }
  284. }();
  285. var master;
  286. master = function () {
  287. 'use strict';
  288. /**
  289. * Master contains AudioContext and the master sound output.
  290. */
  291. var Master = function () {
  292. var audiocontext = p5.prototype.getAudioContext();
  293. this.input = audiocontext.createGain();
  294. this.output = audiocontext.createGain();
  295. //put a hard limiter on the output
  296. this.limiter = audiocontext.createDynamicsCompressor();
  297. this.limiter.threshold.value = 0;
  298. this.limiter.ratio.value = 20;
  299. this.audiocontext = audiocontext;
  300. this.output.disconnect();
  301. // an array of input sources
  302. this.inputSources = [];
  303. // connect input to limiter
  304. this.input.connect(this.limiter);
  305. // connect limiter to output
  306. this.limiter.connect(this.output);
  307. // meter is just for global Amplitude / FFT analysis
  308. this.meter = audiocontext.createGain();
  309. this.fftMeter = audiocontext.createGain();
  310. this.output.connect(this.meter);
  311. this.output.connect(this.fftMeter);
  312. // connect output to destination
  313. this.output.connect(this.audiocontext.destination);
  314. // an array of all sounds in the sketch
  315. this.soundArray = [];
  316. // an array of all musical parts in the sketch
  317. this.parts = [];
  318. // file extensions to search for
  319. this.extensions = [];
  320. };
  321. // create a single instance of the p5Sound / master output for use within this sketch
  322. var p5sound = new Master();
  323. /**
  324. * Returns a number representing the master amplitude (volume) for sound
  325. * in this sketch.
  326. *
  327. * @method getMasterVolume
  328. * @return {Number} Master amplitude (volume) for sound in this sketch.
  329. * Should be between 0.0 (silence) and 1.0.
  330. */
  331. p5.prototype.getMasterVolume = function () {
  332. return p5sound.output.gain.value;
  333. };
  334. /**
  335. * <p>Scale the output of all sound in this sketch</p>
  336. * Scaled between 0.0 (silence) and 1.0 (full volume).
  337. * 1.0 is the maximum amplitude of a digital sound, so multiplying
  338. * by greater than 1.0 may cause digital distortion. To
  339. * fade, provide a <code>rampTime</code> parameter. For more
  340. * complex fades, see the Env class.
  341. *
  342. * Alternately, you can pass in a signal source such as an
  343. * oscillator to modulate the amplitude with an audio signal.
  344. *
  345. * <p><b>How This Works</b>: When you load the p5.sound module, it
  346. * creates a single instance of p5sound. All sound objects in this
  347. * module output to p5sound before reaching your computer's output.
  348. * So if you change the amplitude of p5sound, it impacts all of the
  349. * sound in this module.</p>
  350. *
  351. * <p>If no value is provided, returns a Web Audio API Gain Node</p>
  352. *
  353. * @method masterVolume
  354. * @param {Number|Object} volume Volume (amplitude) between 0.0
  355. * and 1.0 or modulating signal/oscillator
  356. * @param {Number} [rampTime] Fade for t seconds
  357. * @param {Number} [timeFromNow] Schedule this event to happen at
  358. * t seconds in the future
  359. */
  360. p5.prototype.masterVolume = function (vol, rampTime, tFromNow) {
  361. if (typeof vol === 'number') {
  362. var rampTime = rampTime || 0;
  363. var tFromNow = tFromNow || 0;
  364. var now = p5sound.audiocontext.currentTime;
  365. var currentVol = p5sound.output.gain.value;
  366. p5sound.output.gain.cancelScheduledValues(now + tFromNow);
  367. p5sound.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
  368. p5sound.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
  369. } else if (vol) {
  370. vol.connect(p5sound.output.gain);
  371. } else {
  372. // return the Gain Node
  373. return p5sound.output.gain;
  374. }
  375. };
  376. /**
  377. * `p5.soundOut` is the p5.sound master output. It sends output to
  378. * the destination of this window's web audio context. It contains
  379. * Web Audio API nodes including a dyanmicsCompressor (<code>.limiter</code>),
  380. * and Gain Nodes for <code>.input</code> and <code>.output</code>.
  381. *
  382. * @property soundOut
  383. * @type {Object}
  384. */
  385. p5.prototype.soundOut = p5.soundOut = p5sound;
  386. /**
  387. * a silent connection to the DesinationNode
  388. * which will ensure that anything connected to it
  389. * will not be garbage collected
  390. *
  391. * @private
  392. */
  393. p5.soundOut._silentNode = p5sound.audiocontext.createGain();
  394. p5.soundOut._silentNode.gain.value = 0;
  395. p5.soundOut._silentNode.connect(p5sound.audiocontext.destination);
  396. return p5sound;
  397. }(sndcore);
  398. var helpers;
  399. helpers = function () {
  400. 'use strict';
  401. var p5sound = master;
  402. /**
  403. * Returns a number representing the sample rate, in samples per second,
  404. * of all sound objects in this audio context. It is determined by the
  405. * sampling rate of your operating system's sound card, and it is not
  406. * currently possile to change.
  407. * It is often 44100, or twice the range of human hearing.
  408. *
  409. * @method sampleRate
  410. * @return {Number} samplerate samples per second
  411. */
  412. p5.prototype.sampleRate = function () {
  413. return p5sound.audiocontext.sampleRate;
  414. };
  415. /**
  416. * Returns the closest MIDI note value for
  417. * a given frequency.
  418. *
  419. * @param {Number} frequency A freqeuncy, for example, the "A"
  420. * above Middle C is 440Hz
  421. * @return {Number} MIDI note value
  422. */
  423. p5.prototype.freqToMidi = function (f) {
  424. var mathlog2 = Math.log(f / 440) / Math.log(2);
  425. var m = Math.round(12 * mathlog2) + 57;
  426. return m;
  427. };
  428. /**
  429. * Returns the frequency value of a MIDI note value.
  430. * General MIDI treats notes as integers where middle C
  431. * is 60, C# is 61, D is 62 etc. Useful for generating
  432. * musical frequencies with oscillators.
  433. *
  434. * @method midiToFreq
  435. * @param {Number} midiNote The number of a MIDI note
  436. * @return {Number} Frequency value of the given MIDI note
  437. * @example
  438. * <div><code>
  439. * var notes = [60, 64, 67, 72];
  440. * var i = 0;
  441. *
  442. * function setup() {
  443. * osc = new p5.Oscillator('Triangle');
  444. * osc.start();
  445. * frameRate(1);
  446. * }
  447. *
  448. * function draw() {
  449. * var freq = midiToFreq(notes[i]);
  450. * osc.freq(freq);
  451. * i++;
  452. * if (i >= notes.length){
  453. * i = 0;
  454. * }
  455. * }
  456. * </code></div>
  457. */
  458. p5.prototype.midiToFreq = function (m) {
  459. return 440 * Math.pow(2, (m - 69) / 12);
  460. };
  461. /**
  462. * List the SoundFile formats that you will include. LoadSound
  463. * will search your directory for these extensions, and will pick
  464. * a format that is compatable with the client's web browser.
  465. * <a href="http://media.io/">Here</a> is a free online file
  466. * converter.
  467. *
  468. * @method soundFormats
  469. * @param {String} formats i.e. 'mp3', 'wav', 'ogg'
  470. * @example
  471. * <div><code>
  472. * function preload() {
  473. * // set the global sound formats
  474. * soundFormats('mp3', 'ogg');
  475. *
  476. * // load either beatbox.mp3, or .ogg, depending on browser
  477. * mySound = loadSound('../sounds/beatbox.mp3');
  478. * }
  479. *
  480. * function setup() {
  481. * mySound.play();
  482. * }
  483. * </code></div>
  484. */
  485. p5.prototype.soundFormats = function () {
  486. // reset extensions array
  487. p5sound.extensions = [];
  488. // add extensions
  489. for (var i = 0; i < arguments.length; i++) {
  490. arguments[i] = arguments[i].toLowerCase();
  491. if ([
  492. 'mp3',
  493. 'wav',
  494. 'ogg',
  495. 'm4a',
  496. 'aac'
  497. ].indexOf(arguments[i]) > -1) {
  498. p5sound.extensions.push(arguments[i]);
  499. } else {
  500. throw arguments[i] + ' is not a valid sound format!';
  501. }
  502. }
  503. };
  504. p5.prototype.disposeSound = function () {
  505. for (var i = 0; i < p5sound.soundArray.length; i++) {
  506. p5sound.soundArray[i].dispose();
  507. }
  508. };
  509. // register removeSound to dispose of p5sound SoundFiles, Convolvers,
  510. // Oscillators etc when sketch ends
  511. p5.prototype.registerMethod('remove', p5.prototype.disposeSound);
  512. p5.prototype._checkFileFormats = function (paths) {
  513. var path;
  514. // if path is a single string, check to see if extension is provided
  515. if (typeof paths === 'string') {
  516. path = paths;
  517. // see if extension is provided
  518. var extTest = path.split('.').pop();
  519. // if an extension is provided...
  520. if ([
  521. 'mp3',
  522. 'wav',
  523. 'ogg',
  524. 'm4a',
  525. 'aac'
  526. ].indexOf(extTest) > -1) {
  527. var supported = p5.prototype.isFileSupported(extTest);
  528. if (supported) {
  529. path = path;
  530. } else {
  531. var pathSplit = path.split('.');
  532. var pathCore = pathSplit[pathSplit.length - 1];
  533. for (var i = 0; i < p5sound.extensions.length; i++) {
  534. var extension = p5sound.extensions[i];
  535. var supported = p5.prototype.isFileSupported(extension);
  536. if (supported) {
  537. pathCore = '';
  538. if (pathSplit.length === 2) {
  539. pathCore += pathSplit[0];
  540. }
  541. for (var i = 1; i <= pathSplit.length - 2; i++) {
  542. var p = pathSplit[i];
  543. pathCore += '.' + p;
  544. }
  545. path = pathCore += '.';
  546. path = path += extension;
  547. break;
  548. }
  549. }
  550. }
  551. } else {
  552. for (var i = 0; i < p5sound.extensions.length; i++) {
  553. var extension = p5sound.extensions[i];
  554. var supported = p5.prototype.isFileSupported(extension);
  555. if (supported) {
  556. path = path + '.' + extension;
  557. break;
  558. }
  559. }
  560. }
  561. } else if (typeof paths === 'object') {
  562. for (var i = 0; i < paths.length; i++) {
  563. var extension = paths[i].split('.').pop();
  564. var supported = p5.prototype.isFileSupported(extension);
  565. if (supported) {
  566. // console.log('.'+extension + ' is ' + supported +
  567. // ' supported by your browser.');
  568. path = paths[i];
  569. break;
  570. }
  571. }
  572. }
  573. return path;
  574. };
  575. /**
  576. * Used by Osc and Env to chain signal math
  577. */
  578. p5.prototype._mathChain = function (o, math, thisChain, nextChain, type) {
  579. // if this type of math already exists in the chain, replace it
  580. for (var i in o.mathOps) {
  581. if (o.mathOps[i] instanceof type) {
  582. o.mathOps[i].dispose();
  583. thisChain = i;
  584. if (thisChain < o.mathOps.length - 1) {
  585. nextChain = o.mathOps[i + 1];
  586. }
  587. }
  588. }
  589. o.mathOps[thisChain - 1].disconnect();
  590. o.mathOps[thisChain - 1].connect(math);
  591. math.connect(nextChain);
  592. o.mathOps[thisChain] = math;
  593. return o;
  594. };
  595. }(master);
  596. var errorHandler;
  597. errorHandler = function () {
  598. 'use strict';
  599. /**
  600. * Helper function to generate an error
  601. * with a custom stack trace that points to the sketch
  602. * and removes other parts of the stack trace.
  603. *
  604. * @private
  605. *
  606. * @param {String} name custom error name
  607. * @param {String} errorTrace custom error trace
  608. * @param {String} failedPath path to the file that failed to load
  609. * @property {String} name custom error name
  610. * @property {String} message custom error message
  611. * @property {String} stack trace the error back to a line in the user's sketch.
  612. * Note: this edits out stack trace within p5.js and p5.sound.
  613. * @property {String} originalStack unedited, original stack trace
  614. * @property {String} failedPath path to the file that failed to load
  615. * @return {Error} returns a custom Error object
  616. */
  617. var CustomError = function (name, errorTrace, failedPath) {
  618. var err = new Error();
  619. var tempStack, splitStack;
  620. err.name = name;
  621. err.originalStack = err.stack + errorTrace;
  622. tempStack = err.stack + errorTrace;
  623. err.failedPath = failedPath;
  624. // only print the part of the stack trace that refers to the user code:
  625. var splitStack = tempStack.split('\n');
  626. splitStack = splitStack.filter(function (ln) {
  627. return !ln.match(/(p5.|native code|globalInit)/g);
  628. });
  629. err.stack = splitStack.join('\n');
  630. return err;
  631. };
  632. return CustomError;
  633. }();
  634. var panner;
  635. panner = function () {
  636. 'use strict';
  637. var p5sound = master;
  638. var ac = p5sound.audiocontext;
  639. // Stereo panner
  640. // if there is a stereo panner node use it
  641. if (typeof ac.createStereoPanner !== 'undefined') {
  642. p5.Panner = function (input, output, numInputChannels) {
  643. this.stereoPanner = this.input = ac.createStereoPanner();
  644. input.connect(this.stereoPanner);
  645. this.stereoPanner.connect(output);
  646. };
  647. p5.Panner.prototype.pan = function (val, tFromNow) {
  648. var time = tFromNow || 0;
  649. var t = ac.currentTime + time;
  650. this.stereoPanner.pan.linearRampToValueAtTime(val, t);
  651. };
  652. p5.Panner.prototype.inputChannels = function (numChannels) {
  653. };
  654. p5.Panner.prototype.connect = function (obj) {
  655. this.stereoPanner.connect(obj);
  656. };
  657. p5.Panner.prototype.disconnect = function (obj) {
  658. this.stereoPanner.disconnect();
  659. };
  660. } else {
  661. // if there is no createStereoPanner object
  662. // such as in safari 7.1.7 at the time of writing this
  663. // use this method to create the effect
  664. p5.Panner = function (input, output, numInputChannels) {
  665. this.input = ac.createGain();
  666. input.connect(this.input);
  667. this.left = ac.createGain();
  668. this.right = ac.createGain();
  669. this.left.channelInterpretation = 'discrete';
  670. this.right.channelInterpretation = 'discrete';
  671. // if input is stereo
  672. if (numInputChannels > 1) {
  673. this.splitter = ac.createChannelSplitter(2);
  674. this.input.connect(this.splitter);
  675. this.splitter.connect(this.left, 1);
  676. this.splitter.connect(this.right, 0);
  677. } else {
  678. this.input.connect(this.left);
  679. this.input.connect(this.right);
  680. }
  681. this.output = ac.createChannelMerger(2);
  682. this.left.connect(this.output, 0, 1);
  683. this.right.connect(this.output, 0, 0);
  684. this.output.connect(output);
  685. };
  686. // -1 is left, +1 is right
  687. p5.Panner.prototype.pan = function (val, tFromNow) {
  688. var time = tFromNow || 0;
  689. var t = ac.currentTime + time;
  690. var v = (val + 1) / 2;
  691. var rightVal = Math.cos(v * Math.PI / 2);
  692. var leftVal = Math.sin(v * Math.PI / 2);
  693. this.left.gain.linearRampToValueAtTime(leftVal, t);
  694. this.right.gain.linearRampToValueAtTime(rightVal, t);
  695. };
  696. p5.Panner.prototype.inputChannels = function (numChannels) {
  697. if (numChannels === 1) {
  698. this.input.disconnect();
  699. this.input.connect(this.left);
  700. this.input.connect(this.right);
  701. } else if (numChannels === 2) {
  702. if (typeof (this.splitter === 'undefined')) {
  703. this.splitter = ac.createChannelSplitter(2);
  704. }
  705. this.input.disconnect();
  706. this.input.connect(this.splitter);
  707. this.splitter.connect(this.left, 1);
  708. this.splitter.connect(this.right, 0);
  709. }
  710. };
  711. p5.Panner.prototype.connect = function (obj) {
  712. this.output.connect(obj);
  713. };
  714. p5.Panner.prototype.disconnect = function (obj) {
  715. this.output.disconnect();
  716. };
  717. }
  718. // 3D panner
  719. p5.Panner3D = function (input, output) {
  720. var panner3D = ac.createPanner();
  721. panner3D.panningModel = 'HRTF';
  722. panner3D.distanceModel = 'linear';
  723. panner3D.setPosition(0, 0, 0);
  724. input.connect(panner3D);
  725. panner3D.connect(output);
  726. panner3D.pan = function (xVal, yVal, zVal) {
  727. panner3D.setPosition(xVal, yVal, zVal);
  728. };
  729. return panner3D;
  730. };
  731. }(master);
  732. var soundfile;
  733. soundfile = function () {
  734. 'use strict';
  735. var CustomError = errorHandler;
  736. var p5sound = master;
  737. var ac = p5sound.audiocontext;
  738. /**
  739. * <p>SoundFile object with a path to a file.</p>
  740. *
  741. * <p>The p5.SoundFile may not be available immediately because
  742. * it loads the file information asynchronously.</p>
  743. *
  744. * <p>To do something with the sound as soon as it loads
  745. * pass the name of a function as the second parameter.</p>
  746. *
  747. * <p>Only one file path is required. However, audio file formats
  748. * (i.e. mp3, ogg, wav and m4a/aac) are not supported by all
  749. * web browsers. If you want to ensure compatability, instead of a single
  750. * file path, you may include an Array of filepaths, and the browser will
  751. * choose a format that works.</p>
  752. *
  753. * @class p5.SoundFile
  754. * @constructor
  755. * @param {String|Array} path path to a sound file (String). Optionally,
  756. * you may include multiple file formats in
  757. * an array. Alternately, accepts an object
  758. * from the HTML5 File API, or a p5.File.
  759. * @param {Function} [successCallback] Name of a function to call once file loads
  760. * @param {Function} [errorCallback] Name of a function to call if file fails to
  761. * load. This function will receive an error or
  762. * XMLHttpRequest object with information
  763. * about what went wrong.
  764. * @param {Function} [whileLoadingCallback] Name of a function to call while file
  765. * is loading. That function will
  766. * receive progress of the request to
  767. * load the sound file
  768. * (between 0 and 1) as its first
  769. * parameter. This progress
  770. * does not account for the additional
  771. * time needed to decode the audio data.
  772. *
  773. * @return {Object} p5.SoundFile Object
  774. * @example
  775. * <div><code>
  776. *
  777. * function preload() {
  778. * mySound = loadSound('assets/doorbell.mp3');
  779. * }
  780. *
  781. * function setup() {
  782. * mySound.setVolume(0.1);
  783. * mySound.play();
  784. * }
  785. *
  786. * </code></div>
  787. */
  788. p5.SoundFile = function (paths, onload, onerror, whileLoading) {
  789. if (typeof paths !== 'undefined') {
  790. if (typeof paths == 'string' || typeof paths[0] == 'string') {
  791. var path = p5.prototype._checkFileFormats(paths);
  792. this.url = path;
  793. } else if (typeof paths == 'object') {
  794. if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
  795. // The File API isn't supported in this browser
  796. throw 'Unable to load file because the File API is not supported';
  797. }
  798. }
  799. // if type is a p5.File...get the actual file
  800. if (paths.file) {
  801. paths = paths.file;
  802. }
  803. this.file = paths;
  804. }
  805. // private _onended callback, set by the method: onended(callback)
  806. this._onended = function () {
  807. };
  808. this._looping = false;
  809. this._playing = false;
  810. this._paused = false;
  811. this._pauseTime = 0;
  812. // cues for scheduling events with addCue() removeCue()
  813. this._cues = [];
  814. // position of the most recently played sample
  815. this._lastPos = 0;
  816. this._counterNode;
  817. this._scopeNode;
  818. // array of sources so that they can all be stopped!
  819. this.bufferSourceNodes = [];
  820. // current source
  821. this.bufferSourceNode = null;
  822. this.buffer = null;
  823. this.playbackRate = 1;
  824. this.gain = 1;
  825. this.input = p5sound.audiocontext.createGain();
  826. this.output = p5sound.audiocontext.createGain();
  827. this.reversed = false;
  828. // start and end of playback / loop
  829. this.startTime = 0;
  830. this.endTime = null;
  831. this.pauseTime = 0;
  832. // "restart" would stop playback before retriggering
  833. this.mode = 'sustain';
  834. // time that playback was started, in millis
  835. this.startMillis = null;
  836. // stereo panning
  837. this.panPosition = 0;
  838. this.panner = new p5.Panner(this.output, p5sound.input, 2);
  839. // it is possible to instantiate a soundfile with no path
  840. if (this.url || this.file) {
  841. this.load(onload, onerror);
  842. }
  843. // add this p5.SoundFile to the soundArray
  844. p5sound.soundArray.push(this);
  845. if (typeof whileLoading === 'function') {
  846. this._whileLoading = whileLoading;
  847. } else {
  848. this._whileLoading = function () {
  849. };
  850. }
  851. };
  852. // register preload handling of loadSound
  853. p5.prototype.registerPreloadMethod('loadSound', p5.prototype);
  854. /**
  855. * loadSound() returns a new p5.SoundFile from a specified
  856. * path. If called during preload(), the p5.SoundFile will be ready
  857. * to play in time for setup() and draw(). If called outside of
  858. * preload, the p5.SoundFile will not be ready immediately, so
  859. * loadSound accepts a callback as the second parameter. Using a
  860. * <a href="https://github.com/processing/p5.js/wiki/Local-server">
  861. * local server</a> is recommended when loading external files.
  862. *
  863. * @method loadSound
  864. * @param {String|Array} path Path to the sound file, or an array with
  865. * paths to soundfiles in multiple formats
  866. * i.e. ['sound.ogg', 'sound.mp3'].
  867. * Alternately, accepts an object: either
  868. * from the HTML5 File API, or a p5.File.
  869. * @param {Function} [successCallback] Name of a function to call once file loads
  870. * @param {Function} [errorCallback] Name of a function to call if there is
  871. * an error loading the file.
  872. * @param {Function} [whileLoading] Name of a function to call while file is loading.
  873. * This function will receive the percentage loaded
  874. * so far, from 0.0 to 1.0.
  875. * @return {SoundFile} Returns a p5.SoundFile
  876. * @example
  877. * <div><code>
  878. * function preload() {
  879. * mySound = loadSound('assets/doorbell.mp3');
  880. * }
  881. *
  882. * function setup() {
  883. * mySound.setVolume(0.1);
  884. * mySound.play();
  885. * }
  886. * </code></div>
  887. */
  888. p5.prototype.loadSound = function (path, callback, onerror, whileLoading) {
  889. // if loading locally without a server
  890. if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
  891. alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
  892. }
  893. var s = new p5.SoundFile(path, callback, onerror, whileLoading);
  894. return s;
  895. };
  896. /**
  897. * This is a helper function that the p5.SoundFile calls to load
  898. * itself. Accepts a callback (the name of another function)
  899. * as an optional parameter.
  900. *
  901. * @private
  902. * @param {Function} [successCallback] Name of a function to call once file loads
  903. * @param {Function} [errorCallback] Name of a function to call if there is an error
  904. */
  905. p5.SoundFile.prototype.load = function (callback, errorCallback) {
  906. var loggedError = false;
  907. var self = this;
  908. var errorTrace = new Error().stack;
  909. if (this.url != undefined && this.url != '') {
  910. var request = new XMLHttpRequest();
  911. request.addEventListener('progress', function (evt) {
  912. self._updateProgress(evt);
  913. }, false);
  914. request.open('GET', this.url, true);
  915. request.responseType = 'arraybuffer';
  916. request.onload = function () {
  917. if (request.status == 200) {
  918. // on sucess loading file:
  919. ac.decodeAudioData(request.response, // success decoding buffer:
  920. function (buff) {
  921. self.buffer = buff;
  922. self.panner.inputChannels(buff.numberOfChannels);
  923. if (callback) {
  924. callback(self);
  925. }
  926. }, // error decoding buffer. "e" is undefined in Chrome 11/22/2015
  927. function (e) {
  928. var err = new CustomError('decodeAudioData', errorTrace, self.url);
  929. var msg = 'AudioContext error at decodeAudioData for ' + self.url;
  930. if (errorCallback) {
  931. err.msg = msg;
  932. errorCallback(err);
  933. } else {
  934. console.error(msg + '\n The error stack trace includes: \n' + err.stack);
  935. }
  936. });
  937. } else {
  938. var err = new CustomError('loadSound', errorTrace, self.url);
  939. var msg = 'Unable to load ' + self.url + '. The request status was: ' + request.status + ' (' + request.statusText + ')';
  940. if (errorCallback) {
  941. err.message = msg;
  942. errorCallback(err);
  943. } else {
  944. console.error(msg + '\n The error stack trace includes: \n' + err.stack);
  945. }
  946. }
  947. };
  948. // if there is another error, aside from 404...
  949. request.onerror = function (e) {
  950. var err = new CustomError('loadSound', errorTrace, self.url);
  951. var msg = 'There was no response from the server at ' + self.url + '. Check the url and internet connectivity.';
  952. if (errorCallback) {
  953. err.message = msg;
  954. errorCallback(err);
  955. } else {
  956. console.error(msg + '\n The error stack trace includes: \n' + err.stack);
  957. }
  958. };
  959. request.send();
  960. } else if (this.file != undefined) {
  961. var reader = new FileReader();
  962. var self = this;
  963. reader.onload = function () {
  964. ac.decodeAudioData(reader.result, function (buff) {
  965. self.buffer = buff;
  966. self.panner.inputChannels(buff.numberOfChannels);
  967. if (callback) {
  968. callback(self);
  969. }
  970. });
  971. };
  972. reader.onerror = function (e) {
  973. if (onerror)
  974. onerror(e);
  975. };
  976. reader.readAsArrayBuffer(this.file);
  977. }
  978. };
  979. // TO DO: use this method to create a loading bar that shows progress during file upload/decode.
  980. p5.SoundFile.prototype._updateProgress = function (evt) {
  981. if (evt.lengthComputable) {
  982. var percentComplete = evt.loaded / evt.total * 0.99;
  983. this._whileLoading(percentComplete, evt);
  984. } else {
  985. // Unable to compute progress information since the total size is unknown
  986. this._whileLoading('size unknown');
  987. }
  988. };
  989. /**
  990. * Returns true if the sound file finished loading successfully.
  991. *
  992. * @method isLoaded
  993. * @return {Boolean}
  994. */
  995. p5.SoundFile.prototype.isLoaded = function () {
  996. if (this.buffer) {
  997. return true;
  998. } else {
  999. return false;
  1000. }
  1001. };
  1002. /**
  1003. * Play the p5.SoundFile
  1004. *
  1005. * @method play
  1006. * @param {Number} [startTime] (optional) schedule playback to start (in seconds from now).
  1007. * @param {Number} [rate] (optional) playback rate
  1008. * @param {Number} [amp] (optional) amplitude (volume)
  1009. * of playback
  1010. * @param {Number} [cueStart] (optional) cue start time in seconds
  1011. * @param {Number} [duration] (optional) duration of playback in seconds
  1012. */
  1013. p5.SoundFile.prototype.play = function (time, rate, amp, _cueStart, duration) {
  1014. var self = this;
  1015. var now = p5sound.audiocontext.currentTime;
  1016. var cueStart, cueEnd;
  1017. var time = time || 0;
  1018. if (time < 0) {
  1019. time = 0;
  1020. }
  1021. time = time + now;
  1022. // TO DO: if already playing, create array of buffers for easy stop()
  1023. if (this.buffer) {
  1024. // reset the pause time (if it was paused)
  1025. this._pauseTime = 0;
  1026. // handle restart playmode
  1027. if (this.mode === 'restart' && this.buffer && this.bufferSourceNode) {
  1028. var now = p5sound.audiocontext.currentTime;
  1029. this.bufferSourceNode.stop(time);
  1030. this._counterNode.stop(time);
  1031. }
  1032. // set playback rate
  1033. if (rate)
  1034. this.playbackRate = rate;
  1035. // make a new source and counter. They are automatically assigned playbackRate and buffer
  1036. this.bufferSourceNode = this._initSourceNode();
  1037. // garbage collect counterNode and create a new one
  1038. if (this._counterNode)
  1039. this._counterNode = undefined;
  1040. this._counterNode = this._initCounterNode();
  1041. if (_cueStart) {
  1042. if (_cueStart >= 0 && _cueStart < this.buffer.duration) {
  1043. // this.startTime = cueStart;
  1044. cueStart = _cueStart;
  1045. } else {
  1046. throw 'start time out of range';
  1047. }
  1048. } else {
  1049. cueStart = 0;
  1050. }
  1051. if (duration) {
  1052. // if duration is greater than buffer.duration, just play entire file anyway rather than throw an error
  1053. duration = duration <= this.buffer.duration - cueStart ? duration : this.buffer.duration;
  1054. } else {
  1055. duration = this.buffer.duration - cueStart;
  1056. }
  1057. // TO DO: Fix this. It broke in Safari
  1058. //
  1059. // method of controlling gain for individual bufferSourceNodes, without resetting overall soundfile volume
  1060. // if (typeof(this.bufferSourceNode.gain === 'undefined' ) ) {
  1061. // this.bufferSourceNode.gain = p5sound.audiocontext.createGain();
  1062. // }
  1063. // this.bufferSourceNode.connect(this.bufferSourceNode.gain);
  1064. // set local amp if provided, otherwise 1
  1065. var a = amp || 1;
  1066. // this.bufferSourceNode.gain.gain.setValueAtTime(a, p5sound.audiocontext.currentTime);
  1067. // this.bufferSourceNode.gain.connect(this.output);
  1068. this.bufferSourceNode.connect(this.output);
  1069. this.output.gain.value = a;
  1070. // if it was paused, play at the pause position
  1071. if (this._paused) {
  1072. this.bufferSourceNode.start(time, this.pauseTime, duration);
  1073. this._counterNode.start(time, this.pauseTime, duration);
  1074. } else {
  1075. this.bufferSourceNode.start(time, cueStart, duration);
  1076. this._counterNode.start(time, cueStart, duration);
  1077. }
  1078. this._playing = true;
  1079. this._paused = false;
  1080. // add source to sources array, which is used in stopAll()
  1081. this.bufferSourceNodes.push(this.bufferSourceNode);
  1082. this.bufferSourceNode._arrayIndex = this.bufferSourceNodes.length - 1;
  1083. // delete this.bufferSourceNode from the sources array when it is done playing:
  1084. var clearOnEnd = function (e) {
  1085. this._playing = false;
  1086. this.removeEventListener('ended', clearOnEnd, false);
  1087. // call the onended callback
  1088. self._onended(self);
  1089. self.bufferSourceNodes.forEach(function (n, i) {
  1090. if (n._playing === false) {
  1091. self.bufferSourceNodes.splice(i);
  1092. }
  1093. });
  1094. if (self.bufferSourceNodes.length === 0) {
  1095. self._playing = false;
  1096. }
  1097. };
  1098. this.bufferSourceNode.onended = clearOnEnd;
  1099. } else {
  1100. throw 'not ready to play file, buffer has yet to load. Try preload()';
  1101. }
  1102. // if looping, will restart at original time
  1103. this.bufferSourceNode.loop = this._looping;
  1104. this._counterNode.loop = this._looping;
  1105. if (this._looping === true) {
  1106. var cueEnd = cueStart + duration;
  1107. this.bufferSourceNode.loopStart = cueStart;
  1108. this.bufferSourceNode.loopEnd = cueEnd;
  1109. this._counterNode.loopStart = cueStart;
  1110. this._counterNode.loopEnd = cueEnd;
  1111. }
  1112. };
  1113. /**
  1114. * p5.SoundFile has two play modes: <code>restart</code> and
  1115. * <code>sustain</code>. Play Mode determines what happens to a
  1116. * p5.SoundFile if it is triggered while in the middle of playback.
  1117. * In sustain mode, playback will continue simultaneous to the
  1118. * new playback. In restart mode, play() will stop playback
  1119. * and start over. Sustain is the default mode.
  1120. *
  1121. * @method playMode
  1122. * @param {String} str 'restart' or 'sustain'
  1123. * @example
  1124. * <div><code>
  1125. * function setup(){
  1126. * mySound = loadSound('assets/Damscray_DancingTiger.mp3');
  1127. * }
  1128. * function mouseClicked() {
  1129. * mySound.playMode('sustain');
  1130. * mySound.play();
  1131. * }
  1132. * function keyPressed() {
  1133. * mySound.playMode('restart');
  1134. * mySound.play();
  1135. * }
  1136. *
  1137. * </code></div>
  1138. */
  1139. p5.SoundFile.prototype.playMode = function (str) {
  1140. var s = str.toLowerCase();
  1141. // if restart, stop all other sounds from playing
  1142. if (s === 'restart' && this.buffer && this.bufferSourceNode) {
  1143. for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) {
  1144. var now = p5sound.audiocontext.currentTime;
  1145. this.bufferSourceNodes[i].stop(now);
  1146. }
  1147. }
  1148. // set play mode to effect future playback
  1149. if (s === 'restart' || s === 'sustain') {
  1150. this.mode = s;
  1151. } else {
  1152. throw 'Invalid play mode. Must be either "restart" or "sustain"';
  1153. }
  1154. };
  1155. /**
  1156. * Pauses a file that is currently playing. If the file is not
  1157. * playing, then nothing will happen.
  1158. *
  1159. * After pausing, .play() will resume from the paused
  1160. * position.
  1161. * If p5.SoundFile had been set to loop before it was paused,
  1162. * it will continue to loop after it is unpaused with .play().
  1163. *
  1164. * @method pause
  1165. * @param {Number} [startTime] (optional) schedule event to occur
  1166. * seconds from now
  1167. * @example
  1168. * <div><code>
  1169. * var soundFile;
  1170. *
  1171. * function preload() {
  1172. * soundFormats('ogg', 'mp3');
  1173. * soundFile = loadSound('assets/Damscray_-_Dancing_Tiger_02.mp3');
  1174. * }
  1175. * function setup() {
  1176. * background(0, 255, 0);
  1177. * soundFile.setVolume(0.1);
  1178. * soundFile.loop();
  1179. * }
  1180. * function keyTyped() {
  1181. * if (key == 'p') {
  1182. * soundFile.pause();
  1183. * background(255, 0, 0);
  1184. * }
  1185. * }
  1186. *
  1187. * function keyReleased() {
  1188. * if (key == 'p') {
  1189. * soundFile.play();
  1190. * background(0, 255, 0);
  1191. * }
  1192. * }
  1193. * </code>
  1194. * </div>
  1195. */
  1196. p5.SoundFile.prototype.pause = function (time) {
  1197. var now = p5sound.audiocontext.currentTime;
  1198. var time = time || 0;
  1199. var pTime = time + now;
  1200. if (this.isPlaying() && this.buffer && this.bufferSourceNode) {
  1201. this.pauseTime = this.currentTime();
  1202. this.bufferSourceNode.stop(pTime);
  1203. this._counterNode.stop(pTime);
  1204. this._paused = true;
  1205. this._playing = false;
  1206. this._pauseTime = this.currentTime();
  1207. } else {
  1208. this._pauseTime = 0;
  1209. }
  1210. };
  1211. /**
  1212. * Loop the p5.SoundFile. Accepts optional parameters to set the
  1213. * playback rate, playback volume, loopStart, loopEnd.
  1214. *
  1215. * @method loop
  1216. * @param {Number} [startTime] (optional) schedule event to occur
  1217. * seconds from now
  1218. * @param {Number} [rate] (optional) playback rate
  1219. * @param {Number} [amp] (optional) playback volume
  1220. * @param {Number} [cueLoopStart](optional) startTime in seconds
  1221. * @param {Number} [duration] (optional) loop duration in seconds
  1222. */
  1223. p5.SoundFile.prototype.loop = function (startTime, rate, amp, loopStart, duration) {
  1224. this._looping = true;
  1225. this.play(startTime, rate, amp, loopStart, duration);
  1226. };
  1227. /**
  1228. * Set a p5.SoundFile's looping flag to true or false. If the sound
  1229. * is currently playing, this change will take effect when it
  1230. * reaches the end of the current playback.
  1231. *
  1232. * @param {Boolean} Boolean set looping to true or false
  1233. */
  1234. p5.SoundFile.prototype.setLoop = function (bool) {
  1235. if (bool === true) {
  1236. this._looping = true;
  1237. } else if (bool === false) {
  1238. this._looping = false;
  1239. } else {
  1240. throw 'Error: setLoop accepts either true or false';
  1241. }
  1242. if (this.bufferSourceNode) {
  1243. this.bufferSourceNode.loop = this._looping;
  1244. this._counterNode.loop = this._looping;
  1245. }
  1246. };
  1247. /**
  1248. * Returns 'true' if a p5.SoundFile is currently looping and playing, 'false' if not.
  1249. *
  1250. * @return {Boolean}
  1251. */
  1252. p5.SoundFile.prototype.isLooping = function () {
  1253. if (!this.bufferSourceNode) {
  1254. return false;
  1255. }
  1256. if (this._looping === true && this.isPlaying() === true) {
  1257. return true;
  1258. }
  1259. return false;
  1260. };
  1261. /**
  1262. * Returns true if a p5.SoundFile is playing, false if not (i.e.
  1263. * paused or stopped).
  1264. *
  1265. * @method isPlaying
  1266. * @return {Boolean}
  1267. */
  1268. p5.SoundFile.prototype.isPlaying = function () {
  1269. return this._playing;
  1270. };
  1271. /**
  1272. * Returns true if a p5.SoundFile is paused, false if not (i.e.
  1273. * playing or stopped).
  1274. *
  1275. * @method isPaused
  1276. * @return {Boolean}
  1277. */
  1278. p5.SoundFile.prototype.isPaused = function () {
  1279. return this._paused;
  1280. };
  1281. /**
  1282. * Stop soundfile playback.
  1283. *
  1284. * @method stop
  1285. * @param {Number} [startTime] (optional) schedule event to occur
  1286. * in seconds from now
  1287. */
  1288. p5.SoundFile.prototype.stop = function (timeFromNow) {
  1289. var time = timeFromNow || 0;
  1290. if (this.mode == 'sustain') {
  1291. this.stopAll(time);
  1292. this._playing = false;
  1293. this.pauseTime = 0;
  1294. this._paused = false;
  1295. } else if (this.buffer && this.bufferSourceNode) {
  1296. var now = p5sound.audiocontext.currentTime;
  1297. var t = time || 0;
  1298. this.pauseTime = 0;
  1299. this.bufferSourceNode.stop(now + t);
  1300. this._counterNode.stop(now + t);
  1301. this._playing = false;
  1302. this._paused = false;
  1303. }
  1304. };
  1305. /**
  1306. * Stop playback on all of this soundfile's sources.
  1307. * @private
  1308. */
  1309. p5.SoundFile.prototype.stopAll = function (_time) {
  1310. var now = p5sound.audiocontext.currentTime;
  1311. var time = _time || 0;
  1312. if (this.buffer && this.bufferSourceNode) {
  1313. for (var i = 0; i < this.bufferSourceNodes.length; i++) {
  1314. if (typeof this.bufferSourceNodes[i] != undefined) {
  1315. try {
  1316. this.bufferSourceNodes[i].onended = function () {
  1317. };
  1318. this.bufferSourceNodes[i].stop(now + time);
  1319. } catch (e) {
  1320. }
  1321. }
  1322. }
  1323. this._counterNode.stop(now + time);
  1324. this._onended(this);
  1325. }
  1326. };
  1327. /**
  1328. * Multiply the output volume (amplitude) of a sound file
  1329. * between 0.0 (silence) and 1.0 (full volume).
  1330. * 1.0 is the maximum amplitude of a digital sound, so multiplying
  1331. * by greater than 1.0 may cause digital distortion. To
  1332. * fade, provide a <code>rampTime</code> parameter. For more
  1333. * complex fades, see the Env class.
  1334. *
  1335. * Alternately, you can pass in a signal source such as an
  1336. * oscillator to modulate the amplitude with an audio signal.
  1337. *
  1338. * @method setVolume
  1339. * @param {Number|Object} volume Volume (amplitude) between 0.0
  1340. * and 1.0 or modulating signal/oscillator
  1341. * @param {Number} [rampTime] Fade for t seconds
  1342. * @param {Number} [timeFromNow] Schedule this event to happen at
  1343. * t seconds in the future
  1344. */
  1345. p5.SoundFile.prototype.setVolume = function (vol, rampTime, tFromNow) {
  1346. if (typeof vol === 'number') {
  1347. var rampTime = rampTime || 0;
  1348. var tFromNow = tFromNow || 0;
  1349. var now = p5sound.audiocontext.currentTime;
  1350. var currentVol = this.output.gain.value;
  1351. this.output.gain.cancelScheduledValues(now + tFromNow);
  1352. this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
  1353. this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
  1354. } else if (vol) {
  1355. vol.connect(this.output.gain);
  1356. } else {
  1357. // return the Gain Node
  1358. return this.output.gain;
  1359. }
  1360. };
  1361. // same as setVolume, to match Processing Sound
  1362. p5.SoundFile.prototype.amp = p5.SoundFile.prototype.setVolume;
  1363. // these are the same thing
  1364. p5.SoundFile.prototype.fade = p5.SoundFile.prototype.setVolume;
  1365. p5.SoundFile.prototype.getVolume = function () {
  1366. return this.output.gain.value;
  1367. };
  1368. /**
  1369. * Set the stereo panning of a p5.sound object to
  1370. * a floating point number between -1.0 (left) and 1.0 (right).
  1371. * Default is 0.0 (center).
  1372. *
  1373. * @method pan
  1374. * @param {Number} [panValue] Set the stereo panner
  1375. * @param {Number} timeFromNow schedule this event to happen
  1376. * seconds from now
  1377. * @example
  1378. * <div><code>
  1379. *
  1380. * var ball = {};
  1381. * var soundFile;
  1382. *
  1383. * function setup() {
  1384. * soundFormats('ogg', 'mp3');
  1385. * soundFile = loadSound('assets/beatbox.mp3');
  1386. * }
  1387. *
  1388. * function draw() {
  1389. * background(0);
  1390. * ball.x = constrain(mouseX, 0, width);
  1391. * ellipse(ball.x, height/2, 20, 20)
  1392. * }
  1393. *
  1394. * function mousePressed(){
  1395. * // map the ball's x location to a panning degree
  1396. * // between -1.0 (left) and 1.0 (right)
  1397. * var panning = map(ball.x, 0., width,-1.0, 1.0);
  1398. * soundFile.pan(panning);
  1399. * soundFile.play();
  1400. * }
  1401. * </div></code>
  1402. */
  1403. p5.SoundFile.prototype.pan = function (pval, tFromNow) {
  1404. this.panPosition = pval;
  1405. this.panner.pan(pval, tFromNow);
  1406. };
  1407. /**
  1408. * Returns the current stereo pan position (-1.0 to 1.0)
  1409. *
  1410. * @return {Number} Returns the stereo pan setting of the Oscillator
  1411. * as a number between -1.0 (left) and 1.0 (right).
  1412. * 0.0 is center and default.
  1413. */
  1414. p5.SoundFile.prototype.getPan = function () {
  1415. return this.panPosition;
  1416. };
  1417. /**
  1418. * Set the playback rate of a sound file. Will change the speed and the pitch.
  1419. * Values less than zero will reverse the audio buffer.
  1420. *
  1421. * @method rate
  1422. * @param {Number} [playbackRate] Set the playback rate. 1.0 is normal,
  1423. * .5 is half-speed, 2.0 is twice as fast.
  1424. * Values less than zero play backwards.
  1425. * @example
  1426. * <div><code>
  1427. * var song;
  1428. *
  1429. * function preload() {
  1430. * song = loadSound('assets/Damscray_DancingTiger.mp3');
  1431. * }
  1432. *
  1433. * function setup() {
  1434. * song.loop();
  1435. * }
  1436. *
  1437. * function draw() {
  1438. * background(200);
  1439. *
  1440. * // Set the rate to a range between 0.1 and 4
  1441. * // Changing the rate also alters the pitch
  1442. * var speed = map(mouseY, 0.1, height, 0, 2);
  1443. * speed = constrain(speed, 0.01, 4);
  1444. * song.rate(speed);
  1445. *
  1446. * // Draw a circle to show what is going on
  1447. * stroke(0);
  1448. * fill(51, 100);
  1449. * ellipse(mouseX, 100, 48, 48);
  1450. * }
  1451. *
  1452. * </code>
  1453. * </div>
  1454. *
  1455. */
  1456. p5.SoundFile.prototype.rate = function (playbackRate) {
  1457. if (this.playbackRate === playbackRate && this.bufferSourceNode) {
  1458. if (this.bufferSourceNode.playbackRate.value === playbackRate) {
  1459. return;
  1460. }
  1461. }
  1462. this.playbackRate = playbackRate;
  1463. var rate = playbackRate;
  1464. if (this.playbackRate === 0 && this._playing) {
  1465. this.pause();
  1466. }
  1467. if (this.playbackRate < 0 && !this.reversed) {
  1468. var cPos = this.currentTime();
  1469. var cRate = this.bufferSourceNode.playbackRate.value;
  1470. // this.pause();
  1471. this.reverseBuffer();
  1472. rate = Math.abs(playbackRate);
  1473. var newPos = (cPos - this.duration()) / rate;
  1474. this.pauseTime = newPos;
  1475. } else if (this.playbackRate > 0 && this.reversed) {
  1476. this.reverseBuffer();
  1477. }
  1478. if (this.bufferSourceNode) {
  1479. var now = p5sound.audiocontext.currentTime;
  1480. this.bufferSourceNode.playbackRate.cancelScheduledValues(now);
  1481. this.bufferSourceNode.playbackRate.linearRampToValueAtTime(Math.abs(rate), now);
  1482. this._counterNode.playbackRate.cancelScheduledValues(now);
  1483. this._counterNode.playbackRate.linearRampToValueAtTime(Math.abs(rate), now);
  1484. }
  1485. };
  1486. // TO DO: document this
  1487. p5.SoundFile.prototype.setPitch = function (num) {
  1488. var newPlaybackRate = midiToFreq(num) / midiToFreq(60);
  1489. this.rate(newPlaybackRate);
  1490. };
  1491. p5.SoundFile.prototype.getPlaybackRate = function () {
  1492. return this.playbackRate;
  1493. };
  1494. /**
  1495. * Returns the duration of a sound file in seconds.
  1496. *
  1497. * @method duration
  1498. * @return {Number} The duration of the soundFile in seconds.
  1499. */
  1500. p5.SoundFile.prototype.duration = function () {
  1501. // Return Duration
  1502. if (this.buffer) {
  1503. return this.buffer.duration;
  1504. } else {
  1505. return 0;
  1506. }
  1507. };
  1508. /**
  1509. * Return the current position of the p5.SoundFile playhead, in seconds.
  1510. * Note that if you change the playbackRate while the p5.SoundFile is
  1511. * playing, the results may not be accurate.
  1512. *
  1513. * @method currentTime
  1514. * @return {Number} currentTime of the soundFile in seconds.
  1515. */
  1516. p5.SoundFile.prototype.currentTime = function () {
  1517. // TO DO --> make reverse() flip these values appropriately
  1518. if (this._pauseTime > 0) {
  1519. return this._pauseTime;
  1520. } else {
  1521. return this._lastPos / ac.sampleRate;
  1522. }
  1523. };
  1524. /**
  1525. * Move the playhead of the song to a position, in seconds. Start
  1526. * and Stop time. If none are given, will reset the file to play
  1527. * entire duration from start to finish.
  1528. *
  1529. * @method jump
  1530. * @param {Number} cueTime cueTime of the soundFile in seconds.
  1531. * @param {Number} duration duration in seconds.
  1532. */
  1533. p5.SoundFile.prototype.jump = function (cueTime, duration) {
  1534. if (cueTime < 0 || cueTime > this.buffer.duration) {
  1535. throw 'jump time out of range';
  1536. }
  1537. if (duration > this.buffer.duration - cueTime) {
  1538. throw 'end time out of range';
  1539. }
  1540. var cTime = cueTime || 0;
  1541. var eTime = duration || this.buffer.duration - cueTime;
  1542. if (this.isPlaying()) {
  1543. this.stop();
  1544. }
  1545. this.play(0, this.playbackRate, this.output.gain.value, cTime, eTime);
  1546. };
  1547. /**
  1548. * Return the number of channels in a sound file.
  1549. * For example, Mono = 1, Stereo = 2.
  1550. *
  1551. * @method channels
  1552. * @return {Number} [channels]
  1553. */
  1554. p5.SoundFile.prototype.channels = function () {
  1555. return this.buffer.numberOfChannels;
  1556. };
  1557. /**
  1558. * Return the sample rate of the sound file.
  1559. *
  1560. * @method sampleRate
  1561. * @return {Number} [sampleRate]
  1562. */
  1563. p5.SoundFile.prototype.sampleRate = function () {
  1564. return this.buffer.sampleRate;
  1565. };
  1566. /**
  1567. * Return the number of samples in a sound file.
  1568. * Equal to sampleRate * duration.
  1569. *
  1570. * @method frames
  1571. * @return {Number} [sampleCount]
  1572. */
  1573. p5.SoundFile.prototype.frames = function () {
  1574. return this.buffer.length;
  1575. };
  1576. /**
  1577. * Returns an array of amplitude peaks in a p5.SoundFile that can be
  1578. * used to draw a static waveform. Scans through the p5.SoundFile's
  1579. * audio buffer to find the greatest amplitudes. Accepts one
  1580. * parameter, 'length', which determines size of the array.
  1581. * Larger arrays result in more precise waveform visualizations.
  1582. *
  1583. * Inspired by Wavesurfer.js.
  1584. *
  1585. * @method getPeaks
  1586. * @params {Number} [length] length is the size of the returned array.
  1587. * Larger length results in more precision.
  1588. * Defaults to 5*width of the browser window.
  1589. * @returns {Float32Array} Array of peaks.
  1590. */
  1591. p5.SoundFile.prototype.getPeaks = function (length) {
  1592. if (this.buffer) {
  1593. // set length to window's width if no length is provided
  1594. if (!length) {
  1595. length = window.width * 5;
  1596. }
  1597. if (this.buffer) {
  1598. var buffer = this.buffer;
  1599. var sampleSize = buffer.length / length;
  1600. var sampleStep = ~~(sampleSize / 10) || 1;
  1601. var channels = buffer.numberOfChannels;
  1602. var peaks = new Float32Array(Math.round(length));
  1603. for (var c = 0; c < channels; c++) {
  1604. var chan = buffer.getChannelData(c);
  1605. for (var i = 0; i < length; i++) {
  1606. var start = ~~(i * sampleSize);
  1607. var end = ~~(start + sampleSize);
  1608. var max = 0;
  1609. for (var j = start; j < end; j += sampleStep) {
  1610. var value = chan[j];
  1611. if (value > max) {
  1612. max = value;
  1613. } else if (-value > max) {
  1614. max = value;
  1615. }
  1616. }
  1617. if (c === 0 || Math.abs(max) > peaks[i]) {
  1618. peaks[i] = max;
  1619. }
  1620. }
  1621. }
  1622. return peaks;
  1623. }
  1624. } else {
  1625. throw 'Cannot load peaks yet, buffer is not loaded';
  1626. }
  1627. };
  1628. /**
  1629. * Reverses the p5.SoundFile's buffer source.
  1630. * Playback must be handled separately (see example).
  1631. *
  1632. * @method reverseBuffer
  1633. * @example
  1634. * <div><code>
  1635. * var drum;
  1636. *
  1637. * function preload() {
  1638. * drum = loadSound('assets/drum.mp3');
  1639. * }
  1640. *
  1641. * function setup() {
  1642. * drum.reverseBuffer();
  1643. * drum.play();
  1644. * }
  1645. *
  1646. * </code>
  1647. * </div>
  1648. */
  1649. p5.SoundFile.prototype.reverseBuffer = function () {
  1650. var curVol = this.getVolume();
  1651. this.setVolume(0, 0.01, 0);
  1652. this.pause();
  1653. if (this.buffer) {
  1654. for (var i = 0; i < this.buffer.numberOfChannels; i++) {
  1655. Array.prototype.reverse.call(this.buffer.getChannelData(i));
  1656. }
  1657. // set reversed flag
  1658. this.reversed = !this.reversed;
  1659. } else {
  1660. throw 'SoundFile is not done loading';
  1661. }
  1662. this.setVolume(curVol, 0.01, 0.0101);
  1663. this.play();
  1664. };
  1665. /**
  1666. * Schedule an event to be called when the soundfile
  1667. * reaches the end of a buffer. If the soundfile is
  1668. * playing through once, this will be called when it
  1669. * ends. If it is looping, it will be called when
  1670. * stop is called.
  1671. *
  1672. * @method onended
  1673. * @param {Function} callback function to call when the
  1674. * soundfile has ended.
  1675. */
  1676. p5.SoundFile.prototype.onended = function (callback) {
  1677. this._onended = callback;
  1678. return this;
  1679. };
  1680. p5.SoundFile.prototype.add = function () {
  1681. };
  1682. p5.SoundFile.prototype.dispose = function () {
  1683. var now = p5sound.audiocontext.currentTime;
  1684. // remove reference to soundfile
  1685. var index = p5sound.soundArray.indexOf(this);
  1686. p5sound.soundArray.splice(index, 1);
  1687. this.stop(now);
  1688. if (this.buffer && this.bufferSourceNode) {
  1689. for (var i = 0; i < this.bufferSourceNodes.length - 1; i++) {
  1690. if (this.bufferSourceNodes[i] !== null) {
  1691. this.bufferSourceNodes[i].disconnect();
  1692. try {
  1693. this.bufferSourceNodes[i].stop(now);
  1694. } catch (e) {
  1695. }
  1696. this.bufferSourceNodes[i] = null;
  1697. }
  1698. }
  1699. if (this.isPlaying()) {
  1700. try {
  1701. this._counterNode.stop(now);
  1702. } catch (e) {
  1703. console.log(e);
  1704. }
  1705. this._counterNode = null;
  1706. }
  1707. }
  1708. if (this.output) {
  1709. this.output.disconnect();
  1710. this.output = null;
  1711. }
  1712. if (this.panner) {
  1713. this.panner.disconnect();
  1714. this.panner = null;
  1715. }
  1716. };
  1717. /**
  1718. * Connects the output of a p5sound object to input of another
  1719. * p5.sound object. For example, you may connect a p5.SoundFile to an
  1720. * FFT or an Effect. If no parameter is given, it will connect to
  1721. * the master output. Most p5sound objects connect to the master
  1722. * output when they are created.
  1723. *
  1724. * @method connect
  1725. * @param {Object} [object] Audio object that accepts an input
  1726. */
  1727. p5.SoundFile.prototype.connect = function (unit) {
  1728. if (!unit) {
  1729. this.panner.connect(p5sound.input);
  1730. } else {
  1731. if (unit.hasOwnProperty('input')) {
  1732. this.panner.connect(unit.input);
  1733. } else {
  1734. this.panner.connect(unit);
  1735. }
  1736. }
  1737. };
  1738. /**
  1739. * Disconnects the output of this p5sound object.
  1740. *
  1741. * @method disconnect
  1742. */
  1743. p5.SoundFile.prototype.disconnect = function () {
  1744. this.panner.disconnect();
  1745. };
  1746. /**
  1747. */
  1748. p5.SoundFile.prototype.getLevel = function (smoothing) {
  1749. console.warn('p5.SoundFile.getLevel has been removed from the library. Use p5.Amplitude instead');
  1750. };
  1751. /**
  1752. * Reset the source for this SoundFile to a
  1753. * new path (URL).
  1754. *
  1755. * @method setPath
  1756. * @param {String} path path to audio file
  1757. * @param {Function} callback Callback
  1758. */
  1759. p5.SoundFile.prototype.setPath = function (p, callback) {
  1760. var path = p5.prototype._checkFileFormats(p);
  1761. this.url = path;
  1762. this.load(callback);
  1763. };
  1764. /**
  1765. * Replace the current Audio Buffer with a new Buffer.
  1766. *
  1767. * @param {Array} buf Array of Float32 Array(s). 2 Float32 Arrays
  1768. * will create a stereo source. 1 will create
  1769. * a mono source.
  1770. */
  1771. p5.SoundFile.prototype.setBuffer = function (buf) {
  1772. var numChannels = buf.length;
  1773. var size = buf[0].length;
  1774. var newBuffer = ac.createBuffer(numChannels, size, ac.sampleRate);
  1775. if (!buf[0] instanceof Float32Array) {
  1776. buf[0] = new Float32Array(buf[0]);
  1777. }
  1778. for (var channelNum = 0; channelNum < numChannels; channelNum++) {
  1779. var channel = newBuffer.getChannelData(channelNum);
  1780. channel.set(buf[channelNum]);
  1781. }
  1782. this.buffer = newBuffer;
  1783. // set numbers of channels on input to the panner
  1784. this.panner.inputChannels(numChannels);
  1785. };
  1786. //////////////////////////////////////////////////
  1787. // script processor node with an empty buffer to help
  1788. // keep a sample-accurate position in playback buffer.
  1789. // Inspired by Chinmay Pendharkar's technique for Sonoport --> http://bit.ly/1HwdCsV
  1790. // Copyright [2015] [Sonoport (Asia) Pte. Ltd.],
  1791. // Licensed under the Apache License http://apache.org/licenses/LICENSE-2.0
  1792. ////////////////////////////////////////////////////////////////////////////////////
  1793. // initialize counterNode, set its initial buffer and playbackRate
  1794. p5.SoundFile.prototype._initCounterNode = function () {
  1795. var self = this;
  1796. var now = ac.currentTime;
  1797. var cNode = ac.createBufferSource();
  1798. // dispose of scope node if it already exists
  1799. if (self._scopeNode) {
  1800. self._scopeNode.disconnect();
  1801. self._scopeNode.onaudioprocess = undefined;
  1802. self._scopeNode = null;
  1803. }
  1804. self._scopeNode = ac.createScriptProcessor(256, 1, 1);
  1805. // create counter buffer of the same length as self.buffer
  1806. cNode.buffer = _createCounterBuffer(self.buffer);
  1807. cNode.playbackRate.setValueAtTime(self.playbackRate, now);
  1808. cNode.connect(self._scopeNode);
  1809. self._scopeNode.connect(p5.soundOut._silentNode);
  1810. self._scopeNode.onaudioprocess = function (processEvent) {
  1811. var inputBuffer = processEvent.inputBuffer.getChannelData(0);
  1812. // update the lastPos
  1813. self._lastPos = inputBuffer[inputBuffer.length - 1] || 0;
  1814. // do any callbacks that have been scheduled
  1815. self._onTimeUpdate(self._lastPos);
  1816. };
  1817. return cNode;
  1818. };
  1819. // initialize sourceNode, set its initial buffer and playbackRate
  1820. p5.SoundFile.prototype._initSourceNode = function () {
  1821. var self = this;
  1822. var now = ac.currentTime;
  1823. var bufferSourceNode = ac.createBufferSource();
  1824. bufferSourceNode.buffer = self.buffer;
  1825. bufferSourceNode.playbackRate.value = self.playbackRate;
  1826. return bufferSourceNode;
  1827. };
  1828. var _createCounterBuffer = function (buffer) {
  1829. var array = new Float32Array(buffer.length);
  1830. var audioBuf = ac.createBuffer(1, buffer.length, 44100);
  1831. for (var index = 0; index < buffer.length; index++) {
  1832. array[index] = index;
  1833. }
  1834. audioBuf.getChannelData(0).set(array);
  1835. return audioBuf;
  1836. };
  1837. /**
  1838. * processPeaks returns an array of timestamps where it thinks there is a beat.
  1839. *
  1840. * This is an asynchronous function that processes the soundfile in an offline audio context,
  1841. * and sends the results to your callback function.
  1842. *
  1843. * The process involves running the soundfile through a lowpass filter, and finding all of the
  1844. * peaks above the initial threshold. If the total number of peaks are below the minimum number of peaks,
  1845. * it decreases the threshold and re-runs the analysis until either minPeaks or minThreshold are reached.
  1846. *
  1847. * @method processPeaks
  1848. * @param {Function} callback a function to call once this data is returned
  1849. * @param {Number} [initThreshold] initial threshold defaults to 0.9
  1850. * @param {Number} [minThreshold] minimum threshold defaults to 0.22
  1851. * @param {Number} [minPeaks] minimum number of peaks defaults to 200
  1852. * @return {Array} Array of timestamped peaks
  1853. */
  1854. p5.SoundFile.prototype.processPeaks = function (callback, _initThreshold, _minThreshold, _minPeaks) {
  1855. var bufLen = this.buffer.length;
  1856. var sampleRate = this.buffer.sampleRate;
  1857. var buffer = this.buffer;
  1858. var initialThreshold = _initThreshold || 0.9, threshold = initialThreshold, minThreshold = _minThreshold || 0.22, minPeaks = _minPeaks || 200;
  1859. // Create offline context
  1860. var offlineContext = new OfflineAudioContext(1, bufLen, sampleRate);
  1861. // create buffer source
  1862. var source = offlineContext.createBufferSource();
  1863. source.buffer = buffer;
  1864. // Create filter. TO DO: allow custom setting of filter
  1865. var filter = offlineContext.createBiquadFilter();
  1866. filter.type = 'lowpass';
  1867. source.connect(filter);
  1868. filter.connect(offlineContext.destination);
  1869. // start playing at time:0
  1870. source.start(0);
  1871. offlineContext.startRendering();
  1872. // Render the song
  1873. // act on the result
  1874. offlineContext.oncomplete = function (e) {
  1875. var data = {};
  1876. var filteredBuffer = e.renderedBuffer;
  1877. var bufferData = filteredBuffer.getChannelData(0);
  1878. // step 1:
  1879. // create Peak instances, add them to array, with strength and sampleIndex
  1880. do {
  1881. allPeaks = getPeaksAtThreshold(bufferData, threshold);
  1882. threshold -= 0.005;
  1883. } while (Object.keys(allPeaks).length < minPeaks && threshold >= minThreshold);
  1884. // step 2:
  1885. // find intervals for each peak in the sampleIndex, add tempos array
  1886. var intervalCounts = countIntervalsBetweenNearbyPeaks(allPeaks);
  1887. // step 3: find top tempos
  1888. var groups = groupNeighborsByTempo(intervalCounts, filteredBuffer.sampleRate);
  1889. // sort top intervals
  1890. var topTempos = groups.sort(function (intA, intB) {
  1891. return intB.count - intA.count;
  1892. }).splice(0, 5);
  1893. // set this SoundFile's tempo to the top tempo ??
  1894. this.tempo = topTempos[0].tempo;
  1895. // step 4:
  1896. // new array of peaks at top tempo within a bpmVariance
  1897. var bpmVariance = 5;
  1898. var tempoPeaks = getPeaksAtTopTempo(allPeaks, topTempos[0].tempo, filteredBuffer.sampleRate, bpmVariance);
  1899. callback(tempoPeaks);
  1900. };
  1901. };
  1902. // process peaks
  1903. var Peak = function (amp, i) {
  1904. this.sampleIndex = i;
  1905. this.amplitude = amp;
  1906. this.tempos = [];
  1907. this.intervals = [];
  1908. };
  1909. var allPeaks = [];
  1910. // 1. for processPeaks() Function to identify peaks above a threshold
  1911. // returns an array of peak indexes as frames (samples) of the original soundfile
  1912. function getPeaksAtThreshold(data, threshold) {
  1913. var peaksObj = {};
  1914. var length = data.length;
  1915. for (var i = 0; i < length; i++) {
  1916. if (data[i] > threshold) {
  1917. var amp = data[i];
  1918. var peak = new Peak(amp, i);
  1919. peaksObj[i] = peak;
  1920. // Skip forward ~ 1/8s to get past this peak.
  1921. i += 6000;
  1922. }
  1923. i++;
  1924. }
  1925. return peaksObj;
  1926. }
  1927. // 2. for processPeaks()
  1928. function countIntervalsBetweenNearbyPeaks(peaksObj) {
  1929. var intervalCounts = [];
  1930. var peaksArray = Object.keys(peaksObj).sort();
  1931. for (var index = 0; index < peaksArray.length; index++) {
  1932. // find intervals in comparison to nearby peaks
  1933. for (var i = 0; i < 10; i++) {
  1934. var startPeak = peaksObj[peaksArray[index]];
  1935. var endPeak = peaksObj[peaksArray[index + i]];
  1936. if (startPeak && endPeak) {
  1937. var startPos = startPeak.sampleIndex;
  1938. var endPos = endPeak.sampleIndex;
  1939. var interval = endPos - startPos;
  1940. // add a sample interval to the startPeek in the allPeaks array
  1941. if (interval > 0) {
  1942. startPeak.intervals.push(interval);
  1943. }
  1944. // tally the intervals and return interval counts
  1945. var foundInterval = intervalCounts.some(function (intervalCount, p) {
  1946. if (intervalCount.interval === interval) {
  1947. intervalCount.count++;
  1948. return intervalCount;
  1949. }
  1950. });
  1951. // store with JSON like formatting
  1952. if (!foundInterval) {
  1953. intervalCounts.push({
  1954. interval: interval,
  1955. count: 1
  1956. });
  1957. }
  1958. }
  1959. }
  1960. }
  1961. return intervalCounts;
  1962. }
  1963. // 3. for processPeaks --> find tempo
  1964. function groupNeighborsByTempo(intervalCounts, sampleRate) {
  1965. var tempoCounts = [];
  1966. intervalCounts.forEach(function (intervalCount, i) {
  1967. try {
  1968. // Convert an interval to tempo
  1969. var theoreticalTempo = Math.abs(60 / (intervalCount.interval / sampleRate));
  1970. theoreticalTempo = mapTempo(theoreticalTempo);
  1971. var foundTempo = tempoCounts.some(function (tempoCount) {
  1972. if (tempoCount.tempo === theoreticalTempo)
  1973. return tempoCount.count += intervalCount.count;
  1974. });
  1975. if (!foundTempo) {
  1976. if (isNaN(theoreticalTempo)) {
  1977. return;
  1978. }
  1979. tempoCounts.push({
  1980. tempo: Math.round(theoreticalTempo),
  1981. count: intervalCount.count
  1982. });
  1983. }
  1984. } catch (e) {
  1985. throw e;
  1986. }
  1987. });
  1988. return tempoCounts;
  1989. }
  1990. // 4. for processPeaks - get peaks at top tempo
  1991. function getPeaksAtTopTempo(peaksObj, tempo, sampleRate, bpmVariance) {
  1992. var peaksAtTopTempo = [];
  1993. var peaksArray = Object.keys(peaksObj).sort();
  1994. // TO DO: filter out peaks that have the tempo and return
  1995. for (var i = 0; i < peaksArray.length; i++) {
  1996. var key = peaksArray[i];
  1997. var peak = peaksObj[key];
  1998. for (var j = 0; j < peak.intervals.length; j++) {
  1999. var intervalBPM = Math.round(Math.abs(60 / (peak.intervals[j] / sampleRate)));
  2000. intervalBPM = mapTempo(intervalBPM);
  2001. var dif = intervalBPM - tempo;
  2002. if (Math.abs(intervalBPM - tempo) < bpmVariance) {
  2003. // convert sampleIndex to seconds
  2004. peaksAtTopTempo.push(peak.sampleIndex / 44100);
  2005. }
  2006. }
  2007. }
  2008. // filter out peaks that are very close to each other
  2009. peaksAtTopTempo = peaksAtTopTempo.filter(function (peakTime, index, arr) {
  2010. var dif = arr[index + 1] - peakTime;
  2011. if (dif > 0.01) {
  2012. return true;
  2013. }
  2014. });
  2015. return peaksAtTopTempo;
  2016. }
  2017. // helper function for processPeaks
  2018. function mapTempo(theoreticalTempo) {
  2019. // these scenarios create infinite while loop
  2020. if (!isFinite(theoreticalTempo) || theoreticalTempo == 0) {
  2021. return;
  2022. }
  2023. // Adjust the tempo to fit within the 90-180 BPM range
  2024. while (theoreticalTempo < 90)
  2025. theoreticalTempo *= 2;
  2026. while (theoreticalTempo > 180 && theoreticalTempo > 90)
  2027. theoreticalTempo /= 2;
  2028. return theoreticalTempo;
  2029. }
  2030. /*** SCHEDULE EVENTS ***/
  2031. /**
  2032. * Schedule events to trigger every time a MediaElement
  2033. * (audio/video) reaches a playback cue point.
  2034. *
  2035. * Accepts a callback function, a time (in seconds) at which to trigger
  2036. * the callback, and an optional parameter for the callback.
  2037. *
  2038. * Time will be passed as the first parameter to the callback function,
  2039. * and param will be the second parameter.
  2040. *
  2041. *
  2042. * @method addCue
  2043. * @param {Number} time Time in seconds, relative to this media
  2044. * element's playback. For example, to trigger
  2045. * an event every time playback reaches two
  2046. * seconds, pass in the number 2. This will be
  2047. * passed as the first parameter to
  2048. * the callback function.
  2049. * @param {Function} callback Name of a function that will be
  2050. * called at the given time. The callback will
  2051. * receive time and (optionally) param as its
  2052. * two parameters.
  2053. * @param {Object} [value] An object to be passed as the
  2054. * second parameter to the
  2055. * callback function.
  2056. * @return {Number} id ID of this cue,
  2057. * useful for removeCue(id)
  2058. * @example
  2059. * <div><code>
  2060. * function setup() {
  2061. * background(0);
  2062. * noStroke();
  2063. * fill(255);
  2064. * textAlign(CENTER);
  2065. * text('click to play', width/2, height/2);
  2066. *
  2067. * mySound = loadSound('assets/beat.mp3');
  2068. *
  2069. * // schedule calls to changeText
  2070. * mySound.addCue(0.50, changeText, "hello" );
  2071. * mySound.addCue(1.00, changeText, "p5" );
  2072. * mySound.addCue(1.50, changeText, "what" );
  2073. * mySound.addCue(2.00, changeText, "do" );
  2074. * mySound.addCue(2.50, changeText, "you" );
  2075. * mySound.addCue(3.00, changeText, "want" );
  2076. * mySound.addCue(4.00, changeText, "to" );
  2077. * mySound.addCue(5.00, changeText, "make" );
  2078. * mySound.addCue(6.00, changeText, "?" );
  2079. * }
  2080. *
  2081. * function changeText(val) {
  2082. * background(0);
  2083. * text(val, width/2, height/2);
  2084. * }
  2085. *
  2086. * function mouseClicked() {
  2087. * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
  2088. * if (mySound.isPlaying() ) {
  2089. * mySound.stop();
  2090. * } else {
  2091. * mySound.play();
  2092. * }
  2093. * }
  2094. * }
  2095. * </code></div>
  2096. */
  2097. p5.SoundFile.prototype.addCue = function (time, callback, val) {
  2098. var id = this._cueIDCounter++;
  2099. var cue = new Cue(callback, time, id, val);
  2100. this._cues.push(cue);
  2101. // if (!this.elt.ontimeupdate) {
  2102. // this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
  2103. // }
  2104. return id;
  2105. };
  2106. /**
  2107. * Remove a callback based on its ID. The ID is returned by the
  2108. * addCue method.
  2109. *
  2110. * @method removeCue
  2111. * @param {Number} id ID of the cue, as returned by addCue
  2112. */
  2113. p5.SoundFile.prototype.removeCue = function (id) {
  2114. var cueLength = this._cues.length;
  2115. for (var i = 0; i < cueLength; i++) {
  2116. var cue = this._cues[i];
  2117. if (cue.id === id) {
  2118. this.cues.splice(i, 1);
  2119. }
  2120. }
  2121. if (this._cues.length === 0) {
  2122. }
  2123. };
  2124. /**
  2125. * Remove all of the callbacks that had originally been scheduled
  2126. * via the addCue method.
  2127. *
  2128. * @method clearCues
  2129. */
  2130. p5.SoundFile.prototype.clearCues = function () {
  2131. this._cues = [];
  2132. };
  2133. // private method that checks for cues to be fired if events
  2134. // have been scheduled using addCue(callback, time).
  2135. p5.SoundFile.prototype._onTimeUpdate = function (position) {
  2136. var playbackTime = position / this.buffer.sampleRate;
  2137. var cueLength = this._cues.length;
  2138. for (var i = 0; i < cueLength; i++) {
  2139. var cue = this._cues[i];
  2140. var callbackTime = cue.time;
  2141. var val = cue.val;
  2142. if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
  2143. // pass the scheduled callbackTime as parameter to the callback
  2144. cue.callback(val);
  2145. }
  2146. }
  2147. this._prevTime = playbackTime;
  2148. };
  2149. // Cue inspired by JavaScript setTimeout, and the
  2150. // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
  2151. var Cue = function (callback, time, id, val) {
  2152. this.callback = callback;
  2153. this.time = time;
  2154. this.id = id;
  2155. this.val = val;
  2156. };
  2157. }(sndcore, errorHandler, master);
  2158. var amplitude;
  2159. amplitude = function () {
  2160. 'use strict';
  2161. var p5sound = master;
  2162. /**
  2163. * Amplitude measures volume between 0.0 and 1.0.
  2164. * Listens to all p5sound by default, or use setInput()
  2165. * to listen to a specific sound source. Accepts an optional
  2166. * smoothing value, which defaults to 0.
  2167. *
  2168. * @class p5.Amplitude
  2169. * @constructor
  2170. * @param {Number} [smoothing] between 0.0 and .999 to smooth
  2171. * amplitude readings (defaults to 0)
  2172. * @return {Object} Amplitude Object
  2173. * @example
  2174. * <div><code>
  2175. * var sound, amplitude, cnv;
  2176. *
  2177. * function preload(){
  2178. * sound = loadSound('assets/beat.mp3');
  2179. * }
  2180. * function setup() {
  2181. * cnv = createCanvas(100,100);
  2182. * amplitude = new p5.Amplitude();
  2183. *
  2184. * // start / stop the sound when canvas is clicked
  2185. * cnv.mouseClicked(function() {
  2186. * if (sound.isPlaying() ){
  2187. * sound.stop();
  2188. * } else {
  2189. * sound.play();
  2190. * }
  2191. * });
  2192. * }
  2193. * function draw() {
  2194. * background(0);
  2195. * fill(255);
  2196. * var level = amplitude.getLevel();
  2197. * var size = map(level, 0, 1, 0, 200);
  2198. * ellipse(width/2, height/2, size, size);
  2199. * }
  2200. *
  2201. * </code></div>
  2202. */
  2203. p5.Amplitude = function (smoothing) {
  2204. // Set to 2048 for now. In future iterations, this should be inherited or parsed from p5sound's default
  2205. this.bufferSize = 2048;
  2206. // set audio context
  2207. this.audiocontext = p5sound.audiocontext;
  2208. this.processor = this.audiocontext.createScriptProcessor(this.bufferSize, 2, 1);
  2209. // for connections
  2210. this.input = this.processor;
  2211. this.output = this.audiocontext.createGain();
  2212. // smoothing defaults to 0
  2213. this.smoothing = smoothing || 0;
  2214. // the variables to return
  2215. this.volume = 0;
  2216. this.average = 0;
  2217. this.stereoVol = [
  2218. 0,
  2219. 0
  2220. ];
  2221. this.stereoAvg = [
  2222. 0,
  2223. 0
  2224. ];
  2225. this.stereoVolNorm = [
  2226. 0,
  2227. 0
  2228. ];
  2229. this.volMax = 0.001;
  2230. this.normalize = false;
  2231. this.processor.onaudioprocess = this._audioProcess.bind(this);
  2232. this.processor.connect(this.output);
  2233. this.output.gain.value = 0;
  2234. // this may only be necessary because of a Chrome bug
  2235. this.output.connect(this.audiocontext.destination);
  2236. // connect to p5sound master output by default, unless set by input()
  2237. p5sound.meter.connect(this.processor);
  2238. // add this p5.SoundFile to the soundArray
  2239. p5sound.soundArray.push(this);
  2240. };
  2241. /**
  2242. * Connects to the p5sound instance (master output) by default.
  2243. * Optionally, you can pass in a specific source (i.e. a soundfile).
  2244. *
  2245. * @method setInput
  2246. * @param {soundObject|undefined} [snd] set the sound source
  2247. * (optional, defaults to
  2248. * master output)
  2249. * @param {Number|undefined} [smoothing] a range between 0.0 and 1.0
  2250. * to smooth amplitude readings
  2251. * @example
  2252. * <div><code>
  2253. * function preload(){
  2254. * sound1 = loadSound('assets/beat.mp3');
  2255. * sound2 = loadSound('assets/drum.mp3');
  2256. * }
  2257. * function setup(){
  2258. * amplitude = new p5.Amplitude();
  2259. * sound1.play();
  2260. * sound2.play();
  2261. * amplitude.setInput(sound2);
  2262. * }
  2263. * function draw() {
  2264. * background(0);
  2265. * fill(255);
  2266. * var level = amplitude.getLevel();
  2267. * var size = map(level, 0, 1, 0, 200);
  2268. * ellipse(width/2, height/2, size, size);
  2269. * }
  2270. * function mouseClicked(){
  2271. * sound1.stop();
  2272. * sound2.stop();
  2273. * }
  2274. * </code></div>
  2275. */
  2276. p5.Amplitude.prototype.setInput = function (source, smoothing) {
  2277. p5sound.meter.disconnect();
  2278. if (smoothing) {
  2279. this.smoothing = smoothing;
  2280. }
  2281. // connect to the master out of p5s instance if no snd is provided
  2282. if (source == null) {
  2283. console.log('Amplitude input source is not ready! Connecting to master output instead');
  2284. p5sound.meter.connect(this.processor);
  2285. } else if (source instanceof p5.Signal) {
  2286. source.output.connect(this.processor);
  2287. } else if (source) {
  2288. source.connect(this.processor);
  2289. this.processor.disconnect();
  2290. this.processor.connect(this.output);
  2291. } else {
  2292. p5sound.meter.connect(this.processor);
  2293. }
  2294. };
  2295. p5.Amplitude.prototype.connect = function (unit) {
  2296. if (unit) {
  2297. if (unit.hasOwnProperty('input')) {
  2298. this.output.connect(unit.input);
  2299. } else {
  2300. this.output.connect(unit);
  2301. }
  2302. } else {
  2303. this.output.connect(this.panner.connect(p5sound.input));
  2304. }
  2305. };
  2306. p5.Amplitude.prototype.disconnect = function (unit) {
  2307. this.output.disconnect();
  2308. };
  2309. // TO DO make this stereo / dependent on # of audio channels
  2310. p5.Amplitude.prototype._audioProcess = function (event) {
  2311. for (var channel = 0; channel < event.inputBuffer.numberOfChannels; channel++) {
  2312. var inputBuffer = event.inputBuffer.getChannelData(channel);
  2313. var bufLength = inputBuffer.length;
  2314. var total = 0;
  2315. var sum = 0;
  2316. var x;
  2317. for (var i = 0; i < bufLength; i++) {
  2318. x = inputBuffer[i];
  2319. if (this.normalize) {
  2320. total += Math.max(Math.min(x / this.volMax, 1), -1);
  2321. sum += Math.max(Math.min(x / this.volMax, 1), -1) * Math.max(Math.min(x / this.volMax, 1), -1);
  2322. } else {
  2323. total += x;
  2324. sum += x * x;
  2325. }
  2326. }
  2327. var average = total / bufLength;
  2328. // ... then take the square root of the sum.
  2329. var rms = Math.sqrt(sum / bufLength);
  2330. this.stereoVol[channel] = Math.max(rms, this.stereoVol[channel] * this.smoothing);
  2331. this.stereoAvg[channel] = Math.max(average, this.stereoVol[channel] * this.smoothing);
  2332. this.volMax = Math.max(this.stereoVol[channel], this.volMax);
  2333. }
  2334. // add volume from all channels together
  2335. var self = this;
  2336. var volSum = this.stereoVol.reduce(function (previousValue, currentValue, index) {
  2337. self.stereoVolNorm[index - 1] = Math.max(Math.min(self.stereoVol[index - 1] / self.volMax, 1), 0);
  2338. self.stereoVolNorm[index] = Math.max(Math.min(self.stereoVol[index] / self.volMax, 1), 0);
  2339. return previousValue + currentValue;
  2340. });
  2341. // volume is average of channels
  2342. this.volume = volSum / this.stereoVol.length;
  2343. // normalized value
  2344. this.volNorm = Math.max(Math.min(this.volume / this.volMax, 1), 0);
  2345. };
  2346. /**
  2347. * Returns a single Amplitude reading at the moment it is called.
  2348. * For continuous readings, run in the draw loop.
  2349. *
  2350. * @method getLevel
  2351. * @param {Number} [channel] Optionally return only channel 0 (left) or 1 (right)
  2352. * @return {Number} Amplitude as a number between 0.0 and 1.0
  2353. * @example
  2354. * <div><code>
  2355. * function preload(){
  2356. * sound = loadSound('assets/beat.mp3');
  2357. * }
  2358. * function setup() {
  2359. * amplitude = new p5.Amplitude();
  2360. * sound.play();
  2361. * }
  2362. * function draw() {
  2363. * background(0);
  2364. * fill(255);
  2365. * var level = amplitude.getLevel();
  2366. * var size = map(level, 0, 1, 0, 200);
  2367. * ellipse(width/2, height/2, size, size);
  2368. * }
  2369. * function mouseClicked(){
  2370. * sound.stop();
  2371. * }
  2372. * </code></div>
  2373. */
  2374. p5.Amplitude.prototype.getLevel = function (channel) {
  2375. if (typeof channel !== 'undefined') {
  2376. if (this.normalize) {
  2377. return this.stereoVolNorm[channel];
  2378. } else {
  2379. return this.stereoVol[channel];
  2380. }
  2381. } else if (this.normalize) {
  2382. return this.volNorm;
  2383. } else {
  2384. return this.volume;
  2385. }
  2386. };
  2387. /**
  2388. * Determines whether the results of Amplitude.process() will be
  2389. * Normalized. To normalize, Amplitude finds the difference the
  2390. * loudest reading it has processed and the maximum amplitude of
  2391. * 1.0. Amplitude adds this difference to all values to produce
  2392. * results that will reliably map between 0.0 and 1.0. However,
  2393. * if a louder moment occurs, the amount that Normalize adds to
  2394. * all the values will change. Accepts an optional boolean parameter
  2395. * (true or false). Normalizing is off by default.
  2396. *
  2397. * @method toggleNormalize
  2398. * @param {boolean} [boolean] set normalize to true (1) or false (0)
  2399. */
  2400. p5.Amplitude.prototype.toggleNormalize = function (bool) {
  2401. if (typeof bool === 'boolean') {
  2402. this.normalize = bool;
  2403. } else {
  2404. this.normalize = !this.normalize;
  2405. }
  2406. };
  2407. /**
  2408. * Smooth Amplitude analysis by averaging with the last analysis
  2409. * frame. Off by default.
  2410. *
  2411. * @method smooth
  2412. * @param {Number} set smoothing from 0.0 <= 1
  2413. */
  2414. p5.Amplitude.prototype.smooth = function (s) {
  2415. if (s >= 0 && s < 1) {
  2416. this.smoothing = s;
  2417. } else {
  2418. console.log('Error: smoothing must be between 0 and 1');
  2419. }
  2420. };
  2421. p5.Amplitude.prototype.dispose = function () {
  2422. // remove reference from soundArray
  2423. var index = p5sound.soundArray.indexOf(this);
  2424. p5sound.soundArray.splice(index, 1);
  2425. this.input.disconnect();
  2426. this.output.disconnect();
  2427. this.input = this.processor = undefined;
  2428. this.output = undefined;
  2429. };
  2430. }(master);
  2431. var fft;
  2432. fft = function () {
  2433. 'use strict';
  2434. var p5sound = master;
  2435. /**
  2436. * <p>FFT (Fast Fourier Transform) is an analysis algorithm that
  2437. * isolates individual
  2438. * <a href="https://en.wikipedia.org/wiki/Audio_frequency">
  2439. * audio frequencies</a> within a waveform.</p>
  2440. *
  2441. * <p>Once instantiated, a p5.FFT object can return an array based on
  2442. * two types of analyses: <br> • <code>FFT.waveform()</code> computes
  2443. * amplitude values along the time domain. The array indices correspond
  2444. * to samples across a brief moment in time. Each value represents
  2445. * amplitude of the waveform at that sample of time.<br>
  2446. * • <code>FFT.analyze() </code> computes amplitude values along the
  2447. * frequency domain. The array indices correspond to frequencies (i.e.
  2448. * pitches), from the lowest to the highest that humans can hear. Each
  2449. * value represents amplitude at that slice of the frequency spectrum.
  2450. * Use with <code>getEnergy()</code> to measure amplitude at specific
  2451. * frequencies, or within a range of frequencies. </p>
  2452. *
  2453. * <p>FFT analyzes a very short snapshot of sound called a sample
  2454. * buffer. It returns an array of amplitude measurements, referred
  2455. * to as <code>bins</code>. The array is 1024 bins long by default.
  2456. * You can change the bin array length, but it must be a power of 2
  2457. * between 16 and 1024 in order for the FFT algorithm to function
  2458. * correctly. The actual size of the FFT buffer is twice the
  2459. * number of bins, so given a standard sample rate, the buffer is
  2460. * 2048/44100 seconds long.</p>
  2461. *
  2462. *
  2463. * @class p5.FFT
  2464. * @constructor
  2465. * @param {Number} [smoothing] Smooth results of Freq Spectrum.
  2466. * 0.0 < smoothing < 1.0.
  2467. * Defaults to 0.8.
  2468. * @param {Number} [bins] Length of resulting array.
  2469. * Must be a power of two between
  2470. * 16 and 1024. Defaults to 1024.
  2471. * @return {Object} FFT Object
  2472. * @example
  2473. * <div><code>
  2474. * function preload(){
  2475. * sound = loadSound('assets/Damscray_DancingTiger.mp3');
  2476. * }
  2477. *
  2478. * function setup(){
  2479. * var cnv = createCanvas(100,100);
  2480. * cnv.mouseClicked(togglePlay);
  2481. * fft = new p5.FFT();
  2482. * sound.amp(0.2);
  2483. * }
  2484. *
  2485. * function draw(){
  2486. * background(0);
  2487. *
  2488. * var spectrum = fft.analyze();
  2489. * noStroke();
  2490. * fill(0,255,0); // spectrum is green
  2491. * for (var i = 0; i< spectrum.length; i++){
  2492. * var x = map(i, 0, spectrum.length, 0, width);
  2493. * var h = -height + map(spectrum[i], 0, 255, height, 0);
  2494. * rect(x, height, width / spectrum.length, h )
  2495. * }
  2496. *
  2497. * var waveform = fft.waveform();
  2498. * noFill();
  2499. * beginShape();
  2500. * stroke(255,0,0); // waveform is red
  2501. * strokeWeight(1);
  2502. * for (var i = 0; i< waveform.length; i++){
  2503. * var x = map(i, 0, waveform.length, 0, width);
  2504. * var y = map( waveform[i], -1, 1, 0, height);
  2505. * vertex(x,y);
  2506. * }
  2507. * endShape();
  2508. *
  2509. * text('click to play/pause', 4, 10);
  2510. * }
  2511. *
  2512. * // fade sound if mouse is over canvas
  2513. * function togglePlay() {
  2514. * if (sound.isPlaying()) {
  2515. * sound.pause();
  2516. * } else {
  2517. * sound.loop();
  2518. * }
  2519. * }
  2520. * </code></div>
  2521. */
  2522. p5.FFT = function (smoothing, bins) {
  2523. this.smoothing = smoothing || 0.8;
  2524. this.bins = bins || 1024;
  2525. var FFT_SIZE = bins * 2 || 2048;
  2526. this.input = this.analyser = p5sound.audiocontext.createAnalyser();
  2527. // default connections to p5sound fftMeter
  2528. p5sound.fftMeter.connect(this.analyser);
  2529. this.analyser.smoothingTimeConstant = this.smoothing;
  2530. this.analyser.fftSize = FFT_SIZE;
  2531. this.freqDomain = new Uint8Array(this.analyser.frequencyBinCount);
  2532. this.timeDomain = new Uint8Array(this.analyser.frequencyBinCount);
  2533. // predefined frequency ranages, these will be tweakable
  2534. this.bass = [
  2535. 20,
  2536. 140
  2537. ];
  2538. this.lowMid = [
  2539. 140,
  2540. 400
  2541. ];
  2542. this.mid = [
  2543. 400,
  2544. 2600
  2545. ];
  2546. this.highMid = [
  2547. 2600,
  2548. 5200
  2549. ];
  2550. this.treble = [
  2551. 5200,
  2552. 14000
  2553. ];
  2554. // add this p5.SoundFile to the soundArray
  2555. p5sound.soundArray.push(this);
  2556. };
  2557. /**
  2558. * Set the input source for the FFT analysis. If no source is
  2559. * provided, FFT will analyze all sound in the sketch.
  2560. *
  2561. * @method setInput
  2562. * @param {Object} [source] p5.sound object (or web audio API source node)
  2563. */
  2564. p5.FFT.prototype.setInput = function (source) {
  2565. if (!source) {
  2566. p5sound.fftMeter.connect(this.analyser);
  2567. } else {
  2568. if (source.output) {
  2569. source.output.connect(this.analyser);
  2570. } else if (source.connect) {
  2571. source.connect(this.analyser);
  2572. }
  2573. p5sound.fftMeter.disconnect();
  2574. }
  2575. };
  2576. /**
  2577. * Returns an array of amplitude values (between -1.0 and +1.0) that represent
  2578. * a snapshot of amplitude readings in a single buffer. Length will be
  2579. * equal to bins (defaults to 1024). Can be used to draw the waveform
  2580. * of a sound.
  2581. *
  2582. * @method waveform
  2583. * @param {Number} [bins] Must be a power of two between
  2584. * 16 and 1024. Defaults to 1024.
  2585. * @param {String} [precision] If any value is provided, will return results
  2586. * in a Float32 Array which is more precise
  2587. * than a regular array.
  2588. * @return {Array} Array Array of amplitude values (-1 to 1)
  2589. * over time. Array length = bins.
  2590. *
  2591. */
  2592. p5.FFT.prototype.waveform = function () {
  2593. var bins, mode, normalArray;
  2594. for (var i = 0; i < arguments.length; i++) {
  2595. if (typeof arguments[i] === 'number') {
  2596. bins = arguments[i];
  2597. this.analyser.fftSize = bins * 2;
  2598. }
  2599. if (typeof arguments[i] === 'string') {
  2600. mode = arguments[i];
  2601. }
  2602. }
  2603. // getFloatFrequencyData doesnt work in Safari as of 5/2015
  2604. if (mode && !p5.prototype._isSafari()) {
  2605. timeToFloat(this, this.timeDomain);
  2606. this.analyser.getFloatTimeDomainData(this.timeDomain);
  2607. return this.timeDomain;
  2608. } else {
  2609. timeToInt(this, this.timeDomain);
  2610. this.analyser.getByteTimeDomainData(this.timeDomain);
  2611. var normalArray = new Array();
  2612. for (var i = 0; i < this.timeDomain.length; i++) {
  2613. var scaled = p5.prototype.map(this.timeDomain[i], 0, 255, -1, 1);
  2614. normalArray.push(scaled);
  2615. }
  2616. return normalArray;
  2617. }
  2618. };
  2619. /**
  2620. * Returns an array of amplitude values (between 0 and 255)
  2621. * across the frequency spectrum. Length is equal to FFT bins
  2622. * (1024 by default). The array indices correspond to frequencies
  2623. * (i.e. pitches), from the lowest to the highest that humans can
  2624. * hear. Each value represents amplitude at that slice of the
  2625. * frequency spectrum. Must be called prior to using
  2626. * <code>getEnergy()</code>.
  2627. *
  2628. * @method analyze
  2629. * @param {Number} [bins] Must be a power of two between
  2630. * 16 and 1024. Defaults to 1024.
  2631. * @param {Number} [scale] If "dB," returns decibel
  2632. * float measurements between
  2633. * -140 and 0 (max).
  2634. * Otherwise returns integers from 0-255.
  2635. * @return {Array} spectrum Array of energy (amplitude/volume)
  2636. * values across the frequency spectrum.
  2637. * Lowest energy (silence) = 0, highest
  2638. * possible is 255.
  2639. * @example
  2640. * <div><code>
  2641. * var osc;
  2642. * var fft;
  2643. *
  2644. * function setup(){
  2645. * createCanvas(100,100);
  2646. * osc = new p5.Oscillator();
  2647. * osc.amp(0);
  2648. * osc.start();
  2649. * fft = new p5.FFT();
  2650. * }
  2651. *
  2652. * function draw(){
  2653. * background(0);
  2654. *
  2655. * var freq = map(mouseX, 0, 800, 20, 15000);
  2656. * freq = constrain(freq, 1, 20000);
  2657. * osc.freq(freq);
  2658. *
  2659. * var spectrum = fft.analyze();
  2660. * noStroke();
  2661. * fill(0,255,0); // spectrum is green
  2662. * for (var i = 0; i< spectrum.length; i++){
  2663. * var x = map(i, 0, spectrum.length, 0, width);
  2664. * var h = -height + map(spectrum[i], 0, 255, height, 0);
  2665. * rect(x, height, width / spectrum.length, h );
  2666. * }
  2667. *
  2668. * stroke(255);
  2669. * text('Freq: ' + round(freq)+'Hz', 10, 10);
  2670. *
  2671. * isMouseOverCanvas();
  2672. * }
  2673. *
  2674. * // only play sound when mouse is over canvas
  2675. * function isMouseOverCanvas() {
  2676. * var mX = mouseX, mY = mouseY;
  2677. * if (mX > 0 && mX < width && mY < height && mY > 0) {
  2678. * osc.amp(0.5, 0.2);
  2679. * } else {
  2680. * osc.amp(0, 0.2);
  2681. * }
  2682. * }
  2683. * </code></div>
  2684. *
  2685. *
  2686. */
  2687. p5.FFT.prototype.analyze = function () {
  2688. var bins, mode;
  2689. for (var i = 0; i < arguments.length; i++) {
  2690. if (typeof arguments[i] === 'number') {
  2691. bins = this.bins = arguments[i];
  2692. this.analyser.fftSize = this.bins * 2;
  2693. }
  2694. if (typeof arguments[i] === 'string') {
  2695. mode = arguments[i];
  2696. }
  2697. }
  2698. if (mode && mode.toLowerCase() === 'db') {
  2699. freqToFloat(this);
  2700. this.analyser.getFloatFrequencyData(this.freqDomain);
  2701. return this.freqDomain;
  2702. } else {
  2703. freqToInt(this, this.freqDomain);
  2704. this.analyser.getByteFrequencyData(this.freqDomain);
  2705. var normalArray = Array.apply([], this.freqDomain);
  2706. normalArray.length === this.analyser.fftSize;
  2707. normalArray.constructor === Array;
  2708. return normalArray;
  2709. }
  2710. };
  2711. /**
  2712. * Returns the amount of energy (volume) at a specific
  2713. * <a href="en.wikipedia.org/wiki/Audio_frequency" target="_blank">
  2714. * frequency</a>, or the average amount of energy between two
  2715. * frequencies. Accepts Number(s) corresponding
  2716. * to frequency (in Hz), or a String corresponding to predefined
  2717. * frequency ranges ("bass", "lowMid", "mid", "highMid", "treble").
  2718. * Returns a range between 0 (no energy/volume at that frequency) and
  2719. * 255 (maximum energy).
  2720. * <em>NOTE: analyze() must be called prior to getEnergy(). Analyze()
  2721. * tells the FFT to analyze frequency data, and getEnergy() uses
  2722. * the results determine the value at a specific frequency or
  2723. * range of frequencies.</em></p>
  2724. *
  2725. * @method getEnergy
  2726. * @param {Number|String} frequency1 Will return a value representing
  2727. * energy at this frequency. Alternately,
  2728. * the strings "bass", "lowMid" "mid",
  2729. * "highMid", and "treble" will return
  2730. * predefined frequency ranges.
  2731. * @param {Number} [frequency2] If a second frequency is given,
  2732. * will return average amount of
  2733. * energy that exists between the
  2734. * two frequencies.
  2735. * @return {Number} Energy Energy (volume/amplitude) from
  2736. * 0 and 255.
  2737. *
  2738. */
  2739. p5.FFT.prototype.getEnergy = function (frequency1, frequency2) {
  2740. var nyquist = p5sound.audiocontext.sampleRate / 2;
  2741. if (frequency1 === 'bass') {
  2742. frequency1 = this.bass[0];
  2743. frequency2 = this.bass[1];
  2744. } else if (frequency1 === 'lowMid') {
  2745. frequency1 = this.lowMid[0];
  2746. frequency2 = this.lowMid[1];
  2747. } else if (frequency1 === 'mid') {
  2748. frequency1 = this.mid[0];
  2749. frequency2 = this.mid[1];
  2750. } else if (frequency1 === 'highMid') {
  2751. frequency1 = this.highMid[0];
  2752. frequency2 = this.highMid[1];
  2753. } else if (frequency1 === 'treble') {
  2754. frequency1 = this.treble[0];
  2755. frequency2 = this.treble[1];
  2756. }
  2757. if (typeof frequency1 !== 'number') {
  2758. throw 'invalid input for getEnergy()';
  2759. } else if (!frequency2) {
  2760. var index = Math.round(frequency1 / nyquist * this.freqDomain.length);
  2761. return this.freqDomain[index];
  2762. } else if (frequency1 && frequency2) {
  2763. // if second is higher than first
  2764. if (frequency1 > frequency2) {
  2765. var swap = frequency2;
  2766. frequency2 = frequency1;
  2767. frequency1 = swap;
  2768. }
  2769. var lowIndex = Math.round(frequency1 / nyquist * this.freqDomain.length);
  2770. var highIndex = Math.round(frequency2 / nyquist * this.freqDomain.length);
  2771. var total = 0;
  2772. var numFrequencies = 0;
  2773. // add up all of the values for the frequencies
  2774. for (var i = lowIndex; i <= highIndex; i++) {
  2775. total += this.freqDomain[i];
  2776. numFrequencies += 1;
  2777. }
  2778. // divide by total number of frequencies
  2779. var toReturn = total / numFrequencies;
  2780. return toReturn;
  2781. } else {
  2782. throw 'invalid input for getEnergy()';
  2783. }
  2784. };
  2785. // compatability with v.012, changed to getEnergy in v.0121. Will be deprecated...
  2786. p5.FFT.prototype.getFreq = function (freq1, freq2) {
  2787. console.log('getFreq() is deprecated. Please use getEnergy() instead.');
  2788. var x = this.getEnergy(freq1, freq2);
  2789. return x;
  2790. };
  2791. /**
  2792. * Returns the
  2793. * <a href="http://en.wikipedia.org/wiki/Spectral_centroid" target="_blank">
  2794. * spectral centroid</a> of the input signal.
  2795. * <em>NOTE: analyze() must be called prior to getCentroid(). Analyze()
  2796. * tells the FFT to analyze frequency data, and getCentroid() uses
  2797. * the results determine the spectral centroid.</em></p>
  2798. *
  2799. * @method getCentroid
  2800. * @return {Number} Spectral Centroid Frequency Frequency of the spectral centroid in Hz.
  2801. *
  2802. *
  2803. * @example
  2804. * <div><code>
  2805. *
  2806. *
  2807. *function setup(){
  2808. * cnv = createCanvas(800,400);
  2809. * sound = new p5.AudioIn();
  2810. * sound.start();
  2811. * fft = new p5.FFT();
  2812. * sound.connect(fft);
  2813. *}
  2814. *
  2815. *
  2816. *function draw(){
  2817. *
  2818. * var centroidplot = 0.0;
  2819. * var spectralCentroid = 0;
  2820. *
  2821. *
  2822. * background(0);
  2823. * stroke(0,255,0);
  2824. * var spectrum = fft.analyze();
  2825. * fill(0,255,0); // spectrum is green
  2826. *
  2827. * //draw the spectrum
  2828. *
  2829. * for (var i = 0; i< spectrum.length; i++){
  2830. * var x = map(log(i), 0, log(spectrum.length), 0, width);
  2831. * var h = map(spectrum[i], 0, 255, 0, height);
  2832. * var rectangle_width = (log(i+1)-log(i))*(width/log(spectrum.length));
  2833. * rect(x, height, rectangle_width, -h )
  2834. * }
  2835. * var nyquist = 22050;
  2836. *
  2837. * // get the centroid
  2838. * spectralCentroid = fft.getCentroid();
  2839. *
  2840. * // the mean_freq_index calculation is for the display.
  2841. * var mean_freq_index = spectralCentroid/(nyquist/spectrum.length);
  2842. *
  2843. * centroidplot = map(log(mean_freq_index), 0, log(spectrum.length), 0, width);
  2844. *
  2845. *
  2846. * stroke(255,0,0); // the line showing where the centroid is will be red
  2847. *
  2848. * rect(centroidplot, 0, width / spectrum.length, height)
  2849. * noStroke();
  2850. * fill(255,255,255); // text is white
  2851. * textSize(40);
  2852. * text("centroid: "+round(spectralCentroid)+" Hz", 10, 40);
  2853. *}
  2854. * </code></div>
  2855. */
  2856. p5.FFT.prototype.getCentroid = function () {
  2857. var nyquist = p5sound.audiocontext.sampleRate / 2;
  2858. var cumulative_sum = 0;
  2859. var centroid_normalization = 0;
  2860. for (var i = 0; i < this.freqDomain.length; i++) {
  2861. cumulative_sum += i * this.freqDomain[i];
  2862. centroid_normalization += this.freqDomain[i];
  2863. }
  2864. var mean_freq_index = 0;
  2865. if (centroid_normalization != 0) {
  2866. mean_freq_index = cumulative_sum / centroid_normalization;
  2867. }
  2868. var spec_centroid_freq = mean_freq_index * (nyquist / this.freqDomain.length);
  2869. return spec_centroid_freq;
  2870. };
  2871. /**
  2872. * Smooth FFT analysis by averaging with the last analysis frame.
  2873. *
  2874. * @method smooth
  2875. * @param {Number} smoothing 0.0 < smoothing < 1.0.
  2876. * Defaults to 0.8.
  2877. */
  2878. p5.FFT.prototype.smooth = function (s) {
  2879. if (s) {
  2880. this.smoothing = s;
  2881. }
  2882. this.analyser.smoothingTimeConstant = s;
  2883. };
  2884. p5.FFT.prototype.dispose = function () {
  2885. // remove reference from soundArray
  2886. var index = p5sound.soundArray.indexOf(this);
  2887. p5sound.soundArray.splice(index, 1);
  2888. this.analyser.disconnect();
  2889. this.analyser = undefined;
  2890. };
  2891. /**
  2892. * Returns an array of average amplitude values for a given number
  2893. * of frequency bands split equally. N defaults to 16.
  2894. * <em>NOTE: analyze() must be called prior to linAverages(). Analyze()
  2895. * tells the FFT to analyze frequency data, and linAverages() uses
  2896. * the results to group them into a smaller set of averages.</em></p>
  2897. *
  2898. * @method linAverages
  2899. * @param {Number} N Number of returned frequency groups
  2900. * @return {Array} linearAverages Array of average amplitude values for each group
  2901. */
  2902. p5.FFT.prototype.linAverages = function (N) {
  2903. var N = N || 16;
  2904. // This prevents undefined, null or 0 values of N
  2905. var spectrum = this.freqDomain;
  2906. var spectrumLength = spectrum.length;
  2907. var spectrumStep = Math.floor(spectrumLength / N);
  2908. var linearAverages = new Array(N);
  2909. // Keep a second index for the current average group and place the values accordingly
  2910. // with only one loop in the spectrum data
  2911. var groupIndex = 0;
  2912. for (var specIndex = 0; specIndex < spectrumLength; specIndex++) {
  2913. linearAverages[groupIndex] = linearAverages[groupIndex] !== undefined ? (linearAverages[groupIndex] + spectrum[specIndex]) / 2 : spectrum[specIndex];
  2914. // Increase the group index when the last element of the group is processed
  2915. if (specIndex % spectrumStep == spectrumStep - 1) {
  2916. groupIndex++;
  2917. }
  2918. }
  2919. return linearAverages;
  2920. };
  2921. /**
  2922. * Returns an array of average amplitude values of the spectrum, for a given
  2923. * set of <a href="https://en.wikipedia.org/wiki/Octave_band" target="_blank">
  2924. * Octave Bands</a>
  2925. * <em>NOTE: analyze() must be called prior to logAverages(). Analyze()
  2926. * tells the FFT to analyze frequency data, and logAverages() uses
  2927. * the results to group them into a smaller set of averages.</em></p>
  2928. *
  2929. * @method logAverages
  2930. * @param {Array} octaveBands Array of Octave Bands objects for grouping
  2931. * @return {Array} logAverages Array of average amplitude values for each group
  2932. */
  2933. p5.FFT.prototype.logAverages = function (octaveBands) {
  2934. var nyquist = p5sound.audiocontext.sampleRate / 2;
  2935. var spectrum = this.freqDomain;
  2936. var spectrumLength = spectrum.length;
  2937. var logAverages = new Array(octaveBands.length);
  2938. // Keep a second index for the current average group and place the values accordingly
  2939. // With only one loop in the spectrum data
  2940. var octaveIndex = 0;
  2941. for (var specIndex = 0; specIndex < spectrumLength; specIndex++) {
  2942. var specIndexFrequency = Math.round(specIndex * nyquist / this.freqDomain.length);
  2943. // Increase the group index if the current frequency exceeds the limits of the band
  2944. if (specIndexFrequency > octaveBands[octaveIndex].hi) {
  2945. octaveIndex++;
  2946. }
  2947. logAverages[octaveIndex] = logAverages[octaveIndex] !== undefined ? (logAverages[octaveIndex] + spectrum[specIndex]) / 2 : spectrum[specIndex];
  2948. }
  2949. return logAverages;
  2950. };
  2951. /**
  2952. * Calculates and Returns the 1/N
  2953. * <a href="https://en.wikipedia.org/wiki/Octave_band" target="_blank">Octave Bands</a>
  2954. * N defaults to 3 and minimum central frequency to 15.625Hz.
  2955. * (1/3 Octave Bands ~= 31 Frequency Bands)
  2956. * Setting fCtr0 to a central value of a higher octave will ignore the lower bands
  2957. * and produce less frequency groups.
  2958. *
  2959. * @method getOctaveBands
  2960. * @param {Number} N Specifies the 1/N type of generated octave bands
  2961. * @param {Number} fCtr0 Minimum central frequency for the lowest band
  2962. * @return {Array} octaveBands Array of octave band objects with their bounds
  2963. */
  2964. p5.FFT.prototype.getOctaveBands = function (N, fCtr0) {
  2965. var N = N || 3;
  2966. // Default to 1/3 Octave Bands
  2967. var fCtr0 = fCtr0 || 15.625;
  2968. // Minimum central frequency, defaults to 15.625Hz
  2969. var octaveBands = [];
  2970. var lastFrequencyBand = {
  2971. lo: fCtr0 / Math.pow(2, 1 / (2 * N)),
  2972. ctr: fCtr0,
  2973. hi: fCtr0 * Math.pow(2, 1 / (2 * N))
  2974. };
  2975. octaveBands.push(lastFrequencyBand);
  2976. var nyquist = p5sound.audiocontext.sampleRate / 2;
  2977. while (lastFrequencyBand.hi < nyquist) {
  2978. var newFrequencyBand = {};
  2979. newFrequencyBand.lo = lastFrequencyBand.hi, newFrequencyBand.ctr = lastFrequencyBand.ctr * Math.pow(2, 1 / N), newFrequencyBand.hi = newFrequencyBand.ctr * Math.pow(2, 1 / (2 * N)), octaveBands.push(newFrequencyBand);
  2980. lastFrequencyBand = newFrequencyBand;
  2981. }
  2982. return octaveBands;
  2983. };
  2984. // helper methods to convert type from float (dB) to int (0-255)
  2985. var freqToFloat = function (fft) {
  2986. if (fft.freqDomain instanceof Float32Array === false) {
  2987. fft.freqDomain = new Float32Array(fft.analyser.frequencyBinCount);
  2988. }
  2989. };
  2990. var freqToInt = function (fft) {
  2991. if (fft.freqDomain instanceof Uint8Array === false) {
  2992. fft.freqDomain = new Uint8Array(fft.analyser.frequencyBinCount);
  2993. }
  2994. };
  2995. var timeToFloat = function (fft) {
  2996. if (fft.timeDomain instanceof Float32Array === false) {
  2997. fft.timeDomain = new Float32Array(fft.analyser.frequencyBinCount);
  2998. }
  2999. };
  3000. var timeToInt = function (fft) {
  3001. if (fft.timeDomain instanceof Uint8Array === false) {
  3002. fft.timeDomain = new Uint8Array(fft.analyser.frequencyBinCount);
  3003. }
  3004. };
  3005. }(master);
  3006. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  3007. var Tone_core_Tone;
  3008. Tone_core_Tone = function () {
  3009. 'use strict';
  3010. function isUndef(val) {
  3011. return val === void 0;
  3012. }
  3013. function isFunction(val) {
  3014. return typeof val === 'function';
  3015. }
  3016. var audioContext;
  3017. if (isUndef(window.AudioContext)) {
  3018. window.AudioContext = window.webkitAudioContext;
  3019. }
  3020. if (isUndef(window.OfflineAudioContext)) {
  3021. window.OfflineAudioContext = window.webkitOfflineAudioContext;
  3022. }
  3023. if (!isUndef(AudioContext)) {
  3024. audioContext = new AudioContext();
  3025. } else {
  3026. throw new Error('Web Audio is not supported in this browser');
  3027. }
  3028. if (!isFunction(AudioContext.prototype.createGain)) {
  3029. AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
  3030. }
  3031. if (!isFunction(AudioContext.prototype.createDelay)) {
  3032. AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
  3033. }
  3034. if (!isFunction(AudioContext.prototype.createPeriodicWave)) {
  3035. AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
  3036. }
  3037. if (!isFunction(AudioBufferSourceNode.prototype.start)) {
  3038. AudioBufferSourceNode.prototype.start = AudioBufferSourceNode.prototype.noteGrainOn;
  3039. }
  3040. if (!isFunction(AudioBufferSourceNode.prototype.stop)) {
  3041. AudioBufferSourceNode.prototype.stop = AudioBufferSourceNode.prototype.noteOff;
  3042. }
  3043. if (!isFunction(OscillatorNode.prototype.start)) {
  3044. OscillatorNode.prototype.start = OscillatorNode.prototype.noteOn;
  3045. }
  3046. if (!isFunction(OscillatorNode.prototype.stop)) {
  3047. OscillatorNode.prototype.stop = OscillatorNode.prototype.noteOff;
  3048. }
  3049. if (!isFunction(OscillatorNode.prototype.setPeriodicWave)) {
  3050. OscillatorNode.prototype.setPeriodicWave = OscillatorNode.prototype.setWaveTable;
  3051. }
  3052. AudioNode.prototype._nativeConnect = AudioNode.prototype.connect;
  3053. AudioNode.prototype.connect = function (B, outNum, inNum) {
  3054. if (B.input) {
  3055. if (Array.isArray(B.input)) {
  3056. if (isUndef(inNum)) {
  3057. inNum = 0;
  3058. }
  3059. this.connect(B.input[inNum]);
  3060. } else {
  3061. this.connect(B.input, outNum, inNum);
  3062. }
  3063. } else {
  3064. try {
  3065. if (B instanceof AudioNode) {
  3066. this._nativeConnect(B, outNum, inNum);
  3067. } else {
  3068. this._nativeConnect(B, outNum);
  3069. }
  3070. } catch (e) {
  3071. throw new Error('error connecting to node: ' + B);
  3072. }
  3073. }
  3074. };
  3075. var Tone = function (inputs, outputs) {
  3076. if (isUndef(inputs) || inputs === 1) {
  3077. this.input = this.context.createGain();
  3078. } else if (inputs > 1) {
  3079. this.input = new Array(inputs);
  3080. }
  3081. if (isUndef(outputs) || outputs === 1) {
  3082. this.output = this.context.createGain();
  3083. } else if (outputs > 1) {
  3084. this.output = new Array(inputs);
  3085. }
  3086. };
  3087. Tone.prototype.set = function (params, value, rampTime) {
  3088. if (this.isObject(params)) {
  3089. rampTime = value;
  3090. } else if (this.isString(params)) {
  3091. var tmpObj = {};
  3092. tmpObj[params] = value;
  3093. params = tmpObj;
  3094. }
  3095. for (var attr in params) {
  3096. value = params[attr];
  3097. var parent = this;
  3098. if (attr.indexOf('.') !== -1) {
  3099. var attrSplit = attr.split('.');
  3100. for (var i = 0; i < attrSplit.length - 1; i++) {
  3101. parent = parent[attrSplit[i]];
  3102. }
  3103. attr = attrSplit[attrSplit.length - 1];
  3104. }
  3105. var param = parent[attr];
  3106. if (isUndef(param)) {
  3107. continue;
  3108. }
  3109. if (Tone.Signal && param instanceof Tone.Signal || Tone.Param && param instanceof Tone.Param) {
  3110. if (param.value !== value) {
  3111. if (isUndef(rampTime)) {
  3112. param.value = value;
  3113. } else {
  3114. param.rampTo(value, rampTime);
  3115. }
  3116. }
  3117. } else if (param instanceof AudioParam) {
  3118. if (param.value !== value) {
  3119. param.value = value;
  3120. }
  3121. } else if (param instanceof Tone) {
  3122. param.set(value);
  3123. } else if (param !== value) {
  3124. parent[attr] = value;
  3125. }
  3126. }
  3127. return this;
  3128. };
  3129. Tone.prototype.get = function (params) {
  3130. if (isUndef(params)) {
  3131. params = this._collectDefaults(this.constructor);
  3132. } else if (this.isString(params)) {
  3133. params = [params];
  3134. }
  3135. var ret = {};
  3136. for (var i = 0; i < params.length; i++) {
  3137. var attr = params[i];
  3138. var parent = this;
  3139. var subRet = ret;
  3140. if (attr.indexOf('.') !== -1) {
  3141. var attrSplit = attr.split('.');
  3142. for (var j = 0; j < attrSplit.length - 1; j++) {
  3143. var subAttr = attrSplit[j];
  3144. subRet[subAttr] = subRet[subAttr] || {};
  3145. subRet = subRet[subAttr];
  3146. parent = parent[subAttr];
  3147. }
  3148. attr = attrSplit[attrSplit.length - 1];
  3149. }
  3150. var param = parent[attr];
  3151. if (this.isObject(params[attr])) {
  3152. subRet[attr] = param.get();
  3153. } else if (Tone.Signal && param instanceof Tone.Signal) {
  3154. subRet[attr] = param.value;
  3155. } else if (Tone.Param && param instanceof Tone.Param) {
  3156. subRet[attr] = param.value;
  3157. } else if (param instanceof AudioParam) {
  3158. subRet[attr] = param.value;
  3159. } else if (param instanceof Tone) {
  3160. subRet[attr] = param.get();
  3161. } else if (!isFunction(param) && !isUndef(param)) {
  3162. subRet[attr] = param;
  3163. }
  3164. }
  3165. return ret;
  3166. };
  3167. Tone.prototype._collectDefaults = function (constr) {
  3168. var ret = [];
  3169. if (!isUndef(constr.defaults)) {
  3170. ret = Object.keys(constr.defaults);
  3171. }
  3172. if (!isUndef(constr._super)) {
  3173. var superDefs = this._collectDefaults(constr._super);
  3174. for (var i = 0; i < superDefs.length; i++) {
  3175. if (ret.indexOf(superDefs[i]) === -1) {
  3176. ret.push(superDefs[i]);
  3177. }
  3178. }
  3179. }
  3180. return ret;
  3181. };
  3182. Tone.prototype.toString = function () {
  3183. for (var className in Tone) {
  3184. var isLetter = className[0].match(/^[A-Z]$/);
  3185. var sameConstructor = Tone[className] === this.constructor;
  3186. if (isFunction(Tone[className]) && isLetter && sameConstructor) {
  3187. return className;
  3188. }
  3189. }
  3190. return 'Tone';
  3191. };
  3192. Tone.context = audioContext;
  3193. Tone.prototype.context = Tone.context;
  3194. Tone.prototype.bufferSize = 2048;
  3195. Tone.prototype.blockTime = 128 / Tone.context.sampleRate;
  3196. Tone.prototype.dispose = function () {
  3197. if (!this.isUndef(this.input)) {
  3198. if (this.input instanceof AudioNode) {
  3199. this.input.disconnect();
  3200. }
  3201. this.input = null;
  3202. }
  3203. if (!this.isUndef(this.output)) {
  3204. if (this.output instanceof AudioNode) {
  3205. this.output.disconnect();
  3206. }
  3207. this.output = null;
  3208. }
  3209. return this;
  3210. };
  3211. var _silentNode = null;
  3212. Tone.prototype.noGC = function () {
  3213. this.output.connect(_silentNode);
  3214. return this;
  3215. };
  3216. AudioNode.prototype.noGC = function () {
  3217. this.connect(_silentNode);
  3218. return this;
  3219. };
  3220. Tone.prototype.connect = function (unit, outputNum, inputNum) {
  3221. if (Array.isArray(this.output)) {
  3222. outputNum = this.defaultArg(outputNum, 0);
  3223. this.output[outputNum].connect(unit, 0, inputNum);
  3224. } else {
  3225. this.output.connect(unit, outputNum, inputNum);
  3226. }
  3227. return this;
  3228. };
  3229. Tone.prototype.disconnect = function (outputNum) {
  3230. if (Array.isArray(this.output)) {
  3231. outputNum = this.defaultArg(outputNum, 0);
  3232. this.output[outputNum].disconnect();
  3233. } else {
  3234. this.output.disconnect();
  3235. }
  3236. return this;
  3237. };
  3238. Tone.prototype.connectSeries = function () {
  3239. if (arguments.length > 1) {
  3240. var currentUnit = arguments[0];
  3241. for (var i = 1; i < arguments.length; i++) {
  3242. var toUnit = arguments[i];
  3243. currentUnit.connect(toUnit);
  3244. currentUnit = toUnit;
  3245. }
  3246. }
  3247. return this;
  3248. };
  3249. Tone.prototype.connectParallel = function () {
  3250. var connectFrom = arguments[0];
  3251. if (arguments.length > 1) {
  3252. for (var i = 1; i < arguments.length; i++) {
  3253. var connectTo = arguments[i];
  3254. connectFrom.connect(connectTo);
  3255. }
  3256. }
  3257. return this;
  3258. };
  3259. Tone.prototype.chain = function () {
  3260. if (arguments.length > 0) {
  3261. var currentUnit = this;
  3262. for (var i = 0; i < arguments.length; i++) {
  3263. var toUnit = arguments[i];
  3264. currentUnit.connect(toUnit);
  3265. currentUnit = toUnit;
  3266. }
  3267. }
  3268. return this;
  3269. };
  3270. Tone.prototype.fan = function () {
  3271. if (arguments.length > 0) {
  3272. for (var i = 0; i < arguments.length; i++) {
  3273. this.connect(arguments[i]);
  3274. }
  3275. }
  3276. return this;
  3277. };
  3278. AudioNode.prototype.chain = Tone.prototype.chain;
  3279. AudioNode.prototype.fan = Tone.prototype.fan;
  3280. Tone.prototype.defaultArg = function (given, fallback) {
  3281. if (this.isObject(given) && this.isObject(fallback)) {
  3282. var ret = {};
  3283. for (var givenProp in given) {
  3284. ret[givenProp] = this.defaultArg(fallback[givenProp], given[givenProp]);
  3285. }
  3286. for (var fallbackProp in fallback) {
  3287. ret[fallbackProp] = this.defaultArg(given[fallbackProp], fallback[fallbackProp]);
  3288. }
  3289. return ret;
  3290. } else {
  3291. return isUndef(given) ? fallback : given;
  3292. }
  3293. };
  3294. Tone.prototype.optionsObject = function (values, keys, defaults) {
  3295. var options = {};
  3296. if (values.length === 1 && this.isObject(values[0])) {
  3297. options = values[0];
  3298. } else {
  3299. for (var i = 0; i < keys.length; i++) {
  3300. options[keys[i]] = values[i];
  3301. }
  3302. }
  3303. if (!this.isUndef(defaults)) {
  3304. return this.defaultArg(options, defaults);
  3305. } else {
  3306. return options;
  3307. }
  3308. };
  3309. Tone.prototype.isUndef = isUndef;
  3310. Tone.prototype.isFunction = isFunction;
  3311. Tone.prototype.isNumber = function (arg) {
  3312. return typeof arg === 'number';
  3313. };
  3314. Tone.prototype.isObject = function (arg) {
  3315. return Object.prototype.toString.call(arg) === '[object Object]' && arg.constructor === Object;
  3316. };
  3317. Tone.prototype.isBoolean = function (arg) {
  3318. return typeof arg === 'boolean';
  3319. };
  3320. Tone.prototype.isArray = function (arg) {
  3321. return Array.isArray(arg);
  3322. };
  3323. Tone.prototype.isString = function (arg) {
  3324. return typeof arg === 'string';
  3325. };
  3326. Tone.noOp = function () {
  3327. };
  3328. Tone.prototype._readOnly = function (property) {
  3329. if (Array.isArray(property)) {
  3330. for (var i = 0; i < property.length; i++) {
  3331. this._readOnly(property[i]);
  3332. }
  3333. } else {
  3334. Object.defineProperty(this, property, {
  3335. writable: false,
  3336. enumerable: true
  3337. });
  3338. }
  3339. };
  3340. Tone.prototype._writable = function (property) {
  3341. if (Array.isArray(property)) {
  3342. for (var i = 0; i < property.length; i++) {
  3343. this._writable(property[i]);
  3344. }
  3345. } else {
  3346. Object.defineProperty(this, property, { writable: true });
  3347. }
  3348. };
  3349. Tone.State = {
  3350. Started: 'started',
  3351. Stopped: 'stopped',
  3352. Paused: 'paused'
  3353. };
  3354. Tone.prototype.equalPowerScale = function (percent) {
  3355. var piFactor = 0.5 * Math.PI;
  3356. return Math.sin(percent * piFactor);
  3357. };
  3358. Tone.prototype.dbToGain = function (db) {
  3359. return Math.pow(2, db / 6);
  3360. };
  3361. Tone.prototype.gainToDb = function (gain) {
  3362. return 20 * (Math.log(gain) / Math.LN10);
  3363. };
  3364. Tone.prototype.now = function () {
  3365. return this.context.currentTime;
  3366. };
  3367. Tone.extend = function (child, parent) {
  3368. if (isUndef(parent)) {
  3369. parent = Tone;
  3370. }
  3371. function TempConstructor() {
  3372. }
  3373. TempConstructor.prototype = parent.prototype;
  3374. child.prototype = new TempConstructor();
  3375. child.prototype.constructor = child;
  3376. child._super = parent;
  3377. };
  3378. var newContextCallbacks = [];
  3379. Tone._initAudioContext = function (callback) {
  3380. callback(Tone.context);
  3381. newContextCallbacks.push(callback);
  3382. };
  3383. Tone.setContext = function (ctx) {
  3384. Tone.prototype.context = ctx;
  3385. Tone.context = ctx;
  3386. for (var i = 0; i < newContextCallbacks.length; i++) {
  3387. newContextCallbacks[i](ctx);
  3388. }
  3389. };
  3390. Tone.startMobile = function () {
  3391. var osc = Tone.context.createOscillator();
  3392. var silent = Tone.context.createGain();
  3393. silent.gain.value = 0;
  3394. osc.connect(silent);
  3395. silent.connect(Tone.context.destination);
  3396. var now = Tone.context.currentTime;
  3397. osc.start(now);
  3398. osc.stop(now + 1);
  3399. };
  3400. Tone._initAudioContext(function (audioContext) {
  3401. Tone.prototype.blockTime = 128 / audioContext.sampleRate;
  3402. _silentNode = audioContext.createGain();
  3403. _silentNode.gain.value = 0;
  3404. _silentNode.connect(audioContext.destination);
  3405. });
  3406. Tone.version = 'r7-dev';
  3407. return Tone;
  3408. }();
  3409. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  3410. var Tone_signal_SignalBase;
  3411. Tone_signal_SignalBase = function (Tone) {
  3412. 'use strict';
  3413. Tone.SignalBase = function () {
  3414. };
  3415. Tone.extend(Tone.SignalBase);
  3416. Tone.SignalBase.prototype.connect = function (node, outputNumber, inputNumber) {
  3417. if (Tone.Signal && Tone.Signal === node.constructor || Tone.Param && Tone.Param === node.constructor || Tone.TimelineSignal && Tone.TimelineSignal === node.constructor) {
  3418. node._param.cancelScheduledValues(0);
  3419. node._param.value = 0;
  3420. node.overridden = true;
  3421. } else if (node instanceof AudioParam) {
  3422. node.cancelScheduledValues(0);
  3423. node.value = 0;
  3424. }
  3425. Tone.prototype.connect.call(this, node, outputNumber, inputNumber);
  3426. return this;
  3427. };
  3428. return Tone.SignalBase;
  3429. }(Tone_core_Tone);
  3430. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  3431. var Tone_signal_WaveShaper;
  3432. Tone_signal_WaveShaper = function (Tone) {
  3433. 'use strict';
  3434. Tone.WaveShaper = function (mapping, bufferLen) {
  3435. this._shaper = this.input = this.output = this.context.createWaveShaper();
  3436. this._curve = null;
  3437. if (Array.isArray(mapping)) {
  3438. this.curve = mapping;
  3439. } else if (isFinite(mapping) || this.isUndef(mapping)) {
  3440. this._curve = new Float32Array(this.defaultArg(mapping, 1024));
  3441. } else if (this.isFunction(mapping)) {
  3442. this._curve = new Float32Array(this.defaultArg(bufferLen, 1024));
  3443. this.setMap(mapping);
  3444. }
  3445. };
  3446. Tone.extend(Tone.WaveShaper, Tone.SignalBase);
  3447. Tone.WaveShaper.prototype.setMap = function (mapping) {
  3448. for (var i = 0, len = this._curve.length; i < len; i++) {
  3449. var normalized = i / len * 2 - 1;
  3450. this._curve[i] = mapping(normalized, i);
  3451. }
  3452. this._shaper.curve = this._curve;
  3453. return this;
  3454. };
  3455. Object.defineProperty(Tone.WaveShaper.prototype, 'curve', {
  3456. get: function () {
  3457. return this._shaper.curve;
  3458. },
  3459. set: function (mapping) {
  3460. this._curve = new Float32Array(mapping);
  3461. this._shaper.curve = this._curve;
  3462. }
  3463. });
  3464. Object.defineProperty(Tone.WaveShaper.prototype, 'oversample', {
  3465. get: function () {
  3466. return this._shaper.oversample;
  3467. },
  3468. set: function (oversampling) {
  3469. if ([
  3470. 'none',
  3471. '2x',
  3472. '4x'
  3473. ].indexOf(oversampling) !== -1) {
  3474. this._shaper.oversample = oversampling;
  3475. } else {
  3476. throw new Error('invalid oversampling: ' + oversampling);
  3477. }
  3478. }
  3479. });
  3480. Tone.WaveShaper.prototype.dispose = function () {
  3481. Tone.prototype.dispose.call(this);
  3482. this._shaper.disconnect();
  3483. this._shaper = null;
  3484. this._curve = null;
  3485. return this;
  3486. };
  3487. return Tone.WaveShaper;
  3488. }(Tone_core_Tone);
  3489. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  3490. var Tone_core_Type;
  3491. Tone_core_Type = function (Tone) {
  3492. 'use strict';
  3493. Tone.Type = {
  3494. Default: 'number',
  3495. Time: 'time',
  3496. Frequency: 'frequency',
  3497. NormalRange: 'normalRange',
  3498. AudioRange: 'audioRange',
  3499. Decibels: 'db',
  3500. Interval: 'interval',
  3501. BPM: 'bpm',
  3502. Positive: 'positive',
  3503. Cents: 'cents',
  3504. Degrees: 'degrees',
  3505. MIDI: 'midi',
  3506. TransportTime: 'transportTime',
  3507. Ticks: 'tick',
  3508. Note: 'note',
  3509. Milliseconds: 'milliseconds',
  3510. Notation: 'notation'
  3511. };
  3512. Tone.prototype.isNowRelative = function () {
  3513. var nowRelative = new RegExp(/^\s*\+(.)+/i);
  3514. return function (note) {
  3515. return nowRelative.test(note);
  3516. };
  3517. }();
  3518. Tone.prototype.isTicks = function () {
  3519. var tickFormat = new RegExp(/^\d+i$/i);
  3520. return function (note) {
  3521. return tickFormat.test(note);
  3522. };
  3523. }();
  3524. Tone.prototype.isNotation = function () {
  3525. var notationFormat = new RegExp(/^[0-9]+[mnt]$/i);
  3526. return function (note) {
  3527. return notationFormat.test(note);
  3528. };
  3529. }();
  3530. Tone.prototype.isTransportTime = function () {
  3531. var transportTimeFormat = new RegExp(/^(\d+(\.\d+)?\:){1,2}(\d+(\.\d+)?)?$/i);
  3532. return function (transportTime) {
  3533. return transportTimeFormat.test(transportTime);
  3534. };
  3535. }();
  3536. Tone.prototype.isNote = function () {
  3537. var noteFormat = new RegExp(/^[a-g]{1}(b|#|x|bb)?-?[0-9]+$/i);
  3538. return function (note) {
  3539. return noteFormat.test(note);
  3540. };
  3541. }();
  3542. Tone.prototype.isFrequency = function () {
  3543. var freqFormat = new RegExp(/^\d*\.?\d+hz$/i);
  3544. return function (freq) {
  3545. return freqFormat.test(freq);
  3546. };
  3547. }();
  3548. function getTransportBpm() {
  3549. if (Tone.Transport && Tone.Transport.bpm) {
  3550. return Tone.Transport.bpm.value;
  3551. } else {
  3552. return 120;
  3553. }
  3554. }
  3555. function getTransportTimeSignature() {
  3556. if (Tone.Transport && Tone.Transport.timeSignature) {
  3557. return Tone.Transport.timeSignature;
  3558. } else {
  3559. return 4;
  3560. }
  3561. }
  3562. Tone.prototype.notationToSeconds = function (notation, bpm, timeSignature) {
  3563. bpm = this.defaultArg(bpm, getTransportBpm());
  3564. timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
  3565. var beatTime = 60 / bpm;
  3566. if (notation === '1n') {
  3567. notation = '1m';
  3568. }
  3569. var subdivision = parseInt(notation, 10);
  3570. var beats = 0;
  3571. if (subdivision === 0) {
  3572. beats = 0;
  3573. }
  3574. var lastLetter = notation.slice(-1);
  3575. if (lastLetter === 't') {
  3576. beats = 4 / subdivision * 2 / 3;
  3577. } else if (lastLetter === 'n') {
  3578. beats = 4 / subdivision;
  3579. } else if (lastLetter === 'm') {
  3580. beats = subdivision * timeSignature;
  3581. } else {
  3582. beats = 0;
  3583. }
  3584. return beatTime * beats;
  3585. };
  3586. Tone.prototype.transportTimeToSeconds = function (transportTime, bpm, timeSignature) {
  3587. bpm = this.defaultArg(bpm, getTransportBpm());
  3588. timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
  3589. var measures = 0;
  3590. var quarters = 0;
  3591. var sixteenths = 0;
  3592. var split = transportTime.split(':');
  3593. if (split.length === 2) {
  3594. measures = parseFloat(split[0]);
  3595. quarters = parseFloat(split[1]);
  3596. } else if (split.length === 1) {
  3597. quarters = parseFloat(split[0]);
  3598. } else if (split.length === 3) {
  3599. measures = parseFloat(split[0]);
  3600. quarters = parseFloat(split[1]);
  3601. sixteenths = parseFloat(split[2]);
  3602. }
  3603. var beats = measures * timeSignature + quarters + sixteenths / 4;
  3604. return beats * (60 / bpm);
  3605. };
  3606. Tone.prototype.ticksToSeconds = function (ticks, bpm) {
  3607. if (this.isUndef(Tone.Transport)) {
  3608. return 0;
  3609. }
  3610. ticks = parseFloat(ticks);
  3611. bpm = this.defaultArg(bpm, getTransportBpm());
  3612. var tickTime = 60 / bpm / Tone.Transport.PPQ;
  3613. return tickTime * ticks;
  3614. };
  3615. Tone.prototype.frequencyToSeconds = function (freq) {
  3616. return 1 / parseFloat(freq);
  3617. };
  3618. Tone.prototype.samplesToSeconds = function (samples) {
  3619. return samples / this.context.sampleRate;
  3620. };
  3621. Tone.prototype.secondsToSamples = function (seconds) {
  3622. return seconds * this.context.sampleRate;
  3623. };
  3624. Tone.prototype.secondsToTransportTime = function (seconds, bpm, timeSignature) {
  3625. bpm = this.defaultArg(bpm, getTransportBpm());
  3626. timeSignature = this.defaultArg(timeSignature, getTransportTimeSignature());
  3627. var quarterTime = 60 / bpm;
  3628. var quarters = seconds / quarterTime;
  3629. var measures = Math.floor(quarters / timeSignature);
  3630. var sixteenths = quarters % 1 * 4;
  3631. quarters = Math.floor(quarters) % timeSignature;
  3632. var progress = [
  3633. measures,
  3634. quarters,
  3635. sixteenths
  3636. ];
  3637. return progress.join(':');
  3638. };
  3639. Tone.prototype.secondsToFrequency = function (seconds) {
  3640. return 1 / seconds;
  3641. };
  3642. Tone.prototype.toTransportTime = function (time, bpm, timeSignature) {
  3643. var seconds = this.toSeconds(time);
  3644. return this.secondsToTransportTime(seconds, bpm, timeSignature);
  3645. };
  3646. Tone.prototype.toFrequency = function (freq, now) {
  3647. if (this.isFrequency(freq)) {
  3648. return parseFloat(freq);
  3649. } else if (this.isNotation(freq) || this.isTransportTime(freq)) {
  3650. return this.secondsToFrequency(this.toSeconds(freq, now));
  3651. } else if (this.isNote(freq)) {
  3652. return this.noteToFrequency(freq);
  3653. } else {
  3654. return freq;
  3655. }
  3656. };
  3657. Tone.prototype.toTicks = function (time) {
  3658. if (this.isUndef(Tone.Transport)) {
  3659. return 0;
  3660. }
  3661. var bpm = Tone.Transport.bpm.value;
  3662. var plusNow = 0;
  3663. if (this.isNowRelative(time)) {
  3664. time = time.replace('+', '');
  3665. plusNow = Tone.Transport.ticks;
  3666. } else if (this.isUndef(time)) {
  3667. return Tone.Transport.ticks;
  3668. }
  3669. var seconds = this.toSeconds(time);
  3670. var quarter = 60 / bpm;
  3671. var quarters = seconds / quarter;
  3672. var tickNum = quarters * Tone.Transport.PPQ;
  3673. return Math.round(tickNum + plusNow);
  3674. };
  3675. Tone.prototype.toSamples = function (time) {
  3676. var seconds = this.toSeconds(time);
  3677. return Math.round(seconds * this.context.sampleRate);
  3678. };
  3679. Tone.prototype.toSeconds = function (time, now) {
  3680. now = this.defaultArg(now, this.now());
  3681. if (this.isNumber(time)) {
  3682. return time;
  3683. } else if (this.isString(time)) {
  3684. var plusTime = 0;
  3685. if (this.isNowRelative(time)) {
  3686. time = time.replace('+', '');
  3687. plusTime = now;
  3688. }
  3689. var betweenParens = time.match(/\(([^)(]+)\)/g);
  3690. if (betweenParens) {
  3691. for (var j = 0; j < betweenParens.length; j++) {
  3692. var symbol = betweenParens[j].replace(/[\(\)]/g, '');
  3693. var symbolVal = this.toSeconds(symbol);
  3694. time = time.replace(betweenParens[j], symbolVal);
  3695. }
  3696. }
  3697. if (time.indexOf('@') !== -1) {
  3698. var quantizationSplit = time.split('@');
  3699. if (!this.isUndef(Tone.Transport)) {
  3700. var toQuantize = quantizationSplit[0].trim();
  3701. if (toQuantize === '') {
  3702. toQuantize = undefined;
  3703. }
  3704. if (plusTime > 0) {
  3705. toQuantize = '+' + toQuantize;
  3706. plusTime = 0;
  3707. }
  3708. var subdivision = quantizationSplit[1].trim();
  3709. time = Tone.Transport.quantize(toQuantize, subdivision);
  3710. } else {
  3711. throw new Error('quantization requires Tone.Transport');
  3712. }
  3713. } else {
  3714. var components = time.split(/[\(\)\-\+\/\*]/);
  3715. if (components.length > 1) {
  3716. var originalTime = time;
  3717. for (var i = 0; i < components.length; i++) {
  3718. var symb = components[i].trim();
  3719. if (symb !== '') {
  3720. var val = this.toSeconds(symb);
  3721. time = time.replace(symb, val);
  3722. }
  3723. }
  3724. try {
  3725. time = eval(time);
  3726. } catch (e) {
  3727. throw new EvalError('cannot evaluate Time: ' + originalTime);
  3728. }
  3729. } else if (this.isNotation(time)) {
  3730. time = this.notationToSeconds(time);
  3731. } else if (this.isTransportTime(time)) {
  3732. time = this.transportTimeToSeconds(time);
  3733. } else if (this.isFrequency(time)) {
  3734. time = this.frequencyToSeconds(time);
  3735. } else if (this.isTicks(time)) {
  3736. time = this.ticksToSeconds(time);
  3737. } else {
  3738. time = parseFloat(time);
  3739. }
  3740. }
  3741. return time + plusTime;
  3742. } else {
  3743. return now;
  3744. }
  3745. };
  3746. Tone.prototype.toNotation = function (time, bpm, timeSignature) {
  3747. var testNotations = [
  3748. '1m',
  3749. '2n',
  3750. '4n',
  3751. '8n',
  3752. '16n',
  3753. '32n',
  3754. '64n',
  3755. '128n'
  3756. ];
  3757. var retNotation = toNotationHelper.call(this, time, bpm, timeSignature, testNotations);
  3758. var testTripletNotations = [
  3759. '1m',
  3760. '2n',
  3761. '2t',
  3762. '4n',
  3763. '4t',
  3764. '8n',
  3765. '8t',
  3766. '16n',
  3767. '16t',
  3768. '32n',
  3769. '32t',
  3770. '64n',
  3771. '64t',
  3772. '128n'
  3773. ];
  3774. var retTripletNotation = toNotationHelper.call(this, time, bpm, timeSignature, testTripletNotations);
  3775. if (retTripletNotation.split('+').length < retNotation.split('+').length) {
  3776. return retTripletNotation;
  3777. } else {
  3778. return retNotation;
  3779. }
  3780. };
  3781. function toNotationHelper(time, bpm, timeSignature, testNotations) {
  3782. var seconds = this.toSeconds(time);
  3783. var threshold = this.notationToSeconds(testNotations[testNotations.length - 1], bpm, timeSignature);
  3784. var retNotation = '';
  3785. for (var i = 0; i < testNotations.length; i++) {
  3786. var notationTime = this.notationToSeconds(testNotations[i], bpm, timeSignature);
  3787. var multiple = seconds / notationTime;
  3788. var floatingPointError = 0.000001;
  3789. if (1 - multiple % 1 < floatingPointError) {
  3790. multiple += floatingPointError;
  3791. }
  3792. multiple = Math.floor(multiple);
  3793. if (multiple > 0) {
  3794. if (multiple === 1) {
  3795. retNotation += testNotations[i];
  3796. } else {
  3797. retNotation += multiple.toString() + '*' + testNotations[i];
  3798. }
  3799. seconds -= multiple * notationTime;
  3800. if (seconds < threshold) {
  3801. break;
  3802. } else {
  3803. retNotation += ' + ';
  3804. }
  3805. }
  3806. }
  3807. if (retNotation === '') {
  3808. retNotation = '0';
  3809. }
  3810. return retNotation;
  3811. }
  3812. Tone.prototype.fromUnits = function (val, units) {
  3813. if (this.convert || this.isUndef(this.convert)) {
  3814. switch (units) {
  3815. case Tone.Type.Time:
  3816. return this.toSeconds(val);
  3817. case Tone.Type.Frequency:
  3818. return this.toFrequency(val);
  3819. case Tone.Type.Decibels:
  3820. return this.dbToGain(val);
  3821. case Tone.Type.NormalRange:
  3822. return Math.min(Math.max(val, 0), 1);
  3823. case Tone.Type.AudioRange:
  3824. return Math.min(Math.max(val, -1), 1);
  3825. case Tone.Type.Positive:
  3826. return Math.max(val, 0);
  3827. default:
  3828. return val;
  3829. }
  3830. } else {
  3831. return val;
  3832. }
  3833. };
  3834. Tone.prototype.toUnits = function (val, units) {
  3835. if (this.convert || this.isUndef(this.convert)) {
  3836. switch (units) {
  3837. case Tone.Type.Decibels:
  3838. return this.gainToDb(val);
  3839. default:
  3840. return val;
  3841. }
  3842. } else {
  3843. return val;
  3844. }
  3845. };
  3846. var noteToScaleIndex = {
  3847. 'cbb': -2,
  3848. 'cb': -1,
  3849. 'c': 0,
  3850. 'c#': 1,
  3851. 'cx': 2,
  3852. 'dbb': 0,
  3853. 'db': 1,
  3854. 'd': 2,
  3855. 'd#': 3,
  3856. 'dx': 4,
  3857. 'ebb': 2,
  3858. 'eb': 3,
  3859. 'e': 4,
  3860. 'e#': 5,
  3861. 'ex': 6,
  3862. 'fbb': 3,
  3863. 'fb': 4,
  3864. 'f': 5,
  3865. 'f#': 6,
  3866. 'fx': 7,
  3867. 'gbb': 5,
  3868. 'gb': 6,
  3869. 'g': 7,
  3870. 'g#': 8,
  3871. 'gx': 9,
  3872. 'abb': 7,
  3873. 'ab': 8,
  3874. 'a': 9,
  3875. 'a#': 10,
  3876. 'ax': 11,
  3877. 'bbb': 9,
  3878. 'bb': 10,
  3879. 'b': 11,
  3880. 'b#': 12,
  3881. 'bx': 13
  3882. };
  3883. var scaleIndexToNote = [
  3884. 'C',
  3885. 'C#',
  3886. 'D',
  3887. 'D#',
  3888. 'E',
  3889. 'F',
  3890. 'F#',
  3891. 'G',
  3892. 'G#',
  3893. 'A',
  3894. 'A#',
  3895. 'B'
  3896. ];
  3897. Tone.A4 = 440;
  3898. Tone.prototype.noteToFrequency = function (note) {
  3899. var parts = note.split(/(-?\d+)/);
  3900. if (parts.length === 3) {
  3901. var index = noteToScaleIndex[parts[0].toLowerCase()];
  3902. var octave = parts[1];
  3903. var noteNumber = index + (parseInt(octave, 10) + 1) * 12;
  3904. return this.midiToFrequency(noteNumber);
  3905. } else {
  3906. return 0;
  3907. }
  3908. };
  3909. Tone.prototype.frequencyToNote = function (freq) {
  3910. var log = Math.log(freq / Tone.A4) / Math.LN2;
  3911. var noteNumber = Math.round(12 * log) + 57;
  3912. var octave = Math.floor(noteNumber / 12);
  3913. if (octave < 0) {
  3914. noteNumber += -12 * octave;
  3915. }
  3916. var noteName = scaleIndexToNote[noteNumber % 12];
  3917. return noteName + octave.toString();
  3918. };
  3919. Tone.prototype.intervalToFrequencyRatio = function (interval) {
  3920. return Math.pow(2, interval / 12);
  3921. };
  3922. Tone.prototype.midiToNote = function (midiNumber) {
  3923. var octave = Math.floor(midiNumber / 12) - 1;
  3924. var note = midiNumber % 12;
  3925. return scaleIndexToNote[note] + octave;
  3926. };
  3927. Tone.prototype.noteToMidi = function (note) {
  3928. var parts = note.split(/(\d+)/);
  3929. if (parts.length === 3) {
  3930. var index = noteToScaleIndex[parts[0].toLowerCase()];
  3931. var octave = parts[1];
  3932. return index + (parseInt(octave, 10) + 1) * 12;
  3933. } else {
  3934. return 0;
  3935. }
  3936. };
  3937. Tone.prototype.midiToFrequency = function (midi) {
  3938. return Tone.A4 * Math.pow(2, (midi - 69) / 12);
  3939. };
  3940. return Tone;
  3941. }(Tone_core_Tone);
  3942. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  3943. var Tone_core_Param;
  3944. Tone_core_Param = function (Tone) {
  3945. 'use strict';
  3946. Tone.Param = function () {
  3947. var options = this.optionsObject(arguments, [
  3948. 'param',
  3949. 'units',
  3950. 'convert'
  3951. ], Tone.Param.defaults);
  3952. this._param = this.input = options.param;
  3953. this.units = options.units;
  3954. this.convert = options.convert;
  3955. this.overridden = false;
  3956. if (!this.isUndef(options.value)) {
  3957. this.value = options.value;
  3958. }
  3959. };
  3960. Tone.extend(Tone.Param);
  3961. Tone.Param.defaults = {
  3962. 'units': Tone.Type.Default,
  3963. 'convert': true,
  3964. 'param': undefined
  3965. };
  3966. Object.defineProperty(Tone.Param.prototype, 'value', {
  3967. get: function () {
  3968. return this._toUnits(this._param.value);
  3969. },
  3970. set: function (value) {
  3971. var convertedVal = this._fromUnits(value);
  3972. this._param.value = convertedVal;
  3973. }
  3974. });
  3975. Tone.Param.prototype._fromUnits = function (val) {
  3976. if (this.convert || this.isUndef(this.convert)) {
  3977. switch (this.units) {
  3978. case Tone.Type.Time:
  3979. return this.toSeconds(val);
  3980. case Tone.Type.Frequency:
  3981. return this.toFrequency(val);
  3982. case Tone.Type.Decibels:
  3983. return this.dbToGain(val);
  3984. case Tone.Type.NormalRange:
  3985. return Math.min(Math.max(val, 0), 1);
  3986. case Tone.Type.AudioRange:
  3987. return Math.min(Math.max(val, -1), 1);
  3988. case Tone.Type.Positive:
  3989. return Math.max(val, 0);
  3990. default:
  3991. return val;
  3992. }
  3993. } else {
  3994. return val;
  3995. }
  3996. };
  3997. Tone.Param.prototype._toUnits = function (val) {
  3998. if (this.convert || this.isUndef(this.convert)) {
  3999. switch (this.units) {
  4000. case Tone.Type.Decibels:
  4001. return this.gainToDb(val);
  4002. default:
  4003. return val;
  4004. }
  4005. } else {
  4006. return val;
  4007. }
  4008. };
  4009. Tone.Param.prototype._minOutput = 0.00001;
  4010. Tone.Param.prototype.setValueAtTime = function (value, time) {
  4011. value = this._fromUnits(value);
  4012. this._param.setValueAtTime(value, this.toSeconds(time));
  4013. return this;
  4014. };
  4015. Tone.Param.prototype.setRampPoint = function (now) {
  4016. now = this.defaultArg(now, this.now());
  4017. var currentVal = this._param.value;
  4018. this._param.setValueAtTime(currentVal, now);
  4019. return this;
  4020. };
  4021. Tone.Param.prototype.linearRampToValueAtTime = function (value, endTime) {
  4022. value = this._fromUnits(value);
  4023. this._param.linearRampToValueAtTime(value, this.toSeconds(endTime));
  4024. return this;
  4025. };
  4026. Tone.Param.prototype.exponentialRampToValueAtTime = function (value, endTime) {
  4027. value = this._fromUnits(value);
  4028. value = Math.max(this._minOutput, value);
  4029. this._param.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
  4030. return this;
  4031. };
  4032. Tone.Param.prototype.exponentialRampToValue = function (value, rampTime) {
  4033. var now = this.now();
  4034. var currentVal = this.value;
  4035. this.setValueAtTime(Math.max(currentVal, this._minOutput), now);
  4036. this.exponentialRampToValueAtTime(value, now + this.toSeconds(rampTime));
  4037. return this;
  4038. };
  4039. Tone.Param.prototype.linearRampToValue = function (value, rampTime) {
  4040. var now = this.now();
  4041. this.setRampPoint(now);
  4042. this.linearRampToValueAtTime(value, now + this.toSeconds(rampTime));
  4043. return this;
  4044. };
  4045. Tone.Param.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
  4046. value = this._fromUnits(value);
  4047. value = Math.max(this._minOutput, value);
  4048. timeConstant = Math.max(this._minOutput, timeConstant);
  4049. this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
  4050. return this;
  4051. };
  4052. Tone.Param.prototype.setValueCurveAtTime = function (values, startTime, duration) {
  4053. for (var i = 0; i < values.length; i++) {
  4054. values[i] = this._fromUnits(values[i]);
  4055. }
  4056. this._param.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration));
  4057. return this;
  4058. };
  4059. Tone.Param.prototype.cancelScheduledValues = function (startTime) {
  4060. this._param.cancelScheduledValues(this.toSeconds(startTime));
  4061. return this;
  4062. };
  4063. Tone.Param.prototype.rampTo = function (value, rampTime) {
  4064. rampTime = this.defaultArg(rampTime, 0);
  4065. if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM) {
  4066. this.exponentialRampToValue(value, rampTime);
  4067. } else {
  4068. this.linearRampToValue(value, rampTime);
  4069. }
  4070. return this;
  4071. };
  4072. Tone.Param.prototype.dispose = function () {
  4073. Tone.prototype.dispose.call(this);
  4074. this._param = null;
  4075. return this;
  4076. };
  4077. return Tone.Param;
  4078. }(Tone_core_Tone);
  4079. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  4080. var Tone_core_Gain;
  4081. Tone_core_Gain = function (Tone) {
  4082. 'use strict';
  4083. Tone.Gain = function () {
  4084. var options = this.optionsObject(arguments, [
  4085. 'gain',
  4086. 'units'
  4087. ], Tone.Gain.defaults);
  4088. this.input = this.output = this._gainNode = this.context.createGain();
  4089. this.gain = new Tone.Param({
  4090. 'param': this._gainNode.gain,
  4091. 'units': options.units,
  4092. 'value': options.gain,
  4093. 'convert': options.convert
  4094. });
  4095. this._readOnly('gain');
  4096. };
  4097. Tone.extend(Tone.Gain);
  4098. Tone.Gain.defaults = {
  4099. 'gain': 1,
  4100. 'convert': true
  4101. };
  4102. Tone.Gain.prototype.dispose = function () {
  4103. Tone.Param.prototype.dispose.call(this);
  4104. this._gainNode.disconnect();
  4105. this._gainNode = null;
  4106. this._writable('gain');
  4107. this.gain.dispose();
  4108. this.gain = null;
  4109. };
  4110. return Tone.Gain;
  4111. }(Tone_core_Tone, Tone_core_Param);
  4112. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  4113. var Tone_signal_Signal;
  4114. Tone_signal_Signal = function (Tone) {
  4115. 'use strict';
  4116. Tone.Signal = function () {
  4117. var options = this.optionsObject(arguments, [
  4118. 'value',
  4119. 'units'
  4120. ], Tone.Signal.defaults);
  4121. this.output = this._gain = this.context.createGain();
  4122. options.param = this._gain.gain;
  4123. Tone.Param.call(this, options);
  4124. this.input = this._param = this._gain.gain;
  4125. Tone.Signal._constant.chain(this._gain);
  4126. };
  4127. Tone.extend(Tone.Signal, Tone.Param);
  4128. Tone.Signal.defaults = {
  4129. 'value': 0,
  4130. 'units': Tone.Type.Default,
  4131. 'convert': true
  4132. };
  4133. Tone.Signal.prototype.connect = Tone.SignalBase.prototype.connect;
  4134. Tone.Signal.prototype.dispose = function () {
  4135. Tone.Param.prototype.dispose.call(this);
  4136. this._param = null;
  4137. this._gain.disconnect();
  4138. this._gain = null;
  4139. return this;
  4140. };
  4141. Tone.Signal._constant = null;
  4142. Tone._initAudioContext(function (audioContext) {
  4143. var buffer = audioContext.createBuffer(1, 128, audioContext.sampleRate);
  4144. var arr = buffer.getChannelData(0);
  4145. for (var i = 0; i < arr.length; i++) {
  4146. arr[i] = 1;
  4147. }
  4148. Tone.Signal._constant = audioContext.createBufferSource();
  4149. Tone.Signal._constant.channelCount = 1;
  4150. Tone.Signal._constant.channelCountMode = 'explicit';
  4151. Tone.Signal._constant.buffer = buffer;
  4152. Tone.Signal._constant.loop = true;
  4153. Tone.Signal._constant.start(0);
  4154. Tone.Signal._constant.noGC();
  4155. });
  4156. return Tone.Signal;
  4157. }(Tone_core_Tone, Tone_signal_WaveShaper, Tone_core_Type, Tone_core_Param);
  4158. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  4159. var Tone_signal_Add;
  4160. Tone_signal_Add = function (Tone) {
  4161. 'use strict';
  4162. Tone.Add = function (value) {
  4163. Tone.call(this, 2, 0);
  4164. this._sum = this.input[0] = this.input[1] = this.output = this.context.createGain();
  4165. this._param = this.input[1] = new Tone.Signal(value);
  4166. this._param.connect(this._sum);
  4167. };
  4168. Tone.extend(Tone.Add, Tone.Signal);
  4169. Tone.Add.prototype.dispose = function () {
  4170. Tone.prototype.dispose.call(this);
  4171. this._sum.disconnect();
  4172. this._sum = null;
  4173. this._param.dispose();
  4174. this._param = null;
  4175. return this;
  4176. };
  4177. return Tone.Add;
  4178. }(Tone_core_Tone);
  4179. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  4180. var Tone_signal_Multiply;
  4181. Tone_signal_Multiply = function (Tone) {
  4182. 'use strict';
  4183. Tone.Multiply = function (value) {
  4184. Tone.call(this, 2, 0);
  4185. this._mult = this.input[0] = this.output = this.context.createGain();
  4186. this._param = this.input[1] = this.output.gain;
  4187. this._param.value = this.defaultArg(value, 0);
  4188. };
  4189. Tone.extend(Tone.Multiply, Tone.Signal);
  4190. Tone.Multiply.prototype.dispose = function () {
  4191. Tone.prototype.dispose.call(this);
  4192. this._mult.disconnect();
  4193. this._mult = null;
  4194. this._param = null;
  4195. return this;
  4196. };
  4197. return Tone.Multiply;
  4198. }(Tone_core_Tone);
  4199. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  4200. var Tone_signal_Scale;
  4201. Tone_signal_Scale = function (Tone) {
  4202. 'use strict';
  4203. Tone.Scale = function (outputMin, outputMax) {
  4204. this._outputMin = this.defaultArg(outputMin, 0);
  4205. this._outputMax = this.defaultArg(outputMax, 1);
  4206. this._scale = this.input = new Tone.Multiply(1);
  4207. this._add = this.output = new Tone.Add(0);
  4208. this._scale.connect(this._add);
  4209. this._setRange();
  4210. };
  4211. Tone.extend(Tone.Scale, Tone.SignalBase);
  4212. Object.defineProperty(Tone.Scale.prototype, 'min', {
  4213. get: function () {
  4214. return this._outputMin;
  4215. },
  4216. set: function (min) {
  4217. this._outputMin = min;
  4218. this._setRange();
  4219. }
  4220. });
  4221. Object.defineProperty(Tone.Scale.prototype, 'max', {
  4222. get: function () {
  4223. return this._outputMax;
  4224. },
  4225. set: function (max) {
  4226. this._outputMax = max;
  4227. this._setRange();
  4228. }
  4229. });
  4230. Tone.Scale.prototype._setRange = function () {
  4231. this._add.value = this._outputMin;
  4232. this._scale.value = this._outputMax - this._outputMin;
  4233. };
  4234. Tone.Scale.prototype.dispose = function () {
  4235. Tone.prototype.dispose.call(this);
  4236. this._add.dispose();
  4237. this._add = null;
  4238. this._scale.dispose();
  4239. this._scale = null;
  4240. return this;
  4241. };
  4242. return Tone.Scale;
  4243. }(Tone_core_Tone, Tone_signal_Add, Tone_signal_Multiply);
  4244. var signal;
  4245. signal = function () {
  4246. 'use strict';
  4247. // Signal is built with the Tone.js signal by Yotam Mann
  4248. // https://github.com/TONEnoTONE/Tone.js/
  4249. var Signal = Tone_signal_Signal;
  4250. var Add = Tone_signal_Add;
  4251. var Mult = Tone_signal_Multiply;
  4252. var Scale = Tone_signal_Scale;
  4253. var Tone = Tone_core_Tone;
  4254. var p5sound = master;
  4255. Tone.setContext(p5sound.audiocontext);
  4256. /**
  4257. * <p>p5.Signal is a constant audio-rate signal used by p5.Oscillator
  4258. * and p5.Envelope for modulation math.</p>
  4259. *
  4260. * <p>This is necessary because Web Audio is processed on a seprate clock.
  4261. * For example, the p5 draw loop runs about 60 times per second. But
  4262. * the audio clock must process samples 44100 times per second. If we
  4263. * want to add a value to each of those samples, we can't do it in the
  4264. * draw loop, but we can do it by adding a constant-rate audio signal.</p.
  4265. *
  4266. * <p>This class mostly functions behind the scenes in p5.sound, and returns
  4267. * a Tone.Signal from the Tone.js library by Yotam Mann.
  4268. * If you want to work directly with audio signals for modular
  4269. * synthesis, check out
  4270. * <a href='http://bit.ly/1oIoEng' target=_'blank'>tone.js.</a></p>
  4271. *
  4272. * @class p5.Signal
  4273. * @constructor
  4274. * @return {Tone.Signal} A Signal object from the Tone.js library
  4275. * @example
  4276. * <div><code>
  4277. * function setup() {
  4278. * carrier = new p5.Oscillator('sine');
  4279. * carrier.amp(1); // set amplitude
  4280. * carrier.freq(220); // set frequency
  4281. * carrier.start(); // start oscillating
  4282. *
  4283. * modulator = new p5.Oscillator('sawtooth');
  4284. * modulator.disconnect();
  4285. * modulator.amp(1);
  4286. * modulator.freq(4);
  4287. * modulator.start();
  4288. *
  4289. * // Modulator's default amplitude range is -1 to 1.
  4290. * // Multiply it by -200, so the range is -200 to 200
  4291. * // then add 220 so the range is 20 to 420
  4292. * carrier.freq( modulator.mult(-200).add(220) );
  4293. * }
  4294. * </code></div>
  4295. */
  4296. p5.Signal = function (value) {
  4297. var s = new Signal(value);
  4298. // p5sound.soundArray.push(s);
  4299. return s;
  4300. };
  4301. /**
  4302. * Fade to value, for smooth transitions
  4303. *
  4304. * @method fade
  4305. * @param {Number} value Value to set this signal
  4306. * @param {Number} [secondsFromNow] Length of fade, in seconds from now
  4307. */
  4308. Signal.prototype.fade = Signal.prototype.linearRampToValueAtTime;
  4309. Mult.prototype.fade = Signal.prototype.fade;
  4310. Add.prototype.fade = Signal.prototype.fade;
  4311. Scale.prototype.fade = Signal.prototype.fade;
  4312. /**
  4313. * Connect a p5.sound object or Web Audio node to this
  4314. * p5.Signal so that its amplitude values can be scaled.
  4315. *
  4316. * @param {Object} input
  4317. */
  4318. Signal.prototype.setInput = function (_input) {
  4319. _input.connect(this);
  4320. };
  4321. Mult.prototype.setInput = Signal.prototype.setInput;
  4322. Add.prototype.setInput = Signal.prototype.setInput;
  4323. Scale.prototype.setInput = Signal.prototype.setInput;
  4324. // signals can add / mult / scale themselves
  4325. /**
  4326. * Add a constant value to this audio signal,
  4327. * and return the resulting audio signal. Does
  4328. * not change the value of the original signal,
  4329. * instead it returns a new p5.SignalAdd.
  4330. *
  4331. * @method add
  4332. * @param {Number} number
  4333. * @return {p5.SignalAdd} object
  4334. */
  4335. Signal.prototype.add = function (num) {
  4336. var add = new Add(num);
  4337. // add.setInput(this);
  4338. this.connect(add);
  4339. return add;
  4340. };
  4341. Mult.prototype.add = Signal.prototype.add;
  4342. Add.prototype.add = Signal.prototype.add;
  4343. Scale.prototype.add = Signal.prototype.add;
  4344. /**
  4345. * Multiply this signal by a constant value,
  4346. * and return the resulting audio signal. Does
  4347. * not change the value of the original signal,
  4348. * instead it returns a new p5.SignalMult.
  4349. *
  4350. * @method mult
  4351. * @param {Number} number to multiply
  4352. * @return {Tone.Multiply} object
  4353. */
  4354. Signal.prototype.mult = function (num) {
  4355. var mult = new Mult(num);
  4356. // mult.setInput(this);
  4357. this.connect(mult);
  4358. return mult;
  4359. };
  4360. Mult.prototype.mult = Signal.prototype.mult;
  4361. Add.prototype.mult = Signal.prototype.mult;
  4362. Scale.prototype.mult = Signal.prototype.mult;
  4363. /**
  4364. * Scale this signal value to a given range,
  4365. * and return the result as an audio signal. Does
  4366. * not change the value of the original signal,
  4367. * instead it returns a new p5.SignalScale.
  4368. *
  4369. * @method scale
  4370. * @param {Number} number to multiply
  4371. * @param {Number} inMin input range minumum
  4372. * @param {Number} inMax input range maximum
  4373. * @param {Number} outMin input range minumum
  4374. * @param {Number} outMax input range maximum
  4375. * @return {p5.SignalScale} object
  4376. */
  4377. Signal.prototype.scale = function (inMin, inMax, outMin, outMax) {
  4378. var mapOutMin, mapOutMax;
  4379. if (arguments.length === 4) {
  4380. mapOutMin = p5.prototype.map(outMin, inMin, inMax, 0, 1) - 0.5;
  4381. mapOutMax = p5.prototype.map(outMax, inMin, inMax, 0, 1) - 0.5;
  4382. } else {
  4383. mapOutMin = arguments[0];
  4384. mapOutMax = arguments[1];
  4385. }
  4386. var scale = new Scale(mapOutMin, mapOutMax);
  4387. this.connect(scale);
  4388. return scale;
  4389. };
  4390. Mult.prototype.scale = Signal.prototype.scale;
  4391. Add.prototype.scale = Signal.prototype.scale;
  4392. Scale.prototype.scale = Signal.prototype.scale;
  4393. }(Tone_signal_Signal, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_core_Tone, master);
  4394. var oscillator;
  4395. oscillator = function () {
  4396. 'use strict';
  4397. var p5sound = master;
  4398. var Signal = Tone_signal_Signal;
  4399. var Add = Tone_signal_Add;
  4400. var Mult = Tone_signal_Multiply;
  4401. var Scale = Tone_signal_Scale;
  4402. /**
  4403. * <p>Creates a signal that oscillates between -1.0 and 1.0.
  4404. * By default, the oscillation takes the form of a sinusoidal
  4405. * shape ('sine'). Additional types include 'triangle',
  4406. * 'sawtooth' and 'square'. The frequency defaults to
  4407. * 440 oscillations per second (440Hz, equal to the pitch of an
  4408. * 'A' note).</p>
  4409. *
  4410. * <p>Set the type of oscillation with setType(), or by creating a
  4411. * specific oscillator.</p> For example:
  4412. * <code>new p5.SinOsc(freq)</code>
  4413. * <code>new p5.TriOsc(freq)</code>
  4414. * <code>new p5.SqrOsc(freq)</code>
  4415. * <code>new p5.SawOsc(freq)</code>.
  4416. * </p>
  4417. *
  4418. * @class p5.Oscillator
  4419. * @constructor
  4420. * @param {Number} [freq] frequency defaults to 440Hz
  4421. * @param {String} [type] type of oscillator. Options:
  4422. * 'sine' (default), 'triangle',
  4423. * 'sawtooth', 'square'
  4424. * @return {Object} Oscillator object
  4425. * @example
  4426. * <div><code>
  4427. * var osc;
  4428. * var playing = false;
  4429. *
  4430. * function setup() {
  4431. * backgroundColor = color(255,0,255);
  4432. * textAlign(CENTER);
  4433. *
  4434. * osc = new p5.Oscillator();
  4435. * osc.setType('sine');
  4436. * osc.freq(240);
  4437. * osc.amp(0);
  4438. * osc.start();
  4439. * }
  4440. *
  4441. * function draw() {
  4442. * background(backgroundColor)
  4443. * text('click to play', width/2, height/2);
  4444. * }
  4445. *
  4446. * function mouseClicked() {
  4447. * if (mouseX > 0 && mouseX < width && mouseY < height && mouseY > 0) {
  4448. * if (!playing) {
  4449. * // ramp amplitude to 0.5 over 0.1 seconds
  4450. * osc.amp(0.5, 0.05);
  4451. * playing = true;
  4452. * backgroundColor = color(0,255,255);
  4453. * } else {
  4454. * // ramp amplitude to 0 over 0.5 seconds
  4455. * osc.amp(0, 0.5);
  4456. * playing = false;
  4457. * backgroundColor = color(255,0,255);
  4458. * }
  4459. * }
  4460. * }
  4461. * </code> </div>
  4462. */
  4463. p5.Oscillator = function (freq, type) {
  4464. if (typeof freq === 'string') {
  4465. var f = type;
  4466. type = freq;
  4467. freq = f;
  4468. }
  4469. if (typeof type === 'number') {
  4470. var f = type;
  4471. type = freq;
  4472. freq = f;
  4473. }
  4474. this.started = false;
  4475. // components
  4476. this.phaseAmount = undefined;
  4477. this.oscillator = p5sound.audiocontext.createOscillator();
  4478. this.f = freq || 440;
  4479. // frequency
  4480. this.oscillator.type = type || 'sine';
  4481. this.oscillator.frequency.setValueAtTime(this.f, p5sound.audiocontext.currentTime);
  4482. var o = this.oscillator;
  4483. // connections
  4484. this.output = p5sound.audiocontext.createGain();
  4485. this._freqMods = [];
  4486. // modulators connected to this oscillator's frequency
  4487. // set default output gain to 0.5
  4488. this.output.gain.value = 0.5;
  4489. this.output.gain.setValueAtTime(0.5, p5sound.audiocontext.currentTime);
  4490. this.oscillator.connect(this.output);
  4491. // stereo panning
  4492. this.panPosition = 0;
  4493. this.connection = p5sound.input;
  4494. // connect to p5sound by default
  4495. this.panner = new p5.Panner(this.output, this.connection, 1);
  4496. //array of math operation signal chaining
  4497. this.mathOps = [this.output];
  4498. // add to the soundArray so we can dispose of the osc later
  4499. p5sound.soundArray.push(this);
  4500. };
  4501. /**
  4502. * Start an oscillator. Accepts an optional parameter to
  4503. * determine how long (in seconds from now) until the
  4504. * oscillator starts.
  4505. *
  4506. * @method start
  4507. * @param {Number} [time] startTime in seconds from now.
  4508. * @param {Number} [frequency] frequency in Hz.
  4509. */
  4510. p5.Oscillator.prototype.start = function (time, f) {
  4511. if (this.started) {
  4512. var now = p5sound.audiocontext.currentTime;
  4513. this.stop(now);
  4514. }
  4515. if (!this.started) {
  4516. var freq = f || this.f;
  4517. var type = this.oscillator.type;
  4518. // set old osc free to be garbage collected (memory)
  4519. if (this.oscillator) {
  4520. this.oscillator.disconnect();
  4521. this.oscillator = undefined;
  4522. }
  4523. // var detune = this.oscillator.frequency.value;
  4524. this.oscillator = p5sound.audiocontext.createOscillator();
  4525. this.oscillator.frequency.exponentialRampToValueAtTime(Math.abs(freq), p5sound.audiocontext.currentTime);
  4526. this.oscillator.type = type;
  4527. // this.oscillator.detune.value = detune;
  4528. this.oscillator.connect(this.output);
  4529. time = time || 0;
  4530. this.oscillator.start(time + p5sound.audiocontext.currentTime);
  4531. this.freqNode = this.oscillator.frequency;
  4532. // if other oscillators are already connected to this osc's freq
  4533. for (var i in this._freqMods) {
  4534. if (typeof this._freqMods[i].connect !== 'undefined') {
  4535. this._freqMods[i].connect(this.oscillator.frequency);
  4536. }
  4537. }
  4538. this.started = true;
  4539. }
  4540. };
  4541. /**
  4542. * Stop an oscillator. Accepts an optional parameter
  4543. * to determine how long (in seconds from now) until the
  4544. * oscillator stops.
  4545. *
  4546. * @method stop
  4547. * @param {Number} secondsFromNow Time, in seconds from now.
  4548. */
  4549. p5.Oscillator.prototype.stop = function (time) {
  4550. if (this.started) {
  4551. var t = time || 0;
  4552. var now = p5sound.audiocontext.currentTime;
  4553. this.oscillator.stop(t + now);
  4554. this.started = false;
  4555. }
  4556. };
  4557. /**
  4558. * Set the amplitude between 0 and 1.0. Or, pass in an object
  4559. * such as an oscillator to modulate amplitude with an audio signal.
  4560. *
  4561. * @method amp
  4562. * @param {Number|Object} vol between 0 and 1.0
  4563. * or a modulating signal/oscillator
  4564. * @param {Number} [rampTime] create a fade that lasts rampTime
  4565. * @param {Number} [timeFromNow] schedule this event to happen
  4566. * seconds from now
  4567. * @return {AudioParam} gain If no value is provided,
  4568. * returns the Web Audio API
  4569. * AudioParam that controls
  4570. * this oscillator's
  4571. * gain/amplitude/volume)
  4572. */
  4573. p5.Oscillator.prototype.amp = function (vol, rampTime, tFromNow) {
  4574. var self = this;
  4575. if (typeof vol === 'number') {
  4576. var rampTime = rampTime || 0;
  4577. var tFromNow = tFromNow || 0;
  4578. var now = p5sound.audiocontext.currentTime;
  4579. var currentVol = this.output.gain.value;
  4580. this.output.gain.cancelScheduledValues(now);
  4581. this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
  4582. this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
  4583. } else if (vol) {
  4584. vol.connect(self.output.gain);
  4585. } else {
  4586. // return the Gain Node
  4587. return this.output.gain;
  4588. }
  4589. };
  4590. // these are now the same thing
  4591. p5.Oscillator.prototype.fade = p5.Oscillator.prototype.amp;
  4592. p5.Oscillator.prototype.getAmp = function () {
  4593. return this.output.gain.value;
  4594. };
  4595. /**
  4596. * Set frequency of an oscillator to a value. Or, pass in an object
  4597. * such as an oscillator to modulate the frequency with an audio signal.
  4598. *
  4599. * @method freq
  4600. * @param {Number|Object} Frequency Frequency in Hz
  4601. * or modulating signal/oscillator
  4602. * @param {Number} [rampTime] Ramp time (in seconds)
  4603. * @param {Number} [timeFromNow] Schedule this event to happen
  4604. * at x seconds from now
  4605. * @return {AudioParam} Frequency If no value is provided,
  4606. * returns the Web Audio API
  4607. * AudioParam that controls
  4608. * this oscillator's frequency
  4609. * @example
  4610. * <div><code>
  4611. * var osc = new p5.Oscillator(300);
  4612. * osc.start();
  4613. * osc.freq(40, 10);
  4614. * </code></div>
  4615. */
  4616. p5.Oscillator.prototype.freq = function (val, rampTime, tFromNow) {
  4617. if (typeof val === 'number' && !isNaN(val)) {
  4618. this.f = val;
  4619. var now = p5sound.audiocontext.currentTime;
  4620. var rampTime = rampTime || 0;
  4621. var tFromNow = tFromNow || 0;
  4622. // var currentFreq = this.oscillator.frequency.value;
  4623. // this.oscillator.frequency.cancelScheduledValues(now);
  4624. if (rampTime == 0) {
  4625. this.oscillator.frequency.cancelScheduledValues(now);
  4626. this.oscillator.frequency.setValueAtTime(val, tFromNow + now);
  4627. } else {
  4628. if (val > 0) {
  4629. this.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
  4630. } else {
  4631. this.oscillator.frequency.linearRampToValueAtTime(val, tFromNow + rampTime + now);
  4632. }
  4633. }
  4634. // reset phase if oscillator has a phase
  4635. if (this.phaseAmount) {
  4636. this.phase(this.phaseAmount);
  4637. }
  4638. } else if (val) {
  4639. if (val.output) {
  4640. val = val.output;
  4641. }
  4642. val.connect(this.oscillator.frequency);
  4643. // keep track of what is modulating this param
  4644. // so it can be re-connected if
  4645. this._freqMods.push(val);
  4646. } else {
  4647. // return the Frequency Node
  4648. return this.oscillator.frequency;
  4649. }
  4650. };
  4651. p5.Oscillator.prototype.getFreq = function () {
  4652. return this.oscillator.frequency.value;
  4653. };
  4654. /**
  4655. * Set type to 'sine', 'triangle', 'sawtooth' or 'square'.
  4656. *
  4657. * @method setType
  4658. * @param {String} type 'sine', 'triangle', 'sawtooth' or 'square'.
  4659. */
  4660. p5.Oscillator.prototype.setType = function (type) {
  4661. this.oscillator.type = type;
  4662. };
  4663. p5.Oscillator.prototype.getType = function () {
  4664. return this.oscillator.type;
  4665. };
  4666. /**
  4667. * Connect to a p5.sound / Web Audio object.
  4668. *
  4669. * @method connect
  4670. * @param {Object} unit A p5.sound or Web Audio object
  4671. */
  4672. p5.Oscillator.prototype.connect = function (unit) {
  4673. if (!unit) {
  4674. this.panner.connect(p5sound.input);
  4675. } else if (unit.hasOwnProperty('input')) {
  4676. this.panner.connect(unit.input);
  4677. this.connection = unit.input;
  4678. } else {
  4679. this.panner.connect(unit);
  4680. this.connection = unit;
  4681. }
  4682. };
  4683. /**
  4684. * Disconnect all outputs
  4685. *
  4686. * @method disconnect
  4687. */
  4688. p5.Oscillator.prototype.disconnect = function (unit) {
  4689. this.output.disconnect();
  4690. this.panner.disconnect();
  4691. this.output.connect(this.panner);
  4692. this.oscMods = [];
  4693. };
  4694. /**
  4695. * Pan between Left (-1) and Right (1)
  4696. *
  4697. * @method pan
  4698. * @param {Number} panning Number between -1 and 1
  4699. * @param {Number} timeFromNow schedule this event to happen
  4700. * seconds from now
  4701. */
  4702. p5.Oscillator.prototype.pan = function (pval, tFromNow) {
  4703. this.panPosition = pval;
  4704. this.panner.pan(pval, tFromNow);
  4705. };
  4706. p5.Oscillator.prototype.getPan = function () {
  4707. return this.panPosition;
  4708. };
  4709. // get rid of the oscillator
  4710. p5.Oscillator.prototype.dispose = function () {
  4711. // remove reference from soundArray
  4712. var index = p5sound.soundArray.indexOf(this);
  4713. p5sound.soundArray.splice(index, 1);
  4714. if (this.oscillator) {
  4715. var now = p5sound.audiocontext.currentTime;
  4716. this.stop(now);
  4717. this.disconnect();
  4718. this.panner = null;
  4719. this.oscillator = null;
  4720. }
  4721. // if it is a Pulse
  4722. if (this.osc2) {
  4723. this.osc2.dispose();
  4724. }
  4725. };
  4726. /**
  4727. * Set the phase of an oscillator between 0.0 and 1.0.
  4728. * In this implementation, phase is a delay time
  4729. * based on the oscillator's current frequency.
  4730. *
  4731. * @method phase
  4732. * @param {Number} phase float between 0.0 and 1.0
  4733. */
  4734. p5.Oscillator.prototype.phase = function (p) {
  4735. var delayAmt = p5.prototype.map(p, 0, 1, 0, 1 / this.f);
  4736. var now = p5sound.audiocontext.currentTime;
  4737. this.phaseAmount = p;
  4738. if (!this.dNode) {
  4739. // create a delay node
  4740. this.dNode = p5sound.audiocontext.createDelay();
  4741. // put the delay node in between output and panner
  4742. this.oscillator.disconnect();
  4743. this.oscillator.connect(this.dNode);
  4744. this.dNode.connect(this.output);
  4745. }
  4746. // set delay time to match phase:
  4747. this.dNode.delayTime.setValueAtTime(delayAmt, now);
  4748. };
  4749. // ========================== //
  4750. // SIGNAL MATH FOR MODULATION //
  4751. // ========================== //
  4752. // return sigChain(this, scale, thisChain, nextChain, Scale);
  4753. var sigChain = function (o, mathObj, thisChain, nextChain, type) {
  4754. var chainSource = o.oscillator;
  4755. // if this type of math already exists in the chain, replace it
  4756. for (var i in o.mathOps) {
  4757. if (o.mathOps[i] instanceof type) {
  4758. chainSource.disconnect();
  4759. o.mathOps[i].dispose();
  4760. thisChain = i;
  4761. // assume nextChain is output gain node unless...
  4762. if (thisChain < o.mathOps.length - 2) {
  4763. nextChain = o.mathOps[i + 1];
  4764. }
  4765. }
  4766. }
  4767. if (thisChain == o.mathOps.length - 1) {
  4768. o.mathOps.push(nextChain);
  4769. }
  4770. // assume source is the oscillator unless i > 0
  4771. if (i > 0) {
  4772. chainSource = o.mathOps[i - 1];
  4773. }
  4774. chainSource.disconnect();
  4775. chainSource.connect(mathObj);
  4776. mathObj.connect(nextChain);
  4777. o.mathOps[thisChain] = mathObj;
  4778. return o;
  4779. };
  4780. /**
  4781. * Add a value to the p5.Oscillator's output amplitude,
  4782. * and return the oscillator. Calling this method again
  4783. * will override the initial add() with a new value.
  4784. *
  4785. * @method add
  4786. * @param {Number} number Constant number to add
  4787. * @return {p5.Oscillator} Oscillator Returns this oscillator
  4788. * with scaled output
  4789. *
  4790. */
  4791. p5.Oscillator.prototype.add = function (num) {
  4792. var add = new Add(num);
  4793. var thisChain = this.mathOps.length - 1;
  4794. var nextChain = this.output;
  4795. return sigChain(this, add, thisChain, nextChain, Add);
  4796. };
  4797. /**
  4798. * Multiply the p5.Oscillator's output amplitude
  4799. * by a fixed value (i.e. turn it up!). Calling this method
  4800. * again will override the initial mult() with a new value.
  4801. *
  4802. * @method mult
  4803. * @param {Number} number Constant number to multiply
  4804. * @return {p5.Oscillator} Oscillator Returns this oscillator
  4805. * with multiplied output
  4806. */
  4807. p5.Oscillator.prototype.mult = function (num) {
  4808. var mult = new Mult(num);
  4809. var thisChain = this.mathOps.length - 1;
  4810. var nextChain = this.output;
  4811. return sigChain(this, mult, thisChain, nextChain, Mult);
  4812. };
  4813. /**
  4814. * Scale this oscillator's amplitude values to a given
  4815. * range, and return the oscillator. Calling this method
  4816. * again will override the initial scale() with new values.
  4817. *
  4818. * @method scale
  4819. * @param {Number} inMin input range minumum
  4820. * @param {Number} inMax input range maximum
  4821. * @param {Number} outMin input range minumum
  4822. * @param {Number} outMax input range maximum
  4823. * @return {p5.Oscillator} Oscillator Returns this oscillator
  4824. * with scaled output
  4825. */
  4826. p5.Oscillator.prototype.scale = function (inMin, inMax, outMin, outMax) {
  4827. var mapOutMin, mapOutMax;
  4828. if (arguments.length === 4) {
  4829. mapOutMin = p5.prototype.map(outMin, inMin, inMax, 0, 1) - 0.5;
  4830. mapOutMax = p5.prototype.map(outMax, inMin, inMax, 0, 1) - 0.5;
  4831. } else {
  4832. mapOutMin = arguments[0];
  4833. mapOutMax = arguments[1];
  4834. }
  4835. var scale = new Scale(mapOutMin, mapOutMax);
  4836. var thisChain = this.mathOps.length - 1;
  4837. var nextChain = this.output;
  4838. return sigChain(this, scale, thisChain, nextChain, Scale);
  4839. };
  4840. // ============================== //
  4841. // SinOsc, TriOsc, SqrOsc, SawOsc //
  4842. // ============================== //
  4843. /**
  4844. * Constructor: <code>new p5.SinOsc()</code>.
  4845. * This creates a Sine Wave Oscillator and is
  4846. * equivalent to <code> new p5.Oscillator('sine')
  4847. * </code> or creating a p5.Oscillator and then calling
  4848. * its method <code>setType('sine')</code>.
  4849. * See p5.Oscillator for methods.
  4850. *
  4851. * @method p5.SinOsc
  4852. * @param {Number} [freq] Set the frequency
  4853. */
  4854. p5.SinOsc = function (freq) {
  4855. p5.Oscillator.call(this, freq, 'sine');
  4856. };
  4857. p5.SinOsc.prototype = Object.create(p5.Oscillator.prototype);
  4858. /**
  4859. * Constructor: <code>new p5.TriOsc()</code>.
  4860. * This creates a Triangle Wave Oscillator and is
  4861. * equivalent to <code>new p5.Oscillator('triangle')
  4862. * </code> or creating a p5.Oscillator and then calling
  4863. * its method <code>setType('triangle')</code>.
  4864. * See p5.Oscillator for methods.
  4865. *
  4866. * @method p5.TriOsc
  4867. * @param {Number} [freq] Set the frequency
  4868. */
  4869. p5.TriOsc = function (freq) {
  4870. p5.Oscillator.call(this, freq, 'triangle');
  4871. };
  4872. p5.TriOsc.prototype = Object.create(p5.Oscillator.prototype);
  4873. /**
  4874. * Constructor: <code>new p5.SawOsc()</code>.
  4875. * This creates a SawTooth Wave Oscillator and is
  4876. * equivalent to <code> new p5.Oscillator('sawtooth')
  4877. * </code> or creating a p5.Oscillator and then calling
  4878. * its method <code>setType('sawtooth')</code>.
  4879. * See p5.Oscillator for methods.
  4880. *
  4881. * @method p5.SawOsc
  4882. * @param {Number} [freq] Set the frequency
  4883. */
  4884. p5.SawOsc = function (freq) {
  4885. p5.Oscillator.call(this, freq, 'sawtooth');
  4886. };
  4887. p5.SawOsc.prototype = Object.create(p5.Oscillator.prototype);
  4888. /**
  4889. * Constructor: <code>new p5.SqrOsc()</code>.
  4890. * This creates a Square Wave Oscillator and is
  4891. * equivalent to <code> new p5.Oscillator('square')
  4892. * </code> or creating a p5.Oscillator and then calling
  4893. * its method <code>setType('square')</code>.
  4894. * See p5.Oscillator for methods.
  4895. *
  4896. * @method p5.SqrOsc
  4897. * @param {Number} [freq] Set the frequency
  4898. */
  4899. p5.SqrOsc = function (freq) {
  4900. p5.Oscillator.call(this, freq, 'square');
  4901. };
  4902. p5.SqrOsc.prototype = Object.create(p5.Oscillator.prototype);
  4903. }(master, Tone_signal_Signal, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale);
  4904. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  4905. var Tone_core_Timeline;
  4906. Tone_core_Timeline = function (Tone) {
  4907. 'use strict';
  4908. Tone.Timeline = function () {
  4909. var options = this.optionsObject(arguments, ['memory'], Tone.Timeline.defaults);
  4910. this._timeline = [];
  4911. this._toRemove = [];
  4912. this._iterating = false;
  4913. this.memory = options.memory;
  4914. };
  4915. Tone.extend(Tone.Timeline);
  4916. Tone.Timeline.defaults = { 'memory': Infinity };
  4917. Object.defineProperty(Tone.Timeline.prototype, 'length', {
  4918. get: function () {
  4919. return this._timeline.length;
  4920. }
  4921. });
  4922. Tone.Timeline.prototype.addEvent = function (event) {
  4923. if (this.isUndef(event.time)) {
  4924. throw new Error('events must have a time attribute');
  4925. }
  4926. event.time = this.toSeconds(event.time);
  4927. if (this._timeline.length) {
  4928. var index = this._search(event.time);
  4929. this._timeline.splice(index + 1, 0, event);
  4930. } else {
  4931. this._timeline.push(event);
  4932. }
  4933. if (this.length > this.memory) {
  4934. var diff = this.length - this.memory;
  4935. this._timeline.splice(0, diff);
  4936. }
  4937. return this;
  4938. };
  4939. Tone.Timeline.prototype.removeEvent = function (event) {
  4940. if (this._iterating) {
  4941. this._toRemove.push(event);
  4942. } else {
  4943. var index = this._timeline.indexOf(event);
  4944. if (index !== -1) {
  4945. this._timeline.splice(index, 1);
  4946. }
  4947. }
  4948. return this;
  4949. };
  4950. Tone.Timeline.prototype.getEvent = function (time) {
  4951. time = this.toSeconds(time);
  4952. var index = this._search(time);
  4953. if (index !== -1) {
  4954. return this._timeline[index];
  4955. } else {
  4956. return null;
  4957. }
  4958. };
  4959. Tone.Timeline.prototype.getEventAfter = function (time) {
  4960. time = this.toSeconds(time);
  4961. var index = this._search(time);
  4962. if (index + 1 < this._timeline.length) {
  4963. return this._timeline[index + 1];
  4964. } else {
  4965. return null;
  4966. }
  4967. };
  4968. Tone.Timeline.prototype.getEventBefore = function (time) {
  4969. time = this.toSeconds(time);
  4970. var index = this._search(time);
  4971. if (index - 1 >= 0) {
  4972. return this._timeline[index - 1];
  4973. } else {
  4974. return null;
  4975. }
  4976. };
  4977. Tone.Timeline.prototype.cancel = function (after) {
  4978. if (this._timeline.length > 1) {
  4979. after = this.toSeconds(after);
  4980. var index = this._search(after);
  4981. if (index >= 0) {
  4982. this._timeline = this._timeline.slice(0, index);
  4983. } else {
  4984. this._timeline = [];
  4985. }
  4986. } else if (this._timeline.length === 1) {
  4987. if (this._timeline[0].time >= after) {
  4988. this._timeline = [];
  4989. }
  4990. }
  4991. return this;
  4992. };
  4993. Tone.Timeline.prototype.cancelBefore = function (time) {
  4994. if (this._timeline.length) {
  4995. time = this.toSeconds(time);
  4996. var index = this._search(time);
  4997. if (index >= 0) {
  4998. this._timeline = this._timeline.slice(index + 1);
  4999. }
  5000. }
  5001. return this;
  5002. };
  5003. Tone.Timeline.prototype._search = function (time) {
  5004. var beginning = 0;
  5005. var len = this._timeline.length;
  5006. var end = len;
  5007. while (beginning <= end && beginning < len) {
  5008. var midPoint = Math.floor(beginning + (end - beginning) / 2);
  5009. var event = this._timeline[midPoint];
  5010. if (event.time === time) {
  5011. for (var i = midPoint; i < this._timeline.length; i++) {
  5012. var testEvent = this._timeline[i];
  5013. if (testEvent.time === time) {
  5014. midPoint = i;
  5015. }
  5016. }
  5017. return midPoint;
  5018. } else if (event.time > time) {
  5019. end = midPoint - 1;
  5020. } else if (event.time < time) {
  5021. beginning = midPoint + 1;
  5022. }
  5023. }
  5024. return beginning - 1;
  5025. };
  5026. Tone.Timeline.prototype._iterate = function (callback, lowerBound, upperBound) {
  5027. this._iterating = true;
  5028. lowerBound = this.defaultArg(lowerBound, 0);
  5029. upperBound = this.defaultArg(upperBound, this._timeline.length - 1);
  5030. for (var i = lowerBound; i <= upperBound; i++) {
  5031. callback(this._timeline[i]);
  5032. }
  5033. this._iterating = false;
  5034. if (this._toRemove.length > 0) {
  5035. for (var j = 0; j < this._toRemove.length; j++) {
  5036. var index = this._timeline.indexOf(this._toRemove[j]);
  5037. if (index !== -1) {
  5038. this._timeline.splice(index, 1);
  5039. }
  5040. }
  5041. this._toRemove = [];
  5042. }
  5043. };
  5044. Tone.Timeline.prototype.forEach = function (callback) {
  5045. this._iterate(callback);
  5046. return this;
  5047. };
  5048. Tone.Timeline.prototype.forEachBefore = function (time, callback) {
  5049. time = this.toSeconds(time);
  5050. var upperBound = this._search(time);
  5051. if (upperBound !== -1) {
  5052. this._iterate(callback, 0, upperBound);
  5053. }
  5054. return this;
  5055. };
  5056. Tone.Timeline.prototype.forEachAfter = function (time, callback) {
  5057. time = this.toSeconds(time);
  5058. var lowerBound = this._search(time);
  5059. this._iterate(callback, lowerBound + 1);
  5060. return this;
  5061. };
  5062. Tone.Timeline.prototype.forEachFrom = function (time, callback) {
  5063. time = this.toSeconds(time);
  5064. var lowerBound = this._search(time);
  5065. while (lowerBound >= 0 && this._timeline[lowerBound].time >= time) {
  5066. lowerBound--;
  5067. }
  5068. this._iterate(callback, lowerBound + 1);
  5069. return this;
  5070. };
  5071. Tone.Timeline.prototype.forEachAtTime = function (time, callback) {
  5072. time = this.toSeconds(time);
  5073. var upperBound = this._search(time);
  5074. if (upperBound !== -1) {
  5075. this._iterate(function (event) {
  5076. if (event.time === time) {
  5077. callback(event);
  5078. }
  5079. }, 0, upperBound);
  5080. }
  5081. return this;
  5082. };
  5083. Tone.Timeline.prototype.dispose = function () {
  5084. Tone.prototype.dispose.call(this);
  5085. this._timeline = null;
  5086. this._toRemove = null;
  5087. };
  5088. return Tone.Timeline;
  5089. }(Tone_core_Tone);
  5090. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  5091. var Tone_signal_TimelineSignal;
  5092. Tone_signal_TimelineSignal = function (Tone) {
  5093. 'use strict';
  5094. Tone.TimelineSignal = function () {
  5095. var options = this.optionsObject(arguments, [
  5096. 'value',
  5097. 'units'
  5098. ], Tone.Signal.defaults);
  5099. Tone.Signal.apply(this, options);
  5100. options.param = this._param;
  5101. Tone.Param.call(this, options);
  5102. this._events = new Tone.Timeline(10);
  5103. this._initial = this._fromUnits(this._param.value);
  5104. };
  5105. Tone.extend(Tone.TimelineSignal, Tone.Param);
  5106. Tone.TimelineSignal.Type = {
  5107. Linear: 'linear',
  5108. Exponential: 'exponential',
  5109. Target: 'target',
  5110. Set: 'set'
  5111. };
  5112. Object.defineProperty(Tone.TimelineSignal.prototype, 'value', {
  5113. get: function () {
  5114. return this._toUnits(this._param.value);
  5115. },
  5116. set: function (value) {
  5117. var convertedVal = this._fromUnits(value);
  5118. this._initial = convertedVal;
  5119. this._param.value = convertedVal;
  5120. }
  5121. });
  5122. Tone.TimelineSignal.prototype.setValueAtTime = function (value, startTime) {
  5123. value = this._fromUnits(value);
  5124. startTime = this.toSeconds(startTime);
  5125. this._events.addEvent({
  5126. 'type': Tone.TimelineSignal.Type.Set,
  5127. 'value': value,
  5128. 'time': startTime
  5129. });
  5130. this._param.setValueAtTime(value, startTime);
  5131. return this;
  5132. };
  5133. Tone.TimelineSignal.prototype.linearRampToValueAtTime = function (value, endTime) {
  5134. value = this._fromUnits(value);
  5135. endTime = this.toSeconds(endTime);
  5136. this._events.addEvent({
  5137. 'type': Tone.TimelineSignal.Type.Linear,
  5138. 'value': value,
  5139. 'time': endTime
  5140. });
  5141. this._param.linearRampToValueAtTime(value, endTime);
  5142. return this;
  5143. };
  5144. Tone.TimelineSignal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
  5145. value = this._fromUnits(value);
  5146. value = Math.max(this._minOutput, value);
  5147. endTime = this.toSeconds(endTime);
  5148. this._events.addEvent({
  5149. 'type': Tone.TimelineSignal.Type.Exponential,
  5150. 'value': value,
  5151. 'time': endTime
  5152. });
  5153. this._param.exponentialRampToValueAtTime(value, endTime);
  5154. return this;
  5155. };
  5156. Tone.TimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
  5157. value = this._fromUnits(value);
  5158. value = Math.max(this._minOutput, value);
  5159. timeConstant = Math.max(this._minOutput, timeConstant);
  5160. startTime = this.toSeconds(startTime);
  5161. this._events.addEvent({
  5162. 'type': Tone.TimelineSignal.Type.Target,
  5163. 'value': value,
  5164. 'time': startTime,
  5165. 'constant': timeConstant
  5166. });
  5167. this._param.setTargetAtTime(value, startTime, timeConstant);
  5168. return this;
  5169. };
  5170. Tone.TimelineSignal.prototype.cancelScheduledValues = function (after) {
  5171. this._events.cancel(after);
  5172. this._param.cancelScheduledValues(this.toSeconds(after));
  5173. return this;
  5174. };
  5175. Tone.TimelineSignal.prototype.setRampPoint = function (time) {
  5176. time = this.toSeconds(time);
  5177. var val = this.getValueAtTime(time);
  5178. var after = this._searchAfter(time);
  5179. if (after) {
  5180. this.cancelScheduledValues(time);
  5181. if (after.type === Tone.TimelineSignal.Type.Linear) {
  5182. this.linearRampToValueAtTime(val, time);
  5183. } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
  5184. this.exponentialRampToValueAtTime(val, time);
  5185. }
  5186. }
  5187. this.setValueAtTime(val, time);
  5188. return this;
  5189. };
  5190. Tone.TimelineSignal.prototype.linearRampToValueBetween = function (value, start, finish) {
  5191. this.setRampPoint(start);
  5192. this.linearRampToValueAtTime(value, finish);
  5193. return this;
  5194. };
  5195. Tone.TimelineSignal.prototype.exponentialRampToValueBetween = function (value, start, finish) {
  5196. this.setRampPoint(start);
  5197. this.exponentialRampToValueAtTime(value, finish);
  5198. return this;
  5199. };
  5200. Tone.TimelineSignal.prototype._searchBefore = function (time) {
  5201. return this._events.getEvent(time);
  5202. };
  5203. Tone.TimelineSignal.prototype._searchAfter = function (time) {
  5204. return this._events.getEventAfter(time);
  5205. };
  5206. Tone.TimelineSignal.prototype.getValueAtTime = function (time) {
  5207. var after = this._searchAfter(time);
  5208. var before = this._searchBefore(time);
  5209. var value = this._initial;
  5210. if (before === null) {
  5211. value = this._initial;
  5212. } else if (before.type === Tone.TimelineSignal.Type.Target) {
  5213. var previous = this._events.getEventBefore(before.time);
  5214. var previouVal;
  5215. if (previous === null) {
  5216. previouVal = this._initial;
  5217. } else {
  5218. previouVal = previous.value;
  5219. }
  5220. value = this._exponentialApproach(before.time, previouVal, before.value, before.constant, time);
  5221. } else if (after === null) {
  5222. value = before.value;
  5223. } else if (after.type === Tone.TimelineSignal.Type.Linear) {
  5224. value = this._linearInterpolate(before.time, before.value, after.time, after.value, time);
  5225. } else if (after.type === Tone.TimelineSignal.Type.Exponential) {
  5226. value = this._exponentialInterpolate(before.time, before.value, after.time, after.value, time);
  5227. } else {
  5228. value = before.value;
  5229. }
  5230. return value;
  5231. };
  5232. Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
  5233. Tone.TimelineSignal.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
  5234. return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
  5235. };
  5236. Tone.TimelineSignal.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
  5237. return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
  5238. };
  5239. Tone.TimelineSignal.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
  5240. v0 = Math.max(this._minOutput, v0);
  5241. return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
  5242. };
  5243. Tone.TimelineSignal.prototype.dispose = function () {
  5244. Tone.Signal.prototype.dispose.call(this);
  5245. Tone.Param.prototype.dispose.call(this);
  5246. this._events.dispose();
  5247. this._events = null;
  5248. };
  5249. return Tone.TimelineSignal;
  5250. }(Tone_core_Tone, Tone_signal_Signal);
  5251. var env;
  5252. env = function () {
  5253. 'use strict';
  5254. var p5sound = master;
  5255. var Add = Tone_signal_Add;
  5256. var Mult = Tone_signal_Multiply;
  5257. var Scale = Tone_signal_Scale;
  5258. var TimelineSignal = Tone_signal_TimelineSignal;
  5259. var Tone = Tone_core_Tone;
  5260. Tone.setContext(p5sound.audiocontext);
  5261. /**
  5262. * <p>Envelopes are pre-defined amplitude distribution over time.
  5263. * Typically, envelopes are used to control the output volume
  5264. * of an object, a series of fades referred to as Attack, Decay,
  5265. * Sustain and Release (
  5266. * <a href="https://upload.wikimedia.org/wikipedia/commons/e/ea/ADSR_parameter.svg">ADSR</a>
  5267. * ). Envelopes can also control other Web Audio Parameters—for example, a p5.Env can
  5268. * control an Oscillator's frequency like this: <code>osc.freq(env)</code>.</p>
  5269. * <p>Use <code><a href="#/p5.Env/setRange">setRange</a></code> to change the attack/release level.
  5270. * Use <code><a href="#/p5.Env/setADSR">setADSR</a></code> to change attackTime, decayTime, sustainPercent and releaseTime.</p>
  5271. * <p>Use the <code><a href="#/p5.Env/play">play</a></code> method to play the entire envelope,
  5272. * the <code><a href="#/p5.Env/ramp">ramp</a></code> method for a pingable trigger,
  5273. * or <code><a href="#/p5.Env/triggerAttack">triggerAttack</a></code>/
  5274. * <code><a href="#/p5.Env/triggerRelease">triggerRelease</a></code> to trigger noteOn/noteOff.</p>
  5275. *
  5276. * @class p5.Env
  5277. * @constructor
  5278. * @example
  5279. * <div><code>
  5280. * var attackLevel = 1.0;
  5281. * var releaseLevel = 0;
  5282. *
  5283. * var attackTime = 0.001
  5284. * var decayTime = 0.2;
  5285. * var susPercent = 0.2;
  5286. * var releaseTime = 0.5;
  5287. *
  5288. * var env, triOsc;
  5289. *
  5290. * function setup() {
  5291. * var cnv = createCanvas(100, 100);
  5292. *
  5293. * textAlign(CENTER);
  5294. * text('click to play', width/2, height/2);
  5295. *
  5296. * env = new p5.Env();
  5297. * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  5298. * env.setRange(attackLevel, releaseLevel);
  5299. *
  5300. * triOsc = new p5.Oscillator('triangle');
  5301. * triOsc.amp(env);
  5302. * triOsc.start();
  5303. * triOsc.freq(220);
  5304. *
  5305. * cnv.mousePressed(playEnv);
  5306. * }
  5307. *
  5308. * function playEnv(){
  5309. * env.play();
  5310. * }
  5311. * </code></div>
  5312. */
  5313. p5.Env = function (t1, l1, t2, l2, t3, l3) {
  5314. var now = p5sound.audiocontext.currentTime;
  5315. /**
  5316. * Time until envelope reaches attackLevel
  5317. * @property attackTime
  5318. */
  5319. this.aTime = t1 || 0.1;
  5320. /**
  5321. * Level once attack is complete.
  5322. * @property attackLevel
  5323. */
  5324. this.aLevel = l1 || 1;
  5325. /**
  5326. * Time until envelope reaches decayLevel.
  5327. * @property decayTime
  5328. */
  5329. this.dTime = t2 || 0.5;
  5330. /**
  5331. * Level after decay. The envelope will sustain here until it is released.
  5332. * @property decayLevel
  5333. */
  5334. this.dLevel = l2 || 0;
  5335. /**
  5336. * Duration of the release portion of the envelope.
  5337. * @property releaseTime
  5338. */
  5339. this.rTime = t3 || 0;
  5340. /**
  5341. * Level at the end of the release.
  5342. * @property releaseLevel
  5343. */
  5344. this.rLevel = l3 || 0;
  5345. this._rampHighPercentage = 0.98;
  5346. this._rampLowPercentage = 0.02;
  5347. this.output = p5sound.audiocontext.createGain();
  5348. this.control = new TimelineSignal();
  5349. this._init();
  5350. // this makes sure the envelope starts at zero
  5351. this.control.connect(this.output);
  5352. // connect to the output
  5353. this.connection = null;
  5354. // store connection
  5355. //array of math operation signal chaining
  5356. this.mathOps = [this.control];
  5357. //whether envelope should be linear or exponential curve
  5358. this.isExponential = false;
  5359. // oscillator or buffer source to clear on env complete
  5360. // to save resources if/when it is retriggered
  5361. this.sourceToClear = null;
  5362. // set to true if attack is set, then false on release
  5363. this.wasTriggered = false;
  5364. // add to the soundArray so we can dispose of the env later
  5365. p5sound.soundArray.push(this);
  5366. };
  5367. // this init function just smooths the starting value to zero and gives a start point for the timeline
  5368. // - it was necessary to remove glitches at the beginning.
  5369. p5.Env.prototype._init = function () {
  5370. var now = p5sound.audiocontext.currentTime;
  5371. var t = now;
  5372. this.control.setTargetAtTime(0.00001, t, 0.001);
  5373. //also, compute the correct time constants
  5374. this._setRampAD(this.aTime, this.dTime);
  5375. };
  5376. /**
  5377. * Reset the envelope with a series of time/value pairs.
  5378. *
  5379. * @method set
  5380. * @param {Number} attackTime Time (in seconds) before level
  5381. * reaches attackLevel
  5382. * @param {Number} attackLevel Typically an amplitude between
  5383. * 0.0 and 1.0
  5384. * @param {Number} decayTime Time
  5385. * @param {Number} decayLevel Amplitude (In a standard ADSR envelope,
  5386. * decayLevel = sustainLevel)
  5387. * @param {Number} releaseTime Release Time (in seconds)
  5388. * @param {Number} releaseLevel Amplitude
  5389. * @example
  5390. * <div><code>
  5391. * var t1 = 0.1; // attack time in seconds
  5392. * var l1 = 0.7; // attack level 0.0 to 1.0
  5393. * var t2 = 0.3; // decay time in seconds
  5394. * var l2 = 0.1; // decay level 0.0 to 1.0
  5395. * var t3 = 0.2; // sustain time in seconds
  5396. * var l3 = dL; // sustain level 0.0 to 1.0
  5397. * // release level defaults to zero
  5398. *
  5399. * var env;
  5400. * var triOsc;
  5401. *
  5402. * function setup() {
  5403. * background(0);
  5404. * noStroke();
  5405. * fill(255);
  5406. * textAlign(CENTER);
  5407. * text('click to play', width/2, height/2);
  5408. *
  5409. * env = new p5.Env(t1, l1, t2, l2, t3, l3);
  5410. * triOsc = new p5.Oscillator('triangle');
  5411. * triOsc.amp(env); // give the env control of the triOsc's amp
  5412. * triOsc.start();
  5413. * }
  5414. *
  5415. * // mouseClick triggers envelope if over canvas
  5416. * function mouseClicked() {
  5417. * // is mouse over canvas?
  5418. * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
  5419. * env.play(triOsc);
  5420. * }
  5421. * }
  5422. * </code></div>
  5423. *
  5424. */
  5425. p5.Env.prototype.set = function (t1, l1, t2, l2, t3, l3) {
  5426. this.aTime = t1;
  5427. this.aLevel = l1;
  5428. this.dTime = t2 || 0;
  5429. this.dLevel = l2 || 0;
  5430. this.rTime = t3 || 0;
  5431. this.rLevel = l3 || 0;
  5432. // set time constants for ramp
  5433. this._setRampAD(t1, t2);
  5434. };
  5435. /**
  5436. * Set values like a traditional
  5437. * <a href="https://en.wikipedia.org/wiki/Synthesizer#/media/File:ADSR_parameter.svg">
  5438. * ADSR envelope
  5439. * </a>.
  5440. *
  5441. * @method setADSR
  5442. * @param {Number} attackTime Time (in seconds before envelope
  5443. * reaches Attack Level
  5444. * @param {Number} [decayTime] Time (in seconds) before envelope
  5445. * reaches Decay/Sustain Level
  5446. * @param {Number} [susRatio] Ratio between attackLevel and releaseLevel, on a scale from 0 to 1,
  5447. * where 1.0 = attackLevel, 0.0 = releaseLevel.
  5448. * The susRatio determines the decayLevel and the level at which the
  5449. * sustain portion of the envelope will sustain.
  5450. * For example, if attackLevel is 0.4, releaseLevel is 0,
  5451. * and susAmt is 0.5, the decayLevel would be 0.2. If attackLevel is
  5452. * increased to 1.0 (using <code>setRange</code>),
  5453. * then decayLevel would increase proportionally, to become 0.5.
  5454. * @param {Number} [releaseTime] Time in seconds from now (defaults to 0)
  5455. * @example
  5456. * <div><code>
  5457. * var attackLevel = 1.0;
  5458. * var releaseLevel = 0;
  5459. *
  5460. * var attackTime = 0.001
  5461. * var decayTime = 0.2;
  5462. * var susPercent = 0.2;
  5463. * var releaseTime = 0.5;
  5464. *
  5465. * var env, triOsc;
  5466. *
  5467. * function setup() {
  5468. * var cnv = createCanvas(100, 100);
  5469. *
  5470. * textAlign(CENTER);
  5471. * text('click to play', width/2, height/2);
  5472. *
  5473. * env = new p5.Env();
  5474. * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  5475. * env.setRange(attackLevel, releaseLevel);
  5476. *
  5477. * triOsc = new p5.Oscillator('triangle');
  5478. * triOsc.amp(env);
  5479. * triOsc.start();
  5480. * triOsc.freq(220);
  5481. *
  5482. * cnv.mousePressed(playEnv);
  5483. * }
  5484. *
  5485. * function playEnv(){
  5486. * env.play();
  5487. * }
  5488. * </code></div>
  5489. */
  5490. p5.Env.prototype.setADSR = function (aTime, dTime, sPercent, rTime) {
  5491. this.aTime = aTime;
  5492. this.dTime = dTime || 0;
  5493. // lerp
  5494. this.sPercent = sPercent || 0;
  5495. this.dLevel = typeof sPercent !== 'undefined' ? sPercent * (this.aLevel - this.rLevel) + this.rLevel : 0;
  5496. this.rTime = rTime || 0;
  5497. // also set time constants for ramp
  5498. this._setRampAD(aTime, dTime);
  5499. };
  5500. /**
  5501. * Set max (attackLevel) and min (releaseLevel) of envelope.
  5502. *
  5503. * @method setRange
  5504. * @param {Number} aLevel attack level (defaults to 1)
  5505. * @param {Number} rLevel release level (defaults to 0)
  5506. * @example
  5507. * <div><code>
  5508. * var attackLevel = 1.0;
  5509. * var releaseLevel = 0;
  5510. *
  5511. * var attackTime = 0.001
  5512. * var decayTime = 0.2;
  5513. * var susPercent = 0.2;
  5514. * var releaseTime = 0.5;
  5515. *
  5516. * var env, triOsc;
  5517. *
  5518. * function setup() {
  5519. * var cnv = createCanvas(100, 100);
  5520. *
  5521. * textAlign(CENTER);
  5522. * text('click to play', width/2, height/2);
  5523. *
  5524. * env = new p5.Env();
  5525. * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  5526. * env.setRange(attackLevel, releaseLevel);
  5527. *
  5528. * triOsc = new p5.Oscillator('triangle');
  5529. * triOsc.amp(env);
  5530. * triOsc.start();
  5531. * triOsc.freq(220);
  5532. *
  5533. * cnv.mousePressed(playEnv);
  5534. * }
  5535. *
  5536. * function playEnv(){
  5537. * env.play();
  5538. * }
  5539. * </code></div>
  5540. */
  5541. p5.Env.prototype.setRange = function (aLevel, rLevel) {
  5542. this.aLevel = aLevel || 1;
  5543. this.rLevel = rLevel || 0;
  5544. };
  5545. // private (undocumented) method called when ADSR is set to set time constants for ramp
  5546. //
  5547. // Set the <a href="https://en.wikipedia.org/wiki/RC_time_constant">
  5548. // time constants</a> for simple exponential ramps.
  5549. // The larger the time constant value, the slower the
  5550. // transition will be.
  5551. //
  5552. // method _setRampAD
  5553. // param {Number} attackTimeConstant attack time constant
  5554. // param {Number} decayTimeConstant decay time constant
  5555. //
  5556. p5.Env.prototype._setRampAD = function (t1, t2) {
  5557. this._rampAttackTime = this.checkExpInput(t1);
  5558. this._rampDecayTime = this.checkExpInput(t2);
  5559. var TCDenominator = 1;
  5560. /// Aatish Bhatia's calculation for time constant for rise(to adjust 1/1-e calculation to any percentage)
  5561. TCDenominator = Math.log(1 / this.checkExpInput(1 - this._rampHighPercentage));
  5562. this._rampAttackTC = t1 / this.checkExpInput(TCDenominator);
  5563. TCDenominator = Math.log(1 / this._rampLowPercentage);
  5564. this._rampDecayTC = t2 / this.checkExpInput(TCDenominator);
  5565. };
  5566. // private method
  5567. p5.Env.prototype.setRampPercentages = function (p1, p2) {
  5568. //set the percentages that the simple exponential ramps go to
  5569. this._rampHighPercentage = this.checkExpInput(p1);
  5570. this._rampLowPercentage = this.checkExpInput(p2);
  5571. var TCDenominator = 1;
  5572. //now re-compute the time constants based on those percentages
  5573. /// Aatish Bhatia's calculation for time constant for rise(to adjust 1/1-e calculation to any percentage)
  5574. TCDenominator = Math.log(1 / this.checkExpInput(1 - this._rampHighPercentage));
  5575. this._rampAttackTC = this._rampAttackTime / this.checkExpInput(TCDenominator);
  5576. TCDenominator = Math.log(1 / this._rampLowPercentage);
  5577. this._rampDecayTC = this._rampDecayTime / this.checkExpInput(TCDenominator);
  5578. };
  5579. /**
  5580. * Assign a parameter to be controlled by this envelope.
  5581. * If a p5.Sound object is given, then the p5.Env will control its
  5582. * output gain. If multiple inputs are provided, the env will
  5583. * control all of them.
  5584. *
  5585. * @method setInput
  5586. * @param {Object} unit A p5.sound object or
  5587. * Web Audio Param.
  5588. */
  5589. p5.Env.prototype.setInput = function (unit) {
  5590. for (var i = 0; i < arguments.length; i++) {
  5591. this.connect(arguments[i]);
  5592. }
  5593. };
  5594. /**
  5595. * Set whether the envelope ramp is linear (default) or exponential.
  5596. * Exponential ramps can be useful because we perceive amplitude
  5597. * and frequency logarithmically.
  5598. *
  5599. * @method setExp
  5600. * @param {Boolean} isExp true is exponential, false is linear
  5601. */
  5602. p5.Env.prototype.setExp = function (isExp) {
  5603. this.isExponential = isExp;
  5604. };
  5605. //helper method to protect against zero values being sent to exponential functions
  5606. p5.Env.prototype.checkExpInput = function (value) {
  5607. if (value <= 0) {
  5608. value = 1e-8;
  5609. }
  5610. return value;
  5611. };
  5612. /**
  5613. * Play tells the envelope to start acting on a given input.
  5614. * If the input is a p5.sound object (i.e. AudioIn, Oscillator,
  5615. * SoundFile), then Env will control its output volume.
  5616. * Envelopes can also be used to control any <a href="
  5617. * http://docs.webplatform.org/wiki/apis/webaudio/AudioParam">
  5618. * Web Audio Audio Param.</a>
  5619. *
  5620. * @method play
  5621. * @param {Object} unit A p5.sound object or
  5622. * Web Audio Param.
  5623. * @param {Number} [startTime] time from now (in seconds) at which to play
  5624. * @param {Number} [sustainTime] time to sustain before releasing the envelope
  5625. * @example
  5626. * <div><code>
  5627. * var attackLevel = 1.0;
  5628. * var releaseLevel = 0;
  5629. *
  5630. * var attackTime = 0.001
  5631. * var decayTime = 0.2;
  5632. * var susPercent = 0.2;
  5633. * var releaseTime = 0.5;
  5634. *
  5635. * var env, triOsc;
  5636. *
  5637. * function setup() {
  5638. * var cnv = createCanvas(100, 100);
  5639. *
  5640. * textAlign(CENTER);
  5641. * text('click to play', width/2, height/2);
  5642. *
  5643. * env = new p5.Env();
  5644. * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  5645. * env.setRange(attackLevel, releaseLevel);
  5646. *
  5647. * triOsc = new p5.Oscillator('triangle');
  5648. * triOsc.amp(env);
  5649. * triOsc.start();
  5650. * triOsc.freq(220);
  5651. *
  5652. * cnv.mousePressed(playEnv);
  5653. * }
  5654. *
  5655. * function playEnv(){
  5656. * // trigger env on triOsc, 0 seconds from now
  5657. * // After decay, sustain for 0.2 seconds before release
  5658. * env.play(triOsc, 0, 0.2);
  5659. * }
  5660. * </code></div>
  5661. */
  5662. p5.Env.prototype.play = function (unit, secondsFromNow, susTime) {
  5663. var now = p5sound.audiocontext.currentTime;
  5664. var tFromNow = secondsFromNow || 0;
  5665. var susTime = susTime || 0;
  5666. if (unit) {
  5667. if (this.connection !== unit) {
  5668. this.connect(unit);
  5669. }
  5670. }
  5671. this.triggerAttack(unit, tFromNow);
  5672. this.triggerRelease(unit, tFromNow + this.aTime + this.dTime + susTime);
  5673. };
  5674. /**
  5675. * Trigger the Attack, and Decay portion of the Envelope.
  5676. * Similar to holding down a key on a piano, but it will
  5677. * hold the sustain level until you let go. Input can be
  5678. * any p5.sound object, or a <a href="
  5679. * http://docs.webplatform.org/wiki/apis/webaudio/AudioParam">
  5680. * Web Audio Param</a>.
  5681. *
  5682. * @method triggerAttack
  5683. * @param {Object} unit p5.sound Object or Web Audio Param
  5684. * @param {Number} secondsFromNow time from now (in seconds)
  5685. * @example
  5686. * <div><code>
  5687. *
  5688. * var attackLevel = 1.0;
  5689. * var releaseLevel = 0;
  5690. *
  5691. * var attackTime = 0.001
  5692. * var decayTime = 0.3;
  5693. * var susPercent = 0.4;
  5694. * var releaseTime = 0.5;
  5695. *
  5696. * var env, triOsc;
  5697. *
  5698. * function setup() {
  5699. * var cnv = createCanvas(100, 100);
  5700. * background(200);
  5701. * textAlign(CENTER);
  5702. * text('click to play', width/2, height/2);
  5703. *
  5704. * env = new p5.Env();
  5705. * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  5706. * env.setRange(attackLevel, releaseLevel);
  5707. *
  5708. * triOsc = new p5.Oscillator('triangle');
  5709. * triOsc.amp(env);
  5710. * triOsc.start();
  5711. * triOsc.freq(220);
  5712. *
  5713. * cnv.mousePressed(envAttack);
  5714. * }
  5715. *
  5716. * function envAttack(){
  5717. * console.log('trigger attack');
  5718. * env.triggerAttack();
  5719. *
  5720. * background(0,255,0);
  5721. * text('attack!', width/2, height/2);
  5722. * }
  5723. *
  5724. * function mouseReleased() {
  5725. * env.triggerRelease();
  5726. *
  5727. * background(200);
  5728. * text('click to play', width/2, height/2);
  5729. * }
  5730. * </code></div>
  5731. */
  5732. p5.Env.prototype.triggerAttack = function (unit, secondsFromNow) {
  5733. var now = p5sound.audiocontext.currentTime;
  5734. var tFromNow = secondsFromNow || 0;
  5735. var t = now + tFromNow;
  5736. this.lastAttack = t;
  5737. this.wasTriggered = true;
  5738. if (unit) {
  5739. if (this.connection !== unit) {
  5740. this.connect(unit);
  5741. }
  5742. }
  5743. // get and set value (with linear ramp) to anchor automation
  5744. var valToSet = this.control.getValueAtTime(t);
  5745. this.control.cancelScheduledValues(t);
  5746. // not sure if this is necessary
  5747. if (this.isExponential == true) {
  5748. this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
  5749. } else {
  5750. this.control.linearRampToValueAtTime(valToSet, t);
  5751. }
  5752. // after each ramp completes, cancel scheduled values
  5753. // (so they can be overridden in case env has been re-triggered)
  5754. // then, set current value (with linearRamp to avoid click)
  5755. // then, schedule the next automation...
  5756. // attack
  5757. t += this.aTime;
  5758. if (this.isExponential == true) {
  5759. this.control.exponentialRampToValueAtTime(this.checkExpInput(this.aLevel), t);
  5760. valToSet = this.checkExpInput(this.control.getValueAtTime(t));
  5761. this.control.cancelScheduledValues(t);
  5762. this.control.exponentialRampToValueAtTime(valToSet, t);
  5763. } else {
  5764. this.control.linearRampToValueAtTime(this.aLevel, t);
  5765. valToSet = this.control.getValueAtTime(t);
  5766. this.control.cancelScheduledValues(t);
  5767. this.control.linearRampToValueAtTime(valToSet, t);
  5768. }
  5769. // decay to decay level (if using ADSR, then decay level == sustain level)
  5770. t += this.dTime;
  5771. if (this.isExponential == true) {
  5772. this.control.exponentialRampToValueAtTime(this.checkExpInput(this.dLevel), t);
  5773. valToSet = this.checkExpInput(this.control.getValueAtTime(t));
  5774. this.control.cancelScheduledValues(t);
  5775. this.control.exponentialRampToValueAtTime(valToSet, t);
  5776. } else {
  5777. this.control.linearRampToValueAtTime(this.dLevel, t);
  5778. valToSet = this.control.getValueAtTime(t);
  5779. this.control.cancelScheduledValues(t);
  5780. this.control.linearRampToValueAtTime(valToSet, t);
  5781. }
  5782. };
  5783. /**
  5784. * Trigger the Release of the Envelope. This is similar to releasing
  5785. * the key on a piano and letting the sound fade according to the
  5786. * release level and release time.
  5787. *
  5788. * @method triggerRelease
  5789. * @param {Object} unit p5.sound Object or Web Audio Param
  5790. * @param {Number} secondsFromNow time to trigger the release
  5791. * @example
  5792. * <div><code>
  5793. *
  5794. * var attackLevel = 1.0;
  5795. * var releaseLevel = 0;
  5796. *
  5797. * var attackTime = 0.001
  5798. * var decayTime = 0.3;
  5799. * var susPercent = 0.4;
  5800. * var releaseTime = 0.5;
  5801. *
  5802. * var env, triOsc;
  5803. *
  5804. * function setup() {
  5805. * var cnv = createCanvas(100, 100);
  5806. * background(200);
  5807. * textAlign(CENTER);
  5808. * text('click to play', width/2, height/2);
  5809. *
  5810. * env = new p5.Env();
  5811. * env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  5812. * env.setRange(attackLevel, releaseLevel);
  5813. *
  5814. * triOsc = new p5.Oscillator('triangle');
  5815. * triOsc.amp(env);
  5816. * triOsc.start();
  5817. * triOsc.freq(220);
  5818. *
  5819. * cnv.mousePressed(envAttack);
  5820. * }
  5821. *
  5822. * function envAttack(){
  5823. * console.log('trigger attack');
  5824. * env.triggerAttack();
  5825. *
  5826. * background(0,255,0);
  5827. * text('attack!', width/2, height/2);
  5828. * }
  5829. *
  5830. * function mouseReleased() {
  5831. * env.triggerRelease();
  5832. *
  5833. * background(200);
  5834. * text('click to play', width/2, height/2);
  5835. * }
  5836. * </code></div>
  5837. */
  5838. p5.Env.prototype.triggerRelease = function (unit, secondsFromNow) {
  5839. // only trigger a release if an attack was triggered
  5840. if (!this.wasTriggered) {
  5841. // this currently causes a bit of trouble:
  5842. // if a later release has been scheduled (via the play function)
  5843. // a new earlier release won't interrupt it, because
  5844. // this.wasTriggered has already been set to false.
  5845. // If we want new earlier releases to override, then we need to
  5846. // keep track of the last release time, and if the new release time is
  5847. // earlier, then use it.
  5848. return;
  5849. }
  5850. var now = p5sound.audiocontext.currentTime;
  5851. var tFromNow = secondsFromNow || 0;
  5852. var t = now + tFromNow;
  5853. if (unit) {
  5854. if (this.connection !== unit) {
  5855. this.connect(unit);
  5856. }
  5857. }
  5858. // get and set value (with linear or exponential ramp) to anchor automation
  5859. var valToSet = this.control.getValueAtTime(t);
  5860. this.control.cancelScheduledValues(t);
  5861. // not sure if this is necessary
  5862. if (this.isExponential == true) {
  5863. this.control.exponentialRampToValueAtTime(this.checkExpInput(valToSet), t);
  5864. } else {
  5865. this.control.linearRampToValueAtTime(valToSet, t);
  5866. }
  5867. // release
  5868. t += this.rTime;
  5869. if (this.isExponential == true) {
  5870. this.control.exponentialRampToValueAtTime(this.checkExpInput(this.rLevel), t);
  5871. valToSet = this.checkExpInput(this.control.getValueAtTime(t));
  5872. this.control.cancelScheduledValues(t);
  5873. this.control.exponentialRampToValueAtTime(valToSet, t);
  5874. } else {
  5875. this.control.linearRampToValueAtTime(this.rLevel, t);
  5876. valToSet = this.control.getValueAtTime(t);
  5877. this.control.cancelScheduledValues(t);
  5878. this.control.linearRampToValueAtTime(valToSet, t);
  5879. }
  5880. this.wasTriggered = false;
  5881. };
  5882. /**
  5883. * Exponentially ramp to a value using the first two
  5884. * values from <code><a href="#/p5.Env/setADSR">setADSR(attackTime, decayTime)</a></code>
  5885. * as <a href="https://en.wikipedia.org/wiki/RC_time_constant">
  5886. * time constants</a> for simple exponential ramps.
  5887. * If the value is higher than current value, it uses attackTime,
  5888. * while a decrease uses decayTime.
  5889. *
  5890. * @method ramp
  5891. * @param {Object} unit p5.sound Object or Web Audio Param
  5892. * @param {Number} secondsFromNow When to trigger the ramp
  5893. * @param {Number} v Target value
  5894. * @param {Number} [v2] Second target value (optional)
  5895. * @example
  5896. * <div><code>
  5897. * var env, osc, amp, cnv;
  5898. *
  5899. * var attackTime = 0.001;
  5900. * var decayTime = 0.2;
  5901. * var attackLevel = 1;
  5902. * var decayLevel = 0;
  5903. *
  5904. * function setup() {
  5905. * cnv = createCanvas(100, 100);
  5906. * fill(0,255,0);
  5907. * noStroke();
  5908. *
  5909. * env = new p5.Env();
  5910. * env.setADSR(attackTime, decayTime);
  5911. *
  5912. * osc = new p5.Oscillator();
  5913. * osc.amp(env);
  5914. * osc.start();
  5915. *
  5916. * amp = new p5.Amplitude();
  5917. *
  5918. * cnv.mousePressed(triggerRamp);
  5919. * }
  5920. *
  5921. * function triggerRamp() {
  5922. * env.ramp(osc, 0, attackLevel, decayLevel);
  5923. * }
  5924. *
  5925. * function draw() {
  5926. * background(20,20,20);
  5927. * text('click me', 10, 20);
  5928. * var h = map(amp.getLevel(), 0, 0.4, 0, height);;
  5929. *
  5930. * rect(0, height, width, -h);
  5931. * }
  5932. * </code></div>
  5933. */
  5934. p5.Env.prototype.ramp = function (unit, secondsFromNow, v1, v2) {
  5935. var now = p5sound.audiocontext.currentTime;
  5936. var tFromNow = secondsFromNow || 0;
  5937. var t = now + tFromNow;
  5938. var destination1 = this.checkExpInput(v1);
  5939. var destination2 = typeof v2 !== 'undefined' ? this.checkExpInput(v2) : undefined;
  5940. // connect env to unit if not already connected
  5941. if (unit) {
  5942. if (this.connection !== unit) {
  5943. this.connect(unit);
  5944. }
  5945. }
  5946. //get current value
  5947. var currentVal = this.checkExpInput(this.control.getValueAtTime(t));
  5948. this.control.cancelScheduledValues(t);
  5949. //if it's going up
  5950. if (destination1 > currentVal) {
  5951. this.control.setTargetAtTime(destination1, t, this._rampAttackTC);
  5952. t += this._rampAttackTime;
  5953. } else if (destination1 < currentVal) {
  5954. this.control.setTargetAtTime(destination1, t, this._rampDecayTC);
  5955. t += this._rampDecayTime;
  5956. }
  5957. // Now the second part of envelope begins
  5958. if (destination2 === undefined)
  5959. return;
  5960. //if it's going up
  5961. if (destination2 > destination1) {
  5962. this.control.setTargetAtTime(destination2, t, this._rampAttackTC);
  5963. } else if (destination2 < destination1) {
  5964. this.control.setTargetAtTime(destination2, t, this._rampDecayTC);
  5965. }
  5966. };
  5967. p5.Env.prototype.connect = function (unit) {
  5968. this.connection = unit;
  5969. // assume we're talking about output gain
  5970. // unless given a different audio param
  5971. if (unit instanceof p5.Oscillator || unit instanceof p5.SoundFile || unit instanceof p5.AudioIn || unit instanceof p5.Reverb || unit instanceof p5.Noise || unit instanceof p5.Filter || unit instanceof p5.Delay) {
  5972. unit = unit.output.gain;
  5973. }
  5974. if (unit instanceof AudioParam) {
  5975. //set the initial value
  5976. unit.setValueAtTime(0, p5sound.audiocontext.currentTime);
  5977. }
  5978. if (unit instanceof p5.Signal) {
  5979. unit.setValue(0);
  5980. }
  5981. this.output.connect(unit);
  5982. };
  5983. p5.Env.prototype.disconnect = function (unit) {
  5984. this.output.disconnect();
  5985. };
  5986. // Signal Math
  5987. /**
  5988. * Add a value to the p5.Oscillator's output amplitude,
  5989. * and return the oscillator. Calling this method
  5990. * again will override the initial add() with new values.
  5991. *
  5992. * @method add
  5993. * @param {Number} number Constant number to add
  5994. * @return {p5.Env} Envelope Returns this envelope
  5995. * with scaled output
  5996. */
  5997. p5.Env.prototype.add = function (num) {
  5998. var add = new Add(num);
  5999. var thisChain = this.mathOps.length;
  6000. var nextChain = this.output;
  6001. return p5.prototype._mathChain(this, add, thisChain, nextChain, Add);
  6002. };
  6003. /**
  6004. * Multiply the p5.Env's output amplitude
  6005. * by a fixed value. Calling this method
  6006. * again will override the initial mult() with new values.
  6007. *
  6008. * @method mult
  6009. * @param {Number} number Constant number to multiply
  6010. * @return {p5.Env} Envelope Returns this envelope
  6011. * with scaled output
  6012. */
  6013. p5.Env.prototype.mult = function (num) {
  6014. var mult = new Mult(num);
  6015. var thisChain = this.mathOps.length;
  6016. var nextChain = this.output;
  6017. return p5.prototype._mathChain(this, mult, thisChain, nextChain, Mult);
  6018. };
  6019. /**
  6020. * Scale this envelope's amplitude values to a given
  6021. * range, and return the envelope. Calling this method
  6022. * again will override the initial scale() with new values.
  6023. *
  6024. * @method scale
  6025. * @param {Number} inMin input range minumum
  6026. * @param {Number} inMax input range maximum
  6027. * @param {Number} outMin input range minumum
  6028. * @param {Number} outMax input range maximum
  6029. * @return {p5.Env} Envelope Returns this envelope
  6030. * with scaled output
  6031. */
  6032. p5.Env.prototype.scale = function (inMin, inMax, outMin, outMax) {
  6033. var scale = new Scale(inMin, inMax, outMin, outMax);
  6034. var thisChain = this.mathOps.length;
  6035. var nextChain = this.output;
  6036. return p5.prototype._mathChain(this, scale, thisChain, nextChain, Scale);
  6037. };
  6038. // get rid of the oscillator
  6039. p5.Env.prototype.dispose = function () {
  6040. // remove reference from soundArray
  6041. var index = p5sound.soundArray.indexOf(this);
  6042. p5sound.soundArray.splice(index, 1);
  6043. var now = p5sound.audiocontext.currentTime;
  6044. this.disconnect();
  6045. try {
  6046. this.control.dispose();
  6047. this.control = null;
  6048. } catch (e) {
  6049. }
  6050. for (var i = 1; i < this.mathOps.length; i++) {
  6051. mathOps[i].dispose();
  6052. }
  6053. };
  6054. }(master, Tone_signal_Add, Tone_signal_Multiply, Tone_signal_Scale, Tone_signal_TimelineSignal, Tone_core_Tone);
  6055. var pulse;
  6056. pulse = function () {
  6057. 'use strict';
  6058. var p5sound = master;
  6059. /**
  6060. * Creates a Pulse object, an oscillator that implements
  6061. * Pulse Width Modulation.
  6062. * The pulse is created with two oscillators.
  6063. * Accepts a parameter for frequency, and to set the
  6064. * width between the pulses. See <a href="
  6065. * http://p5js.org/reference/#/p5.Oscillator">
  6066. * <code>p5.Oscillator</code> for a full list of methods.
  6067. *
  6068. * @class p5.Pulse
  6069. * @constructor
  6070. * @param {Number} [freq] Frequency in oscillations per second (Hz)
  6071. * @param {Number} [w] Width between the pulses (0 to 1.0,
  6072. * defaults to 0)
  6073. * @example
  6074. * <div><code>
  6075. * var pulse;
  6076. * function setup() {
  6077. * background(0);
  6078. *
  6079. * // Create and start the pulse wave oscillator
  6080. * pulse = new p5.Pulse();
  6081. * pulse.amp(0.5);
  6082. * pulse.freq(220);
  6083. * pulse.start();
  6084. * }
  6085. *
  6086. * function draw() {
  6087. * var w = map(mouseX, 0, width, 0, 1);
  6088. * w = constrain(w, 0, 1);
  6089. * pulse.width(w)
  6090. * }
  6091. * </code></div>
  6092. */
  6093. p5.Pulse = function (freq, w) {
  6094. p5.Oscillator.call(this, freq, 'sawtooth');
  6095. // width of PWM, should be betw 0 to 1.0
  6096. this.w = w || 0;
  6097. // create a second oscillator with inverse frequency
  6098. this.osc2 = new p5.SawOsc(freq);
  6099. // create a delay node
  6100. this.dNode = p5sound.audiocontext.createDelay();
  6101. // dc offset
  6102. this.dcOffset = createDCOffset();
  6103. this.dcGain = p5sound.audiocontext.createGain();
  6104. this.dcOffset.connect(this.dcGain);
  6105. this.dcGain.connect(this.output);
  6106. // set delay time based on PWM width
  6107. this.f = freq || 440;
  6108. var mW = this.w / this.oscillator.frequency.value;
  6109. this.dNode.delayTime.value = mW;
  6110. this.dcGain.gain.value = 1.7 * (0.5 - this.w);
  6111. // disconnect osc2 and connect it to delay, which is connected to output
  6112. this.osc2.disconnect();
  6113. this.osc2.panner.disconnect();
  6114. this.osc2.amp(-1);
  6115. // inverted amplitude
  6116. this.osc2.output.connect(this.dNode);
  6117. this.dNode.connect(this.output);
  6118. this.output.gain.value = 1;
  6119. this.output.connect(this.panner);
  6120. };
  6121. p5.Pulse.prototype = Object.create(p5.Oscillator.prototype);
  6122. /**
  6123. * Set the width of a Pulse object (an oscillator that implements
  6124. * Pulse Width Modulation).
  6125. *
  6126. * @method width
  6127. * @param {Number} [width] Width between the pulses (0 to 1.0,
  6128. * defaults to 0)
  6129. */
  6130. p5.Pulse.prototype.width = function (w) {
  6131. if (typeof w === 'number') {
  6132. if (w <= 1 && w >= 0) {
  6133. this.w = w;
  6134. // set delay time based on PWM width
  6135. // var mW = map(this.w, 0, 1.0, 0, 1/this.f);
  6136. var mW = this.w / this.oscillator.frequency.value;
  6137. this.dNode.delayTime.value = mW;
  6138. }
  6139. this.dcGain.gain.value = 1.7 * (0.5 - this.w);
  6140. } else {
  6141. w.connect(this.dNode.delayTime);
  6142. var sig = new p5.SignalAdd(-0.5);
  6143. sig.setInput(w);
  6144. sig = sig.mult(-1);
  6145. sig = sig.mult(1.7);
  6146. sig.connect(this.dcGain.gain);
  6147. }
  6148. };
  6149. p5.Pulse.prototype.start = function (f, time) {
  6150. var now = p5sound.audiocontext.currentTime;
  6151. var t = time || 0;
  6152. if (!this.started) {
  6153. var freq = f || this.f;
  6154. var type = this.oscillator.type;
  6155. this.oscillator = p5sound.audiocontext.createOscillator();
  6156. this.oscillator.frequency.setValueAtTime(freq, now);
  6157. this.oscillator.type = type;
  6158. this.oscillator.connect(this.output);
  6159. this.oscillator.start(t + now);
  6160. // set up osc2
  6161. this.osc2.oscillator = p5sound.audiocontext.createOscillator();
  6162. this.osc2.oscillator.frequency.setValueAtTime(freq, t + now);
  6163. this.osc2.oscillator.type = type;
  6164. this.osc2.oscillator.connect(this.osc2.output);
  6165. this.osc2.start(t + now);
  6166. this.freqNode = [
  6167. this.oscillator.frequency,
  6168. this.osc2.oscillator.frequency
  6169. ];
  6170. // start dcOffset, too
  6171. this.dcOffset = createDCOffset();
  6172. this.dcOffset.connect(this.dcGain);
  6173. this.dcOffset.start(t + now);
  6174. // if LFO connections depend on these oscillators
  6175. if (this.mods !== undefined && this.mods.frequency !== undefined) {
  6176. this.mods.frequency.connect(this.freqNode[0]);
  6177. this.mods.frequency.connect(this.freqNode[1]);
  6178. }
  6179. this.started = true;
  6180. this.osc2.started = true;
  6181. }
  6182. };
  6183. p5.Pulse.prototype.stop = function (time) {
  6184. if (this.started) {
  6185. var t = time || 0;
  6186. var now = p5sound.audiocontext.currentTime;
  6187. this.oscillator.stop(t + now);
  6188. this.osc2.oscillator.stop(t + now);
  6189. this.dcOffset.stop(t + now);
  6190. this.started = false;
  6191. this.osc2.started = false;
  6192. }
  6193. };
  6194. p5.Pulse.prototype.freq = function (val, rampTime, tFromNow) {
  6195. if (typeof val === 'number') {
  6196. this.f = val;
  6197. var now = p5sound.audiocontext.currentTime;
  6198. var rampTime = rampTime || 0;
  6199. var tFromNow = tFromNow || 0;
  6200. var currentFreq = this.oscillator.frequency.value;
  6201. this.oscillator.frequency.cancelScheduledValues(now);
  6202. this.oscillator.frequency.setValueAtTime(currentFreq, now + tFromNow);
  6203. this.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
  6204. this.osc2.oscillator.frequency.cancelScheduledValues(now);
  6205. this.osc2.oscillator.frequency.setValueAtTime(currentFreq, now + tFromNow);
  6206. this.osc2.oscillator.frequency.exponentialRampToValueAtTime(val, tFromNow + rampTime + now);
  6207. if (this.freqMod) {
  6208. this.freqMod.output.disconnect();
  6209. this.freqMod = null;
  6210. }
  6211. } else if (val.output) {
  6212. val.output.disconnect();
  6213. val.output.connect(this.oscillator.frequency);
  6214. val.output.connect(this.osc2.oscillator.frequency);
  6215. this.freqMod = val;
  6216. }
  6217. };
  6218. // inspiration: http://webaudiodemos.appspot.com/oscilloscope/
  6219. function createDCOffset() {
  6220. var ac = p5sound.audiocontext;
  6221. var buffer = ac.createBuffer(1, 2048, ac.sampleRate);
  6222. var data = buffer.getChannelData(0);
  6223. for (var i = 0; i < 2048; i++)
  6224. data[i] = 1;
  6225. var bufferSource = ac.createBufferSource();
  6226. bufferSource.buffer = buffer;
  6227. bufferSource.loop = true;
  6228. return bufferSource;
  6229. }
  6230. }(master, oscillator);
  6231. var noise;
  6232. noise = function () {
  6233. 'use strict';
  6234. var p5sound = master;
  6235. /**
  6236. * Noise is a type of oscillator that generates a buffer with random values.
  6237. *
  6238. * @class p5.Noise
  6239. * @constructor
  6240. * @param {String} type Type of noise can be 'white' (default),
  6241. * 'brown' or 'pink'.
  6242. * @return {Object} Noise Object
  6243. */
  6244. p5.Noise = function (type) {
  6245. var assignType;
  6246. p5.Oscillator.call(this);
  6247. delete this.f;
  6248. delete this.freq;
  6249. delete this.oscillator;
  6250. if (type === 'brown') {
  6251. assignType = _brownNoise;
  6252. } else if (type === 'pink') {
  6253. assignType = _pinkNoise;
  6254. } else {
  6255. assignType = _whiteNoise;
  6256. }
  6257. this.buffer = assignType;
  6258. };
  6259. p5.Noise.prototype = Object.create(p5.Oscillator.prototype);
  6260. // generate noise buffers
  6261. var _whiteNoise = function () {
  6262. var bufferSize = 2 * p5sound.audiocontext.sampleRate;
  6263. var whiteBuffer = p5sound.audiocontext.createBuffer(1, bufferSize, p5sound.audiocontext.sampleRate);
  6264. var noiseData = whiteBuffer.getChannelData(0);
  6265. for (var i = 0; i < bufferSize; i++) {
  6266. noiseData[i] = Math.random() * 2 - 1;
  6267. }
  6268. whiteBuffer.type = 'white';
  6269. return whiteBuffer;
  6270. }();
  6271. var _pinkNoise = function () {
  6272. var bufferSize = 2 * p5sound.audiocontext.sampleRate;
  6273. var pinkBuffer = p5sound.audiocontext.createBuffer(1, bufferSize, p5sound.audiocontext.sampleRate);
  6274. var noiseData = pinkBuffer.getChannelData(0);
  6275. var b0, b1, b2, b3, b4, b5, b6;
  6276. b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0;
  6277. for (var i = 0; i < bufferSize; i++) {
  6278. var white = Math.random() * 2 - 1;
  6279. b0 = 0.99886 * b0 + white * 0.0555179;
  6280. b1 = 0.99332 * b1 + white * 0.0750759;
  6281. b2 = 0.969 * b2 + white * 0.153852;
  6282. b3 = 0.8665 * b3 + white * 0.3104856;
  6283. b4 = 0.55 * b4 + white * 0.5329522;
  6284. b5 = -0.7616 * b5 - white * 0.016898;
  6285. noiseData[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
  6286. noiseData[i] *= 0.11;
  6287. // (roughly) compensate for gain
  6288. b6 = white * 0.115926;
  6289. }
  6290. pinkBuffer.type = 'pink';
  6291. return pinkBuffer;
  6292. }();
  6293. var _brownNoise = function () {
  6294. var bufferSize = 2 * p5sound.audiocontext.sampleRate;
  6295. var brownBuffer = p5sound.audiocontext.createBuffer(1, bufferSize, p5sound.audiocontext.sampleRate);
  6296. var noiseData = brownBuffer.getChannelData(0);
  6297. var lastOut = 0;
  6298. for (var i = 0; i < bufferSize; i++) {
  6299. var white = Math.random() * 2 - 1;
  6300. noiseData[i] = (lastOut + 0.02 * white) / 1.02;
  6301. lastOut = noiseData[i];
  6302. noiseData[i] *= 3.5;
  6303. }
  6304. brownBuffer.type = 'brown';
  6305. return brownBuffer;
  6306. }();
  6307. /**
  6308. * Set type of noise to 'white', 'pink' or 'brown'.
  6309. * White is the default.
  6310. *
  6311. * @method setType
  6312. * @param {String} [type] 'white', 'pink' or 'brown'
  6313. */
  6314. p5.Noise.prototype.setType = function (type) {
  6315. switch (type) {
  6316. case 'white':
  6317. this.buffer = _whiteNoise;
  6318. break;
  6319. case 'pink':
  6320. this.buffer = _pinkNoise;
  6321. break;
  6322. case 'brown':
  6323. this.buffer = _brownNoise;
  6324. break;
  6325. default:
  6326. this.buffer = _whiteNoise;
  6327. }
  6328. if (this.started) {
  6329. var now = p5sound.audiocontext.currentTime;
  6330. this.stop(now);
  6331. this.start(now + 0.01);
  6332. }
  6333. };
  6334. p5.Noise.prototype.getType = function () {
  6335. return this.buffer.type;
  6336. };
  6337. /**
  6338. * Start the noise
  6339. *
  6340. * @method start
  6341. */
  6342. p5.Noise.prototype.start = function () {
  6343. if (this.started) {
  6344. this.stop();
  6345. }
  6346. this.noise = p5sound.audiocontext.createBufferSource();
  6347. this.noise.buffer = this.buffer;
  6348. this.noise.loop = true;
  6349. this.noise.connect(this.output);
  6350. var now = p5sound.audiocontext.currentTime;
  6351. this.noise.start(now);
  6352. this.started = true;
  6353. };
  6354. /**
  6355. * Stop the noise.
  6356. *
  6357. * @method stop
  6358. */
  6359. p5.Noise.prototype.stop = function () {
  6360. var now = p5sound.audiocontext.currentTime;
  6361. if (this.noise) {
  6362. this.noise.stop(now);
  6363. this.started = false;
  6364. }
  6365. };
  6366. /**
  6367. * Pan the noise.
  6368. *
  6369. * @method pan
  6370. * @param {Number} panning Number between -1 (left)
  6371. * and 1 (right)
  6372. * @param {Number} timeFromNow schedule this event to happen
  6373. * seconds from now
  6374. */
  6375. /**
  6376. * Set the amplitude of the noise between 0 and 1.0. Or,
  6377. * modulate amplitude with an audio signal such as an oscillator.
  6378. *
  6379. * @param {Number|Object} volume amplitude between 0 and 1.0
  6380. * or modulating signal/oscillator
  6381. * @param {Number} [rampTime] create a fade that lasts rampTime
  6382. * @param {Number} [timeFromNow] schedule this event to happen
  6383. * seconds from now
  6384. */
  6385. /**
  6386. * Send output to a p5.sound or web audio object
  6387. *
  6388. * @method connect
  6389. * @param {Object} unit
  6390. */
  6391. /**
  6392. * Disconnect all output.
  6393. *
  6394. * @method disconnect
  6395. */
  6396. p5.Noise.prototype.dispose = function () {
  6397. var now = p5sound.audiocontext.currentTime;
  6398. // remove reference from soundArray
  6399. var index = p5sound.soundArray.indexOf(this);
  6400. p5sound.soundArray.splice(index, 1);
  6401. if (this.noise) {
  6402. this.noise.disconnect();
  6403. this.stop(now);
  6404. }
  6405. if (this.output) {
  6406. this.output.disconnect();
  6407. }
  6408. if (this.panner) {
  6409. this.panner.disconnect();
  6410. }
  6411. this.output = null;
  6412. this.panner = null;
  6413. this.buffer = null;
  6414. this.noise = null;
  6415. };
  6416. }(master);
  6417. var audioin;
  6418. audioin = function () {
  6419. 'use strict';
  6420. var p5sound = master;
  6421. var CustomError = errorHandler;
  6422. /**
  6423. * <p>Get audio from an input, i.e. your computer's microphone.</p>
  6424. *
  6425. * <p>Turn the mic on/off with the start() and stop() methods. When the mic
  6426. * is on, its volume can be measured with getLevel or by connecting an
  6427. * FFT object.</p>
  6428. *
  6429. * <p>If you want to hear the AudioIn, use the .connect() method.
  6430. * AudioIn does not connect to p5.sound output by default to prevent
  6431. * feedback.</p>
  6432. *
  6433. * <p><em>Note: This uses the <a href="http://caniuse.com/stream">getUserMedia/
  6434. * Stream</a> API, which is not supported by certain browsers. Access in Chrome browser
  6435. * is limited to localhost and https, but access over http may be limited.</em></p>
  6436. *
  6437. * @class p5.AudioIn
  6438. * @constructor
  6439. * @param {Function} [errorCallback] A function to call if there is an error
  6440. * accessing the AudioIn. For example,
  6441. * Safari and iOS devices do not
  6442. * currently allow microphone access.
  6443. * @return {Object} AudioIn
  6444. * @example
  6445. * <div><code>
  6446. * var mic;
  6447. * function setup(){
  6448. * mic = new p5.AudioIn()
  6449. * mic.start();
  6450. * }
  6451. * function draw(){
  6452. * background(0);
  6453. * micLevel = mic.getLevel();
  6454. * ellipse(width/2, constrain(height-micLevel*height*5, 0, height), 10, 10);
  6455. * }
  6456. * </code></div>
  6457. */
  6458. p5.AudioIn = function (errorCallback) {
  6459. // set up audio input
  6460. this.input = p5sound.audiocontext.createGain();
  6461. this.output = p5sound.audiocontext.createGain();
  6462. this.stream = null;
  6463. this.mediaStream = null;
  6464. this.currentSource = 0;
  6465. /**
  6466. * Client must allow browser to access their microphone / audioin source.
  6467. * Default: false. Will become true when the client enables acces.
  6468. *
  6469. * @property {Boolean} enabled
  6470. */
  6471. this.enabled = false;
  6472. // create an amplitude, connect to it by default but not to master out
  6473. this.amplitude = new p5.Amplitude();
  6474. this.output.connect(this.amplitude.input);
  6475. // Some browsers let developer determine their input sources
  6476. if (typeof window.MediaStreamTrack === 'undefined') {
  6477. if (errorCallback) {
  6478. errorCallback();
  6479. } else {
  6480. window.alert('This browser does not support AudioIn');
  6481. }
  6482. } else if (typeof window.MediaDevices.enumerateDevices === 'function') {
  6483. // Chrome supports getSources to list inputs. Dev picks default
  6484. window.MediaDevices.enumerateDevices(this._gotSources);
  6485. } else {
  6486. }
  6487. // add to soundArray so we can dispose on close
  6488. p5sound.soundArray.push(this);
  6489. };
  6490. /**
  6491. * Start processing audio input. This enables the use of other
  6492. * AudioIn methods like getLevel(). Note that by default, AudioIn
  6493. * is not connected to p5.sound's output. So you won't hear
  6494. * anything unless you use the connect() method.<br/>
  6495. *
  6496. * Certain browsers limit access to the user's microphone. For example,
  6497. * Chrome only allows access from localhost and over https. For this reason,
  6498. * you may want to include an errorCallback—a function that is called in case
  6499. * the browser won't provide mic access.
  6500. *
  6501. * @method start
  6502. * @param {Function} successCallback Name of a function to call on
  6503. * success.
  6504. * @param {Function} errorCallback Name of a function to call if
  6505. * there was an error. For example,
  6506. * some browsers do not support
  6507. * getUserMedia.
  6508. */
  6509. p5.AudioIn.prototype.start = function (successCallback, errorCallback) {
  6510. var self = this;
  6511. // if stream was already started...
  6512. // if _gotSources() i.e. developers determine which source to use
  6513. if (p5sound.inputSources[self.currentSource]) {
  6514. // set the audio source
  6515. var audioSource = p5sound.inputSources[self.currentSource].id;
  6516. var constraints = { audio: { optional: [{ sourceId: audioSource }] } };
  6517. window.navigator.getUserMedia(constraints, this._onStream = function (stream) {
  6518. self.stream = stream;
  6519. self.enabled = true;
  6520. // Wrap a MediaStreamSourceNode around the live input
  6521. self.mediaStream = p5sound.audiocontext.createMediaStreamSource(stream);
  6522. self.mediaStream.connect(self.output);
  6523. if (successCallback)
  6524. successCallback();
  6525. // only send to the Amplitude reader, so we can see it but not hear it.
  6526. self.amplitude.setInput(self.output);
  6527. }, this._onStreamError = function (e) {
  6528. if (errorCallback)
  6529. errorCallback(e);
  6530. else
  6531. console.error(e);
  6532. });
  6533. } else {
  6534. // if Firefox where users select their source via browser
  6535. // if (typeof MediaStreamTrack.getSources === 'undefined') {
  6536. // Only get the audio stream.
  6537. window.navigator.getUserMedia({ 'audio': true }, this._onStream = function (stream) {
  6538. self.stream = stream;
  6539. self.enabled = true;
  6540. // Wrap a MediaStreamSourceNode around the live input
  6541. self.mediaStream = p5sound.audiocontext.createMediaStreamSource(stream);
  6542. self.mediaStream.connect(self.output);
  6543. // only send to the Amplitude reader, so we can see it but not hear it.
  6544. self.amplitude.setInput(self.output);
  6545. if (successCallback)
  6546. successCallback();
  6547. }, this._onStreamError = function (e) {
  6548. if (errorCallback)
  6549. errorCallback(e);
  6550. else
  6551. console.error(e);
  6552. });
  6553. }
  6554. };
  6555. /**
  6556. * Turn the AudioIn off. If the AudioIn is stopped, it cannot getLevel().
  6557. * If re-starting, the user may be prompted for permission access.
  6558. *
  6559. * @method stop
  6560. */
  6561. p5.AudioIn.prototype.stop = function () {
  6562. if (this.stream) {
  6563. // assume only one track
  6564. this.stream.getTracks()[0].stop();
  6565. }
  6566. };
  6567. /**
  6568. * Connect to an audio unit. If no parameter is provided, will
  6569. * connect to the master output (i.e. your speakers).<br/>
  6570. *
  6571. * @method connect
  6572. * @param {Object} [unit] An object that accepts audio input,
  6573. * such as an FFT
  6574. */
  6575. p5.AudioIn.prototype.connect = function (unit) {
  6576. if (unit) {
  6577. if (unit.hasOwnProperty('input')) {
  6578. this.output.connect(unit.input);
  6579. } else if (unit.hasOwnProperty('analyser')) {
  6580. this.output.connect(unit.analyser);
  6581. } else {
  6582. this.output.connect(unit);
  6583. }
  6584. } else {
  6585. this.output.connect(p5sound.input);
  6586. }
  6587. };
  6588. /**
  6589. * Disconnect the AudioIn from all audio units. For example, if
  6590. * connect() had been called, disconnect() will stop sending
  6591. * signal to your speakers.<br/>
  6592. *
  6593. * @method disconnect
  6594. */
  6595. p5.AudioIn.prototype.disconnect = function () {
  6596. this.output.disconnect();
  6597. // stay connected to amplitude even if not outputting to p5
  6598. this.output.connect(this.amplitude.input);
  6599. };
  6600. /**
  6601. * Read the Amplitude (volume level) of an AudioIn. The AudioIn
  6602. * class contains its own instance of the Amplitude class to help
  6603. * make it easy to get a microphone's volume level. Accepts an
  6604. * optional smoothing value (0.0 < 1.0). <em>NOTE: AudioIn must
  6605. * .start() before using .getLevel().</em><br/>
  6606. *
  6607. * @method getLevel
  6608. * @param {Number} [smoothing] Smoothing is 0.0 by default.
  6609. * Smooths values based on previous values.
  6610. * @return {Number} Volume level (between 0.0 and 1.0)
  6611. */
  6612. p5.AudioIn.prototype.getLevel = function (smoothing) {
  6613. if (smoothing) {
  6614. this.amplitude.smoothing = smoothing;
  6615. }
  6616. return this.amplitude.getLevel();
  6617. };
  6618. /**
  6619. * Add input sources to the list of available sources.
  6620. *
  6621. * @private
  6622. */
  6623. p5.AudioIn.prototype._gotSources = function (sourceInfos) {
  6624. for (var i = 0; i < sourceInfos.length; i++) {
  6625. var sourceInfo = sourceInfos[i];
  6626. if (sourceInfo.kind === 'audio') {
  6627. // add the inputs to inputSources
  6628. //p5sound.inputSources.push(sourceInfo);
  6629. return sourceInfo;
  6630. }
  6631. }
  6632. };
  6633. /**
  6634. * Set amplitude (volume) of a mic input between 0 and 1.0. <br/>
  6635. *
  6636. * @method amp
  6637. * @param {Number} vol between 0 and 1.0
  6638. * @param {Number} [time] ramp time (optional)
  6639. */
  6640. p5.AudioIn.prototype.amp = function (vol, t) {
  6641. if (t) {
  6642. var rampTime = t || 0;
  6643. var currentVol = this.output.gain.value;
  6644. this.output.gain.cancelScheduledValues(p5sound.audiocontext.currentTime);
  6645. this.output.gain.setValueAtTime(currentVol, p5sound.audiocontext.currentTime);
  6646. this.output.gain.linearRampToValueAtTime(vol, rampTime + p5sound.audiocontext.currentTime);
  6647. } else {
  6648. this.output.gain.cancelScheduledValues(p5sound.audiocontext.currentTime);
  6649. this.output.gain.setValueAtTime(vol, p5sound.audiocontext.currentTime);
  6650. }
  6651. };
  6652. p5.AudioIn.prototype.listSources = function () {
  6653. console.log('listSources is deprecated - please use AudioIn.getSources');
  6654. console.log('input sources: ');
  6655. if (p5sound.inputSources.length > 0) {
  6656. return p5sound.inputSources;
  6657. } else {
  6658. return 'This browser does not support MediaStreamTrack.getSources()';
  6659. }
  6660. };
  6661. /**
  6662. * Chrome only. Returns a list of available input sources
  6663. * and allows the user to set the media source. Firefox allows
  6664. * the user to choose from input sources in the permissions dialogue
  6665. * instead of enumerating available sources and selecting one.
  6666. * Note: in order to have descriptive media names your page must be
  6667. * served over a secure (HTTPS) connection and the page should
  6668. * request user media before enumerating devices. Otherwise device
  6669. * ID will be a long device ID number and does not specify device
  6670. * type. For example see
  6671. * https://simpl.info/getusermedia/sources/index.html vs.
  6672. * http://simpl.info/getusermedia/sources/index.html
  6673. *
  6674. * @method getSources
  6675. * @param {Function} callback a callback to handle the sources
  6676. * when they have been enumerated
  6677. * @example
  6678. * <div><code>
  6679. * var audiograb;
  6680. *
  6681. * function setup(){
  6682. * //new audioIn
  6683. * audioGrab = new p5.AudioIn();
  6684. *
  6685. * audioGrab.getSources(function(sourceList) {
  6686. * //print out the array of available sources
  6687. * console.log(sourceList);
  6688. * //set the source to the first item in the inputSources array
  6689. * audioGrab.setSource(0);
  6690. * });
  6691. * }
  6692. * </code></div>
  6693. */
  6694. p5.AudioIn.prototype.getSources = function (callback) {
  6695. if (typeof window.MediaStreamTrack.getSources === 'function') {
  6696. window.MediaStreamTrack.getSources(function (data) {
  6697. for (var i = 0, max = data.length; i < max; i++) {
  6698. var sourceInfo = data[i];
  6699. if (sourceInfo.kind === 'audio') {
  6700. // add the inputs to inputSources
  6701. p5sound.inputSources.push(sourceInfo);
  6702. }
  6703. }
  6704. callback(p5sound.inputSources);
  6705. });
  6706. } else {
  6707. console.log('This browser does not support MediaStreamTrack.getSources()');
  6708. }
  6709. };
  6710. /**
  6711. * Set the input source. Accepts a number representing a
  6712. * position in the array returned by listSources().
  6713. * This is only available in browsers that support
  6714. * MediaStreamTrack.getSources(). Instead, some browsers
  6715. * give users the option to set their own media source.<br/>
  6716. *
  6717. * @method setSource
  6718. * @param {number} num position of input source in the array
  6719. */
  6720. p5.AudioIn.prototype.setSource = function (num) {
  6721. // TO DO - set input by string or # (array position)
  6722. var self = this;
  6723. if (p5sound.inputSources.length > 0 && num < p5sound.inputSources.length) {
  6724. // set the current source
  6725. self.currentSource = num;
  6726. console.log('set source to ' + p5sound.inputSources[self.currentSource].id);
  6727. } else {
  6728. console.log('unable to set input source');
  6729. }
  6730. };
  6731. // private method
  6732. p5.AudioIn.prototype.dispose = function () {
  6733. // remove reference from soundArray
  6734. var index = p5sound.soundArray.indexOf(this);
  6735. p5sound.soundArray.splice(index, 1);
  6736. this.stop();
  6737. if (this.output) {
  6738. this.output.disconnect();
  6739. }
  6740. if (this.amplitude) {
  6741. this.amplitude.disconnect();
  6742. }
  6743. this.amplitude = null;
  6744. this.output = null;
  6745. };
  6746. }(master, errorHandler);
  6747. var filter;
  6748. filter = function () {
  6749. 'use strict';
  6750. var p5sound = master;
  6751. /**
  6752. * A p5.Filter uses a Web Audio Biquad Filter to filter
  6753. * the frequency response of an input source. Inheriting
  6754. * classes include:<br/>
  6755. * * <code>p5.LowPass</code> - allows frequencies below
  6756. * the cutoff frequency to pass through, and attenuates
  6757. * frequencies above the cutoff.<br/>
  6758. * * <code>p5.HighPass</code> - the opposite of a lowpass
  6759. * filter. <br/>
  6760. * * <code>p5.BandPass</code> - allows a range of
  6761. * frequencies to pass through and attenuates the frequencies
  6762. * below and above this frequency range.<br/>
  6763. *
  6764. * The <code>.res()</code> method controls either width of the
  6765. * bandpass, or resonance of the low/highpass cutoff frequency.
  6766. *
  6767. * @class p5.Filter
  6768. * @constructor
  6769. * @param {String} [type] 'lowpass' (default), 'highpass', 'bandpass'
  6770. * @return {Object} p5.Filter
  6771. * @example
  6772. * <div><code>
  6773. * var fft, noise, filter;
  6774. *
  6775. * function setup() {
  6776. * fill(255, 40, 255);
  6777. *
  6778. * filter = new p5.BandPass();
  6779. *
  6780. * noise = new p5.Noise();
  6781. * // disconnect unfiltered noise,
  6782. * // and connect to filter
  6783. * noise.disconnect();
  6784. * noise.connect(filter);
  6785. * noise.start();
  6786. *
  6787. * fft = new p5.FFT();
  6788. * }
  6789. *
  6790. * function draw() {
  6791. * background(30);
  6792. *
  6793. * // set the BandPass frequency based on mouseX
  6794. * var freq = map(mouseX, 0, width, 20, 10000);
  6795. * filter.freq(freq);
  6796. * // give the filter a narrow band (lower res = wider bandpass)
  6797. * filter.res(50);
  6798. *
  6799. * // draw filtered spectrum
  6800. * var spectrum = fft.analyze();
  6801. * noStroke();
  6802. * for (var i = 0; i < spectrum.length; i++) {
  6803. * var x = map(i, 0, spectrum.length, 0, width);
  6804. * var h = -height + map(spectrum[i], 0, 255, height, 0);
  6805. * rect(x, height, width/spectrum.length, h);
  6806. * }
  6807. *
  6808. * isMouseOverCanvas();
  6809. * }
  6810. *
  6811. * function isMouseOverCanvas() {
  6812. * var mX = mouseX, mY = mouseY;
  6813. * if (mX > 0 && mX < width && mY < height && mY > 0) {
  6814. * noise.amp(0.5, 0.2);
  6815. * } else {
  6816. * noise.amp(0, 0.2);
  6817. * }
  6818. * }
  6819. * </code></div>
  6820. */
  6821. p5.Filter = function (type) {
  6822. this.ac = p5sound.audiocontext;
  6823. this.input = this.ac.createGain();
  6824. this.output = this.ac.createGain();
  6825. /**
  6826. * The p5.Filter is built with a
  6827. * <a href="http://www.w3.org/TR/webaudio/#BiquadFilterNode">
  6828. * Web Audio BiquadFilter Node</a>.
  6829. *
  6830. * @property biquadFilter
  6831. * @type {Object} Web Audio Delay Node
  6832. */
  6833. this.biquad = this.ac.createBiquadFilter();
  6834. this.input.connect(this.biquad);
  6835. this.biquad.connect(this.output);
  6836. this.connect();
  6837. if (type) {
  6838. this.setType(type);
  6839. }
  6840. // add to the soundArray
  6841. p5sound.soundArray.push(this);
  6842. };
  6843. /**
  6844. * Filter an audio signal according to a set
  6845. * of filter parameters.
  6846. *
  6847. * @method process
  6848. * @param {Object} Signal An object that outputs audio
  6849. * @param {Number} [freq] Frequency in Hz, from 10 to 22050
  6850. * @param {Number} [res] Resonance/Width of the filter frequency
  6851. * from 0.001 to 1000
  6852. */
  6853. p5.Filter.prototype.process = function (src, freq, res) {
  6854. src.connect(this.input);
  6855. this.set(freq, res);
  6856. };
  6857. /**
  6858. * Set the frequency and the resonance of the filter.
  6859. *
  6860. * @method set
  6861. * @param {Number} freq Frequency in Hz, from 10 to 22050
  6862. * @param {Number} res Resonance (Q) from 0.001 to 1000
  6863. * @param {Number} [timeFromNow] schedule this event to happen
  6864. * seconds from now
  6865. */
  6866. p5.Filter.prototype.set = function (freq, res, time) {
  6867. if (freq) {
  6868. this.freq(freq, time);
  6869. }
  6870. if (res) {
  6871. this.res(res, time);
  6872. }
  6873. };
  6874. /**
  6875. * Set the filter frequency, in Hz, from 10 to 22050 (the range of
  6876. * human hearing, although in reality most people hear in a narrower
  6877. * range).
  6878. *
  6879. * @method freq
  6880. * @param {Number} freq Filter Frequency
  6881. * @param {Number} [timeFromNow] schedule this event to happen
  6882. * seconds from now
  6883. * @return {Number} value Returns the current frequency value
  6884. */
  6885. p5.Filter.prototype.freq = function (freq, time) {
  6886. var self = this;
  6887. var t = time || 0;
  6888. if (freq <= 0) {
  6889. freq = 1;
  6890. }
  6891. if (typeof freq === 'number') {
  6892. self.biquad.frequency.value = freq;
  6893. self.biquad.frequency.cancelScheduledValues(this.ac.currentTime + 0.01 + t);
  6894. self.biquad.frequency.exponentialRampToValueAtTime(freq, this.ac.currentTime + 0.02 + t);
  6895. } else if (freq) {
  6896. freq.connect(this.biquad.frequency);
  6897. }
  6898. return self.biquad.frequency.value;
  6899. };
  6900. /**
  6901. * Controls either width of a bandpass frequency,
  6902. * or the resonance of a low/highpass cutoff frequency.
  6903. *
  6904. * @method res
  6905. * @param {Number} res Resonance/Width of filter freq
  6906. * from 0.001 to 1000
  6907. * @param {Number} [timeFromNow] schedule this event to happen
  6908. * seconds from now
  6909. * @return {Number} value Returns the current res value
  6910. */
  6911. p5.Filter.prototype.res = function (res, time) {
  6912. var self = this;
  6913. var t = time || 0;
  6914. if (typeof res == 'number') {
  6915. self.biquad.Q.value = res;
  6916. self.biquad.Q.cancelScheduledValues(self.ac.currentTime + 0.01 + t);
  6917. self.biquad.Q.linearRampToValueAtTime(res, self.ac.currentTime + 0.02 + t);
  6918. } else if (res) {
  6919. freq.connect(this.biquad.Q);
  6920. }
  6921. return self.biquad.Q.value;
  6922. };
  6923. /**
  6924. * Set the type of a p5.Filter. Possible types include:
  6925. * "lowpass" (default), "highpass", "bandpass",
  6926. * "lowshelf", "highshelf", "peaking", "notch",
  6927. * "allpass".
  6928. *
  6929. * @method setType
  6930. * @param {String} t type of filter
  6931. */
  6932. p5.Filter.prototype.setType = function (t) {
  6933. this.biquad.type = t;
  6934. };
  6935. /**
  6936. * Set the output level of the filter.
  6937. *
  6938. * @method amp
  6939. * @param {Number} volume amplitude between 0 and 1.0
  6940. * @param {Number} [rampTime] create a fade that lasts rampTime
  6941. * @param {Number} [timeFromNow] schedule this event to happen
  6942. * seconds from now
  6943. */
  6944. p5.Filter.prototype.amp = function (vol, rampTime, tFromNow) {
  6945. var rampTime = rampTime || 0;
  6946. var tFromNow = tFromNow || 0;
  6947. var now = p5sound.audiocontext.currentTime;
  6948. var currentVol = this.output.gain.value;
  6949. this.output.gain.cancelScheduledValues(now);
  6950. this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
  6951. this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
  6952. };
  6953. /**
  6954. * Send output to a p5.sound or web audio object
  6955. *
  6956. * @method connect
  6957. * @param {Object} unit
  6958. */
  6959. p5.Filter.prototype.connect = function (unit) {
  6960. var u = unit || p5.soundOut.input;
  6961. this.output.connect(u);
  6962. };
  6963. /**
  6964. * Disconnect all output.
  6965. *
  6966. * @method disconnect
  6967. */
  6968. p5.Filter.prototype.disconnect = function () {
  6969. this.output.disconnect();
  6970. };
  6971. p5.Filter.prototype.dispose = function () {
  6972. // remove reference from soundArray
  6973. var index = p5sound.soundArray.indexOf(this);
  6974. p5sound.soundArray.splice(index, 1);
  6975. this.input.disconnect();
  6976. this.input = undefined;
  6977. this.output.disconnect();
  6978. this.output = undefined;
  6979. this.biquad.disconnect();
  6980. this.biquad = undefined;
  6981. };
  6982. /**
  6983. * Constructor: <code>new p5.LowPass()</code> Filter.
  6984. * This is the same as creating a p5.Filter and then calling
  6985. * its method <code>setType('lowpass')</code>.
  6986. * See p5.Filter for methods.
  6987. *
  6988. * @method p5.LowPass
  6989. */
  6990. p5.LowPass = function () {
  6991. p5.Filter.call(this, 'lowpass');
  6992. };
  6993. p5.LowPass.prototype = Object.create(p5.Filter.prototype);
  6994. /**
  6995. * Constructor: <code>new p5.HighPass()</code> Filter.
  6996. * This is the same as creating a p5.Filter and then calling
  6997. * its method <code>setType('highpass')</code>.
  6998. * See p5.Filter for methods.
  6999. *
  7000. * @method p5.HighPass
  7001. */
  7002. p5.HighPass = function () {
  7003. p5.Filter.call(this, 'highpass');
  7004. };
  7005. p5.HighPass.prototype = Object.create(p5.Filter.prototype);
  7006. /**
  7007. * Constructor: <code>new p5.BandPass()</code> Filter.
  7008. * This is the same as creating a p5.Filter and then calling
  7009. * its method <code>setType('bandpass')</code>.
  7010. * See p5.Filter for methods.
  7011. *
  7012. * @method p5.BandPass
  7013. */
  7014. p5.BandPass = function () {
  7015. p5.Filter.call(this, 'bandpass');
  7016. };
  7017. p5.BandPass.prototype = Object.create(p5.Filter.prototype);
  7018. }(master);
  7019. var delay;
  7020. delay = function () {
  7021. 'use strict';
  7022. var p5sound = master;
  7023. var Filter = filter;
  7024. /**
  7025. * Delay is an echo effect. It processes an existing sound source,
  7026. * and outputs a delayed version of that sound. The p5.Delay can
  7027. * produce different effects depending on the delayTime, feedback,
  7028. * filter, and type. In the example below, a feedback of 0.5 will
  7029. * produce a looping delay that decreases in volume by
  7030. * 50% each repeat. A filter will cut out the high frequencies so
  7031. * that the delay does not sound as piercing as the original source.
  7032. *
  7033. * @class p5.Delay
  7034. * @constructor
  7035. * @return {Object} Returns a p5.Delay object
  7036. * @example
  7037. * <div><code>
  7038. * var noise, env, delay;
  7039. *
  7040. * function setup() {
  7041. * background(0);
  7042. * noStroke();
  7043. * fill(255);
  7044. * textAlign(CENTER);
  7045. * text('click to play', width/2, height/2);
  7046. *
  7047. * noise = new p5.Noise('brown');
  7048. * noise.amp(0);
  7049. * noise.start();
  7050. *
  7051. * delay = new p5.Delay();
  7052. *
  7053. * // delay.process() accepts 4 parameters:
  7054. * // source, delayTime, feedback, filter frequency
  7055. * // play with these numbers!!
  7056. * delay.process(noise, .12, .7, 2300);
  7057. *
  7058. * // play the noise with an envelope,
  7059. * // a series of fades ( time / value pairs )
  7060. * env = new p5.Env(.01, 0.2, .2, .1);
  7061. * }
  7062. *
  7063. * // mouseClick triggers envelope
  7064. * function mouseClicked() {
  7065. * // is mouse over canvas?
  7066. * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
  7067. * env.play(noise);
  7068. * }
  7069. * }
  7070. * </code></div>
  7071. */
  7072. p5.Delay = function () {
  7073. this.ac = p5sound.audiocontext;
  7074. this.input = this.ac.createGain();
  7075. this.output = this.ac.createGain();
  7076. this._split = this.ac.createChannelSplitter(2);
  7077. this._merge = this.ac.createChannelMerger(2);
  7078. this._leftGain = this.ac.createGain();
  7079. this._rightGain = this.ac.createGain();
  7080. /**
  7081. * The p5.Delay is built with two
  7082. * <a href="http://www.w3.org/TR/webaudio/#DelayNode">
  7083. * Web Audio Delay Nodes</a>, one for each stereo channel.
  7084. *
  7085. * @property leftDelay
  7086. * @type {Object} Web Audio Delay Node
  7087. */
  7088. this.leftDelay = this.ac.createDelay();
  7089. /**
  7090. * The p5.Delay is built with two
  7091. * <a href="http://www.w3.org/TR/webaudio/#DelayNode">
  7092. * Web Audio Delay Nodes</a>, one for each stereo channel.
  7093. *
  7094. * @property rightDelay
  7095. * @type {Object} Web Audio Delay Node
  7096. */
  7097. this.rightDelay = this.ac.createDelay();
  7098. this._leftFilter = new p5.Filter();
  7099. this._rightFilter = new p5.Filter();
  7100. this._leftFilter.disconnect();
  7101. this._rightFilter.disconnect();
  7102. this._leftFilter.biquad.frequency.setValueAtTime(1200, this.ac.currentTime);
  7103. this._rightFilter.biquad.frequency.setValueAtTime(1200, this.ac.currentTime);
  7104. this._leftFilter.biquad.Q.setValueAtTime(0.3, this.ac.currentTime);
  7105. this._rightFilter.biquad.Q.setValueAtTime(0.3, this.ac.currentTime);
  7106. // graph routing
  7107. this.input.connect(this._split);
  7108. this.leftDelay.connect(this._leftGain);
  7109. this.rightDelay.connect(this._rightGain);
  7110. this._leftGain.connect(this._leftFilter.input);
  7111. this._rightGain.connect(this._rightFilter.input);
  7112. this._merge.connect(this.output);
  7113. this.output.connect(p5.soundOut.input);
  7114. this._leftFilter.biquad.gain.setValueAtTime(1, this.ac.currentTime);
  7115. this._rightFilter.biquad.gain.setValueAtTime(1, this.ac.currentTime);
  7116. // default routing
  7117. this.setType(0);
  7118. this._maxDelay = this.leftDelay.delayTime.maxValue;
  7119. // add this p5.SoundFile to the soundArray
  7120. p5sound.soundArray.push(this);
  7121. };
  7122. /**
  7123. * Add delay to an audio signal according to a set
  7124. * of delay parameters.
  7125. *
  7126. * @method process
  7127. * @param {Object} Signal An object that outputs audio
  7128. * @param {Number} [delayTime] Time (in seconds) of the delay/echo.
  7129. * Some browsers limit delayTime to
  7130. * 1 second.
  7131. * @param {Number} [feedback] sends the delay back through itself
  7132. * in a loop that decreases in volume
  7133. * each time.
  7134. * @param {Number} [lowPass] Cutoff frequency. Only frequencies
  7135. * below the lowPass will be part of the
  7136. * delay.
  7137. */
  7138. p5.Delay.prototype.process = function (src, _delayTime, _feedback, _filter) {
  7139. var feedback = _feedback || 0;
  7140. var delayTime = _delayTime || 0;
  7141. if (feedback >= 1) {
  7142. throw new Error('Feedback value will force a positive feedback loop.');
  7143. }
  7144. if (delayTime >= this._maxDelay) {
  7145. throw new Error('Delay Time exceeds maximum delay time of ' + this._maxDelay + ' second.');
  7146. }
  7147. src.connect(this.input);
  7148. this.leftDelay.delayTime.setValueAtTime(delayTime, this.ac.currentTime);
  7149. this.rightDelay.delayTime.setValueAtTime(delayTime, this.ac.currentTime);
  7150. this._leftGain.gain.setValueAtTime(feedback, this.ac.currentTime);
  7151. this._rightGain.gain.setValueAtTime(feedback, this.ac.currentTime);
  7152. if (_filter) {
  7153. this._leftFilter.freq(_filter);
  7154. this._rightFilter.freq(_filter);
  7155. }
  7156. };
  7157. /**
  7158. * Set the delay (echo) time, in seconds. Usually this value will be
  7159. * a floating point number between 0.0 and 1.0.
  7160. *
  7161. * @method delayTime
  7162. * @param {Number} delayTime Time (in seconds) of the delay
  7163. */
  7164. p5.Delay.prototype.delayTime = function (t) {
  7165. // if t is an audio node...
  7166. if (typeof t !== 'number') {
  7167. t.connect(this.leftDelay.delayTime);
  7168. t.connect(this.rightDelay.delayTime);
  7169. } else {
  7170. this.leftDelay.delayTime.cancelScheduledValues(this.ac.currentTime);
  7171. this.rightDelay.delayTime.cancelScheduledValues(this.ac.currentTime);
  7172. this.leftDelay.delayTime.linearRampToValueAtTime(t, this.ac.currentTime);
  7173. this.rightDelay.delayTime.linearRampToValueAtTime(t, this.ac.currentTime);
  7174. }
  7175. };
  7176. /**
  7177. * Feedback occurs when Delay sends its signal back through its input
  7178. * in a loop. The feedback amount determines how much signal to send each
  7179. * time through the loop. A feedback greater than 1.0 is not desirable because
  7180. * it will increase the overall output each time through the loop,
  7181. * creating an infinite feedback loop.
  7182. *
  7183. * @method feedback
  7184. * @param {Number|Object} feedback 0.0 to 1.0, or an object such as an
  7185. * Oscillator that can be used to
  7186. * modulate this param
  7187. */
  7188. p5.Delay.prototype.feedback = function (f) {
  7189. // if f is an audio node...
  7190. if (typeof f !== 'number') {
  7191. f.connect(this._leftGain.gain);
  7192. f.connect(this._rightGain.gain);
  7193. } else if (f >= 1) {
  7194. throw new Error('Feedback value will force a positive feedback loop.');
  7195. } else {
  7196. this._leftGain.gain.exponentialRampToValueAtTime(f, this.ac.currentTime);
  7197. this._rightGain.gain.exponentialRampToValueAtTime(f, this.ac.currentTime);
  7198. }
  7199. };
  7200. /**
  7201. * Set a lowpass filter frequency for the delay. A lowpass filter
  7202. * will cut off any frequencies higher than the filter frequency.
  7203. *
  7204. * @method filter
  7205. * @param {Number|Object} cutoffFreq A lowpass filter will cut off any
  7206. * frequencies higher than the filter frequency.
  7207. * @param {Number|Object} res Resonance of the filter frequency
  7208. * cutoff, or an object (i.e. a p5.Oscillator)
  7209. * that can be used to modulate this parameter.
  7210. * High numbers (i.e. 15) will produce a resonance,
  7211. * low numbers (i.e. .2) will produce a slope.
  7212. */
  7213. p5.Delay.prototype.filter = function (freq, q) {
  7214. this._leftFilter.set(freq, q);
  7215. this._rightFilter.set(freq, q);
  7216. };
  7217. /**
  7218. * Choose a preset type of delay. 'pingPong' bounces the signal
  7219. * from the left to the right channel to produce a stereo effect.
  7220. * Any other parameter will revert to the default delay setting.
  7221. *
  7222. * @method setType
  7223. * @param {String|Number} type 'pingPong' (1) or 'default' (0)
  7224. */
  7225. p5.Delay.prototype.setType = function (t) {
  7226. if (t === 1) {
  7227. t = 'pingPong';
  7228. }
  7229. this._split.disconnect();
  7230. this._leftFilter.disconnect();
  7231. this._rightFilter.disconnect();
  7232. this._split.connect(this.leftDelay, 0);
  7233. this._split.connect(this.rightDelay, 1);
  7234. switch (t) {
  7235. case 'pingPong':
  7236. this._rightFilter.setType(this._leftFilter.biquad.type);
  7237. this._leftFilter.output.connect(this._merge, 0, 0);
  7238. this._rightFilter.output.connect(this._merge, 0, 1);
  7239. this._leftFilter.output.connect(this.rightDelay);
  7240. this._rightFilter.output.connect(this.leftDelay);
  7241. break;
  7242. default:
  7243. this._leftFilter.output.connect(this._merge, 0, 0);
  7244. this._leftFilter.output.connect(this._merge, 0, 1);
  7245. this._leftFilter.output.connect(this.leftDelay);
  7246. this._leftFilter.output.connect(this.rightDelay);
  7247. }
  7248. };
  7249. /**
  7250. * Set the output level of the delay effect.
  7251. *
  7252. * @method amp
  7253. * @param {Number} volume amplitude between 0 and 1.0
  7254. * @param {Number} [rampTime] create a fade that lasts rampTime
  7255. * @param {Number} [timeFromNow] schedule this event to happen
  7256. * seconds from now
  7257. */
  7258. p5.Delay.prototype.amp = function (vol, rampTime, tFromNow) {
  7259. var rampTime = rampTime || 0;
  7260. var tFromNow = tFromNow || 0;
  7261. var now = p5sound.audiocontext.currentTime;
  7262. var currentVol = this.output.gain.value;
  7263. this.output.gain.cancelScheduledValues(now);
  7264. this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
  7265. this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
  7266. };
  7267. /**
  7268. * Send output to a p5.sound or web audio object
  7269. *
  7270. * @method connect
  7271. * @param {Object} unit
  7272. */
  7273. p5.Delay.prototype.connect = function (unit) {
  7274. var u = unit || p5.soundOut.input;
  7275. this.output.connect(u);
  7276. };
  7277. /**
  7278. * Disconnect all output.
  7279. *
  7280. * @method disconnect
  7281. */
  7282. p5.Delay.prototype.disconnect = function () {
  7283. this.output.disconnect();
  7284. };
  7285. p5.Delay.prototype.dispose = function () {
  7286. // remove reference from soundArray
  7287. var index = p5sound.soundArray.indexOf(this);
  7288. p5sound.soundArray.splice(index, 1);
  7289. this.input.disconnect();
  7290. this.output.disconnect();
  7291. this._split.disconnect();
  7292. this._leftFilter.disconnect();
  7293. this._rightFilter.disconnect();
  7294. this._merge.disconnect();
  7295. this._leftGain.disconnect();
  7296. this._rightGain.disconnect();
  7297. this.leftDelay.disconnect();
  7298. this.rightDelay.disconnect();
  7299. this.input = undefined;
  7300. this.output = undefined;
  7301. this._split = undefined;
  7302. this._leftFilter = undefined;
  7303. this._rightFilter = undefined;
  7304. this._merge = undefined;
  7305. this._leftGain = undefined;
  7306. this._rightGain = undefined;
  7307. this.leftDelay = undefined;
  7308. this.rightDelay = undefined;
  7309. };
  7310. }(master, filter);
  7311. var reverb;
  7312. reverb = function () {
  7313. 'use strict';
  7314. var p5sound = master;
  7315. var CustomError = errorHandler;
  7316. /**
  7317. * Reverb adds depth to a sound through a large number of decaying
  7318. * echoes. It creates the perception that sound is occurring in a
  7319. * physical space. The p5.Reverb has paramters for Time (how long does the
  7320. * reverb last) and decayRate (how much the sound decays with each echo)
  7321. * that can be set with the .set() or .process() methods. The p5.Convolver
  7322. * extends p5.Reverb allowing you to recreate the sound of actual physical
  7323. * spaces through convolution.
  7324. *
  7325. * @class p5.Reverb
  7326. * @constructor
  7327. * @example
  7328. * <div><code>
  7329. * var soundFile, reverb;
  7330. * function preload() {
  7331. * soundFile = loadSound('assets/Damscray_DancingTiger.mp3');
  7332. * }
  7333. *
  7334. * function setup() {
  7335. * reverb = new p5.Reverb();
  7336. * soundFile.disconnect(); // so we'll only hear reverb...
  7337. *
  7338. * // connect soundFile to reverb, process w/
  7339. * // 3 second reverbTime, decayRate of 2%
  7340. * reverb.process(soundFile, 3, 2);
  7341. * soundFile.play();
  7342. * }
  7343. * </code></div>
  7344. */
  7345. p5.Reverb = function () {
  7346. this.ac = p5sound.audiocontext;
  7347. this.convolverNode = this.ac.createConvolver();
  7348. this.input = this.ac.createGain();
  7349. this.output = this.ac.createGain();
  7350. // otherwise, Safari distorts
  7351. this.input.gain.value = 0.5;
  7352. this.input.connect(this.convolverNode);
  7353. this.convolverNode.connect(this.output);
  7354. // default params
  7355. this._seconds = 3;
  7356. this._decay = 2;
  7357. this._reverse = false;
  7358. this._buildImpulse();
  7359. this.connect();
  7360. p5sound.soundArray.push(this);
  7361. };
  7362. /**
  7363. * Connect a source to the reverb, and assign reverb parameters.
  7364. *
  7365. * @method process
  7366. * @param {Object} src p5.sound / Web Audio object with a sound
  7367. * output.
  7368. * @param {Number} [seconds] Duration of the reverb, in seconds.
  7369. * Min: 0, Max: 10. Defaults to 3.
  7370. * @param {Number} [decayRate] Percentage of decay with each echo.
  7371. * Min: 0, Max: 100. Defaults to 2.
  7372. * @param {Boolean} [reverse] Play the reverb backwards or forwards.
  7373. */
  7374. p5.Reverb.prototype.process = function (src, seconds, decayRate, reverse) {
  7375. src.connect(this.input);
  7376. var rebuild = false;
  7377. if (seconds) {
  7378. this._seconds = seconds;
  7379. rebuild = true;
  7380. }
  7381. if (decayRate) {
  7382. this._decay = decayRate;
  7383. }
  7384. if (reverse) {
  7385. this._reverse = reverse;
  7386. }
  7387. if (rebuild) {
  7388. this._buildImpulse();
  7389. }
  7390. };
  7391. /**
  7392. * Set the reverb settings. Similar to .process(), but without
  7393. * assigning a new input.
  7394. *
  7395. * @method set
  7396. * @param {Number} [seconds] Duration of the reverb, in seconds.
  7397. * Min: 0, Max: 10. Defaults to 3.
  7398. * @param {Number} [decayRate] Percentage of decay with each echo.
  7399. * Min: 0, Max: 100. Defaults to 2.
  7400. * @param {Boolean} [reverse] Play the reverb backwards or forwards.
  7401. */
  7402. p5.Reverb.prototype.set = function (seconds, decayRate, reverse) {
  7403. var rebuild = false;
  7404. if (seconds) {
  7405. this._seconds = seconds;
  7406. rebuild = true;
  7407. }
  7408. if (decayRate) {
  7409. this._decay = decayRate;
  7410. }
  7411. if (reverse) {
  7412. this._reverse = reverse;
  7413. }
  7414. if (rebuild) {
  7415. this._buildImpulse();
  7416. }
  7417. };
  7418. /**
  7419. * Set the output level of the delay effect.
  7420. *
  7421. * @method amp
  7422. * @param {Number} volume amplitude between 0 and 1.0
  7423. * @param {Number} [rampTime] create a fade that lasts rampTime
  7424. * @param {Number} [timeFromNow] schedule this event to happen
  7425. * seconds from now
  7426. */
  7427. p5.Reverb.prototype.amp = function (vol, rampTime, tFromNow) {
  7428. var rampTime = rampTime || 0;
  7429. var tFromNow = tFromNow || 0;
  7430. var now = p5sound.audiocontext.currentTime;
  7431. var currentVol = this.output.gain.value;
  7432. this.output.gain.cancelScheduledValues(now);
  7433. this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow + 0.001);
  7434. this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime + 0.001);
  7435. };
  7436. /**
  7437. * Send output to a p5.sound or web audio object
  7438. *
  7439. * @method connect
  7440. * @param {Object} unit
  7441. */
  7442. p5.Reverb.prototype.connect = function (unit) {
  7443. var u = unit || p5.soundOut.input;
  7444. this.output.connect(u.input ? u.input : u);
  7445. };
  7446. /**
  7447. * Disconnect all output.
  7448. *
  7449. * @method disconnect
  7450. */
  7451. p5.Reverb.prototype.disconnect = function () {
  7452. this.output.disconnect();
  7453. };
  7454. /**
  7455. * Inspired by Simple Reverb by Jordan Santell
  7456. * https://github.com/web-audio-components/simple-reverb/blob/master/index.js
  7457. *
  7458. * Utility function for building an impulse response
  7459. * based on the module parameters.
  7460. *
  7461. * @private
  7462. */
  7463. p5.Reverb.prototype._buildImpulse = function () {
  7464. var rate = this.ac.sampleRate;
  7465. var length = rate * this._seconds;
  7466. var decay = this._decay;
  7467. var impulse = this.ac.createBuffer(2, length, rate);
  7468. var impulseL = impulse.getChannelData(0);
  7469. var impulseR = impulse.getChannelData(1);
  7470. var n, i;
  7471. for (i = 0; i < length; i++) {
  7472. n = this.reverse ? length - i : i;
  7473. impulseL[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
  7474. impulseR[i] = (Math.random() * 2 - 1) * Math.pow(1 - n / length, decay);
  7475. }
  7476. this.convolverNode.buffer = impulse;
  7477. };
  7478. p5.Reverb.prototype.dispose = function () {
  7479. // remove reference from soundArray
  7480. var index = p5sound.soundArray.indexOf(this);
  7481. p5sound.soundArray.splice(index, 1);
  7482. if (this.convolverNode) {
  7483. this.convolverNode.buffer = null;
  7484. this.convolverNode = null;
  7485. }
  7486. if (typeof this.output !== 'undefined') {
  7487. this.output.disconnect();
  7488. this.output = null;
  7489. }
  7490. if (typeof this.panner !== 'undefined') {
  7491. this.panner.disconnect();
  7492. this.panner = null;
  7493. }
  7494. };
  7495. // =======================================================================
  7496. // *** p5.Convolver ***
  7497. // =======================================================================
  7498. /**
  7499. * <p>p5.Convolver extends p5.Reverb. It can emulate the sound of real
  7500. * physical spaces through a process called <a href="
  7501. * https://en.wikipedia.org/wiki/Convolution_reverb#Real_space_simulation">
  7502. * convolution</a>.</p>
  7503. *
  7504. * <p>Convolution multiplies any audio input by an "impulse response"
  7505. * to simulate the dispersion of sound over time. The impulse response is
  7506. * generated from an audio file that you provide. One way to
  7507. * generate an impulse response is to pop a balloon in a reverberant space
  7508. * and record the echo. Convolution can also be used to experiment with
  7509. * sound.</p>
  7510. *
  7511. * <p>Use the method <code>createConvolution(path)</code> to instantiate a
  7512. * p5.Convolver with a path to your impulse response audio file.</p>
  7513. *
  7514. * @class p5.Convolver
  7515. * @constructor
  7516. * @param {String} path path to a sound file
  7517. * @param {Function} [callback] function to call when loading succeeds
  7518. * @param {Function} [errorCallback] function to call if loading fails.
  7519. * This function will receive an error or
  7520. * XMLHttpRequest object with information
  7521. * about what went wrong.
  7522. * @example
  7523. * <div><code>
  7524. * var cVerb, sound;
  7525. * function preload() {
  7526. * // We have both MP3 and OGG versions of all sound assets
  7527. * soundFormats('ogg', 'mp3');
  7528. *
  7529. * // Try replacing 'bx-spring' with other soundfiles like
  7530. * // 'concrete-tunnel' 'small-plate' 'drum' 'beatbox'
  7531. * cVerb = createConvolver('assets/bx-spring.mp3');
  7532. *
  7533. * // Try replacing 'Damscray_DancingTiger' with
  7534. * // 'beat', 'doorbell', lucky_dragons_-_power_melody'
  7535. * sound = loadSound('assets/Damscray_DancingTiger.mp3');
  7536. * }
  7537. *
  7538. * function setup() {
  7539. * // disconnect from master output...
  7540. * sound.disconnect();
  7541. *
  7542. * // ...and process with cVerb
  7543. * // so that we only hear the convolution
  7544. * cVerb.process(sound);
  7545. *
  7546. * sound.play();
  7547. * }
  7548. * </code></div>
  7549. */
  7550. p5.Convolver = function (path, callback, errorCallback) {
  7551. this.ac = p5sound.audiocontext;
  7552. /**
  7553. * Internally, the p5.Convolver uses the a
  7554. * <a href="http://www.w3.org/TR/webaudio/#ConvolverNode">
  7555. * Web Audio Convolver Node</a>.
  7556. *
  7557. * @property convolverNode
  7558. * @type {Object} Web Audio Convolver Node
  7559. */
  7560. this.convolverNode = this.ac.createConvolver();
  7561. this.input = this.ac.createGain();
  7562. this.output = this.ac.createGain();
  7563. // otherwise, Safari distorts
  7564. this.input.gain.value = 0.5;
  7565. this.input.connect(this.convolverNode);
  7566. this.convolverNode.connect(this.output);
  7567. if (path) {
  7568. this.impulses = [];
  7569. this._loadBuffer(path, callback, errorCallback);
  7570. } else {
  7571. // parameters
  7572. this._seconds = 3;
  7573. this._decay = 2;
  7574. this._reverse = false;
  7575. this._buildImpulse();
  7576. }
  7577. this.connect();
  7578. p5sound.soundArray.push(this);
  7579. };
  7580. p5.Convolver.prototype = Object.create(p5.Reverb.prototype);
  7581. p5.prototype.registerPreloadMethod('createConvolver', p5.prototype);
  7582. /**
  7583. * Create a p5.Convolver. Accepts a path to a soundfile
  7584. * that will be used to generate an impulse response.
  7585. *
  7586. * @method createConvolver
  7587. * @param {String} path path to a sound file
  7588. * @param {Function} [callback] function to call if loading is successful.
  7589. * The object will be passed in as the argument
  7590. * to the callback function.
  7591. * @param {Function} [errorCallback] function to call if loading is not successful.
  7592. * A custom error will be passed in as the argument
  7593. * to the callback function.
  7594. * @return {p5.Convolver}
  7595. * @example
  7596. * <div><code>
  7597. * var cVerb, sound;
  7598. * function preload() {
  7599. * // We have both MP3 and OGG versions of all sound assets
  7600. * soundFormats('ogg', 'mp3');
  7601. *
  7602. * // Try replacing 'bx-spring' with other soundfiles like
  7603. * // 'concrete-tunnel' 'small-plate' 'drum' 'beatbox'
  7604. * cVerb = createConvolver('assets/bx-spring.mp3');
  7605. *
  7606. * // Try replacing 'Damscray_DancingTiger' with
  7607. * // 'beat', 'doorbell', lucky_dragons_-_power_melody'
  7608. * sound = loadSound('assets/Damscray_DancingTiger.mp3');
  7609. * }
  7610. *
  7611. * function setup() {
  7612. * // disconnect from master output...
  7613. * sound.disconnect();
  7614. *
  7615. * // ...and process with cVerb
  7616. * // so that we only hear the convolution
  7617. * cVerb.process(sound);
  7618. *
  7619. * sound.play();
  7620. * }
  7621. * </code></div>
  7622. */
  7623. p5.prototype.createConvolver = function (path, callback, errorCallback) {
  7624. // if loading locally without a server
  7625. if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
  7626. alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
  7627. }
  7628. var cReverb = new p5.Convolver(path, callback, errorCallback);
  7629. cReverb.impulses = [];
  7630. return cReverb;
  7631. };
  7632. /**
  7633. * Private method to load a buffer as an Impulse Response,
  7634. * assign it to the convolverNode, and add to the Array of .impulses.
  7635. *
  7636. * @param {String} path
  7637. * @param {Function} callback
  7638. * @param {Function} errorCallback
  7639. * @private
  7640. */
  7641. p5.Convolver.prototype._loadBuffer = function (path, callback, errorCallback) {
  7642. var path = p5.prototype._checkFileFormats(path);
  7643. var self = this;
  7644. var errorTrace = new Error().stack;
  7645. var ac = p5.prototype.getAudioContext();
  7646. var request = new XMLHttpRequest();
  7647. request.open('GET', path, true);
  7648. request.responseType = 'arraybuffer';
  7649. request.onload = function () {
  7650. if (request.status == 200) {
  7651. // on success loading file:
  7652. ac.decodeAudioData(request.response, function (buff) {
  7653. var buffer = {};
  7654. var chunks = path.split('/');
  7655. buffer.name = chunks[chunks.length - 1];
  7656. buffer.audioBuffer = buff;
  7657. self.impulses.push(buffer);
  7658. self.convolverNode.buffer = buffer.audioBuffer;
  7659. if (callback) {
  7660. callback(buffer);
  7661. }
  7662. }, // error decoding buffer. "e" is undefined in Chrome 11/22/2015
  7663. function (e) {
  7664. var err = new CustomError('decodeAudioData', errorTrace, self.url);
  7665. var msg = 'AudioContext error at decodeAudioData for ' + self.url;
  7666. if (errorCallback) {
  7667. err.msg = msg;
  7668. errorCallback(err);
  7669. } else {
  7670. console.error(msg + '\n The error stack trace includes: \n' + err.stack);
  7671. }
  7672. });
  7673. } else {
  7674. var err = new CustomError('loadConvolver', errorTrace, self.url);
  7675. var msg = 'Unable to load ' + self.url + '. The request status was: ' + request.status + ' (' + request.statusText + ')';
  7676. if (errorCallback) {
  7677. err.message = msg;
  7678. errorCallback(err);
  7679. } else {
  7680. console.error(msg + '\n The error stack trace includes: \n' + err.stack);
  7681. }
  7682. }
  7683. };
  7684. // if there is another error, aside from 404...
  7685. request.onerror = function (e) {
  7686. var err = new CustomError('loadConvolver', errorTrace, self.url);
  7687. var msg = 'There was no response from the server at ' + self.url + '. Check the url and internet connectivity.';
  7688. if (errorCallback) {
  7689. err.message = msg;
  7690. errorCallback(err);
  7691. } else {
  7692. console.error(msg + '\n The error stack trace includes: \n' + err.stack);
  7693. }
  7694. };
  7695. request.send();
  7696. };
  7697. p5.Convolver.prototype.set = null;
  7698. /**
  7699. * Connect a source to the reverb, and assign reverb parameters.
  7700. *
  7701. * @method process
  7702. * @param {Object} src p5.sound / Web Audio object with a sound
  7703. * output.
  7704. * @example
  7705. * <div><code>
  7706. * var cVerb, sound;
  7707. * function preload() {
  7708. * soundFormats('ogg', 'mp3');
  7709. *
  7710. * cVerb = createConvolver('assets/concrete-tunnel.mp3');
  7711. *
  7712. * sound = loadSound('assets/beat.mp3');
  7713. * }
  7714. *
  7715. * function setup() {
  7716. * // disconnect from master output...
  7717. * sound.disconnect();
  7718. *
  7719. * // ...and process with (i.e. connect to) cVerb
  7720. * // so that we only hear the convolution
  7721. * cVerb.process(sound);
  7722. *
  7723. * sound.play();
  7724. * }
  7725. * </code></div>
  7726. */
  7727. p5.Convolver.prototype.process = function (src) {
  7728. src.connect(this.input);
  7729. };
  7730. /**
  7731. * If you load multiple impulse files using the .addImpulse method,
  7732. * they will be stored as Objects in this Array. Toggle between them
  7733. * with the <code>toggleImpulse(id)</code> method.
  7734. *
  7735. * @property impulses
  7736. * @type {Array} Array of Web Audio Buffers
  7737. */
  7738. p5.Convolver.prototype.impulses = [];
  7739. /**
  7740. * Load and assign a new Impulse Response to the p5.Convolver.
  7741. * The impulse is added to the <code>.impulses</code> array. Previous
  7742. * impulses can be accessed with the <code>.toggleImpulse(id)</code>
  7743. * method.
  7744. *
  7745. * @method addImpulse
  7746. * @param {String} path path to a sound file
  7747. * @param {Function} callback function (optional)
  7748. * @param {Function} errorCallback function (optional)
  7749. */
  7750. p5.Convolver.prototype.addImpulse = function (path, callback, errorCallback) {
  7751. // if loading locally without a server
  7752. if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
  7753. alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
  7754. }
  7755. this._loadBuffer(path, callback, errorCallback);
  7756. };
  7757. /**
  7758. * Similar to .addImpulse, except that the <code>.impulses</code>
  7759. * Array is reset to save memory. A new <code>.impulses</code>
  7760. * array is created with this impulse as the only item.
  7761. *
  7762. * @method resetImpulse
  7763. * @param {String} path path to a sound file
  7764. * @param {Function} callback function (optional)
  7765. * @param {Function} errorCallback function (optional)
  7766. */
  7767. p5.Convolver.prototype.resetImpulse = function (path, callback, errorCallback) {
  7768. // if loading locally without a server
  7769. if (window.location.origin.indexOf('file://') > -1 && window.cordova === 'undefined') {
  7770. alert('This sketch may require a server to load external files. Please see http://bit.ly/1qcInwS');
  7771. }
  7772. this.impulses = [];
  7773. this._loadBuffer(path, callback, errorCallback);
  7774. };
  7775. /**
  7776. * If you have used <code>.addImpulse()</code> to add multiple impulses
  7777. * to a p5.Convolver, then you can use this method to toggle between
  7778. * the items in the <code>.impulses</code> Array. Accepts a parameter
  7779. * to identify which impulse you wish to use, identified either by its
  7780. * original filename (String) or by its position in the <code>.impulses
  7781. * </code> Array (Number).<br/>
  7782. * You can access the objects in the .impulses Array directly. Each
  7783. * Object has two attributes: an <code>.audioBuffer</code> (type:
  7784. * Web Audio <a href="
  7785. * http://webaudio.github.io/web-audio-api/#the-audiobuffer-interface">
  7786. * AudioBuffer)</a> and a <code>.name</code>, a String that corresponds
  7787. * with the original filename.
  7788. *
  7789. * @method toggleImpulse
  7790. * @param {String|Number} id Identify the impulse by its original filename
  7791. * (String), or by its position in the
  7792. * <code>.impulses</code> Array (Number).
  7793. */
  7794. p5.Convolver.prototype.toggleImpulse = function (id) {
  7795. if (typeof id === 'number' && id < this.impulses.length) {
  7796. this.convolverNode.buffer = this.impulses[id].audioBuffer;
  7797. }
  7798. if (typeof id === 'string') {
  7799. for (var i = 0; i < this.impulses.length; i++) {
  7800. if (this.impulses[i].name === id) {
  7801. this.convolverNode.buffer = this.impulses[i].audioBuffer;
  7802. break;
  7803. }
  7804. }
  7805. }
  7806. };
  7807. p5.Convolver.prototype.dispose = function () {
  7808. // remove all the Impulse Response buffers
  7809. for (var i in this.impulses) {
  7810. this.impulses[i] = null;
  7811. }
  7812. this.convolverNode.disconnect();
  7813. this.concolverNode = null;
  7814. if (typeof this.output !== 'undefined') {
  7815. this.output.disconnect();
  7816. this.output = null;
  7817. }
  7818. if (typeof this.panner !== 'undefined') {
  7819. this.panner.disconnect();
  7820. this.panner = null;
  7821. }
  7822. };
  7823. }(master, errorHandler, sndcore);
  7824. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  7825. var Tone_core_TimelineState;
  7826. Tone_core_TimelineState = function (Tone) {
  7827. 'use strict';
  7828. Tone.TimelineState = function (initial) {
  7829. Tone.Timeline.call(this);
  7830. this._initial = initial;
  7831. };
  7832. Tone.extend(Tone.TimelineState, Tone.Timeline);
  7833. Tone.TimelineState.prototype.getStateAtTime = function (time) {
  7834. var event = this.getEvent(time);
  7835. if (event !== null) {
  7836. return event.state;
  7837. } else {
  7838. return this._initial;
  7839. }
  7840. };
  7841. Tone.TimelineState.prototype.setStateAtTime = function (state, time) {
  7842. this.addEvent({
  7843. 'state': state,
  7844. 'time': this.toSeconds(time)
  7845. });
  7846. };
  7847. return Tone.TimelineState;
  7848. }(Tone_core_Tone, Tone_core_Timeline);
  7849. /** Tone.js module by Yotam Mann, MIT License 2016 http://opensource.org/licenses/MIT **/
  7850. var Tone_core_Clock;
  7851. Tone_core_Clock = function (Tone) {
  7852. 'use strict';
  7853. Tone.Clock = function () {
  7854. var options = this.optionsObject(arguments, [
  7855. 'callback',
  7856. 'frequency'
  7857. ], Tone.Clock.defaults);
  7858. this.callback = options.callback;
  7859. this._lookAhead = 'auto';
  7860. this._computedLookAhead = 1 / 60;
  7861. this._threshold = 0.5;
  7862. this._nextTick = -1;
  7863. this._lastUpdate = 0;
  7864. this._loopID = -1;
  7865. this.frequency = new Tone.TimelineSignal(options.frequency, Tone.Type.Frequency);
  7866. this.ticks = 0;
  7867. this._state = new Tone.TimelineState(Tone.State.Stopped);
  7868. this._boundLoop = this._loop.bind(this);
  7869. this._readOnly('frequency');
  7870. this._loop();
  7871. };
  7872. Tone.extend(Tone.Clock);
  7873. Tone.Clock.defaults = {
  7874. 'callback': Tone.noOp,
  7875. 'frequency': 1,
  7876. 'lookAhead': 'auto'
  7877. };
  7878. Object.defineProperty(Tone.Clock.prototype, 'state', {
  7879. get: function () {
  7880. return this._state.getStateAtTime(this.now());
  7881. }
  7882. });
  7883. Object.defineProperty(Tone.Clock.prototype, 'lookAhead', {
  7884. get: function () {
  7885. return this._lookAhead;
  7886. },
  7887. set: function (val) {
  7888. if (val === 'auto') {
  7889. this._lookAhead = 'auto';
  7890. } else {
  7891. this._lookAhead = this.toSeconds(val);
  7892. }
  7893. }
  7894. });
  7895. Tone.Clock.prototype.start = function (time, offset) {
  7896. time = this.toSeconds(time);
  7897. if (this._state.getStateAtTime(time) !== Tone.State.Started) {
  7898. this._state.addEvent({
  7899. 'state': Tone.State.Started,
  7900. 'time': time,
  7901. 'offset': offset
  7902. });
  7903. }
  7904. return this;
  7905. };
  7906. Tone.Clock.prototype.stop = function (time) {
  7907. time = this.toSeconds(time);
  7908. if (this._state.getStateAtTime(time) !== Tone.State.Stopped) {
  7909. this._state.setStateAtTime(Tone.State.Stopped, time);
  7910. }
  7911. return this;
  7912. };
  7913. Tone.Clock.prototype.pause = function (time) {
  7914. time = this.toSeconds(time);
  7915. if (this._state.getStateAtTime(time) === Tone.State.Started) {
  7916. this._state.setStateAtTime(Tone.State.Paused, time);
  7917. }
  7918. return this;
  7919. };
  7920. Tone.Clock.prototype._loop = function (time) {
  7921. this._loopID = requestAnimationFrame(this._boundLoop);
  7922. if (this._lookAhead === 'auto') {
  7923. if (!this.isUndef(time)) {
  7924. var diff = (time - this._lastUpdate) / 1000;
  7925. this._lastUpdate = time;
  7926. if (diff < this._threshold) {
  7927. this._computedLookAhead = (9 * this._computedLookAhead + diff) / 10;
  7928. }
  7929. }
  7930. } else {
  7931. this._computedLookAhead = this._lookAhead;
  7932. }
  7933. var now = this.now();
  7934. var lookAhead = this._computedLookAhead * 2;
  7935. var event = this._state.getEvent(now + lookAhead);
  7936. var state = Tone.State.Stopped;
  7937. if (event) {
  7938. state = event.state;
  7939. if (this._nextTick === -1 && state === Tone.State.Started) {
  7940. this._nextTick = event.time;
  7941. if (!this.isUndef(event.offset)) {
  7942. this.ticks = event.offset;
  7943. }
  7944. }
  7945. }
  7946. if (state === Tone.State.Started) {
  7947. while (now + lookAhead > this._nextTick) {
  7948. if (now > this._nextTick + this._threshold) {
  7949. this._nextTick = now;
  7950. }
  7951. var tickTime = this._nextTick;
  7952. this._nextTick += 1 / this.frequency.getValueAtTime(this._nextTick);
  7953. this.callback(tickTime);
  7954. this.ticks++;
  7955. }
  7956. } else if (state === Tone.State.Stopped) {
  7957. this._nextTick = -1;
  7958. this.ticks = 0;
  7959. }
  7960. };
  7961. Tone.Clock.prototype.getStateAtTime = function (time) {
  7962. return this._state.getStateAtTime(time);
  7963. };
  7964. Tone.Clock.prototype.dispose = function () {
  7965. cancelAnimationFrame(this._loopID);
  7966. Tone.TimelineState.prototype.dispose.call(this);
  7967. this._writable('frequency');
  7968. this.frequency.dispose();
  7969. this.frequency = null;
  7970. this._boundLoop = Tone.noOp;
  7971. this._nextTick = Infinity;
  7972. this.callback = null;
  7973. this._state.dispose();
  7974. this._state = null;
  7975. };
  7976. return Tone.Clock;
  7977. }(Tone_core_Tone, Tone_signal_TimelineSignal);
  7978. var metro;
  7979. metro = function () {
  7980. 'use strict';
  7981. var p5sound = master;
  7982. // requires the Tone.js library's Clock (MIT license, Yotam Mann)
  7983. // https://github.com/TONEnoTONE/Tone.js/
  7984. var Clock = Tone_core_Clock;
  7985. var ac = p5sound.audiocontext;
  7986. // var upTick = false;
  7987. p5.Metro = function () {
  7988. this.clock = new Clock({ 'callback': this.ontick.bind(this) });
  7989. this.syncedParts = [];
  7990. this.bpm = 120;
  7991. // gets overridden by p5.Part
  7992. this._init();
  7993. this.tickCallback = function () {
  7994. };
  7995. };
  7996. var prevTick = 0;
  7997. var tatumTime = 0;
  7998. p5.Metro.prototype.ontick = function (tickTime) {
  7999. var elapsedTime = tickTime - prevTick;
  8000. var secondsFromNow = tickTime - p5sound.audiocontext.currentTime;
  8001. if (elapsedTime - tatumTime <= -0.02) {
  8002. return;
  8003. } else {
  8004. prevTick = tickTime;
  8005. // for all of the active things on the metro:
  8006. for (var i in this.syncedParts) {
  8007. var thisPart = this.syncedParts[i];
  8008. if (!thisPart.isPlaying)
  8009. return;
  8010. thisPart.incrementStep(secondsFromNow);
  8011. // each synced source keeps track of its own beat number
  8012. for (var j in thisPart.phrases) {
  8013. var thisPhrase = thisPart.phrases[j];
  8014. var phraseArray = thisPhrase.sequence;
  8015. var bNum = this.metroTicks % phraseArray.length;
  8016. if (phraseArray[bNum] !== 0 && (this.metroTicks < phraseArray.length || !thisPhrase.looping)) {
  8017. thisPhrase.callback(secondsFromNow, phraseArray[bNum]);
  8018. }
  8019. }
  8020. }
  8021. this.metroTicks += 1;
  8022. this.tickCallback(secondsFromNow);
  8023. }
  8024. };
  8025. p5.Metro.prototype.setBPM = function (bpm, rampTime) {
  8026. var beatTime = 60 / (bpm * this.tatums);
  8027. var now = p5sound.audiocontext.currentTime;
  8028. tatumTime = beatTime;
  8029. var rampTime = rampTime || 0;
  8030. this.clock.frequency.setValueAtTime(this.clock.frequency.value, now);
  8031. this.clock.frequency.linearRampToValueAtTime(bpm, now + rampTime);
  8032. this.bpm = bpm;
  8033. };
  8034. p5.Metro.prototype.getBPM = function (tempo) {
  8035. return this.clock.getRate() / this.tatums * 60;
  8036. };
  8037. p5.Metro.prototype._init = function () {
  8038. this.metroTicks = 0;
  8039. };
  8040. // clear existing synced parts, add only this one
  8041. p5.Metro.prototype.resetSync = function (part) {
  8042. this.syncedParts = [part];
  8043. };
  8044. // push a new synced part to the array
  8045. p5.Metro.prototype.pushSync = function (part) {
  8046. this.syncedParts.push(part);
  8047. };
  8048. p5.Metro.prototype.start = function (timeFromNow) {
  8049. var t = timeFromNow || 0;
  8050. var now = p5sound.audiocontext.currentTime;
  8051. this.clock.start(now + t);
  8052. this.setBPM(this.bpm);
  8053. };
  8054. p5.Metro.prototype.stop = function (timeFromNow) {
  8055. var t = timeFromNow || 0;
  8056. var now = p5sound.audiocontext.currentTime;
  8057. if (this.clock._oscillator) {
  8058. this.clock.stop(now + t);
  8059. }
  8060. };
  8061. p5.Metro.prototype.beatLength = function (tatums) {
  8062. this.tatums = 1 / tatums / 4;
  8063. };
  8064. }(master, Tone_core_Clock);
  8065. var looper;
  8066. looper = function () {
  8067. 'use strict';
  8068. var p5sound = master;
  8069. var bpm = 120;
  8070. /**
  8071. * Set the global tempo, in beats per minute, for all
  8072. * p5.Parts. This method will impact all active p5.Parts.
  8073. *
  8074. * @param {Number} BPM Beats Per Minute
  8075. * @param {Number} rampTime Seconds from now
  8076. */
  8077. p5.prototype.setBPM = function (BPM, rampTime) {
  8078. bpm = BPM;
  8079. for (var i in p5sound.parts) {
  8080. p5sound.parts[i].setBPM(bpm, rampTime);
  8081. }
  8082. };
  8083. /**
  8084. * <p>A phrase is a pattern of musical events over time, i.e.
  8085. * a series of notes and rests.</p>
  8086. *
  8087. * <p>Phrases must be added to a p5.Part for playback, and
  8088. * each part can play multiple phrases at the same time.
  8089. * For example, one Phrase might be a kick drum, another
  8090. * could be a snare, and another could be the bassline.</p>
  8091. *
  8092. * <p>The first parameter is a name so that the phrase can be
  8093. * modified or deleted later. The callback is a a function that
  8094. * this phrase will call at every step—for example it might be
  8095. * called <code>playNote(value){}</code>. The array determines
  8096. * which value is passed into the callback at each step of the
  8097. * phrase. It can be numbers, an object with multiple numbers,
  8098. * or a zero (0) indicates a rest so the callback won't be called).</p>
  8099. *
  8100. * @class p5.Phrase
  8101. * @constructor
  8102. * @param {String} name Name so that you can access the Phrase.
  8103. * @param {Function} callback The name of a function that this phrase
  8104. * will call. Typically it will play a sound,
  8105. * and accept two parameters: a time at which
  8106. * to play the sound (in seconds from now),
  8107. * and a value from the sequence array. The
  8108. * time should be passed into the play() or
  8109. * start() method to ensure precision.
  8110. * @param {Array} sequence Array of values to pass into the callback
  8111. * at each step of the phrase.
  8112. * @example
  8113. * <div><code>
  8114. * var mySound, myPhrase, myPart;
  8115. * var pattern = [1,0,0,2,0,2,0,0];
  8116. * var msg = 'click to play';
  8117. *
  8118. * function preload() {
  8119. * mySound = loadSound('assets/beatbox.mp3');
  8120. * }
  8121. *
  8122. * function setup() {
  8123. * noStroke();
  8124. * fill(255);
  8125. * textAlign(CENTER);
  8126. * masterVolume(0.1);
  8127. *
  8128. * myPhrase = new p5.Phrase('bbox', makeSound, pattern);
  8129. * myPart = new p5.Part();
  8130. * myPart.addPhrase(myPhrase);
  8131. * myPart.setBPM(60);
  8132. * }
  8133. *
  8134. * function draw() {
  8135. * background(0);
  8136. * text(msg, width/2, height/2);
  8137. * }
  8138. *
  8139. * function makeSound(time, playbackRate) {
  8140. * mySound.rate(playbackRate);
  8141. * mySound.play(time);
  8142. * }
  8143. *
  8144. * function mouseClicked() {
  8145. * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
  8146. * myPart.start();
  8147. * msg = 'playing pattern';
  8148. * }
  8149. * }
  8150. *
  8151. * </code></div>
  8152. */
  8153. p5.Phrase = function (name, callback, sequence) {
  8154. this.phraseStep = 0;
  8155. this.name = name;
  8156. this.callback = callback;
  8157. /**
  8158. * Array of values to pass into the callback
  8159. * at each step of the phrase. Depending on the callback
  8160. * function's requirements, these values may be numbers,
  8161. * strings, or an object with multiple parameters.
  8162. * Zero (0) indicates a rest.
  8163. *
  8164. * @property sequence
  8165. * @type {Array}
  8166. */
  8167. this.sequence = sequence;
  8168. };
  8169. /**
  8170. * <p>A p5.Part plays back one or more p5.Phrases. Instantiate a part
  8171. * with steps and tatums. By default, each step represents 1/16th note.</p>
  8172. *
  8173. * <p>See p5.Phrase for more about musical timing.</p>
  8174. *
  8175. * @class p5.Part
  8176. * @constructor
  8177. * @param {Number} [steps] Steps in the part
  8178. * @param {Number} [tatums] Divisions of a beat (default is 1/16, a quarter note)
  8179. * @example
  8180. * <div><code>
  8181. * var box, drum, myPart;
  8182. * var boxPat = [1,0,0,2,0,2,0,0];
  8183. * var drumPat = [0,1,1,0,2,0,1,0];
  8184. * var msg = 'click to play';
  8185. *
  8186. * function preload() {
  8187. * box = loadSound('assets/beatbox.mp3');
  8188. * drum = loadSound('assets/drum.mp3');
  8189. * }
  8190. *
  8191. * function setup() {
  8192. * noStroke();
  8193. * fill(255);
  8194. * textAlign(CENTER);
  8195. * masterVolume(0.1);
  8196. *
  8197. * var boxPhrase = new p5.Phrase('box', playBox, boxPat);
  8198. * var drumPhrase = new p5.Phrase('drum', playDrum, drumPat);
  8199. * myPart = new p5.Part();
  8200. * myPart.addPhrase(boxPhrase);
  8201. * myPart.addPhrase(drumPhrase);
  8202. * myPart.setBPM(60);
  8203. * masterVolume(0.1);
  8204. * }
  8205. *
  8206. * function draw() {
  8207. * background(0);
  8208. * text(msg, width/2, height/2);
  8209. * }
  8210. *
  8211. * function playBox(time, playbackRate) {
  8212. * box.rate(playbackRate);
  8213. * box.play(time);
  8214. * }
  8215. *
  8216. * function playDrum(time, playbackRate) {
  8217. * drum.rate(playbackRate);
  8218. * drum.play(time);
  8219. * }
  8220. *
  8221. * function mouseClicked() {
  8222. * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
  8223. * myPart.start();
  8224. * msg = 'playing part';
  8225. * }
  8226. * }
  8227. * </code></div>
  8228. */
  8229. p5.Part = function (steps, bLength) {
  8230. this.length = steps || 0;
  8231. // how many beats
  8232. this.partStep = 0;
  8233. this.phrases = [];
  8234. this.isPlaying = false;
  8235. this.noLoop();
  8236. this.tatums = bLength || 0.0625;
  8237. // defaults to quarter note
  8238. this.metro = new p5.Metro();
  8239. this.metro._init();
  8240. this.metro.beatLength(this.tatums);
  8241. this.metro.setBPM(bpm);
  8242. p5sound.parts.push(this);
  8243. this.callback = function () {
  8244. };
  8245. };
  8246. /**
  8247. * Set the tempo of this part, in Beats Per Minute.
  8248. *
  8249. * @method setBPM
  8250. * @param {Number} BPM Beats Per Minute
  8251. * @param {Number} [rampTime] Seconds from now
  8252. */
  8253. p5.Part.prototype.setBPM = function (tempo, rampTime) {
  8254. this.metro.setBPM(tempo, rampTime);
  8255. };
  8256. /**
  8257. * Returns the Beats Per Minute of this currently part.
  8258. *
  8259. * @method getBPM
  8260. * @return {Number}
  8261. */
  8262. p5.Part.prototype.getBPM = function () {
  8263. return this.metro.getBPM();
  8264. };
  8265. /**
  8266. * Start playback of this part. It will play
  8267. * through all of its phrases at a speed
  8268. * determined by setBPM.
  8269. *
  8270. * @method start
  8271. * @param {Number} [time] seconds from now
  8272. */
  8273. p5.Part.prototype.start = function (time) {
  8274. if (!this.isPlaying) {
  8275. this.isPlaying = true;
  8276. this.metro.resetSync(this);
  8277. var t = time || 0;
  8278. this.metro.start(t);
  8279. }
  8280. };
  8281. /**
  8282. * Loop playback of this part. It will begin
  8283. * looping through all of its phrases at a speed
  8284. * determined by setBPM.
  8285. *
  8286. * @method loop
  8287. * @param {Number} [time] seconds from now
  8288. */
  8289. p5.Part.prototype.loop = function (time) {
  8290. this.looping = true;
  8291. // rest onended function
  8292. this.onended = function () {
  8293. this.partStep = 0;
  8294. };
  8295. var t = time || 0;
  8296. this.start(t);
  8297. };
  8298. /**
  8299. * Tell the part to stop looping.
  8300. *
  8301. * @method noLoop
  8302. */
  8303. p5.Part.prototype.noLoop = function () {
  8304. this.looping = false;
  8305. // rest onended function
  8306. this.onended = function () {
  8307. this.stop();
  8308. };
  8309. };
  8310. /**
  8311. * Stop the part and cue it to step 0.
  8312. *
  8313. * @method stop
  8314. * @param {Number} [time] seconds from now
  8315. */
  8316. p5.Part.prototype.stop = function (time) {
  8317. this.partStep = 0;
  8318. this.pause(time);
  8319. };
  8320. /**
  8321. * Pause the part. Playback will resume
  8322. * from the current step.
  8323. *
  8324. * @method pause
  8325. * @param {Number} time seconds from now
  8326. */
  8327. p5.Part.prototype.pause = function (time) {
  8328. this.isPlaying = false;
  8329. var t = time || 0;
  8330. this.metro.stop(t);
  8331. };
  8332. /**
  8333. * Add a p5.Phrase to this Part.
  8334. *
  8335. * @method addPhrase
  8336. * @param {p5.Phrase} phrase reference to a p5.Phrase
  8337. */
  8338. p5.Part.prototype.addPhrase = function (name, callback, array) {
  8339. var p;
  8340. if (arguments.length === 3) {
  8341. p = new p5.Phrase(name, callback, array);
  8342. } else if (arguments[0] instanceof p5.Phrase) {
  8343. p = arguments[0];
  8344. } else {
  8345. throw 'invalid input. addPhrase accepts name, callback, array or a p5.Phrase';
  8346. }
  8347. this.phrases.push(p);
  8348. // reset the length if phrase is longer than part's existing length
  8349. if (p.sequence.length > this.length) {
  8350. this.length = p.sequence.length;
  8351. }
  8352. };
  8353. /**
  8354. * Remove a phrase from this part, based on the name it was
  8355. * given when it was created.
  8356. *
  8357. * @method removePhrase
  8358. * @param {String} phraseName
  8359. */
  8360. p5.Part.prototype.removePhrase = function (name) {
  8361. for (var i in this.phrases) {
  8362. if (this.phrases[i].name === name) {
  8363. this.phrases.splice(i, 1);
  8364. }
  8365. }
  8366. };
  8367. /**
  8368. * Get a phrase from this part, based on the name it was
  8369. * given when it was created. Now you can modify its array.
  8370. *
  8371. * @method getPhrase
  8372. * @param {String} phraseName
  8373. */
  8374. p5.Part.prototype.getPhrase = function (name) {
  8375. for (var i in this.phrases) {
  8376. if (this.phrases[i].name === name) {
  8377. return this.phrases[i];
  8378. }
  8379. }
  8380. };
  8381. /**
  8382. * Get a phrase from this part, based on the name it was
  8383. * given when it was created. Now you can modify its array.
  8384. *
  8385. * @method replaceSequence
  8386. * @param {String} phraseName
  8387. * @param {Array} sequence Array of values to pass into the callback
  8388. * at each step of the phrase.
  8389. */
  8390. p5.Part.prototype.replaceSequence = function (name, array) {
  8391. for (var i in this.phrases) {
  8392. if (this.phrases[i].name === name) {
  8393. this.phrases[i].sequence = array;
  8394. }
  8395. }
  8396. };
  8397. p5.Part.prototype.incrementStep = function (time) {
  8398. if (this.partStep < this.length - 1) {
  8399. this.callback(time);
  8400. this.partStep += 1;
  8401. } else {
  8402. if (!this.looping && this.partStep == this.length - 1) {
  8403. console.log('done');
  8404. // this.callback(time);
  8405. this.onended();
  8406. }
  8407. }
  8408. };
  8409. /**
  8410. * Fire a callback function at every step.
  8411. *
  8412. * @method onStep
  8413. * @param {Function} callback The name of the callback
  8414. * you want to fire
  8415. * on every beat/tatum.
  8416. */
  8417. p5.Part.prototype.onStep = function (callback) {
  8418. this.callback = callback;
  8419. };
  8420. // ===============
  8421. // p5.Score
  8422. // ===============
  8423. /**
  8424. * A Score consists of a series of Parts. The parts will
  8425. * be played back in order. For example, you could have an
  8426. * A part, a B part, and a C part, and play them back in this order
  8427. * <code>new p5.Score(a, a, b, a, c)</code>
  8428. *
  8429. * @class p5.Score
  8430. * @constructor
  8431. * @param {p5.Part} part(s) One or multiple parts, to be played in sequence.
  8432. * @return {p5.Score}
  8433. */
  8434. p5.Score = function () {
  8435. // for all of the arguments
  8436. this.parts = [];
  8437. this.currentPart = 0;
  8438. var thisScore = this;
  8439. for (var i in arguments) {
  8440. this.parts[i] = arguments[i];
  8441. this.parts[i].nextPart = this.parts[i + 1];
  8442. this.parts[i].onended = function () {
  8443. thisScore.resetPart(i);
  8444. playNextPart(thisScore);
  8445. };
  8446. }
  8447. this.looping = false;
  8448. };
  8449. p5.Score.prototype.onended = function () {
  8450. if (this.looping) {
  8451. // this.resetParts();
  8452. this.parts[0].start();
  8453. } else {
  8454. this.parts[this.parts.length - 1].onended = function () {
  8455. this.stop();
  8456. this.resetParts();
  8457. };
  8458. }
  8459. this.currentPart = 0;
  8460. };
  8461. /**
  8462. * Start playback of the score.
  8463. *
  8464. * @method start
  8465. */
  8466. p5.Score.prototype.start = function () {
  8467. this.parts[this.currentPart].start();
  8468. this.scoreStep = 0;
  8469. };
  8470. /**
  8471. * Stop playback of the score.
  8472. *
  8473. * @method stop
  8474. */
  8475. p5.Score.prototype.stop = function () {
  8476. this.parts[this.currentPart].stop();
  8477. this.currentPart = 0;
  8478. this.scoreStep = 0;
  8479. };
  8480. /**
  8481. * Pause playback of the score.
  8482. *
  8483. * @method pause
  8484. */
  8485. p5.Score.prototype.pause = function () {
  8486. this.parts[this.currentPart].stop();
  8487. };
  8488. /**
  8489. * Loop playback of the score.
  8490. *
  8491. * @method loop
  8492. */
  8493. p5.Score.prototype.loop = function () {
  8494. this.looping = true;
  8495. this.start();
  8496. };
  8497. /**
  8498. * Stop looping playback of the score. If it
  8499. * is currently playing, this will go into effect
  8500. * after the current round of playback completes.
  8501. *
  8502. * @method noLoop
  8503. */
  8504. p5.Score.prototype.noLoop = function () {
  8505. this.looping = false;
  8506. };
  8507. p5.Score.prototype.resetParts = function () {
  8508. for (var i in this.parts) {
  8509. this.resetPart(i);
  8510. }
  8511. };
  8512. p5.Score.prototype.resetPart = function (i) {
  8513. this.parts[i].stop();
  8514. this.parts[i].partStep = 0;
  8515. for (var p in this.parts[i].phrases) {
  8516. this.parts[i].phrases[p].phraseStep = 0;
  8517. }
  8518. };
  8519. /**
  8520. * Set the tempo for all parts in the score
  8521. *
  8522. * @param {Number} BPM Beats Per Minute
  8523. * @param {Number} rampTime Seconds from now
  8524. */
  8525. p5.Score.prototype.setBPM = function (bpm, rampTime) {
  8526. for (var i in this.parts) {
  8527. this.parts[i].setBPM(bpm, rampTime);
  8528. }
  8529. };
  8530. function playNextPart(aScore) {
  8531. aScore.currentPart++;
  8532. if (aScore.currentPart >= aScore.parts.length) {
  8533. aScore.scoreStep = 0;
  8534. aScore.onended();
  8535. } else {
  8536. aScore.scoreStep = 0;
  8537. aScore.parts[aScore.currentPart - 1].stop();
  8538. aScore.parts[aScore.currentPart].start();
  8539. }
  8540. }
  8541. }(master);
  8542. var soundRecorder;
  8543. soundRecorder = function () {
  8544. 'use strict';
  8545. var p5sound = master;
  8546. var ac = p5sound.audiocontext;
  8547. /**
  8548. * <p>Record sounds for playback and/or to save as a .wav file.
  8549. * The p5.SoundRecorder records all sound output from your sketch,
  8550. * or can be assigned a specific source with setInput().</p>
  8551. * <p>The record() method accepts a p5.SoundFile as a parameter.
  8552. * When playback is stopped (either after the given amount of time,
  8553. * or with the stop() method), the p5.SoundRecorder will send its
  8554. * recording to that p5.SoundFile for playback.</p>
  8555. *
  8556. * @class p5.SoundRecorder
  8557. * @constructor
  8558. * @example
  8559. * <div><code>
  8560. * var mic, recorder, soundFile;
  8561. * var state = 0;
  8562. *
  8563. * function setup() {
  8564. * background(200);
  8565. * // create an audio in
  8566. * mic = new p5.AudioIn();
  8567. *
  8568. * // prompts user to enable their browser mic
  8569. * mic.start();
  8570. *
  8571. * // create a sound recorder
  8572. * recorder = new p5.SoundRecorder();
  8573. *
  8574. * // connect the mic to the recorder
  8575. * recorder.setInput(mic);
  8576. *
  8577. * // this sound file will be used to
  8578. * // playback & save the recording
  8579. * soundFile = new p5.SoundFile();
  8580. *
  8581. * text('keyPress to record', 20, 20);
  8582. * }
  8583. *
  8584. * function keyPressed() {
  8585. * // make sure user enabled the mic
  8586. * if (state === 0 && mic.enabled) {
  8587. *
  8588. * // record to our p5.SoundFile
  8589. * recorder.record(soundFile);
  8590. *
  8591. * background(255,0,0);
  8592. * text('Recording!', 20, 20);
  8593. * state++;
  8594. * }
  8595. * else if (state === 1) {
  8596. * background(0,255,0);
  8597. *
  8598. * // stop recorder and
  8599. * // send result to soundFile
  8600. * recorder.stop();
  8601. *
  8602. * text('Stopped', 20, 20);
  8603. * state++;
  8604. * }
  8605. *
  8606. * else if (state === 2) {
  8607. * soundFile.play(); // play the result!
  8608. * save(soundFile, 'mySound.wav');
  8609. * state++;
  8610. * }
  8611. * }
  8612. * </div></code>
  8613. */
  8614. p5.SoundRecorder = function () {
  8615. this.input = ac.createGain();
  8616. this.output = ac.createGain();
  8617. this.recording = false;
  8618. this.bufferSize = 1024;
  8619. this._channels = 2;
  8620. // stereo (default)
  8621. this._clear();
  8622. // initialize variables
  8623. this._jsNode = ac.createScriptProcessor(this.bufferSize, this._channels, 2);
  8624. this._jsNode.onaudioprocess = this._audioprocess.bind(this);
  8625. /**
  8626. * callback invoked when the recording is over
  8627. * @private
  8628. * @type {function(Float32Array)}
  8629. */
  8630. this._callback = function () {
  8631. };
  8632. // connections
  8633. this._jsNode.connect(p5.soundOut._silentNode);
  8634. this.setInput();
  8635. // add this p5.SoundFile to the soundArray
  8636. p5sound.soundArray.push(this);
  8637. };
  8638. /**
  8639. * Connect a specific device to the p5.SoundRecorder.
  8640. * If no parameter is given, p5.SoundRecorer will record
  8641. * all audible p5.sound from your sketch.
  8642. *
  8643. * @method setInput
  8644. * @param {Object} [unit] p5.sound object or a web audio unit
  8645. * that outputs sound
  8646. */
  8647. p5.SoundRecorder.prototype.setInput = function (unit) {
  8648. this.input.disconnect();
  8649. this.input = null;
  8650. this.input = ac.createGain();
  8651. this.input.connect(this._jsNode);
  8652. this.input.connect(this.output);
  8653. if (unit) {
  8654. unit.connect(this.input);
  8655. } else {
  8656. p5.soundOut.output.connect(this.input);
  8657. }
  8658. };
  8659. /**
  8660. * Start recording. To access the recording, provide
  8661. * a p5.SoundFile as the first parameter. The p5.SoundRecorder
  8662. * will send its recording to that p5.SoundFile for playback once
  8663. * recording is complete. Optional parameters include duration
  8664. * (in seconds) of the recording, and a callback function that
  8665. * will be called once the complete recording has been
  8666. * transfered to the p5.SoundFile.
  8667. *
  8668. * @method record
  8669. * @param {p5.SoundFile} soundFile p5.SoundFile
  8670. * @param {Number} [duration] Time (in seconds)
  8671. * @param {Function} [callback] The name of a function that will be
  8672. * called once the recording completes
  8673. */
  8674. p5.SoundRecorder.prototype.record = function (sFile, duration, callback) {
  8675. this.recording = true;
  8676. if (duration) {
  8677. this.sampleLimit = Math.round(duration * ac.sampleRate);
  8678. }
  8679. if (sFile && callback) {
  8680. this._callback = function () {
  8681. this.buffer = this._getBuffer();
  8682. sFile.setBuffer(this.buffer);
  8683. callback();
  8684. };
  8685. } else if (sFile) {
  8686. this._callback = function () {
  8687. this.buffer = this._getBuffer();
  8688. sFile.setBuffer(this.buffer);
  8689. };
  8690. }
  8691. };
  8692. /**
  8693. * Stop the recording. Once the recording is stopped,
  8694. * the results will be sent to the p5.SoundFile that
  8695. * was given on .record(), and if a callback function
  8696. * was provided on record, that function will be called.
  8697. *
  8698. * @method stop
  8699. */
  8700. p5.SoundRecorder.prototype.stop = function () {
  8701. this.recording = false;
  8702. this._callback();
  8703. this._clear();
  8704. };
  8705. p5.SoundRecorder.prototype._clear = function () {
  8706. this._leftBuffers = [];
  8707. this._rightBuffers = [];
  8708. this.recordedSamples = 0;
  8709. this.sampleLimit = null;
  8710. };
  8711. /**
  8712. * internal method called on audio process
  8713. *
  8714. * @private
  8715. * @param {AudioProcessorEvent} event
  8716. */
  8717. p5.SoundRecorder.prototype._audioprocess = function (event) {
  8718. if (this.recording === false) {
  8719. return;
  8720. } else if (this.recording === true) {
  8721. // if we are past the duration, then stop... else:
  8722. if (this.sampleLimit && this.recordedSamples >= this.sampleLimit) {
  8723. this.stop();
  8724. } else {
  8725. // get channel data
  8726. var left = event.inputBuffer.getChannelData(0);
  8727. var right = event.inputBuffer.getChannelData(1);
  8728. // clone the samples
  8729. this._leftBuffers.push(new Float32Array(left));
  8730. this._rightBuffers.push(new Float32Array(right));
  8731. this.recordedSamples += this.bufferSize;
  8732. }
  8733. }
  8734. };
  8735. p5.SoundRecorder.prototype._getBuffer = function () {
  8736. var buffers = [];
  8737. buffers.push(this._mergeBuffers(this._leftBuffers));
  8738. buffers.push(this._mergeBuffers(this._rightBuffers));
  8739. return buffers;
  8740. };
  8741. p5.SoundRecorder.prototype._mergeBuffers = function (channelBuffer) {
  8742. var result = new Float32Array(this.recordedSamples);
  8743. var offset = 0;
  8744. var lng = channelBuffer.length;
  8745. for (var i = 0; i < lng; i++) {
  8746. var buffer = channelBuffer[i];
  8747. result.set(buffer, offset);
  8748. offset += buffer.length;
  8749. }
  8750. return result;
  8751. };
  8752. p5.SoundRecorder.prototype.dispose = function () {
  8753. this._clear();
  8754. // remove reference from soundArray
  8755. var index = p5sound.soundArray.indexOf(this);
  8756. p5sound.soundArray.splice(index, 1);
  8757. this._callback = function () {
  8758. };
  8759. if (this.input) {
  8760. this.input.disconnect();
  8761. }
  8762. this.input = null;
  8763. this._jsNode = null;
  8764. };
  8765. /**
  8766. * Save a p5.SoundFile as a .wav audio file.
  8767. *
  8768. * @method saveSound
  8769. * @param {p5.SoundFile} soundFile p5.SoundFile that you wish to save
  8770. * @param {String} name name of the resulting .wav file.
  8771. */
  8772. p5.prototype.saveSound = function (soundFile, name) {
  8773. var leftChannel, rightChannel;
  8774. leftChannel = soundFile.buffer.getChannelData(0);
  8775. // handle mono files
  8776. if (soundFile.buffer.numberOfChannels > 1) {
  8777. rightChannel = soundFile.buffer.getChannelData(1);
  8778. } else {
  8779. rightChannel = leftChannel;
  8780. }
  8781. var interleaved = interleave(leftChannel, rightChannel);
  8782. // create the buffer and view to create the .WAV file
  8783. var buffer = new ArrayBuffer(44 + interleaved.length * 2);
  8784. var view = new DataView(buffer);
  8785. // write the WAV container,
  8786. // check spec at: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
  8787. // RIFF chunk descriptor
  8788. writeUTFBytes(view, 0, 'RIFF');
  8789. view.setUint32(4, 36 + interleaved.length * 2, true);
  8790. writeUTFBytes(view, 8, 'WAVE');
  8791. // FMT sub-chunk
  8792. writeUTFBytes(view, 12, 'fmt ');
  8793. view.setUint32(16, 16, true);
  8794. view.setUint16(20, 1, true);
  8795. // stereo (2 channels)
  8796. view.setUint16(22, 2, true);
  8797. view.setUint32(24, 44100, true);
  8798. view.setUint32(28, 44100 * 4, true);
  8799. view.setUint16(32, 4, true);
  8800. view.setUint16(34, 16, true);
  8801. // data sub-chunk
  8802. writeUTFBytes(view, 36, 'data');
  8803. view.setUint32(40, interleaved.length * 2, true);
  8804. // write the PCM samples
  8805. var lng = interleaved.length;
  8806. var index = 44;
  8807. var volume = 1;
  8808. for (var i = 0; i < lng; i++) {
  8809. view.setInt16(index, interleaved[i] * (32767 * volume), true);
  8810. index += 2;
  8811. }
  8812. p5.prototype.writeFile([view], name, 'wav');
  8813. };
  8814. // helper methods to save waves
  8815. function interleave(leftChannel, rightChannel) {
  8816. var length = leftChannel.length + rightChannel.length;
  8817. var result = new Float32Array(length);
  8818. var inputIndex = 0;
  8819. for (var index = 0; index < length;) {
  8820. result[index++] = leftChannel[inputIndex];
  8821. result[index++] = rightChannel[inputIndex];
  8822. inputIndex++;
  8823. }
  8824. return result;
  8825. }
  8826. function writeUTFBytes(view, offset, string) {
  8827. var lng = string.length;
  8828. for (var i = 0; i < lng; i++) {
  8829. view.setUint8(offset + i, string.charCodeAt(i));
  8830. }
  8831. }
  8832. }(sndcore, master);
  8833. var peakdetect;
  8834. peakdetect = function () {
  8835. 'use strict';
  8836. var p5sound = master;
  8837. /**
  8838. * <p>PeakDetect works in conjunction with p5.FFT to
  8839. * look for onsets in some or all of the frequency spectrum.
  8840. * </p>
  8841. * <p>
  8842. * To use p5.PeakDetect, call <code>update</code> in the draw loop
  8843. * and pass in a p5.FFT object.
  8844. * </p>
  8845. * <p>
  8846. * You can listen for a specific part of the frequency spectrum by
  8847. * setting the range between <code>freq1</code> and <code>freq2</code>.
  8848. * </p>
  8849. *
  8850. * <p><code>threshold</code> is the threshold for detecting a peak,
  8851. * scaled between 0 and 1. It is logarithmic, so 0.1 is half as loud
  8852. * as 1.0.</p>
  8853. *
  8854. * <p>
  8855. * The update method is meant to be run in the draw loop, and
  8856. * <b>frames</b> determines how many loops must pass before
  8857. * another peak can be detected.
  8858. * For example, if the frameRate() = 60, you could detect the beat of a
  8859. * 120 beat-per-minute song with this equation:
  8860. * <code> framesPerPeak = 60 / (estimatedBPM / 60 );</code>
  8861. * </p>
  8862. *
  8863. * <p>
  8864. * Based on example contribtued by @b2renger, and a simple beat detection
  8865. * explanation by <a
  8866. * href="http://www.airtightinteractive.com/2013/10/making-audio-reactive-visuals/"
  8867. * target="_blank">Felix Turner</a>.
  8868. * </p>
  8869. *
  8870. * @class p5.PeakDetect
  8871. * @constructor
  8872. * @param {Number} [freq1] lowFrequency - defaults to 20Hz
  8873. * @param {Number} [freq2] highFrequency - defaults to 20000 Hz
  8874. * @param {Number} [threshold] Threshold for detecting a beat between 0 and 1
  8875. * scaled logarithmically where 0.1 is 1/2 the loudness
  8876. * of 1.0. Defaults to 0.35.
  8877. * @param {Number} [framesPerPeak] Defaults to 20.
  8878. * @example
  8879. * <div><code>
  8880. *
  8881. * var cnv, soundFile, fft, peakDetect;
  8882. * var ellipseWidth = 10;
  8883. *
  8884. * function setup() {
  8885. * background(0);
  8886. * noStroke();
  8887. * fill(255);
  8888. * textAlign(CENTER);
  8889. *
  8890. * soundFile = loadSound('assets/beat.mp3');
  8891. *
  8892. * // p5.PeakDetect requires a p5.FFT
  8893. * fft = new p5.FFT();
  8894. * peakDetect = new p5.PeakDetect();
  8895. *
  8896. * }
  8897. *
  8898. * function draw() {
  8899. * background(0);
  8900. * text('click to play/pause', width/2, height/2);
  8901. *
  8902. * // peakDetect accepts an fft post-analysis
  8903. * fft.analyze();
  8904. * peakDetect.update(fft);
  8905. *
  8906. * if ( peakDetect.isDetected ) {
  8907. * ellipseWidth = 50;
  8908. * } else {
  8909. * ellipseWidth *= 0.95;
  8910. * }
  8911. *
  8912. * ellipse(width/2, height/2, ellipseWidth, ellipseWidth);
  8913. * }
  8914. *
  8915. * // toggle play/stop when canvas is clicked
  8916. * function mouseClicked() {
  8917. * if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
  8918. * if (soundFile.isPlaying() ) {
  8919. * soundFile.stop();
  8920. * } else {
  8921. * soundFile.play();
  8922. * }
  8923. * }
  8924. * }
  8925. * </code></div>
  8926. */
  8927. p5.PeakDetect = function (freq1, freq2, threshold, _framesPerPeak) {
  8928. var framesPerPeak;
  8929. // framesPerPeak determines how often to look for a beat.
  8930. // If a beat is provided, try to look for a beat based on bpm
  8931. this.framesPerPeak = _framesPerPeak || 20;
  8932. this.framesSinceLastPeak = 0;
  8933. this.decayRate = 0.95;
  8934. this.threshold = threshold || 0.35;
  8935. this.cutoff = 0;
  8936. // how much to increase the cutoff
  8937. // TO DO: document this / figure out how to make it accessible
  8938. this.cutoffMult = 1.5;
  8939. this.energy = 0;
  8940. this.penergy = 0;
  8941. // TO DO: document this property / figure out how to make it accessible
  8942. this.currentValue = 0;
  8943. /**
  8944. * isDetected is set to true when a peak is detected.
  8945. *
  8946. * @attribute isDetected
  8947. * @type {Boolean}
  8948. * @default false
  8949. */
  8950. this.isDetected = false;
  8951. this.f1 = freq1 || 40;
  8952. this.f2 = freq2 || 20000;
  8953. // function to call when a peak is detected
  8954. this._onPeak = function () {
  8955. };
  8956. };
  8957. /**
  8958. * The update method is run in the draw loop.
  8959. *
  8960. * Accepts an FFT object. You must call .analyze()
  8961. * on the FFT object prior to updating the peakDetect
  8962. * because it relies on a completed FFT analysis.
  8963. *
  8964. * @method update
  8965. * @param {p5.FFT} fftObject A p5.FFT object
  8966. */
  8967. p5.PeakDetect.prototype.update = function (fftObject) {
  8968. var nrg = this.energy = fftObject.getEnergy(this.f1, this.f2) / 255;
  8969. if (nrg > this.cutoff && nrg > this.threshold && nrg - this.penergy > 0) {
  8970. // trigger callback
  8971. this._onPeak();
  8972. this.isDetected = true;
  8973. // debounce
  8974. this.cutoff = nrg * this.cutoffMult;
  8975. this.framesSinceLastPeak = 0;
  8976. } else {
  8977. this.isDetected = false;
  8978. if (this.framesSinceLastPeak <= this.framesPerPeak) {
  8979. this.framesSinceLastPeak++;
  8980. } else {
  8981. this.cutoff *= this.decayRate;
  8982. this.cutoff = Math.max(this.cutoff, this.threshold);
  8983. }
  8984. }
  8985. this.currentValue = nrg;
  8986. this.penergy = nrg;
  8987. };
  8988. /**
  8989. * onPeak accepts two arguments: a function to call when
  8990. * a peak is detected. The value of the peak,
  8991. * between 0.0 and 1.0, is passed to the callback.
  8992. *
  8993. * @method onPeak
  8994. * @param {Function} callback Name of a function that will
  8995. * be called when a peak is
  8996. * detected.
  8997. * @param {Object} [val] Optional value to pass
  8998. * into the function when
  8999. * a peak is detected.
  9000. * @example
  9001. * <div><code>
  9002. * var cnv, soundFile, fft, peakDetect;
  9003. * var ellipseWidth = 0;
  9004. *
  9005. * function setup() {
  9006. * cnv = createCanvas(100,100);
  9007. * textAlign(CENTER);
  9008. *
  9009. * soundFile = loadSound('assets/beat.mp3');
  9010. * fft = new p5.FFT();
  9011. * peakDetect = new p5.PeakDetect();
  9012. *
  9013. * setupSound();
  9014. *
  9015. * // when a beat is detected, call triggerBeat()
  9016. * peakDetect.onPeak(triggerBeat);
  9017. * }
  9018. *
  9019. * function draw() {
  9020. * background(0);
  9021. * fill(255);
  9022. * text('click to play', width/2, height/2);
  9023. *
  9024. * fft.analyze();
  9025. * peakDetect.update(fft);
  9026. *
  9027. * ellipseWidth *= 0.95;
  9028. * ellipse(width/2, height/2, ellipseWidth, ellipseWidth);
  9029. * }
  9030. *
  9031. * // this function is called by peakDetect.onPeak
  9032. * function triggerBeat() {
  9033. * ellipseWidth = 50;
  9034. * }
  9035. *
  9036. * // mouseclick starts/stops sound
  9037. * function setupSound() {
  9038. * cnv.mouseClicked( function() {
  9039. * if (soundFile.isPlaying() ) {
  9040. * soundFile.stop();
  9041. * } else {
  9042. * soundFile.play();
  9043. * }
  9044. * });
  9045. * }
  9046. * </code></div>
  9047. */
  9048. p5.PeakDetect.prototype.onPeak = function (callback, val) {
  9049. var self = this;
  9050. self._onPeak = function () {
  9051. callback(self.energy, val);
  9052. };
  9053. };
  9054. }(master);
  9055. var gain;
  9056. gain = function () {
  9057. 'use strict';
  9058. var p5sound = master;
  9059. /**
  9060. * A gain node is usefull to set the relative volume of sound.
  9061. * It's typically used to build mixers.
  9062. *
  9063. * @class p5.Gain
  9064. * @constructor
  9065. * @example
  9066. * <div><code>
  9067. *
  9068. * // load two soundfile and crossfade beetween them
  9069. * var sound1,sound2;
  9070. * var gain1, gain2, gain3;
  9071. *
  9072. * function preload(){
  9073. * soundFormats('ogg', 'mp3');
  9074. * sound1 = loadSound('../_files/Damscray_-_Dancing_Tiger_01');
  9075. * sound2 = loadSound('../_files/beat.mp3');
  9076. * }
  9077. *
  9078. * function setup() {
  9079. * createCanvas(400,200);
  9080. *
  9081. * // create a 'master' gain to which we will connect both soundfiles
  9082. * gain3 = new p5.Gain();
  9083. * gain3.connect();
  9084. *
  9085. * // setup first sound for playing
  9086. * sound1.rate(1);
  9087. * sound1.loop();
  9088. * sound1.disconnect(); // diconnect from p5 output
  9089. *
  9090. * gain1 = new p5.Gain(); // setup a gain node
  9091. * gain1.setInput(sound1); // connect the first sound to its input
  9092. * gain1.connect(gain3); // connect its output to the 'master'
  9093. *
  9094. * sound2.rate(1);
  9095. * sound2.disconnect();
  9096. * sound2.loop();
  9097. *
  9098. * gain2 = new p5.Gain();
  9099. * gain2.setInput(sound2);
  9100. * gain2.connect(gain3);
  9101. *
  9102. * }
  9103. *
  9104. * function draw(){
  9105. * background(180);
  9106. *
  9107. * // calculate the horizontal distance beetween the mouse and the right of the screen
  9108. * var d = dist(mouseX,0,width,0);
  9109. *
  9110. * // map the horizontal position of the mouse to values useable for volume control of sound1
  9111. * var vol1 = map(mouseX,0,width,0,1);
  9112. * var vol2 = 1-vol1; // when sound1 is loud, sound2 is quiet and vice versa
  9113. *
  9114. * gain1.amp(vol1,0.5,0);
  9115. * gain2.amp(vol2,0.5,0);
  9116. *
  9117. * // map the vertical position of the mouse to values useable for 'master volume control'
  9118. * var vol3 = map(mouseY,0,height,0,1);
  9119. * gain3.amp(vol3,0.5,0);
  9120. * }
  9121. *</code></div>
  9122. *
  9123. */
  9124. p5.Gain = function () {
  9125. this.ac = p5sound.audiocontext;
  9126. this.input = this.ac.createGain();
  9127. this.output = this.ac.createGain();
  9128. // otherwise, Safari distorts
  9129. this.input.gain.value = 0.5;
  9130. this.input.connect(this.output);
  9131. // add to the soundArray
  9132. p5sound.soundArray.push(this);
  9133. };
  9134. /**
  9135. * Connect a source to the gain node.
  9136. *
  9137. * @method setInput
  9138. * @param {Object} src p5.sound / Web Audio object with a sound
  9139. * output.
  9140. */
  9141. p5.Gain.prototype.setInput = function (src) {
  9142. src.connect(this.input);
  9143. };
  9144. /**
  9145. * Send output to a p5.sound or web audio object
  9146. *
  9147. * @method connect
  9148. * @param {Object} unit
  9149. */
  9150. p5.Gain.prototype.connect = function (unit) {
  9151. var u = unit || p5.soundOut.input;
  9152. this.output.connect(u.input ? u.input : u);
  9153. };
  9154. /**
  9155. * Disconnect all output.
  9156. *
  9157. * @method disconnect
  9158. */
  9159. p5.Gain.prototype.disconnect = function () {
  9160. this.output.disconnect();
  9161. };
  9162. /**
  9163. * Set the output level of the gain node.
  9164. *
  9165. * @method amp
  9166. * @param {Number} volume amplitude between 0 and 1.0
  9167. * @param {Number} [rampTime] create a fade that lasts rampTime
  9168. * @param {Number} [timeFromNow] schedule this event to happen
  9169. * seconds from now
  9170. */
  9171. p5.Gain.prototype.amp = function (vol, rampTime, tFromNow) {
  9172. var rampTime = rampTime || 0;
  9173. var tFromNow = tFromNow || 0;
  9174. var now = p5sound.audiocontext.currentTime;
  9175. var currentVol = this.output.gain.value;
  9176. this.output.gain.cancelScheduledValues(now);
  9177. this.output.gain.linearRampToValueAtTime(currentVol, now + tFromNow);
  9178. this.output.gain.linearRampToValueAtTime(vol, now + tFromNow + rampTime);
  9179. };
  9180. p5.Gain.prototype.dispose = function () {
  9181. // remove reference from soundArray
  9182. var index = p5sound.soundArray.indexOf(this);
  9183. p5sound.soundArray.splice(index, 1);
  9184. this.output.disconnect();
  9185. this.input.disconnect();
  9186. this.output = undefined;
  9187. this.input = undefined;
  9188. };
  9189. }(master, sndcore);
  9190. var distortion;
  9191. distortion = function () {
  9192. 'use strict';
  9193. var p5sound = master;
  9194. /*
  9195. * Adapted from [Kevin Ennis on StackOverflow](http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion)
  9196. */
  9197. function makeDistortionCurve(amount) {
  9198. var k = typeof amount === 'number' ? amount : 50;
  9199. var n_samples = 44100;
  9200. var curve = new Float32Array(n_samples);
  9201. var deg = Math.PI / 180;
  9202. var i = 0;
  9203. var x;
  9204. for (; i < n_samples; ++i) {
  9205. x = i * 2 / n_samples - 1;
  9206. curve[i] = (3 + k) * x * 20 * deg / (Math.PI + k * Math.abs(x));
  9207. }
  9208. return curve;
  9209. }
  9210. /**
  9211. * A Distortion effect created with a Waveshaper Node,
  9212. * with an approach adapted from
  9213. * [Kevin Ennis](http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion)
  9214. *
  9215. * @class p5.Distortion
  9216. * @constructor
  9217. * @param {Number} [amount=0.25] Unbounded distortion amount.
  9218. * Normal values range from 0-1.
  9219. * @param {String} [oversample='none'] 'none', '2x', or '4x'.
  9220. *
  9221. * @return {Object} Distortion object
  9222. */
  9223. p5.Distortion = function (amount, oversample) {
  9224. if (typeof amount === 'undefined') {
  9225. amount = 0.25;
  9226. }
  9227. if (typeof amount !== 'number') {
  9228. throw new Error('amount must be a number');
  9229. }
  9230. if (typeof oversample === 'undefined') {
  9231. oversample = '2x';
  9232. }
  9233. if (typeof oversample !== 'string') {
  9234. throw new Error('oversample must be a String');
  9235. }
  9236. var curveAmount = p5.prototype.map(amount, 0, 1, 0, 2000);
  9237. this.ac = p5sound.audiocontext;
  9238. this.input = this.ac.createGain();
  9239. this.output = this.ac.createGain();
  9240. /**
  9241. * The p5.Distortion is built with a
  9242. * <a href="http://www.w3.org/TR/webaudio/#WaveShaperNode">
  9243. * Web Audio WaveShaper Node</a>.
  9244. *
  9245. * @property WaveShaperNode
  9246. * @type {Object} AudioNode
  9247. */
  9248. this.waveShaperNode = this.ac.createWaveShaper();
  9249. this.amount = curveAmount;
  9250. this.waveShaperNode.curve = makeDistortionCurve(curveAmount);
  9251. this.waveShaperNode.oversample = oversample;
  9252. this.input.connect(this.waveShaperNode);
  9253. this.waveShaperNode.connect(this.output);
  9254. this.connect();
  9255. // add to the soundArray
  9256. p5sound.soundArray.push(this);
  9257. };
  9258. p5.Distortion.prototype.process = function (src, amount, oversample) {
  9259. src.connect(this.input);
  9260. this.set(amount, oversample);
  9261. };
  9262. /**
  9263. * Set the amount and oversample of the waveshaper distortion.
  9264. *
  9265. * @method setType
  9266. * @param {Number} [amount=0.25] Unbounded distortion amount.
  9267. * Normal values range from 0-1.
  9268. * @param {String} [oversample='none'] 'none', '2x', or '4x'.
  9269. */
  9270. p5.Distortion.prototype.set = function (amount, oversample) {
  9271. if (amount) {
  9272. var curveAmount = p5.prototype.map(amount, 0, 1, 0, 2000);
  9273. this.amount = curveAmount;
  9274. this.waveShaperNode.curve = makeDistortionCurve(curveAmount);
  9275. }
  9276. if (oversample) {
  9277. this.waveShaperNode.oversample = oversample;
  9278. }
  9279. };
  9280. /**
  9281. * Return the distortion amount, typically between 0-1.
  9282. *
  9283. * @method getAmount
  9284. * @return {Number} Unbounded distortion amount.
  9285. * Normal values range from 0-1.
  9286. */
  9287. p5.Distortion.prototype.getAmount = function () {
  9288. return this.amount;
  9289. };
  9290. /**
  9291. * Return the oversampling.
  9292. *
  9293. * @return {String} Oversample can either be 'none', '2x', or '4x'.
  9294. */
  9295. p5.Distortion.prototype.getOversample = function () {
  9296. return this.waveShaperNode.oversample;
  9297. };
  9298. /**
  9299. * Send output to a p5.sound or web audio object
  9300. *
  9301. * @method connect
  9302. * @param {Object} unit
  9303. */
  9304. p5.Distortion.prototype.connect = function (unit) {
  9305. var u = unit || p5.soundOut.input;
  9306. this.output.connect(u);
  9307. };
  9308. /**
  9309. * Disconnect all output.
  9310. *
  9311. * @method disconnect
  9312. */
  9313. p5.Distortion.prototype.disconnect = function () {
  9314. this.output.disconnect();
  9315. };
  9316. p5.Distortion.prototype.dispose = function () {
  9317. var index = p5sound.soundArray.indexOf(this);
  9318. p5sound.soundArray.splice(index, 1);
  9319. this.input.disconnect();
  9320. this.waveShaperNode.disconnect();
  9321. this.input = null;
  9322. this.waveShaperNode = null;
  9323. if (typeof this.output !== 'undefined') {
  9324. this.output.disconnect();
  9325. this.output = null;
  9326. }
  9327. };
  9328. }(master);
  9329. var src_app;
  9330. src_app = function () {
  9331. 'use strict';
  9332. var p5SOUND = sndcore;
  9333. return p5SOUND;
  9334. }(sndcore, master, helpers, errorHandler, panner, soundfile, amplitude, fft, signal, oscillator, env, pulse, noise, audioin, filter, delay, reverb, metro, looper, soundRecorder, peakdetect, gain, distortion);
  9335. }));