libcm is a C development framework with an emphasis on audio signal processing applications.
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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmComplexTypes.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmCtx.h"
  8. #include "cmMem.h"
  9. #include "cmMallocDebug.h"
  10. #include "cmLinkedHeap.h"
  11. #include "cmMath.h"
  12. #include "cmFile.h"
  13. #include "cmFileSys.h"
  14. #include "cmSymTbl.h"
  15. #include "cmJson.h"
  16. #include "cmText.h"
  17. #include "cmPrefs.h"
  18. #include "cmProcObj.h"
  19. #include "cmDspValue.h"
  20. #include "cmDspCtx.h"
  21. #include "cmDspClass.h"
  22. #include "cmDspFx.h"
  23. #include "cmDspKr.h"
  24. #include "cmMsgProtocol.h"
  25. #include "cmThread.h"
  26. #include "cmUdpPort.h"
  27. #include "cmUdpNet.h"
  28. #include "cmTime.h"
  29. #include "cmAudioSys.h"
  30. #include "cmDspSys.h"
  31. #include "cmDspPreset.h" // required for cmDspNetSend
  32. #include "cmDspNet.h"
  33. #include "cmAudioFile.h"
  34. #include "cmThread.h" // used for threaded loading in wave table file mode
  35. #include "cmProcObj.h"
  36. #include "cmProcTemplateMain.h"
  37. #include "cmProc.h"
  38. #include "cmMidi.h"
  39. #include "cmProc2.h"
  40. #include "cmVectOpsTemplateMain.h"
  41. #include "cmMidiPort.h"
  42. #include "sa/cmSaProc.h"
  43. /*
  44. About variables:
  45. 1) Variables represent data fields within a DSP object.
  46. 2) Variables may also act as input (kInDsvFl) and/or output (kOutDsvFl) ports.
  47. A. Audio Ports
  48. - Audio output ports (kAudioBufDsvFl) publish a buffer of samples to subscribing
  49. audio input ports on other instances. (See cmDspSysConnectAudio(). )
  50. - Output audio ports are instantiated with physical buffers to hold samples of
  51. audio. Input audio ports contain internal pointers with point to the
  52. audio buffers of connected output ports.
  53. - Set cmDspVarArg_t.cn to the number of channels of audio the buffer will contain.
  54. B. Non-Audio Ports
  55. - Non-audio input ports may register to be called when output port values change.
  56. via cmDspSysInstallCb().
  57. - Type checking is done to guarantee that the output port data type matches one of the
  58. input port types or can be converted to one of the input port types.
  59. [TODO: Check how this is handled in cmDspSetEvent(). Let's say that an input port
  60. takes either a number or string. This conversion is not possible within the
  61. cmDspValue() framework - and yet it should work - or give an error ]
  62. 3) Creating variable instances.
  63. A. All variables must be given default values in the instance constructor
  64. _cmDspXXXAlloc(). This can be done automatically be giving default values
  65. in the var args section of the cmDspSysAllocInst() call or by explicitely
  66. setting default values via cmDspSetDefaultXXX() in _cmDspXXXAlloc().
  67. B. The _cmDspXXXReset() function automatically resets all the instance variables to their
  68. default value. By default, variables do not transmit their values when they are
  69. reset to their default value - unless the variable is marked with the kSendDfltDsvFl.
  70. (See cmDspClass.c:cmDspApplyDefault()). The 'out' port on cmDspScalar_t and cmDspButton_t
  71. are examples of instances that transmit their default value on reset.
  72. The order that instances are reset is determined by the order that they were created.
  73. A subtle case can arise when relying on default values to set the initial value of
  74. another object. Given two object instances A and B. Where The output of A has the
  75. the kSendDfltDsvFl set and is connected to an input port on B. If instance A was
  76. created before instance B then instance A will reset before instance B. During reset
  77. the output port of A will transmit it's default value to B - as expected. However
  78. when reset is called on instance B it will overwrite this value with it's own
  79. default value.
  80. In order for this scenario to work - that is the output of instance A sets the initial
  81. value of instance B - instance B must be created first.
  82. Since creation order also determines execution order this solution may not always
  83. be possible. It may be that we would simultaneously like instance A to exeute first
  84. and also recieve its initial value from instance B. Given the above example however
  85. these two goals are mutually exclusive.
  86. [*] This could be solved by explicitely allowing a variable to be reset via
  87. a callback. In this case if a variable was set via a callback during reset
  88. then it would not overwrite that value with its own internal value. This
  89. functionality would need to be set on a per instance bases - which might
  90. not work well with the current cmDspSysAllocInst() var args scheme.
  91. When a single output port is connected to multiple input ports the input ports
  92. are called in the order that the connections were made.
  93. To overcome this problem the reset cycle has been broken into two parts.
  94. In the first part the resetFunc is called on each instance in creation order.
  95. (as described above). Following this pass a second pass is made where the
  96. sysRecvFunc on any instance tagged with the '_reset' attribute is called.
  97. This allows an event callback chain to be executed prior to the first
  98. set of execFunc calls. Note that the event callback chain(s) are programmed
  99. by cmDspInstallCb() connections which are setup on a per application basis.
  100. This allows the initial state of the network to be set outside of the
  101. creation order.
  102. The 'button' instance implements a sysRecvFunc which will send the buttons
  103. value and symbol when called with the '_reset' attribute symbol. If a
  104. 'reset' button is created and assigned the '_reset' attribute symbol
  105. (via cmDspSysAssignAttrSymbol()) then the output of the button can be used
  106. to drive a networks initial state in an order determined by the application
  107. programmer.
  108. [TODO: Add a check that all instance variables have default values at the end of
  109. the network reset. This should be easy because the instance variable flag
  110. kDfltSetDsvFl is set in cmDspValueSet() when the default value is set.]
  111. C. The default value of variables may be set via the var args portion of cmDspSysAllocInst().
  112. There is currently a weakness during variable instance creation function
  113. cmDspInstAlloc() uses the cmDspVarArg_t.flags to determine the type of the var arg
  114. argument. cmDspVarArg_t.flags can therefore only be set to one DSV type - and the
  115. actual argument in the cmDspSysAllocInst() call must match that type. This is very
  116. easy to mess up - for example by setting the flag to kIntDsvFl and then putting
  117. 0.0 as the var arg value. This limitation also prevents ports which are also
  118. required arguments to cmDspSysAllocInst() (i.e. kReqArgDsvFl) from supporting
  119. multiple types.
  120. There are two possible ways to fix this:
  121. 1) Include the type flag in the var args list.
  122. 2) Specify a seperate var args flag in cmDspVarArg_t.
  123. Option 1 seems better because:
  124. a. [**] It would allow setting other per instance flags in the cmDspSysAllocInst() call [*].
  125. b. It would support setting a var arg termination flag rather than relying on the current
  126. explicit argument count.
  127. c. This is also the method used in cmJsonMemberValues() and it has worked well there.
  128. Either of these options requires a substantial change to existing code.
  129. This change should therefore be made sooner rather than later.
  130. As the system currently works it is possible, and it often happens, that
  131. an instance's recv function will be called before it is reset. It seems like
  132. this is a bad thing. Maybe the process of resetting and transmitting dflt
  133. values should be broken into seperate passes. This idea could be extended
  134. to type checking as follows:
  135. a. a reset-0 pass is made to set the internal state of each instance to
  136. known values.
  137. b. a type check pass is made where each output port
  138. sends a type msg to all connected input ports to provide the types that
  139. it will send.
  140. c. a reset-1 pass is made allowing all of the type information to be
  141. used to determine the initial state.
  142. d. an initial default value transmission pass is made where some instances
  143. (like scalars) may transmit initial values
  144. e. a reset-2 pass is made allowing the initial values to be acted on.
  145. f. runtime occurs
  146. D. Questions:
  147. 0) What are the ways that an instance variable can get set?
  148. a. cmDspValueSet() - This is the canonical value setting function.
  149. All other value setting function call this function.
  150. The cmDspSetXXX() are type safe wrappers to this function.
  151. b. cmDspApplyDefault() - assign the default value to the actual variable
  152. via a call to cmDspValueSet(). This function is automatically called
  153. by cmDspApplyAllDefaults() which is usually in the instance reset function.
  154. c. cmDspSetEvent() - assign the value embedded in an event message
  155. to a variable. This is a wrapper function for cmDspValueSet() which
  156. checks to see if the value need to be echoed to the UI. See
  157. more about UI echoing in the next section.
  158. 1) Where do events arriving at an instance receive function originate from?
  159. There are two sources of events:
  160. 1. The output ports of other instances.
  161. 2. The UI (client application).
  162. Events arriving from the UI can be distinguished from events arriving
  163. from other instances because they have the evt.kUiDspFl flag set.
  164. 2) How does UI updating and echoing actually work?
  165. a. Overview:
  166. On creation (in cmDsUi.c) variables whose value must be reflected to the UI
  167. are marked with the kUiDsvFl - these variables are called 'UI variables'.
  168. When a UI variable receive a new value
  169. the new value must be reflected in the associated UI GUI control.
  170. When and how this is accomplished depends on the source of the new
  171. variable value. There are two sources of events arriving at
  172. an instance's receive function: the output port of another instance
  173. or the UI.
  174. Events which originate from the UI are marked with a evt.kUiDspFl.
  175. (cmDspSys.c:_cmDspSysHandleUiMsg())
  176. When a UI variable receives a value from the output port of another
  177. instance (i.e. kUiDspFl not set) then it must always send that value to
  178. the UI. In other words if the evt.kUiDspFl is NOT set then the
  179. value must be sent to the UI.
  180. When a UI variable receives a value from the UI (i.e. the evt.kUiDspFl is set)
  181. it may or may not need to reflect the value. If the UI already
  182. reflects the value then the value does not need to be sent back
  183. otherwise it does.
  184. The UI control determines whether it wants to receive the value it
  185. is sending back by setting the kDuplexDuiFl flag in it's msg to the engine.
  186. Upon receipt of this msg, in cmDspSys.c:_cmDspSysHandleUiMsg(),
  187. the system converts the msg to an event. If the kDuplexDuiFl
  188. is set then the kUiEchoDspFl is set in the event.
  189. When a UI variable receives a value from the UI and the evt.kUiEchoDspFl
  190. is set then the value must be reflected, otherwise it must not.
  191. The rules for updating UI variables (var. w/ kUiDsvFl set) can
  192. be summarized as follows:
  193. kUiDspFl kUiEchoDspFl Send to UI Notes
  194. -------- ------------ ---------- -------------------------------------------------------------
  195. 0 0 Yes The value originated from another port and therefore must be reflected.
  196. 0 1 <invalid> If kUiEchoDspFl is set then so must kUiDspFl.
  197. 1 0 No
  198. 1 1 Yes The value originated from the UI and ehco was requested.
  199. This logic is automatically handled together by
  200. cmDspSetEvent() and cmDspValueSet().
  201. b. Variable values are sent and received to and from the UI using
  202. kValueDuiId messages.
  203. c. It is possible to prevent values generated in the engine from being
  204. reflected to the UI by setting the kNoUpdateUiDspFl in the call to
  205. cmDspValueSet().
  206. d. Instance variables are marked as UI variables by the cmDspUIXXXCreate()
  207. functions. Note that instances that have assoicated UI controls generally
  208. have multiple UI variables. (e.g. min,max,step,label,value).
  209. e. Messages arriving from the UI are handled by cmDspSys.c:_cmDspSysHandleUiMsg()
  210. where they are converted to cmDspEvt_t's. All events generated from
  211. msgs arriving from the UI are marked with the kUiDspFl. If the msg kDuplexDuiFl
  212. is set then the event flag kUiEchoDspFl is set - to indicate that the instance
  213. should send the value back to the UI.
  214. This leads to the following potential situation: The msg with kUiEchoDspFl
  215. set arrives at its target instance - which in turn calls cmDspSetEvent() to update
  216. the target variable value. Because kUiEchoDspFl is set the value is automatically
  217. reflected to the UI as expected. However as part of the call to
  218. cmDspValueSet() within cmDspSetEvent() the event is also sent to any connected
  219. instances - a problem would occur if kUiEchoDspFl remained set when it
  220. was sent to the connected instances - because they might then also try to update
  221. their own UI inappropriately. In fact this is not a problem because
  222. _cmDspSendEvt() zeros the the evt.flags value prior to resending the event.
  223. 3) Proposed Data Typing Framework:
  224. a. Instance variables are assigned 3 data types:
  225. 1. cmDspSysAllocInst() var args type. This is the type that the var args argument
  226. to the instance constructor must be. The value provided by this method
  227. becomes the default value for the variable. This type must be unique - multiple
  228. type flags cannot be used for this value.
  229. 2. Strict data type. This is the type used to define the variable value.
  230. Any values which will be used to set the value of this variable must be able
  231. to be converted to this type.
  232. If the variable is used as on input then the system will warn if an output port
  233. is assigned which cannot be converted to this type. If the variable is used as
  234. an output then this is the type the variable will publish as the output type.
  235. All values arriving at the functions 'recv' function are guaranteed to be
  236. able to be converted to this type.
  237. 3. Alternate data types. Multiple types may be given to this type.
  238. If no strict data type is given then this will be the set of types accepted
  239. for input and reported for output.
  240. All value messages for the instance which do not fit the strict data type,
  241. but do fit the alternate data type, will be sent to the 'altRecvFunc'
  242. function. The data type of events arriving at this function may therefore
  243. need to be decoded in order to use the assoicated values.
  244. b. Connections from an output with a strict data type can be type checked in
  245. advance - since they are guaranteed to emit a specific data type.
  246. Connections from outputs without strict data types can only be type checked
  247. at runtime - since it is possible for the type to change once the execution
  248. starts.
  249. c. Other notes about data types:
  250. It seems like cmDsvValues() should in general only allow one type flag (along with
  251. the kMtxDsvFl) to be set at a time if the flag state is legal. Is this always the
  252. case? Can a macro be included to routinely check this? Are we careful to
  253. not confuse the actual and possible type flags in DSP instance variables?
  254. This is important when checking the types of variables arriving at in input port.
  255. Include a macro to test the legality of the actual value type leaving output ports
  256. and entering input ports.
  257. Note that the cmXXXDsvFl flags have the problem that it is not possible to
  258. specify some multiple types. For example it is not possible to specify both
  259. scalar and matrix types simultaneously. Once the matrix flag is set it must
  260. be assumed that all specific data types then are matrices.
  261. THIS IS A FUNDAMENTAL PROBLEM THAT MUST BE ADDRESSED.
  262. Note that scalar numeric values can easily be cheaply converted to other numeric
  263. types. It could be expensive however if vectors required conversion.
  264. Vectors are currently being passed as pointers. No conversion is occurring.
  265. d. Another proposal:
  266. Following connection time there is a type determination pass. The network
  267. is traversed in execution order. Each instance computes and emits the single
  268. type assigned to each of its output ports. Once emited these types will not
  269. change during runtime.
  270. Note that this does not preclude an input receiving multiple types.
  271. If a variable arrives at an input port which does not match the type of
  272. the variable associated with that type then it is sent to the NoTypeRecv()
  273. instance function.
  274. 2. Do values only get sent on change? If not - why not?
  275. No - based on cmDspClass.ccmDspValueSet() values are transmitted
  276. whenever they are set - there is not check for a change of value.
  277. 3. Write a function to support generating multiple enumerated
  278. ports - as is required by AMix or ASplit.
  279. (This is now done: See cmDspClass.h:cmDspArgSetup() )
  280. Update the existing code to use this scheme. (This still needs to be done.)
  281. Similar functions need to be written for connecting groups of ports
  282. in cmDspPgm.c. (This still needs to be done.)
  283. 5. It does not appear that the kInDsvFl and kOutDsvFl are actually used during
  284. the connection process. This means that a variable marked as an output
  285. could be used as an input and v.v.. Likewise variable marked as neither input
  286. nor output could be accessed. What are the input and output flags actually
  287. used for?
  288. In fact kInDsvFl and kOutDsvFl are not used at all. (3/18/12).
  289. It might be nice to allow everything to be an output - but to
  290. force inputs to be explicitely named.
  291. 6. Is there any implication for marking a variable as both an input and an output?
  292. 7. The audio buffers allocated in cmDspInstAlloc() may not be memory aligned.
  293. 8. Is there a way to add a generic enable/disable function to all instances?
  294. 9. Should all output audio buffers be zeroed by default during _cmDspXXXReset()?
  295. 10. Is the kConstDsvFl used? respected? necessary?
  296. yes - it is necessary because some variables cannot be changed after the constructor
  297. is completed. For example any instance that take an argument giving the
  298. number of ports as a variable. The port count argument cannot change because it
  299. might invalidate connections which had been already made to the existing ports.
  300. TODO: find all variables which cannot be changed after the constructor and mark
  301. them as const and prevent them from being the target of connections or events.
  302. 11. Is it OK to not assign a variable as either an input or an output. (this would
  303. allow it to be set from cmDspSysAllocInst() but then only changed internally).
  304. 12. Write some template DSP instances that provide commented examples of the
  305. common scenarios which an actual instance might encounter.
  306. 13. All errors in instances should use cmDspClassErr() or cmDspInstErr() not cmErrMsg().
  307. Update existing code.
  308. 14. The way that the master controls are created is wrong. The master controls should
  309. be created during the cmDspSysLoad() process rather than being created in kcApp.cpp.
  310. This would make them essentially identical to other controls - and would allow the
  311. master controls to be manipulated easily from inside a DSP instance.
  312. 15. The code for creating and decoding messages seems to be distributed everywhere.
  313. All of this functionality should be moved to cmMsgProtocol.c. See the code
  314. for encoding/decoding messages in cmAudioSys.c as an example.
  315. 16. The default behavior of buttons should be to to NOT send out their default values or
  316. symbols on instance reset. Determining whether an output value is sent on instance
  317. reset (as they all are currently ??? or are only UI sent out on reset???)
  318. could be another argument flag setting [**].
  319. 17. cmDspInstAlloc() should include another version called cmDspInstAllocV()
  320. which takes the cmDspArg_t fields as var args. This would allow
  321. array variables which currently use cmDspArgSetupN() to be given
  322. in one call - which would be less error prone than using cmDspArgSetupN().
  323. 18. Add helper functions to create common dsp instances like:
  324. scalar,button,check,file,audio in,audio out. These functions should
  325. support default values through literals or through resource paths.
  326. 19. Design a sub-net function for making sub-nets of instance nets
  327. that can then be treated like instances themselves.
  328. For example make a network of audio sources:
  329. audio file, signal generator, audio input, with gain and frequency
  330. controls.
  331. 20. Network construction (cmDspPgm.c) is divided into two parts.
  332. First the instances are allocated and then they are connected.
  333. There should always be a test for a failure between construction
  334. phase and the connection phase and then again after the connection phase.
  335. 21. The existing instances are not using cmReal_t and cmSample_t as they should.
  336. 22. It is possible for cmDspInstAlloc() to fail in an instance constructor and
  337. yet we are not testing for it in many instances.
  338. When a failure occurs after cmDspInstAlloc() how is the instance deleted
  339. prior to returning? ... is it necessary to delete it prior to returning?
  340. 23. For instances which act as files and which take a file name as at an input
  341. port - the correct way to implement the object is to open/reopen the file
  342. both on reset and when a new file name is received.
  343. The reset open covers the case where the default filename is used.
  344. The receieve open covers the case where a filename is received via the input port.
  345. 24. After each call to an instance member function (reset,recv,exec,etc.) the
  346. interal error object should be checked. This way an invalid state can
  347. be signaled inside one of the functions without having to worry about
  348. propagating the error to the return value. THis means that as long as
  349. a member function can report and safely complete it doesn't have to do
  350. much error handling internally.
  351. 25. As it is currently implemented all audio system sub-system share a
  352. single UDP network managers. This is NOT thread-safe. If more than
  353. one audio sub-system is actually used the program will crash.
  354. This can be solved by giving each sub-system it's own UDP network
  355. manager, where each sub-system is given it's own port number.
  356. */
  357. //==========================================================================================================================================
  358. enum
  359. {
  360. kLblPrId,
  361. kMsPrId,
  362. kInPrId
  363. };
  364. cmDspClass_t _cmPrinterDC;
  365. typedef struct
  366. {
  367. cmDspInst_t inst;
  368. unsigned limitCycles;
  369. } cmDspPrinter_t;
  370. cmDspInst_t* _cmDspPrinterAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  371. {
  372. cmDspVarArg_t args[] =
  373. {
  374. { "lbl", kLblPrId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "Label" },
  375. { "ms", kMsPrId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Period"},
  376. { "in", kInPrId, 0, 0, kInDsvFl | kTypeDsvMask, "Input port" },
  377. { NULL, 0, 0, 0, 0 }
  378. };
  379. cmDspPrinter_t* p = cmDspInstAlloc(cmDspPrinter_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  380. cmDspSetDefaultUInt(ctx,&p->inst,kMsPrId, 0, 0 );
  381. return &p->inst;
  382. }
  383. cmDspRC_t _cmDspPrinterReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  384. {
  385. cmDspPrinter_t* p = (cmDspPrinter_t*)inst;
  386. p->limitCycles = ctx->cycleCnt;
  387. return kOkDspRC;
  388. }
  389. cmDspRC_t _cmDspPrinterRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  390. {
  391. cmDspPrinter_t* p = (cmDspPrinter_t*)inst;
  392. if( evt->dstVarId == kInPrId && ctx->cycleCnt >= p->limitCycles )
  393. {
  394. p->limitCycles = ctx->cycleCnt + (unsigned)(cmDspUInt(inst,kMsPrId) * cmDspSampleRate(ctx) / (1000.0 * cmDspSamplesPerCycle(ctx)) );
  395. const cmChar_t* lbl = cmDspStrcz(inst,kLblPrId);
  396. if( cmDsvIsSymbol(evt->valuePtr) )
  397. cmRptPrintf(ctx->rpt,"%s'%s'",lbl==NULL?"":lbl,cmStringNullGuard(cmSymTblLabel(ctx->stH,cmDsvSymbol(evt->valuePtr))));
  398. else
  399. cmDsvPrint(evt->valuePtr,lbl,ctx->rpt);
  400. cmRptPrint(ctx->rpt,"\n");
  401. }
  402. return kOkDspRC;
  403. }
  404. struct cmDspClass_str* cmPrinterClassCons( cmDspCtx_t* ctx )
  405. {
  406. cmDspClassSetup(&_cmPrinterDC,ctx,"Printer",
  407. NULL,
  408. _cmDspPrinterAlloc,
  409. NULL,
  410. _cmDspPrinterReset,
  411. NULL,
  412. _cmDspPrinterRecv,
  413. NULL,
  414. NULL,
  415. "Print the value of any event arriving at 'in'.");
  416. return &_cmPrinterDC;
  417. }
  418. //==========================================================================================================================================
  419. enum
  420. {
  421. kMinCntId,
  422. kMaxCntId,
  423. kIncCntId,
  424. kWrapCntId,
  425. kResetCntId,
  426. kOutCntId,
  427. kCycCntId,
  428. kNxtCntId,
  429. };
  430. cmDspClass_t _cmCounterDC;
  431. typedef struct
  432. {
  433. cmDspInst_t inst;
  434. double val;
  435. bool disableFl;
  436. } cmDspCounter_t;
  437. cmDspRC_t _cmDspCounterValidate( cmDspInst_t* inst, double min, double max, double inc )
  438. {
  439. if( max < min )
  440. return cmErrMsg(&inst->classPtr->err,kInvalidArgDspRC,"The counter maximum (%f) value must be greater than the counter minimum (%f) value.",max,min);
  441. if( max - min < inc )
  442. return cmErrMsg(&inst->classPtr->err,kInvalidArgDspRC,"The counter increment value (%f) must be less than or equal to the maximum - minimum difference (%f).",inc,max-min);
  443. return kOkDspRC;
  444. }
  445. cmDspInst_t* _cmDspCounterAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  446. {
  447. cmDspVarArg_t args[] =
  448. {
  449. { "min", kMinCntId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "minimum" },
  450. { "max", kMaxCntId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "maximum" },
  451. { "inc", kIncCntId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "increment"},
  452. { "wrap", kWrapCntId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "wrap"},
  453. { "reset", kResetCntId,0, 0, kInDsvFl | kTypeDsvMask, "reset"},
  454. { "out", kOutCntId, 0, 0, kOutDsvFl | kDoubleDsvFl, "out"},
  455. { "cycles", kCycCntId, 0, 0, kOutDsvFl | kDoubleDsvFl, "cycles"},
  456. { "next", kNxtCntId, 0, 0, kInDsvFl | kTypeDsvMask, "next"},
  457. { NULL, 0, 0, 0, 0 }
  458. };
  459. cmDspCounter_t* p = cmDspInstAlloc(cmDspCounter_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  460. double min = cmDspDefaultDouble( &p->inst, kMinCntId );
  461. double max = cmDspDefaultDouble( &p->inst, kMaxCntId );
  462. double inc = cmDspDefaultDouble( &p->inst, kIncCntId );
  463. if( _cmDspCounterValidate(&p->inst, min, max, inc ) != kOkDspRC )
  464. return NULL;
  465. cmDspSetDefaultBool( ctx, &p->inst, kWrapCntId, false, true);
  466. cmDspSetDefaultDouble( ctx, &p->inst, kOutCntId, 0.0, min );
  467. cmDspSetDefaultDouble( ctx, &p->inst, kCycCntId, 0.0, 0.0 );
  468. return &p->inst;
  469. }
  470. cmDspRC_t _cmDspCounterReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  471. {
  472. cmDspCounter_t* p = (cmDspCounter_t*)inst;
  473. cmDspApplyAllDefaults(ctx,inst);
  474. p->val = cmDspDouble(inst,kMinCntId);
  475. p->disableFl = false; // the default values must be ok or the constructor fails
  476. return kOkDspRC;
  477. }
  478. void _cmDspCounterIncr( cmDspCtx_t* ctx, cmDspInst_t* inst )
  479. {
  480. cmDspCounter_t* p = (cmDspCounter_t*)inst;
  481. double min = cmDspDouble(inst,kMinCntId);
  482. double max = cmDspDouble(inst,kMaxCntId);
  483. double inc = cmDspDouble(inst,kIncCntId);
  484. bool wrapFl = cmDspBool(inst,kWrapCntId);
  485. bool limitFl = min <= max;
  486. // If min > max then no upper/lower limit is set on the value.
  487. // In this case the ouput will continue to increment and
  488. // no 'cycle' output will be generated.
  489. // If wrapFl is not set then the 'cycle' output will fire
  490. // exactly once when the counter crosses its limit.
  491. // if the new value is in range then send it
  492. if( min <= p->val && p->val < max )
  493. cmDspSetDouble( ctx, inst, kOutCntId, p->val );
  494. // the current value is out of range and wrap flag is not set
  495. if( limitFl && (p->val < min || p->val >= max) && (wrapFl==false))
  496. return;
  497. // do the increment
  498. p->val += inc;
  499. // if the new value is out of range
  500. if( limitFl && (p->val < min || p->val >= max) )
  501. {
  502. // if wrapping is allowed
  503. if( wrapFl )
  504. {
  505. if( p->val >= max )
  506. p->val = min + (p->val - max); // wrap to begin
  507. else
  508. if( p->val < min )
  509. p->val = max - (min - p->val); // wrap to end
  510. }
  511. // increment the cycle counter
  512. cmDspSetDouble( ctx, inst, kCycCntId, cmDspDouble( inst, kCycCntId ) + 1 );
  513. }
  514. }
  515. cmDspRC_t _cmDspCounterRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  516. {
  517. cmDspCounter_t* p = (cmDspCounter_t*)inst;
  518. cmDspRC_t rc = kOkDspRC;
  519. switch( evt->dstVarId )
  520. {
  521. case kWrapCntId:
  522. cmDspSetEvent(ctx,inst, evt );
  523. break;
  524. case kMinCntId:
  525. case kMaxCntId:
  526. case kIncCntId:
  527. {
  528. cmDspSetEvent( ctx, inst, evt );
  529. double min = cmDspDouble( inst, kMinCntId );
  530. double max = cmDspDouble( inst, kMaxCntId );
  531. double inc = cmDspDouble( inst, kIncCntId );
  532. p->disableFl = (rc = _cmDspCounterValidate(inst, min, max, inc)) != kOkDspRC;
  533. }
  534. break;
  535. case kNxtCntId:
  536. if( !p->disableFl )
  537. _cmDspCounterIncr(ctx,inst);
  538. break;
  539. case kResetCntId: // any msg on the 'reset' port causes the min value to be sent on the following 'next'
  540. p->val = cmDspDouble(inst,kMinCntId);
  541. break;
  542. default:
  543. { assert(0); }
  544. }
  545. return rc;
  546. }
  547. struct cmDspClass_str* cmCounterClassCons( cmDspCtx_t* ctx )
  548. {
  549. cmDspClassSetup(&_cmCounterDC,ctx,"Counter",
  550. NULL,
  551. _cmDspCounterAlloc,
  552. NULL,
  553. _cmDspCounterReset,
  554. NULL,
  555. _cmDspCounterRecv,
  556. NULL,
  557. NULL,
  558. "Counter object. Set min => max to have no limit on the value." );
  559. return &_cmCounterDC;
  560. }
  561. //==========================================================================================================================================
  562. enum
  563. {
  564. kMaxPhId,
  565. kMultPhId,
  566. kPhsPhId,
  567. kOutPhId
  568. };
  569. cmDspClass_t _cmPhasorDC;
  570. typedef struct
  571. {
  572. cmDspInst_t inst;
  573. } cmDspPhasor_t;
  574. cmDspInst_t* _cmDspPhasorAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  575. {
  576. unsigned chs = 1;
  577. cmDspVarArg_t args[] =
  578. {
  579. { "max", kMaxPhId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Maximum accumulator value" },
  580. { "mult", kMultPhId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Increment multiplier" },
  581. { "phs", kPhsPhId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Next phase value" },
  582. { "out", kOutPhId, 0, chs, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
  583. { NULL, 0, 0, 0, 0 }
  584. };
  585. // allocate the instance
  586. cmDspPhasor_t* p = cmDspInstAlloc(cmDspPhasor_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  587. // assign default values to any of the the optional arg's which may not have been set from vl.
  588. cmDspSetDefaultSample(ctx, &p->inst, kMaxPhId, 0.0, cmSample_MAX);
  589. cmDspSetDefaultSample(ctx, &p->inst, kMultPhId, 0.0, 1.0);
  590. cmDspSetDefaultDouble(ctx, &p->inst, kPhsPhId, 0.0, 0.0);
  591. return &p->inst;
  592. }
  593. cmDspRC_t _cmDspPhasorReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  594. {
  595. cmDspApplyAllDefaults(ctx,inst);
  596. cmDspZeroAudioBuf( ctx, inst, kOutPhId );
  597. return kOkDspRC;
  598. }
  599. cmDspRC_t _cmDspPhasorExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  600. {
  601. cmSample_t* bp = cmDspAudioBuf(ctx,inst,kOutPhId,0);
  602. const cmSample_t* ep = bp + cmDspAudioBufSmpCount(ctx,inst,kOutPhId,0);
  603. cmSample_t mult = cmDspSample(inst,kMultPhId);
  604. cmSample_t max = cmDspSample(inst,kMaxPhId);
  605. double phs = cmDspDouble(inst,kPhsPhId);
  606. cmSample_t inc = mult;
  607. for(; bp<ep; ++bp)
  608. {
  609. while( phs >= max )
  610. phs -= max;
  611. *bp = (cmSample_t)phs;
  612. phs += inc;
  613. }
  614. cmDspSetSample(ctx,inst,kPhsPhId,phs);
  615. return kOkDspRC;
  616. }
  617. cmDspRC_t _cmDspPhasorRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  618. {
  619. switch( evt->dstVarId )
  620. {
  621. case kMultPhId:
  622. case kMaxPhId:
  623. case kPhsPhId:
  624. cmDspSetEvent(ctx, inst, evt );;
  625. break;
  626. default:
  627. { assert(0); }
  628. }
  629. return kOkDspRC;
  630. }
  631. struct cmDspClass_str* cmPhasorClassCons( cmDspCtx_t* ctx )
  632. {
  633. cmDspClassSetup(&_cmPhasorDC,ctx,"Phasor",
  634. NULL,
  635. _cmDspPhasorAlloc,
  636. NULL,
  637. _cmDspPhasorReset,
  638. _cmDspPhasorExec,
  639. _cmDspPhasorRecv,
  640. NULL,
  641. NULL,
  642. "Ramp wave signal generator.");
  643. return &_cmPhasorDC;
  644. }
  645. //==========================================================================================================================================
  646. enum
  647. {
  648. kHzSgId,
  649. kShapeSgId,
  650. kGainSgId,
  651. kOtCntSgId,
  652. kOutSgId
  653. };
  654. enum
  655. {
  656. kWhiteSgId, // 0
  657. kPinkSgId, // 1
  658. kSineSgId, // 2
  659. kCosSgId, // 3
  660. kSawSgId, // 4
  661. kSqrSgId, // 5
  662. kTriSgId, // 6
  663. kPulseSgId, // 7
  664. kPhasorSgId // 8
  665. };
  666. cmDspClass_t _cmSigGenDC;
  667. typedef struct
  668. {
  669. cmDspInst_t inst;
  670. cmReal_t phs;
  671. cmSample_t reg;
  672. } cmDspSigGen_t;
  673. cmDspInst_t* _cmDspSigGenAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  674. {
  675. cmDspVarArg_t args[] =
  676. {
  677. { "hz", kHzSgId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Signal frequency in Hertz." },
  678. { "shape", kShapeSgId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Wave shape 0=sine 1=cosine 2=white 3=pink" },
  679. { "gain", kGainSgId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain."},
  680. { "ot", kOtCntSgId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Overtone count."},
  681. { "out", kOutSgId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
  682. { NULL, 0, 0, 0, 0 }
  683. };
  684. cmDspSigGen_t* p = cmDspInstAlloc(cmDspSigGen_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  685. cmDspSetDefaultDouble(ctx, &p->inst, kHzSgId, 0.0, 1000);
  686. cmDspSetDefaultUInt( ctx, &p->inst, kShapeSgId, 0, 0);
  687. cmDspSetDefaultUInt( ctx, &p->inst, kOtCntSgId, 0, 0);
  688. cmDspSetDefaultDouble(ctx, &p->inst, kGainSgId, 0.0, 0.9 );
  689. return &p->inst;
  690. }
  691. cmDspRC_t _cmDspSigGenReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  692. {
  693. cmDspSigGen_t* p = (cmDspSigGen_t*)inst;
  694. p->phs = 0;
  695. cmDspApplyAllDefaults(ctx,inst);
  696. cmDspZeroAudioBuf( ctx, inst, kOutSgId );
  697. p->reg = 0;
  698. return kOkDspRC;
  699. }
  700. cmDspRC_t _cmDspSigGenExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  701. {
  702. cmDspSigGen_t* p = (cmDspSigGen_t*)inst;
  703. unsigned chIdx = 0;
  704. cmSample_t* bp = cmDspAudioBuf(ctx,inst,kOutSgId,chIdx);
  705. unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutSgId,chIdx);
  706. const cmSample_t* ep = bp + n;
  707. double hz = cmDspDouble(inst,kHzSgId);
  708. double sr = cmDspSampleRate(ctx);
  709. double fact = 2.0 * M_PI * hz / sr;
  710. int shape = cmDspUInt(inst,kShapeSgId);
  711. double offs = shape == kCosSgId ? (-M_PI / 2.0) : 0;
  712. double gain = cmDspDouble(inst,kGainSgId);
  713. unsigned otCnt = cmDspUInt(inst,kOtCntSgId);
  714. switch( shape )
  715. {
  716. case kWhiteSgId:
  717. while( bp < ep )
  718. *bp++ = gain * 2.0 * ((cmSample_t)rand() / RAND_MAX - 0.5);
  719. break;
  720. case kPinkSgId:
  721. while( bp < ep )
  722. {
  723. cmSample_t s = gain * 2.0 * ((cmSample_t)rand() / RAND_MAX - 0.5);
  724. *bp++ = (s + p->reg)/2;
  725. p->reg = s;
  726. }
  727. break;
  728. case kSineSgId:
  729. case kCosSgId:
  730. while( bp<ep )
  731. {
  732. *bp++ = (cmSample_t)(gain * sin( fact * p->phs + offs ));
  733. p->phs += 1.0;
  734. }
  735. break;
  736. case kSawSgId:
  737. p->phs = cmVOS_SynthSawtooth(bp,n,(unsigned)p->phs,sr,hz,otCnt);
  738. cmVOS_MultVS(bp,n,gain);
  739. break;
  740. case kSqrSgId:
  741. p->phs = cmVOS_SynthSquare( bp,n,(unsigned)p->phs,sr,hz,otCnt );
  742. cmVOS_MultVS(bp,n,gain);
  743. break;
  744. case kTriSgId:
  745. p->phs = cmVOS_SynthTriangle( bp,n,(unsigned)p->phs,sr,hz,otCnt );
  746. cmVOS_MultVS(bp,n,gain);
  747. break;
  748. case kPulseSgId:
  749. p->phs = cmVOS_SynthPulseCos( bp,n,(unsigned)p->phs,sr,hz,otCnt );
  750. cmVOS_MultVS(bp,n,gain);
  751. break;
  752. case kPhasorSgId:
  753. p->phs = cmVOS_SynthPhasor( bp,n,(unsigned)p->phs,sr,hz );
  754. cmVOS_MultVS(bp,n,gain);
  755. break;
  756. default:
  757. { assert(0); }
  758. }
  759. return kOkDspRC;
  760. }
  761. cmDspRC_t _cmDspSigGenRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  762. {
  763. cmDspRC_t rc;
  764. if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
  765. {
  766. switch( evt->dstVarId )
  767. {
  768. case kShapeSgId:
  769. //printf("%s %i\n",cmDspInstLabel(ctx,inst),cmDspUInt(inst,kShapeSgId));
  770. break;
  771. }
  772. }
  773. return rc;
  774. }
  775. struct cmDspClass_str* cmSigGenClassCons( cmDspCtx_t* ctx )
  776. {
  777. cmDspClassSetup(&_cmSigGenDC,ctx,"SigGen",
  778. NULL,
  779. _cmDspSigGenAlloc,
  780. NULL,
  781. _cmDspSigGenReset,
  782. _cmDspSigGenExec,
  783. _cmDspSigGenRecv,
  784. NULL,
  785. NULL,
  786. "Variable frequency and waveshape signal generator." );
  787. return &_cmSigGenDC;
  788. }
  789. //==========================================================================================================================================
  790. enum
  791. {
  792. kDeviceMiId,
  793. kPortMiId,
  794. kSmpIdxMiId,
  795. kStatusMiId,
  796. kD0MiId,
  797. kD1MiId,
  798. kSecMiId,
  799. kNSecMiId
  800. };
  801. cmDspClass_t _cmMidiInDC;
  802. typedef struct
  803. {
  804. cmDspInst_t inst;
  805. unsigned midiSymId;
  806. unsigned prevSmpIdx;
  807. cmTimeSpec_t prevTimeStamp;
  808. } cmDspMidiIn_t;
  809. cmDspInst_t* _cmDspMidiInAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  810. {
  811. cmDspVarArg_t args[] =
  812. {
  813. { "device", kDeviceMiId, 0, 0, kOutDsvFl | kUIntDsvFl, "MIDI device" },
  814. { "port", kPortMiId, 0, 0, kOutDsvFl | kUIntDsvFl, "MIDI device port"},
  815. { "smpidx", kSmpIdxMiId, 0, 0, kOutDsvFl | kUIntDsvFl, "Message time tag as sample index."},
  816. { "status", kStatusMiId, 0, 0, kOutDsvFl | kUIntDsvFl, "MIDI status" },
  817. { "d0", kD0MiId, 0, 0, kOutDsvFl | kUIntDsvFl, "MIDI channel message d0" },
  818. { "d1", kD1MiId, 0, 0, kOutDsvFl | kUIntDsvFl, "MIDI channel message d1" },
  819. { "sec", kSecMiId, 0, 0, kOutDsvFl | kUIntDsvFl, "Time stamp integer seconds."},
  820. { "nsec", kNSecMiId, 0, 0, kOutDsvFl | kUIntDsvFl, "Time stamp fractional second (nanoseconds)."},
  821. { NULL, 0, 0, 0, 0 }
  822. };
  823. cmDspMidiIn_t* p = cmDspInstAlloc(cmDspMidiIn_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  824. p->midiSymId = cmDspSysAssignInstAttrSymbolStr( ctx->dspH, &p->inst, "_midi" );
  825. return &p->inst;
  826. }
  827. cmDspRC_t _cmDspMidiInReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  828. {
  829. cmDspRC_t rc = kOkDspRC;
  830. cmDspMidiIn_t* p = (cmDspMidiIn_t*)inst;
  831. cmDspApplyAllDefaults(ctx,inst);
  832. p->prevSmpIdx = 0;
  833. return rc;
  834. }
  835. cmDspRC_t _cmDspMidiInRecvFunc( cmDspCtx_t* ctx, struct cmDspInst_str* inst, unsigned attrSymId, const cmDspValue_t* value )
  836. {
  837. cmDspMidiIn_t* p = (cmDspMidiIn_t*)inst;
  838. if( attrSymId == p->midiSymId )
  839. {
  840. cmMidiPacket_t* pkt = (cmMidiPacket_t*)(value->u.m.u.vp);
  841. unsigned i;
  842. cmDspSetUInt(ctx, inst, kDeviceMiId, pkt->devIdx);
  843. cmDspSetUInt(ctx, inst, kPortMiId, pkt->portIdx);
  844. for(i=0; i<pkt->msgCnt; ++i)
  845. {
  846. cmMidiMsg* m = pkt->msgArray + i;
  847. unsigned deltaSmpCnt = 0;
  848. if( p->prevTimeStamp.tv_sec!=0 && p->prevTimeStamp.tv_nsec!=0 )
  849. deltaSmpCnt = floor(cmTimeElapsedMicros(&p->prevTimeStamp,&m->timeStamp) * cmDspSampleRate(ctx) / 1000000.0);
  850. if( p->prevSmpIdx == 0 )
  851. p->prevSmpIdx = ctx->cycleCnt * cmDspSamplesPerCycle(ctx);
  852. else
  853. p->prevSmpIdx += deltaSmpCnt;
  854. cmDspSetUInt(ctx, inst, kSmpIdxMiId, p->prevSmpIdx );
  855. cmDspSetUInt(ctx, inst, kSecMiId, m->timeStamp.tv_sec);
  856. cmDspSetUInt(ctx, inst, kNSecMiId, m->timeStamp.tv_nsec);
  857. cmDspSetUInt(ctx, inst, kD1MiId, m->d1 );
  858. cmDspSetUInt(ctx, inst, kD0MiId, m->d0 );
  859. cmDspSetUInt(ctx, inst, kStatusMiId, m->status );
  860. p->prevTimeStamp = m->timeStamp;
  861. }
  862. }
  863. return kOkDspRC;
  864. }
  865. struct cmDspClass_str* cmMidiInClassCons( cmDspCtx_t* ctx )
  866. {
  867. cmDspClassSetup(&_cmMidiInDC,ctx,"MidiIn",
  868. NULL,
  869. _cmDspMidiInAlloc,
  870. NULL,
  871. _cmDspMidiInReset,
  872. NULL,
  873. NULL,
  874. NULL,
  875. _cmDspMidiInRecvFunc,
  876. "Midi input port");
  877. return &_cmMidiInDC;
  878. }
  879. //==========================================================================================================================================
  880. enum
  881. {
  882. kDeviceMoId,
  883. kPortMoId,
  884. kStatusMoId,
  885. kD0MoId,
  886. kD1MoId,
  887. kResetMoId
  888. };
  889. cmDspClass_t _cmMidiOutDC;
  890. typedef struct
  891. {
  892. cmDspInst_t inst;
  893. unsigned devIdx;
  894. unsigned portIdx;
  895. bool enableFl;
  896. } cmDspMidiOut_t;
  897. cmDspRC_t _cmDspMidiOutSetDevice( cmDspCtx_t* ctx, cmDspMidiOut_t* p, const cmChar_t* deviceStr )
  898. {
  899. cmDspRC_t rc = kOkDspRC;
  900. if( deviceStr != NULL )
  901. if((p->devIdx = cmMpDeviceNameToIndex(deviceStr)) == cmInvalidIdx )
  902. rc = cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The MIDI device '%s' could not be found.",cmStringNullGuard(deviceStr));
  903. return rc;
  904. }
  905. cmDspRC_t _cmDspMidiOutSetPort( cmDspCtx_t* ctx, cmDspMidiOut_t* p, const cmChar_t* portStr )
  906. {
  907. cmDspRC_t rc = kOkDspRC;
  908. if( portStr == NULL )
  909. return rc;
  910. if( p->devIdx == cmInvalidIdx )
  911. rc = cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The MIDI port cannot be set until the MIDI device is set.");
  912. else
  913. {
  914. if((p->portIdx = cmMpDevicePortNameToIndex(p->devIdx,kOutMpFl,portStr)) == cmInvalidIdx )
  915. rc = cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The MIDI port '%s' could not be found on device '%s'.",cmStringNullGuard(portStr),cmStringNullGuard(cmMpDeviceName(p->devIdx)));
  916. }
  917. return rc;
  918. }
  919. cmDspInst_t* _cmDspMidiOutAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  920. {
  921. cmDspVarArg_t args[] =
  922. {
  923. { "device", kDeviceMoId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "MIDI device name"},
  924. { "port", kPortMoId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "MIDI port name"},
  925. { "status", kStatusMoId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI status" },
  926. { "d0", kD0MoId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI channel message d0" },
  927. { "d1", kD1MoId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI channel message d1" },
  928. { "reset", kResetMoId, 0, 0, kInDsvFl | kTypeDsvMask,"All notes off" },
  929. { NULL, 0, 0, 0, 0 }
  930. };
  931. cmDspMidiOut_t* p = cmDspInstAlloc(cmDspMidiOut_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  932. p->devIdx = cmInvalidIdx;
  933. p->portIdx = cmInvalidIdx;
  934. cmDspSetDefaultUInt(ctx,&p->inst, kStatusMoId, 0, 0 );
  935. cmDspSetDefaultUInt(ctx,&p->inst, kD0MoId, 0, 0 );
  936. cmDspSetDefaultUInt(ctx,&p->inst, kD1MoId, 0, 0 );
  937. return &p->inst;
  938. }
  939. cmDspRC_t _cmDspMidiOutReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  940. {
  941. cmDspRC_t rc = kOkDspRC;
  942. cmDspMidiOut_t* p = (cmDspMidiOut_t*)inst;
  943. cmDspApplyAllDefaults(ctx,inst);
  944. p->enableFl = false;
  945. if(_cmDspMidiOutSetDevice(ctx,p,cmDspStrcz(inst,kDeviceMoId)) == kOkDspRC )
  946. p->enableFl = _cmDspMidiOutSetPort( ctx,p,cmDspStrcz(inst,kPortMoId)) == kOkDspRC;
  947. return rc;
  948. }
  949. cmDspRC_t _cmDspMidiOutRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  950. {
  951. cmDspMidiOut_t* p = (cmDspMidiOut_t*)inst;
  952. switch( evt->dstVarId )
  953. {
  954. case kDeviceMoId:
  955. if(_cmDspMidiOutSetDevice(ctx, p, cmDsvStrcz(evt->valuePtr) ) != kOkDspRC )
  956. p->enableFl = false;
  957. break;
  958. case kPortMoId:
  959. if( _cmDspMidiOutSetPort(ctx, p, cmDsvStrcz(evt->valuePtr) ) != kOkDspRC )
  960. p->enableFl = false;
  961. break;
  962. case kStatusMoId:
  963. if( p->devIdx != cmInvalidIdx && p->portIdx != cmInvalidIdx )
  964. {
  965. unsigned status = cmDsvGetUInt(evt->valuePtr);
  966. unsigned d0 = cmDspUInt(inst,kD0MoId);
  967. unsigned d1 = cmDspUInt(inst,kD1MoId);
  968. if( p->enableFl )
  969. if( cmMpDeviceSend( p->devIdx, p->portIdx, status, d0, d1 ) != kOkMpRC )
  970. cmDspInstErr(ctx,inst,kInvalidArgDspRC,"MIDI send failed.");
  971. }
  972. break;
  973. case kResetMoId:
  974. {
  975. unsigned i;
  976. if( p->enableFl )
  977. for(i=0; i<kMidiChCnt; ++i)
  978. {
  979. cmMpDeviceSend(p->devIdx,p->portIdx,kCtlMdId+i,121,0); // reset all controllers
  980. cmMpDeviceSend(p->devIdx,p->portIdx,kCtlMdId+i,123,0); // turn all notes off
  981. cmSleepMs(15);
  982. }
  983. }
  984. break;
  985. default:
  986. cmDspSetEvent(ctx,inst,evt);
  987. break;
  988. }
  989. return kOkDspRC;
  990. }
  991. struct cmDspClass_str* cmMidiOutClassCons( cmDspCtx_t* ctx )
  992. {
  993. cmDspClassSetup(&_cmMidiOutDC,ctx,"MidiOut",
  994. NULL,
  995. _cmDspMidiOutAlloc,
  996. NULL,
  997. _cmDspMidiOutReset,
  998. NULL,
  999. _cmDspMidiOutRecv,
  1000. NULL,
  1001. NULL,
  1002. "Midi input port");
  1003. return &_cmMidiOutDC;
  1004. }
  1005. //==========================================================================================================================================
  1006. enum
  1007. {
  1008. kChAiId,
  1009. kGainAiId,
  1010. kOutAiId
  1011. };
  1012. cmDspClass_t _cmAudioInDC;
  1013. typedef struct
  1014. {
  1015. cmDspInst_t inst;
  1016. bool errFl; // used to control error reporting
  1017. } cmDspAudioIn_t;
  1018. cmDspInst_t* _cmDspAudioInAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1019. {
  1020. cmDspVarArg_t args[] =
  1021. {
  1022. { "ch", kChAiId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Audio input channel index"},
  1023. { "gain", kGainAiId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Input gain multiplier" },
  1024. { "out", kOutAiId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output" },
  1025. { NULL, 0, 0, 0, 0 }
  1026. };
  1027. cmDspAudioIn_t* p = cmDspInstAlloc(cmDspAudioIn_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1028. cmDspSetDefaultUInt( ctx, &p->inst, kChAiId, 0, 0);
  1029. cmDspSetDefaultDouble( ctx, &p->inst, kGainAiId, 0, 1.0);
  1030. return &p->inst;
  1031. }
  1032. cmDspRC_t _cmDspAudioInReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1033. {
  1034. cmDspRC_t rc = kOkDspRC;
  1035. cmDspAudioIn_t* p = (cmDspAudioIn_t*)inst;
  1036. p->errFl = false;
  1037. cmDspApplyAllDefaults(ctx,inst);
  1038. return rc;
  1039. }
  1040. cmDspRC_t _cmDspAudioInExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1041. {
  1042. unsigned chIdx = cmDspUInt(inst,kChAiId);
  1043. unsigned iChCnt = ctx->ctx->iChCnt;
  1044. cmDspAudioIn_t* p = (cmDspAudioIn_t*)inst;
  1045. double gain = cmDspDouble(inst,kGainAiId);
  1046. if( chIdx >= iChCnt )
  1047. {
  1048. if( p->errFl== false )
  1049. {
  1050. cmDspInstErr(ctx,inst,kInvalidArgDspRC,"The input channel index %i is invalid. Channel count:%i.",chIdx,iChCnt);
  1051. p->errFl = true;
  1052. }
  1053. cmDspZeroAudioBuf(ctx,inst,kOutAiId);
  1054. return kOkDspRC;
  1055. }
  1056. //unsigned n = cmDspSamplesPerCycle(ctx);
  1057. unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutAiId,0);
  1058. cmSample_t* dp = cmDspAudioBuf(ctx,inst,kOutAiId,0);
  1059. assert( n == cmDspAudioBufSmpCount(ctx,inst,kOutAiId,chIdx));
  1060. assert(dp != NULL);
  1061. // if this channel is disabled then iChArray[chIdx] will be NULL
  1062. if( ctx->ctx->iChArray[chIdx]!=NULL )
  1063. cmVOS_MultVVS(dp,n,ctx->ctx->iChArray[chIdx],(cmSample_t)gain);
  1064. return kOkDspRC;
  1065. }
  1066. cmDspRC_t _cmDspAudioInRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1067. {
  1068. cmDspRC_t rc = kOkDspRC;
  1069. switch( evt->dstVarId )
  1070. {
  1071. case kChAiId:
  1072. if( cmDspSetEvent(ctx,inst,evt) != kOkDspRC )
  1073. {
  1074. // if the exec callback was previously disabled and the new channel value is valid then re-enable the exec callback.
  1075. if( inst->execFunc==NULL && cmDspUInt(inst,kChAiId) < ctx->ctx->iChCnt )
  1076. inst->execFunc = _cmDspAudioInExec;
  1077. }
  1078. break;
  1079. case kGainAiId:
  1080. cmDspSetEvent(ctx,inst,evt);
  1081. break;
  1082. }
  1083. return rc;
  1084. }
  1085. struct cmDspClass_str* cmAudioInClassCons( cmDspCtx_t* ctx )
  1086. {
  1087. cmDspClassSetup(&_cmAudioInDC,ctx,"AudioIn",
  1088. NULL,
  1089. _cmDspAudioInAlloc,
  1090. NULL,
  1091. _cmDspAudioInReset,
  1092. _cmDspAudioInExec,
  1093. _cmDspAudioInRecv,
  1094. NULL,
  1095. NULL,
  1096. "Audio output port");
  1097. return &_cmAudioInDC;
  1098. }
  1099. //==========================================================================================================================================
  1100. enum
  1101. {
  1102. kChAoId,
  1103. kGainAoId,
  1104. kInAoId
  1105. };
  1106. cmDspClass_t _cmAudioOutDC;
  1107. typedef struct
  1108. {
  1109. cmDspInst_t inst;
  1110. } cmDspAudioOut_t;
  1111. cmDspInst_t* _cmDspAudioOutAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1112. {
  1113. cmDspVarArg_t args[] =
  1114. {
  1115. { "ch", kChAoId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Audio output channel index"},
  1116. { "gain",kGainAoId,0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain multiplier"},
  1117. { "in", kInAoId, 0, 1, kInDsvFl | kAudioBufDsvFl, "Audio input" },
  1118. { NULL, 0, 0, 0, 0 }
  1119. };
  1120. cmDspAudioOut_t* p = cmDspInstAlloc(cmDspAudioOut_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1121. cmDspSetDefaultUInt( ctx, &p->inst, kChAoId, 0, 0);
  1122. cmDspSetDefaultDouble( ctx, &p->inst, kGainAoId, 0, 1.0);
  1123. return &p->inst;
  1124. }
  1125. cmDspRC_t _cmDspAudioOutReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1126. {
  1127. cmDspRC_t rc = kOkDspRC;
  1128. cmDspApplyAllDefaults(ctx,inst);
  1129. return rc;
  1130. }
  1131. cmDspRC_t _cmDspAudioOutExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1132. {
  1133. cmDspRC_t rc = kOkDspRC;
  1134. unsigned chIdx = cmDspUInt(inst,kChAoId);
  1135. unsigned oChCnt = ctx->ctx->oChCnt;
  1136. double gain = cmDspDouble(inst,kGainAoId);
  1137. if( chIdx >= oChCnt )
  1138. {
  1139. rc = cmDspInstErr(ctx,inst,kInvalidArgDspRC,"The output channel index %i is invalid. Channel count:%i.",chIdx,oChCnt);
  1140. inst->execFunc = NULL; // disable callbacks - this prevents the error msg from repeating
  1141. return rc;
  1142. }
  1143. const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kInAoId,0);
  1144. if( sp == NULL )
  1145. {
  1146. inst->execFunc = NULL; // if there is no connected input then disable further callbacks
  1147. return kOkDspRC;
  1148. }
  1149. unsigned n = cmDspSamplesPerCycle(ctx);
  1150. assert( n == cmDspVarRows(inst,kInAoId) );
  1151. // if this channel is disabled or set to pass-through then chArray[chIdx] will be NULL
  1152. if( ctx->ctx->oChArray[chIdx] != NULL )
  1153. cmVOS_MultVVS(ctx->ctx->oChArray[chIdx],n,sp,(cmSample_t)gain);
  1154. return kOkDspRC;
  1155. }
  1156. cmDspRC_t _cmDspAudioOutRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1157. {
  1158. cmDspRC_t rc = kOkDspRC;
  1159. switch( evt->dstVarId )
  1160. {
  1161. case kChAoId:
  1162. if( cmDspSetEvent(ctx,inst,evt) != kOkDspRC )
  1163. {
  1164. // if the exec callback was previously disabled and the new channel value is valid then re-enable the exec callback.
  1165. if( inst->execFunc==NULL && cmDspUInt(inst,kChAoId) < ctx->ctx->oChCnt )
  1166. inst->execFunc = _cmDspAudioOutExec;
  1167. }
  1168. break;
  1169. case kGainAoId:
  1170. cmDspSetEvent(ctx,inst,evt);
  1171. break;
  1172. }
  1173. return rc;
  1174. }
  1175. struct cmDspClass_str* cmAudioOutClassCons( cmDspCtx_t* ctx )
  1176. {
  1177. cmDspClassSetup(&_cmAudioOutDC,ctx,"AudioOut",
  1178. NULL,
  1179. _cmDspAudioOutAlloc,
  1180. NULL,
  1181. _cmDspAudioOutReset,
  1182. _cmDspAudioOutExec,
  1183. _cmDspAudioOutRecv,
  1184. NULL,NULL,
  1185. "Audio output port");
  1186. return &_cmAudioOutDC;
  1187. }
  1188. //==========================================================================================================================================
  1189. enum
  1190. {
  1191. kFnAofId,
  1192. kChCntAofId,
  1193. kGain0AofId,
  1194. kGain1AofId,
  1195. kIn0AofId,
  1196. kIn1AofId,
  1197. kSelAofId
  1198. };
  1199. cmDspClass_t _cmAudioFileOutDC;
  1200. typedef struct
  1201. {
  1202. cmDspInst_t inst;
  1203. cmSample_t* smpBuf;
  1204. unsigned smpCnt;
  1205. unsigned bits;
  1206. cmAudioFileH_t afH;
  1207. unsigned openSymId;
  1208. unsigned closeSymId;
  1209. const cmChar_t* afn;
  1210. } cmDspAudioFileOut_t;
  1211. cmDspRC_t _cmDspAudioFileOutCreateFile( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned chCnt )
  1212. {
  1213. cmDspRC_t rc = kOkDspRC;
  1214. cmDspAudioFileOut_t* p = (cmDspAudioFileOut_t*)inst;
  1215. const cmChar_t* fn = cmDspStrcz(inst,kFnAofId);
  1216. if(cmAudioFileIsValid(p->afH) )
  1217. cmAudioFileDelete(&p->afH);
  1218. // if the supplied audio file name is actually a directory name then generate a file name
  1219. if( cmFsIsDir(fn) )
  1220. {
  1221. cmMemPtrFree(&p->afn);
  1222. if( cmFsGenFn(fn,"recd","aiff",&p->afn) != kOkFsRC )
  1223. return cmDspInstErr(ctx,&p->inst,kFileSysFailDspRC,"An output audio file name could not be generated.");
  1224. fn = p->afn;
  1225. }
  1226. if( cmAudioFileIsValid(p->afH = cmAudioFileNewCreate(fn, cmDspSampleRate(ctx), p->bits, chCnt, &rc, ctx->rpt )) == false )
  1227. rc = cmDspClassErr(ctx,inst->classPtr,kVarArgParseFailDspRC,"The output audio file '%s' create failed.",fn);
  1228. return rc;
  1229. }
  1230. cmDspInst_t* _cmDspAudioFileOutAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1231. {
  1232. cmDspVarArg_t args[] =
  1233. {
  1234. { "fn", kFnAofId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Audio file or directory name"},
  1235. { "chs", kChCntAofId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Channel count"},
  1236. { "gain0", kGain0AofId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain 0 multiplier"},
  1237. { "gain1", kGain1AofId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain 1 multiplier"},
  1238. { "in0", kIn0AofId, 0, 1, kInDsvFl | kAudioBufDsvFl, "Audio input 0"},
  1239. { "in1", kIn1AofId, 0, 1, kInDsvFl | kAudioBufDsvFl, "Audio input 1"},
  1240. { "sel", kSelAofId, 0, 0, kInDsvFl, "Open | Close"},
  1241. { NULL, 0, 0, 0, 0 }
  1242. };
  1243. cmDspAudioFileOut_t* p = cmDspInstAlloc(cmDspAudioFileOut_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1244. cmDspValue_t chCntVal;
  1245. cmDsvSetUInt(&chCntVal,cmMin(2,cmDspUInt(&p->inst,kChCntAofId)));
  1246. cmDspSetDefault(ctx, &p->inst, kChCntAofId, &chCntVal );
  1247. cmDspSetDefaultDouble( ctx, &p->inst, kGain0AofId, 0, 1.0);
  1248. cmDspSetDefaultDouble( ctx, &p->inst, kGain1AofId, 0, 1.0);
  1249. p->bits = 16;
  1250. p->afH = cmNullAudioFileH;
  1251. p->openSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"open");
  1252. p->closeSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"close");
  1253. return &p->inst;
  1254. }
  1255. cmDspRC_t _cmDspAudioFileOutReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1256. {
  1257. cmDspRC_t rc = kOkDspRC;
  1258. cmDspAudioFileOut_t* p = (cmDspAudioFileOut_t*)inst;
  1259. unsigned chCnt = cmDspUInt(inst,kChCntAofId);
  1260. cmDspApplyAllDefaults(ctx,inst);
  1261. p->smpCnt = cmDspSamplesPerCycle(ctx) * chCnt;
  1262. p->smpBuf = cmLhResizeN(ctx->lhH, cmSample_t, p->smpBuf, p->smpCnt);
  1263. //rc = _cmDspAudioFileOutCreateFile( ctx, inst, chCnt );
  1264. return rc;
  1265. }
  1266. cmDspRC_t _cmDspAudioFileOutExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1267. {
  1268. cmDspRC_t rc = kOkDspRC;
  1269. cmDspAudioFileOut_t* p = (cmDspAudioFileOut_t*)inst;;
  1270. unsigned chCnt = cmMin(2,cmDspUInt(inst,kChCntAofId));
  1271. unsigned smpCnt = 0;
  1272. cmSample_t* chArray[chCnt];
  1273. unsigned i,j;
  1274. if(!cmAudioFileIsValid(p->afH) )
  1275. return rc;
  1276. for(i=0,j=0; i<chCnt; ++i)
  1277. {
  1278. unsigned chVarId = i == 0 ? kIn0AofId : kIn1AofId; // get audio buf var id for this ch
  1279. unsigned iSmpCnt = cmDspVarRows(inst,chVarId);
  1280. if( iSmpCnt == 0 )
  1281. {
  1282. chArray[j] = NULL;
  1283. }
  1284. else
  1285. {
  1286. cmSample_t gain = cmDspSample(inst,i==0?kGain0AofId:kGain1AofId); // get ch gain
  1287. chArray[j] = cmDspAudioBuf(ctx,inst,chVarId,0); // get incoming audio buf ptr
  1288. if( gain != 1.0 )
  1289. cmVOS_MultVVS(chArray[j], iSmpCnt, chArray[j], gain); // apply gain
  1290. ++j; // incr chArray[] index
  1291. assert( smpCnt==0 || iSmpCnt==smpCnt);
  1292. smpCnt = iSmpCnt; // set outgoing sample count
  1293. }
  1294. }
  1295. // write the samples
  1296. if( cmAudioFileWriteSample(p->afH, smpCnt, j, chArray ) != kOkAfRC )
  1297. rc = cmDspClassErr(ctx,inst->classPtr,kFileWriteFailDspRC,"An audio output file write failed.");
  1298. return rc;
  1299. }
  1300. cmDspRC_t _cmDspAudioFileOutRecv( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1301. {
  1302. cmDspRC_t rc = kOkDspRC;
  1303. switch( evt->dstVarId )
  1304. {
  1305. case kGain1AofId:
  1306. case kGain0AofId:
  1307. cmDspSetEvent(ctx,inst,evt);
  1308. break;
  1309. case kFnAofId:
  1310. cmDspSetEvent(ctx,inst,evt);
  1311. rc = _cmDspAudioFileOutCreateFile(ctx,inst,cmDspUInt(inst,kChCntAofId));
  1312. break;
  1313. case kSelAofId:
  1314. {
  1315. cmDspAudioFileOut_t* p = (cmDspAudioFileOut_t*)inst;
  1316. unsigned symId = cmDsvSymbol(evt->valuePtr);
  1317. if( symId == p->openSymId )
  1318. rc = _cmDspAudioFileOutCreateFile(ctx,inst,cmDspUInt(inst,kChCntAofId));
  1319. else
  1320. {
  1321. if( symId == p->closeSymId )
  1322. {
  1323. if(cmAudioFileIsValid(p->afH) )
  1324. cmAudioFileDelete(&p->afH);
  1325. }
  1326. else
  1327. {
  1328. rc = cmErrMsg(&inst->classPtr->err,kInvalidArgDspRC,"Unknown selector symbol (%i) %s.",symId,cmStringNullGuard(cmSymTblLabel(ctx->stH,symId)));
  1329. }
  1330. }
  1331. }
  1332. }
  1333. return rc;
  1334. }
  1335. cmDspRC_t _cmDspAudioFileOutFree( cmDspCtx_t* ctx, struct cmDspInst_str* inst, const cmDspEvt_t* evtPtr )
  1336. {
  1337. cmDspAudioFileOut_t* p = (cmDspAudioFileOut_t*)inst;
  1338. if(cmAudioFileIsValid(p->afH) )
  1339. cmAudioFileDelete(&p->afH);
  1340. cmMemPtrFree(&p->afn);
  1341. return kOkDspRC;
  1342. }
  1343. struct cmDspClass_str* cmAudioFileOutClassCons( cmDspCtx_t* ctx )
  1344. {
  1345. cmDspClassSetup(&_cmAudioFileOutDC,ctx,"AudioFileOut",
  1346. NULL,
  1347. _cmDspAudioFileOutAlloc,
  1348. _cmDspAudioFileOutFree,
  1349. _cmDspAudioFileOutReset,
  1350. _cmDspAudioFileOutExec,
  1351. _cmDspAudioFileOutRecv,
  1352. NULL,NULL,
  1353. "Audio file output port");
  1354. return &_cmAudioFileOutDC;
  1355. }
  1356. //==========================================================================================================================================
  1357. enum
  1358. {
  1359. kTypScId,
  1360. kMinScId,
  1361. kMaxScId,
  1362. kStpScId,
  1363. kValScId,
  1364. kLblScId,
  1365. kSendScId
  1366. };
  1367. cmDspClass_t _cmScalarDC;
  1368. typedef struct
  1369. {
  1370. cmDspInst_t inst;
  1371. } cmDspScalar_t;
  1372. cmDspInst_t* _cmDspScalarAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1373. {
  1374. cmDspVarArg_t args[] =
  1375. {
  1376. { "typ", kTypScId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Type" },
  1377. { "min", kMinScId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Minimum value"},
  1378. { "max", kMaxScId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Maximum value"},
  1379. { "step", kStpScId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Step value (set to 0 to ignore)"},
  1380. { "val", kValScId, 0, 0, kInDsvFl | kOutDsvFl | kDoubleDsvFl | kReqArgDsvFl | kSendDfltDsvFl, "Current value"},
  1381. { "lbl", kLblScId, 0, 0, kStrzDsvFl | kOptArgDsvFl, "Label"},
  1382. { "send", kSendScId, 0, 0, kInDsvFl | kTypeDsvMask, "Send value on any msg."},
  1383. { NULL, 0, 0, 0, 0 }
  1384. };
  1385. cmDspScalar_t* p = cmDspInstAlloc(cmDspScalar_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1386. cmDspSetDefaultUInt( ctx, &p->inst, kTypScId, 0, kNumberDuiId);
  1387. cmDspSetDefaultDouble(ctx, &p->inst, kMinScId, 0.0, 0);
  1388. cmDspSetDefaultDouble(ctx, &p->inst, kMaxScId, 0.0, 1);
  1389. cmDspSetDefaultDouble(ctx, &p->inst, kStpScId, 0.0, 0);
  1390. unsigned typeId = cmDspDefaultUInt(&p->inst,kTypScId);
  1391. // create the UI control
  1392. cmDspUiScalarCreate(ctx,&p->inst,typeId,kMinScId,kMaxScId,kStpScId,kValScId,kLblScId);
  1393. return &p->inst;
  1394. }
  1395. cmDspRC_t _cmDspScalarReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1396. {
  1397. cmDspApplyAllDefaults(ctx,inst);
  1398. return kOkDspRC;
  1399. }
  1400. cmDspRC_t _cmDspScalarRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1401. {
  1402. if( evt->dstVarId == kSendScId )
  1403. {
  1404. // NOT SURE IF THIS IS CORRECT OR NOT - IT SHOULD FORCE THE CURRENT VALUE TO BE
  1405. // SENT - THE CURRENT VALUE SHOULD ALREADY BE AT THE UI SO THERE DOESN'T SEEM
  1406. // TO BE ANY REASON TO UPDATE IT THERE.
  1407. cmDspSetDouble( ctx, inst, kValScId, cmDspDouble(inst,kValScId ));
  1408. return kOkDspRC;
  1409. }
  1410. switch( evt->dstVarId )
  1411. {
  1412. case kMinScId:
  1413. case kMaxScId:
  1414. case kStpScId:
  1415. case kValScId:
  1416. cmDspSetEvent(ctx,inst,evt);
  1417. break;
  1418. default:
  1419. {assert(0);}
  1420. }
  1421. return kOkDspRC;
  1422. }
  1423. cmDspRC_t _cmDspScalarPresetRdWr( cmDspCtx_t* ctx, cmDspInst_t* inst, bool storeFl )
  1424. {
  1425. return cmDspVarPresetRdWr(ctx,inst,kValScId,storeFl);
  1426. }
  1427. struct cmDspClass_str* cmScalarClassCons( cmDspCtx_t* ctx )
  1428. {
  1429. cmDspClassSetup(&_cmScalarDC,ctx,"Scalar",
  1430. NULL,
  1431. _cmDspScalarAlloc,
  1432. NULL,
  1433. _cmDspScalarReset,
  1434. NULL,
  1435. _cmDspScalarRecv,
  1436. _cmDspScalarPresetRdWr,
  1437. NULL,
  1438. "Scalar value control.");
  1439. return &_cmScalarDC;
  1440. }
  1441. //==========================================================================================================================================
  1442. enum
  1443. {
  1444. kValTxId,
  1445. kLblTxId
  1446. };
  1447. cmDspClass_t _cmTextDC;
  1448. typedef struct
  1449. {
  1450. cmDspInst_t inst;
  1451. } cmDspText_t;
  1452. cmDspInst_t* _cmDspTextAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1453. {
  1454. cmDspVarArg_t args[] =
  1455. {
  1456. { "val", kValTxId, 0, 0, kInDsvFl | kOutDsvFl | kStrzDsvFl | kReqArgDsvFl | kSendDfltDsvFl, "Current string"},
  1457. { "lbl", kLblTxId, 0, 0, kStrzDsvFl | kOptArgDsvFl, "Label"},
  1458. { NULL, 0, 0, 0, 0 }
  1459. };
  1460. cmDspText_t* p = cmDspInstAlloc(cmDspText_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1461. // create the UI control
  1462. cmDspUiTextCreate(ctx,&p->inst,kValTxId,kLblTxId);
  1463. return &p->inst;
  1464. }
  1465. cmDspRC_t _cmDspTextReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1466. {
  1467. cmDspApplyAllDefaults(ctx,inst);
  1468. return kOkDspRC;
  1469. }
  1470. cmDspRC_t _cmDspTextRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1471. {
  1472. switch( evt->dstVarId )
  1473. {
  1474. case kValTxId:
  1475. cmDspSetEvent(ctx,inst,evt);
  1476. break;
  1477. default:
  1478. {assert(0);}
  1479. }
  1480. return kOkDspRC;
  1481. }
  1482. struct cmDspClass_str* cmTextClassCons( cmDspCtx_t* ctx )
  1483. {
  1484. cmDspClassSetup(&_cmTextDC,ctx,"Text",
  1485. NULL,
  1486. _cmDspTextAlloc,
  1487. NULL,
  1488. _cmDspTextReset,
  1489. NULL,
  1490. _cmDspTextRecv,
  1491. NULL,NULL,
  1492. "Text value control.");
  1493. return &_cmTextDC;
  1494. }
  1495. //==========================================================================================================================================
  1496. enum
  1497. {
  1498. kInMtId,
  1499. kMinMtId,
  1500. kMaxMtId,
  1501. kTimeMtId,
  1502. kLblMtId,
  1503. };
  1504. cmDspClass_t _cmMeterDC;
  1505. typedef struct
  1506. {
  1507. cmDspInst_t inst;
  1508. unsigned updSmpCnt;
  1509. unsigned lastCycleCnt;
  1510. double value;
  1511. unsigned cnt;
  1512. } cmDspMeter_t;
  1513. cmDspInst_t* _cmDspMeterAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1514. {
  1515. cmDspVarArg_t args[] =
  1516. {
  1517. { "in", kInMtId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Current value"},
  1518. { "min", kMinMtId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Minimum value"},
  1519. { "max", kMaxMtId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Maximum value"},
  1520. { "time", kTimeMtId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "UI update time in milliseconds (default:50)"},
  1521. { "label",kLblMtId, 0, 0, kStrzDsvFl, "Label"},
  1522. { NULL, 0, 0, 0, 0 }
  1523. };
  1524. cmDspMeter_t* p = cmDspInstAlloc(cmDspMeter_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1525. cmDspSetDefaultDouble(ctx, &p->inst, kInMtId, 0.0, 0);
  1526. cmDspSetDefaultDouble(ctx, &p->inst, kMinMtId, 0.0, 0);
  1527. cmDspSetDefaultDouble(ctx, &p->inst, kMaxMtId, 0.0, 1);
  1528. cmDspSetDefaultDouble(ctx, &p->inst, kTimeMtId, 0.0, 50.0);
  1529. // create the UI control
  1530. cmDspUiMeterCreate(ctx,&p->inst,kMinMtId,kMaxMtId,kInMtId,kLblMtId);
  1531. return &p->inst;
  1532. }
  1533. cmDspRC_t _cmDspMeterReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1534. {
  1535. cmDspMeter_t* p = (cmDspMeter_t*)inst;
  1536. cmDspApplyAllDefaults(ctx,inst);
  1537. double updateMs = cmDspDouble(inst,kTimeMtId);
  1538. p->updSmpCnt = floor(cmDspSampleRate(ctx) * updateMs / 1000.0);
  1539. p->lastCycleCnt = 0;
  1540. p->cnt = 0;
  1541. p->value = 0;
  1542. return kOkDspRC;
  1543. }
  1544. cmDspRC_t _cmDspMeterExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1545. {
  1546. cmDspMeter_t* p = (cmDspMeter_t*)inst;
  1547. double curVal = p->value;
  1548. bool deltaFl = p->cnt!=0 && curVal != cmDspDouble(inst,kInMtId);
  1549. bool expireFl = (ctx->cycleCnt - p->lastCycleCnt) * cmDspSamplesPerCycle(ctx) > p->updSmpCnt;
  1550. // if the meter value changed and the update time has expired
  1551. if( deltaFl && expireFl )
  1552. {
  1553. cmDspSetDouble(ctx,inst,kInMtId,curVal);
  1554. p->value = 0;
  1555. p->cnt = 0;
  1556. p->lastCycleCnt = ctx->cycleCnt;
  1557. }
  1558. return kOkDspRC;
  1559. }
  1560. cmDspRC_t _cmDspMeterRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1561. {
  1562. cmDspMeter_t* p = (cmDspMeter_t*)inst;
  1563. switch( evt->dstVarId )
  1564. {
  1565. case kInMtId:
  1566. p->value = cmDsvGetDouble(evt->valuePtr);
  1567. ++p->cnt;
  1568. return kOkDspRC;
  1569. case kTimeMtId:
  1570. p->updSmpCnt = floor(cmDspSampleRate(ctx) * cmDsvGetDouble(evt->valuePtr) / 1000.0);
  1571. break;
  1572. }
  1573. return cmDspSetEvent(ctx,inst,evt);
  1574. }
  1575. struct cmDspClass_str* cmMeterClassCons( cmDspCtx_t* ctx )
  1576. {
  1577. cmDspClassSetup(&_cmMeterDC,ctx,"Meter",
  1578. NULL,
  1579. _cmDspMeterAlloc,
  1580. NULL,
  1581. _cmDspMeterReset,
  1582. _cmDspMeterExec,
  1583. _cmDspMeterRecv,
  1584. NULL,NULL,
  1585. "Meter display.");
  1586. return &_cmMeterDC;
  1587. }
  1588. //==========================================================================================================================================
  1589. enum
  1590. {
  1591. kInLbId,
  1592. kAlignLbId
  1593. };
  1594. cmDspClass_t _cmLabelDC;
  1595. typedef struct
  1596. {
  1597. cmDspInst_t inst;
  1598. } cmDspLabel_t;
  1599. cmDspInst_t* _cmDspLabelAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1600. {
  1601. cmDspVarArg_t args[] =
  1602. {
  1603. { "in", kInLbId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "LabelText" },
  1604. { "align",kAlignLbId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Alignment 0=right 1=left 2=center" },
  1605. { NULL, 0, 0, 0, 0 }
  1606. };
  1607. cmDspLabel_t* p = cmDspInstAlloc(cmDspLabel_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1608. cmDspSetDefaultDouble(ctx, &p->inst, kAlignLbId, 0.0, kLeftAlignDuiId);
  1609. // create the UI control
  1610. cmDspUiLabelCreate(ctx,&p->inst,kInLbId,kAlignLbId);
  1611. return &p->inst;
  1612. }
  1613. cmDspRC_t _cmDspLabelReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1614. {
  1615. cmDspApplyAllDefaults(ctx,inst);
  1616. return kOkDspRC;
  1617. }
  1618. cmDspRC_t _cmDspLabelRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1619. {
  1620. return cmDspSetEvent(ctx,inst,evt);
  1621. }
  1622. struct cmDspClass_str* cmLabelClassCons( cmDspCtx_t* ctx )
  1623. {
  1624. cmDspClassSetup(&_cmLabelDC,ctx,"Label",
  1625. NULL,
  1626. _cmDspLabelAlloc,
  1627. NULL,
  1628. _cmDspLabelReset,
  1629. NULL,
  1630. _cmDspLabelRecv,
  1631. NULL,NULL,
  1632. "Label control.");
  1633. return &_cmLabelDC;
  1634. }
  1635. //==========================================================================================================================================
  1636. enum
  1637. {
  1638. kTypBtId,
  1639. kOutBtId,
  1640. kSymBtId,
  1641. kLblBtId,
  1642. kInBtId
  1643. };
  1644. cmDspClass_t _cmButtonDC;
  1645. typedef struct
  1646. {
  1647. cmDspInst_t inst;
  1648. unsigned resetSymId;
  1649. } cmDspButton_t;
  1650. cmDspInst_t* _cmDspButtonAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1651. {
  1652. va_list vl1;
  1653. va_copy(vl1,vl);
  1654. assert( va_cnt >= 1 );
  1655. unsigned typeId = va_arg(vl,unsigned);
  1656. // check buttons should transmit their default values - push buttons should not.
  1657. unsigned sendDfltFl = typeId == kCheckDuiId ? kSendDfltDsvFl : 0;
  1658. cmDspVarArg_t args[] =
  1659. {
  1660. { "typ", kTypBtId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Type" },
  1661. { "out", kOutBtId, 0, 0, kOutDsvFl | kDoubleDsvFl | kOptArgDsvFl | sendDfltFl, "Value"},
  1662. { "sym", kSymBtId, 0, 0, kOutDsvFl | kSymDsvFl | kOptArgDsvFl | sendDfltFl, "Symbol Value"},
  1663. { "label",kLblBtId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "Label"},
  1664. { "in", kInBtId, 0, 0, kInDsvFl | kTypeDsvMask, "Simulate UI"},
  1665. { NULL, 0, 0, 0, 0 }
  1666. };
  1667. cmDspButton_t* p = cmDspInstAlloc(cmDspButton_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl1);
  1668. cmDspSetDefaultDouble(ctx, &p->inst, kOutBtId, 0.0, typeId==kCheckDuiId ? 0.0 : 1.0);
  1669. cmDspSetDefaultSymbol(ctx, &p->inst, kSymBtId, instSymId );
  1670. cmDspSetDefaultStrcz( ctx, &p->inst, kLblBtId, NULL, cmSymTblLabel(ctx->stH,instSymId));
  1671. p->resetSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"_reset");
  1672. // create the UI control
  1673. cmDspUiButtonCreate(ctx,&p->inst,typeId,kOutBtId,kLblBtId);
  1674. return &p->inst;
  1675. }
  1676. cmDspRC_t _cmDspButtonReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1677. {
  1678. cmDspApplyAllDefaults(ctx,inst);
  1679. return kOkDspRC;
  1680. }
  1681. cmDspRC_t _cmDspButtonRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1682. {
  1683. // the 'in' port is the only input port
  1684. // but the UI sends button pushes use kOutBtId - should this be changed?
  1685. assert( evt->dstVarId == kInBtId || evt->dstVarId == kOutBtId );
  1686. // We accept all types at the 'in' port but are only interested
  1687. // in transmitting doubles from the 'out' port.
  1688. if( cmDsvCanConvertFlags( kDoubleDsvFl, evt->valuePtr->flags ) )
  1689. {
  1690. // Redirect events which can be converted to type kDoubleDsvFl
  1691. // to the output port.
  1692. //
  1693. // Convert the event dest var id from the 'kInBtId' to 'kOutBtId'
  1694. // and update the UI with the incoming value
  1695. cmDspSetEventUiId(ctx,inst,evt,kOutBtId);
  1696. }
  1697. // no matter what kind of msg enters the 'in' port send a symbol out the 'sym' port
  1698. if( inst->symId != cmInvalidId )
  1699. cmDspSetSymbol( ctx, inst, kSymBtId, inst->symId );
  1700. return kOkDspRC;
  1701. }
  1702. cmDspRC_t _cmDspButtonPresetRdWr( cmDspCtx_t* ctx, cmDspInst_t* inst, bool storeFl )
  1703. {
  1704. cmDspRC_t rc = kOkDspRC;
  1705. if( cmDspUInt(inst,kTypBtId) == kCheckDuiId )
  1706. rc = cmDspVarPresetRdWr(ctx,inst,kOutBtId,storeFl);
  1707. return rc;
  1708. }
  1709. cmDspRC_t _cmDspButtonSysRecvFunc( cmDspCtx_t* ctx, struct cmDspInst_str* inst, unsigned attrSymId, const cmDspValue_t* value )
  1710. {
  1711. cmDspButton_t* p = (cmDspButton_t*)inst;
  1712. if( attrSymId == p->resetSymId )
  1713. {
  1714. cmDspSetSymbol( ctx, inst, kSymBtId, p->resetSymId );
  1715. cmDspSetDouble(ctx,inst,kOutBtId, cmDspDouble(inst,kOutBtId));
  1716. }
  1717. return kOkDspRC;
  1718. }
  1719. struct cmDspClass_str* cmButtonClassCons( cmDspCtx_t* ctx )
  1720. {
  1721. cmDspClassSetup(&_cmButtonDC,ctx,"Button",
  1722. NULL,
  1723. _cmDspButtonAlloc,
  1724. NULL,
  1725. _cmDspButtonReset,
  1726. NULL,
  1727. _cmDspButtonRecv,
  1728. _cmDspButtonPresetRdWr,
  1729. _cmDspButtonSysRecvFunc,
  1730. "Button control.");
  1731. return &_cmButtonDC;
  1732. }
  1733. //==========================================================================================================================================
  1734. enum
  1735. {
  1736. kLblCbId,
  1737. kSym1CbId,
  1738. kSym0CbId,
  1739. kVal1CbId,
  1740. kVal0CbId,
  1741. kOutCbId,
  1742. kSymCbId,
  1743. kInCbId
  1744. };
  1745. cmDspClass_t _cmCheckboxDC;
  1746. typedef struct
  1747. {
  1748. cmDspInst_t inst;
  1749. unsigned resetSymId;
  1750. unsigned onSymId;
  1751. unsigned offSymId;
  1752. } cmDspCheckbox_t;
  1753. cmDspInst_t* _cmDspCheckboxAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1754. {
  1755. // Check buttons should transmit their default values. Set kSendDfltDsvFl on outputs to send default values.
  1756. cmDspVarArg_t args[] =
  1757. {
  1758. { "label",kLblCbId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "Label"},
  1759. { "sym1", kSym1CbId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "'on' symbol value"},
  1760. { "sym0", kSym0CbId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "'off' symbol value"},
  1761. { "val1", kVal1CbId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "'on' value"},
  1762. { "val0", kVal0CbId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "'off' value"},
  1763. { "out", kOutCbId, 0, 0, kOutDsvFl | kDoubleDsvFl | kOptArgDsvFl | kSendDfltDsvFl, "Value"},
  1764. { "sym", kSymCbId, 0, 0, kOutDsvFl | kSymDsvFl | kSendDfltDsvFl, "Symbol Value"},
  1765. { "in", kInCbId, 0, 0, kInDsvFl | kTypeDsvMask, "Simulate UI"},
  1766. { NULL, 0, 0, 0, 0 }
  1767. };
  1768. cmDspCheckbox_t* p = cmDspInstAlloc(cmDspCheckbox_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1769. p->resetSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"_reset");
  1770. cmDspSetDefaultStrcz( ctx, &p->inst, kSym1CbId, NULL, "on");
  1771. cmDspSetDefaultStrcz( ctx, &p->inst, kSym0CbId, NULL, "off");
  1772. p->onSymId = cmSymTblRegisterSymbol(ctx->stH, cmDspDefaultStrcz(&p->inst,kSym1CbId));
  1773. p->offSymId = cmSymTblRegisterSymbol(ctx->stH, cmDspDefaultStrcz(&p->inst,kSym0CbId));
  1774. bool fl = cmDspDefaultDouble(&p->inst,kOutCbId)!=0;
  1775. cmDspSetDefaultDouble(ctx, &p->inst, kVal1CbId, 0.0, 1.0);
  1776. cmDspSetDefaultDouble(ctx, &p->inst, kVal0CbId, 0.0, 0.0);
  1777. cmDspSetDefaultDouble(ctx, &p->inst, kOutCbId, 0.0, 0.0);
  1778. cmDspSetDefaultSymbol(ctx, &p->inst, kSymCbId, fl ? p->onSymId : p->offSymId );
  1779. cmDspSetDefaultStrcz( ctx, &p->inst, kLblCbId, NULL, cmSymTblLabel(ctx->stH,instSymId));
  1780. // create the UI control
  1781. cmDspUiButtonCreate(ctx,&p->inst,kCheckDuiId,kOutCbId,kLblCbId);
  1782. return &p->inst;
  1783. }
  1784. cmDspRC_t _cmDspCheckboxReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1785. {
  1786. cmDspApplyAllDefaults(ctx,inst);
  1787. return kOkDspRC;
  1788. }
  1789. cmDspRC_t _cmDspCheckboxRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1790. {
  1791. cmDspRC_t rc = kOkDspRC;
  1792. cmDspCheckbox_t* p = (cmDspCheckbox_t*)inst;
  1793. switch( evt->dstVarId )
  1794. {
  1795. case kLblCbId:
  1796. // TODO: we have no function for changing a UI control label.
  1797. return rc;
  1798. case kVal1CbId:
  1799. case kVal0CbId:
  1800. case kSym1CbId:
  1801. case kSym0CbId:
  1802. cmDspSetEvent(ctx,inst,evt);
  1803. if( evt->dstVarId == kSym1CbId )
  1804. p->onSymId = cmSymTblRegisterSymbol(ctx->stH, cmDspStrcz(&p->inst,kSym1CbId));
  1805. if( evt->dstVarId == kSym0CbId )
  1806. p->offSymId = cmSymTblRegisterSymbol(ctx->stH, cmDspStrcz(&p->inst,kSym0CbId));
  1807. return rc;
  1808. }
  1809. // the 'in' port is the only input port
  1810. // but the UI button pushes use kOutCbId.
  1811. assert( evt->dstVarId == kInCbId || evt->dstVarId == kOutCbId );
  1812. // We accept all types at the 'in' port but are only interested
  1813. // in transmitting doubles from the 'out' port and symbols from
  1814. // the 'sym' port.
  1815. if( cmDsvCanConvertFlags( kDoubleDsvFl, evt->valuePtr->flags ) )
  1816. {
  1817. bool checkFl = cmDsvGetDouble(evt->valuePtr)!=0;
  1818. unsigned valId = checkFl ? kVal1CbId : kVal0CbId;
  1819. unsigned symId = checkFl ? p->onSymId : p->offSymId;
  1820. // Redirect events which can be converted to type kDoubleDsvFl
  1821. // to the output port.
  1822. //
  1823. // Convert the event dest var id from the 'kInCbId' to 'kOutCbId'
  1824. // and update the UI with the incoming value
  1825. cmDspEvt_t e;
  1826. cmDspValue_t v;
  1827. cmDspEvtCopy(&e,evt);
  1828. e.valuePtr = &v;
  1829. cmDsvSetDouble(&v,cmDspDouble(inst,valId));
  1830. cmDspSetEventUiId(ctx,inst,evt,kOutCbId);
  1831. cmDspSetSymbol( ctx, inst, kSymCbId, symId);
  1832. }
  1833. return kOkDspRC;
  1834. }
  1835. cmDspRC_t _cmDspCheckboxPresetRdWr( cmDspCtx_t* ctx, cmDspInst_t* inst, bool storeFl )
  1836. {
  1837. return cmDspVarPresetRdWr(ctx,inst,kOutCbId,storeFl);
  1838. }
  1839. cmDspRC_t _cmDspCheckboxSysRecvFunc( cmDspCtx_t* ctx, struct cmDspInst_str* inst, unsigned attrSymId, const cmDspValue_t* value )
  1840. {
  1841. cmDspCheckbox_t* p = (cmDspCheckbox_t*)inst;
  1842. if( attrSymId == p->resetSymId )
  1843. {
  1844. cmDspSetSymbol( ctx, inst, kSymCbId, p->resetSymId );
  1845. cmDspSetDouble(ctx,inst,kOutCbId, cmDspDouble(inst,kOutCbId));
  1846. }
  1847. return kOkDspRC;
  1848. }
  1849. struct cmDspClass_str* cmCheckboxClassCons( cmDspCtx_t* ctx )
  1850. {
  1851. cmDspClassSetup(&_cmCheckboxDC,ctx,"Checkbox",
  1852. NULL,
  1853. _cmDspCheckboxAlloc,
  1854. NULL,
  1855. _cmDspCheckboxReset,
  1856. NULL,
  1857. _cmDspCheckboxRecv,
  1858. _cmDspCheckboxPresetRdWr,
  1859. _cmDspCheckboxSysRecvFunc,
  1860. "Checkbox control.");
  1861. return &_cmCheckboxDC;
  1862. }
  1863. //==========================================================================================================================================
  1864. cmDspClass_t _cmReorderDC;
  1865. typedef struct
  1866. {
  1867. cmDspInst_t inst;
  1868. unsigned portCnt; // count of input ports and count of output ports
  1869. unsigned* execFlArray; // execFlArray[portCnt] - true for ports which should cause obj to generate output
  1870. unsigned* orderArray; // orderArray[portCnt] - port output order map
  1871. } cmDspReorder_t;
  1872. cmDspInst_t* _cmDspReorderAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1873. {
  1874. if( va_cnt < 1 )
  1875. {
  1876. cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"Reorder objects must have arguments.");
  1877. return NULL;
  1878. }
  1879. // the first argument is the count of input ports (which is also the count of output ports)
  1880. int portCnt = va_arg(vl,int);
  1881. if( portCnt < 2 )
  1882. {
  1883. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"Reorder objects must have at least 2 ports.");
  1884. return NULL;
  1885. }
  1886. cmDspVarArg_t args[portCnt*2+1];
  1887. unsigned i;
  1888. int maxLabelCharCnt = 15;
  1889. cmChar_t label[ maxLabelCharCnt+1 ];
  1890. label[maxLabelCharCnt] = 0;
  1891. for(i=0; i<portCnt*2; ++i)
  1892. {
  1893. snprintf(label,maxLabelCharCnt,"%s-%i", (i<portCnt?"in":"out"), i%portCnt);
  1894. unsigned symId = cmSymTblRegisterSymbol(ctx->stH,label);
  1895. args[i].label = cmSymTblLabel(ctx->stH,symId);
  1896. args[i].constId = i;
  1897. args[i].rn = 0;
  1898. args[i].cn = 0;
  1899. args[i].flags = (i<portCnt ? kInDsvFl : kOutDsvFl) | kTypeDsvMask;
  1900. args[i].doc = i<portCnt ? "Any input" : "Any output";
  1901. }
  1902. memset(args+i,0,sizeof(args[0]));
  1903. cmDspReorder_t* p = cmDspInstAlloc(cmDspReorder_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  1904. if( p != NULL )
  1905. {
  1906. p->orderArray = cmLhAllocZ(ctx->lhH,unsigned,portCnt*2);
  1907. p->execFlArray = p->orderArray + portCnt;
  1908. p->portCnt = portCnt;
  1909. if( va_cnt-1 < portCnt )
  1910. cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The reorder constructor must contain an output order map.");
  1911. else
  1912. {
  1913. for(i=0; i<portCnt; ++i)
  1914. {
  1915. int order = va_arg(vl,int);
  1916. if( order >= portCnt )
  1917. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The port order index %i is outside the valid range of 0-%i.",order,portCnt-1);
  1918. else
  1919. p->orderArray[ order ] = i;
  1920. }
  1921. va_cnt -= portCnt+1;
  1922. for(i=0; i<va_cnt; ++i)
  1923. {
  1924. int execPortIdx = va_arg(vl,int);
  1925. if( execPortIdx >= portCnt )
  1926. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The port exec index %i is outside the valid range of 0-%i.",execPortIdx,portCnt-1);
  1927. else
  1928. p->execFlArray[ execPortIdx ] = true;
  1929. }
  1930. }
  1931. }
  1932. return &p->inst;
  1933. }
  1934. cmDspRC_t _cmDspReorderFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1935. {
  1936. cmDspReorder_t* p = (cmDspReorder_t*)inst;
  1937. cmLhFree(ctx->lhH,p->orderArray);
  1938. return kOkDspRC;
  1939. }
  1940. cmDspRC_t _cmDspReorderRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  1941. {
  1942. cmDspRC_t rc = kOkDspRC;
  1943. cmDspReorder_t* p = (cmDspReorder_t*)inst;
  1944. cmDspSetEvent(ctx,inst,evt);
  1945. if( evt->dstVarId < p->portCnt )
  1946. if( p->execFlArray[evt->dstVarId] )
  1947. {
  1948. unsigned i;
  1949. for(i=0; i<p->portCnt; ++i)
  1950. {
  1951. unsigned inVarId = p->orderArray[i];
  1952. unsigned outVarId = inVarId + p->portCnt;
  1953. const cmDspVar_t* varPtr = cmDspVarIdToCPtr(inst, inVarId );
  1954. assert(varPtr != NULL);
  1955. if((rc = cmDspValueSet(ctx, inst, outVarId, &varPtr->value, 0 )) != kOkDspRC )
  1956. break;
  1957. }
  1958. }
  1959. return rc;
  1960. }
  1961. struct cmDspClass_str* cmReorderClassCons( cmDspCtx_t* ctx )
  1962. {
  1963. cmDspClassSetup(&_cmReorderDC,ctx,"Reorder",
  1964. NULL,
  1965. _cmDspReorderAlloc,
  1966. _cmDspReorderFree,
  1967. NULL,
  1968. NULL,
  1969. _cmDspReorderRecv,
  1970. NULL,NULL,
  1971. "Reorder value control.");
  1972. return &_cmReorderDC;
  1973. }
  1974. //==========================================================================================================================================
  1975. enum
  1976. {
  1977. kDirFnId,
  1978. kPatFnId,
  1979. kValFnId,
  1980. kSendFnId
  1981. };
  1982. cmDspClass_t _cmFnameDC;
  1983. typedef struct
  1984. {
  1985. cmDspInst_t inst;
  1986. } cmDspFname_t;
  1987. // Pattern string for HTML and image files:
  1988. // "HTML Files (*.html)\tImage Files (*.{bmp,gif,jpg,png})"
  1989. // The va_list must include 3 args:
  1990. // A pointer to a string referning to a default filename or directory or NULL.
  1991. // A pointer to a string referring to a pattern string or NULL.
  1992. // A bool to set the 'dirFl'.
  1993. cmDspInst_t* _cmDspFnameAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  1994. {
  1995. cmDspVarArg_t args[] =
  1996. {
  1997. { "dir", kDirFnId, 0, 0, kInDsvFl | kReqArgDsvFl | kBoolDsvFl, "Dir=true Filename=false" },
  1998. { "pat", kPatFnId, 0, 0, kInDsvFl | kOptArgDsvFl | kStrzDsvFl, "File pattern string (e.g. HTML Files (*.html)\tImage Files (*.{bmp,gif,jpg,png}))" },
  1999. { "out", kValFnId, 0, 0, kOutDsvFl | kOptArgDsvFl | kStrzDsvFl, "Current file or directory name." },
  2000. { "send", kSendFnId, 0, 0, kInDsvFl | kTypeDsvMask, "Send file name on any msg."},
  2001. { NULL, 0, 0, 0, 0 }
  2002. };
  2003. cmDspFname_t* p = cmDspInstAlloc(cmDspFname_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  2004. cmDspSetDefaultBool( ctx, &p->inst, kDirFnId, false, false);
  2005. cmDspSetDefaultStrcz(ctx, &p->inst, kPatFnId, NULL, "All Files (*.*)");
  2006. cmDspSetDefaultStrcz(ctx, &p->inst, kValFnId, NULL, cmFsUserDir());
  2007. cmDspUiFnameCreate(ctx,&p->inst,kValFnId,kPatFnId,kDirFnId);
  2008. return &p->inst;
  2009. }
  2010. cmDspRC_t _cmDspFnameReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2011. {
  2012. cmDspApplyAllDefaults(ctx,inst);
  2013. return kOkDspRC;
  2014. }
  2015. cmDspRC_t _cmDspFnameRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2016. {
  2017. cmDspSetEvent(ctx,inst,evt);
  2018. switch( evt->dstVarId )
  2019. {
  2020. case kSendFnId:
  2021. cmDspSetStrcz(ctx, inst, kValFnId, cmDspStrcz(inst,kValFnId) );
  2022. break;
  2023. case kDirFnId:
  2024. case kPatFnId:
  2025. case kValFnId:
  2026. break;
  2027. default:
  2028. {assert(0);}
  2029. }
  2030. return kOkDspRC;
  2031. }
  2032. struct cmDspClass_str* cmFnameClassCons( cmDspCtx_t* ctx )
  2033. {
  2034. cmDspClassSetup(&_cmFnameDC,ctx,"Fname",
  2035. NULL,
  2036. _cmDspFnameAlloc,
  2037. NULL,
  2038. _cmDspFnameReset,
  2039. NULL,
  2040. _cmDspFnameRecv,
  2041. NULL,NULL,
  2042. "File or directory chooser control.");
  2043. return &_cmFnameDC;
  2044. }
  2045. //==========================================================================================================================================
  2046. cmDspClass_t _cmMsgListDC;
  2047. typedef struct
  2048. {
  2049. cmDspInst_t inst;
  2050. cmJsonH_t jsH; // current JSON tree
  2051. cmJsonNode_t* np; // ptr to JSON array
  2052. unsigned colCnt; // number of elements in the JSON sub-arrays (rows)
  2053. cmJsonH_t dfltJsH; // default JSON tree (the default JSON array node ptr (np) is stored in the var array but we must also maintain the assoc'd JSON handle othwerwise the pointer will not be valid)
  2054. unsigned* typeIdArray; // JSON types for each column size: typeIdArray[ colCnt ]
  2055. unsigned symColCnt; // same as number of elements of typeIdArray[] == kStringTId
  2056. unsigned symRowCnt; // same as cmJsonChildCount(p->np)
  2057. unsigned* symM; // symM[symRowCnt,symColCnt] symbol matrix
  2058. } cmDspMsgList_t;
  2059. // create a matrix to hold the symbol id's associated with any string values
  2060. cmDspRC_t _cmDspMsgListLoadSymbolMtx( cmDspCtx_t* ctx, cmDspMsgList_t* p )
  2061. {
  2062. assert(cmJsonIsArray(p->np));
  2063. unsigned i,j,k;
  2064. // remove any existing symbols
  2065. if( p->symM != NULL )
  2066. for(i=0; i<p->symColCnt*p->symRowCnt; ++i)
  2067. if( p->symM[i] != cmInvalidId )
  2068. cmSymTblRemove(ctx->stH,p->symM[i]);
  2069. // reallocate the symbol matrix
  2070. p->symRowCnt = cmJsonChildCount(p->np);
  2071. p->symM = cmLhResizeN(ctx->lhH,unsigned,p->symM,p->symRowCnt*p->symColCnt);
  2072. // for each row in the JSON array
  2073. for(i=0; i<p->symRowCnt; ++i)
  2074. {
  2075. const cmJsonNode_t* cnp = cmJsonArrayElementC(p->np,i);
  2076. // for each column in row whose data type is a string
  2077. for(j=0,k=0; k<p->symColCnt; ++j)
  2078. if( p->typeIdArray[j] == kStringTId )
  2079. {
  2080. const cmJsonNode_t* vnp = cmJsonArrayElementC(cnp,j);
  2081. unsigned idx = k*p->symRowCnt + i;
  2082. const cmChar_t* text;
  2083. // register the string with the symbol table
  2084. if((text = vnp->u.stringVal) != NULL )
  2085. p->symM[ idx ] = cmSymTblRegisterStaticSymbol(ctx->stH,text);
  2086. else
  2087. p->symM[ idx ] = cmInvalidId;
  2088. ++k;
  2089. }
  2090. }
  2091. return kOkDspRC;
  2092. }
  2093. // Load a JSON file and set the supplied cmJsonH_t handle.
  2094. cmDspRC_t _cmDspMsgListLoadFile( cmDspCtx_t* ctx, cmErr_t* err, const cmChar_t* rsrcLabel, const cmChar_t* fn, cmJsonH_t* hp, unsigned* colCntPtr, cmJsonNode_t** npp )
  2095. {
  2096. cmDspRC_t rc = kOkDspRC; //
  2097. cmJsonNode_t* np = NULL; //
  2098. cmJsonH_t jsH = cmJsonNullHandle; //
  2099. *hp = cmJsonNullHandle;
  2100. *colCntPtr = 0;
  2101. *npp = NULL;
  2102. // if no file name was given ...
  2103. if( fn==NULL || strlen(fn)==0 )
  2104. {
  2105. jsH = ctx->rsrcJsH; // ... use the rsrc file
  2106. if( cmJsonIsValid(ctx->rsrcJsH) == false )
  2107. return cmErrMsg(err,kJsonFailDspRC,"No JSON cfg resource exists for this DSP program.");
  2108. fn = NULL;
  2109. }
  2110. else
  2111. {
  2112. if( cmJsonInitializeFromFile(&jsH,fn,ctx->cmCtx) != kOkJsRC )
  2113. return cmErrMsg(err,kJsonFailDspRC,"The msg list JSON file load failed on '%s'.",fn);
  2114. }
  2115. // find the array named by rsrcLabel
  2116. if((np = cmJsonFindValue(jsH,rsrcLabel,NULL,cmInvalidId)) == NULL)
  2117. return cmErrMsg(err,kJsonFailDspRC,"The msg list JSON tree does not have an array named '%s'.",rsrcLabel);
  2118. // be sure the msg list really is an array
  2119. if( cmJsonIsArray(np) == false )
  2120. return cmErrMsg(err,kJsonFailDspRC,"The msg list JSON element named '%s' is not an array.", rsrcLabel);
  2121. if( fn == NULL )
  2122. fn = "<resource file>";
  2123. // count of elements in the array
  2124. unsigned n = cmJsonChildCount(np);
  2125. unsigned m = 0;
  2126. unsigned i,j;
  2127. // for each line in the array
  2128. for(i=0; i<n; ++i)
  2129. {
  2130. const cmJsonNode_t* cnp;
  2131. // get the ith sub-array (row)
  2132. if((cnp = cmJsonArrayElementC(np,i)) != NULL )
  2133. {
  2134. // verify that it is an array
  2135. if( cmJsonIsArray(cnp ) == false )
  2136. return cmErrMsg(err,kJsonFailDspRC,"The msg list JSON element in '%s' at index %i is not an array.",fn,i);
  2137. // track the number of elements (columns) per row
  2138. unsigned q = cmJsonChildCount(cnp);
  2139. // if this is the first row then use it to set the valid column count
  2140. if( i==0 )
  2141. m = q;
  2142. else
  2143. {
  2144. if( m != q )
  2145. return cmErrMsg(err,kJsonFailDspRC,"The msg list sub-array at index %i has a different number of elements than the preceding sub-arrays in '%s'.",i,fn);
  2146. }
  2147. }
  2148. }
  2149. //
  2150. // determine and validate the column types
  2151. //
  2152. unsigned typeIdArray[m];
  2153. // for each row
  2154. for(i=0; i<n; ++i)
  2155. {
  2156. const cmJsonNode_t* cnp = cmJsonArrayElementC(np,i);
  2157. // for each column
  2158. for(j=0; j<m; ++j)
  2159. {
  2160. const cmJsonNode_t* sap = cmJsonArrayElementC(cnp,j);
  2161. unsigned typeId = sap->typeId & kMaskTId;
  2162. // the first row sets the expected type id for each column
  2163. switch(i)
  2164. {
  2165. case 0:
  2166. if( typeId != kStringTId )
  2167. return cmErrMsg(err,kJsonFailDspRC,"The first row of a msg list (%s) file must contain string elements which set the colum labels.",rsrcLabel);
  2168. break;
  2169. case 1:
  2170. typeIdArray[j] = typeId;
  2171. break;
  2172. default:
  2173. {
  2174. // if the type is a string then it can only
  2175. // match if the column is a string or null
  2176. // so we assume an error
  2177. if( typeId == kStringTId )
  2178. rc = kJsonFailDspRC;
  2179. switch(typeIdArray[j])
  2180. {
  2181. case kStringTId:
  2182. if( typeId != kStringTId && typeId != kNullTId)
  2183. return cmErrMsg(err,kJsonFailDspRC,"The msg list element at row index %i column index %i cannot be converted to a string in '%s'.",i,j,fn);
  2184. rc = kOkDspRC; // clear the assummed error
  2185. break;
  2186. case kNullTId: // null can be converted to anything
  2187. typeIdArray[j] = typeId;
  2188. break;
  2189. case kIntTId:
  2190. if( typeId == kRealTId ) // ints may be promoted to reals
  2191. typeIdArray[j] = kRealTId;
  2192. break;
  2193. case kRealTId:
  2194. break;
  2195. case kFalseTId:
  2196. case kTrueTId: // bools may be promoted to ints or reals
  2197. if( typeId == kIntTId || typeId == kRealTId )
  2198. typeIdArray[j] = typeId;
  2199. break;
  2200. default:
  2201. return cmErrMsg(err,kJsonFailDspRC,"The msg list element at row index %i column index %i is not a string,int,real,bool, or null type in '%s'.",i,j,fn);
  2202. }
  2203. if( rc != kOkDspRC )
  2204. return cmErrMsg(err,kJsonFailDspRC,"The string msg list element at row index %i column index %i cannot be converted to the column type in '%s'.",i,j,fn);
  2205. } // end dflt
  2206. } // end switch
  2207. } // end row
  2208. } // end list
  2209. // VERY TRICKY - store the column type id's in the label columns type id.
  2210. // This is stupid but safe because the column type id's are known to be set to kStringTId.
  2211. cmJsonNode_t* lnp = np->u.childPtr->u.childPtr;
  2212. for(i=0; i<m; ++i,lnp=lnp->siblingPtr)
  2213. lnp->typeId = typeIdArray[i];
  2214. *hp = fn==NULL ? cmJsonNullHandle : jsH;
  2215. *npp = np;
  2216. *colCntPtr = m;
  2217. return rc;
  2218. }
  2219. // use the JSON list labels to setup the cmDspVarArg_t records associated with each msg output var.
  2220. cmDspMsgList_t* _cmDspMsgListCons( cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, const cmDspVarArg_t* args, cmJsonH_t jsH, unsigned colCnt, cmJsonNode_t* np, unsigned typeIdArray[], unsigned va_cnt, va_list vl )
  2221. {
  2222. unsigned i,j;
  2223. unsigned fixedArgCnt = 0;
  2224. for(i=0; args[i].label != NULL; ++i)
  2225. ++fixedArgCnt;
  2226. cmDspVarArg_t a[ fixedArgCnt + colCnt + 1 ];
  2227. // copy the fixed arg's into the first fixedArgCnt ele's of a[]
  2228. for(j=0; j<fixedArgCnt; ++j)
  2229. a[j] = args[j];
  2230. // remove the label row from the JSON array
  2231. cmJsonNode_t* lnp = np->u.childPtr; // store the pointer to the label row
  2232. //np->u.childPtr = lnp->siblingPtr; // remove the labels from the array
  2233. // for each column
  2234. for(i=0; i<colCnt; ++i,++j)
  2235. {
  2236. cmJsonNode_t* lp = cmJsonArrayElement(lnp,i);
  2237. // store a pointer to the label
  2238. a[j].label = lp->u.stringVal;
  2239. a[j].constId = j;
  2240. a[j].rn = 0;
  2241. a[j].cn = 0;
  2242. a[j].flags = kOutDsvFl;
  2243. a[j].doc = "Msg output";
  2244. typeIdArray[i] = lp->typeId;
  2245. // convert the JSON type to a DSV type
  2246. switch( lp->typeId )
  2247. {
  2248. case kNullTId: a[j].flags |= kUIntDsvFl; break;
  2249. case kIntTId: a[j].flags |= kIntDsvFl; break;
  2250. case kRealTId: a[j].flags |= kDoubleDsvFl; break;
  2251. case kTrueTId: a[j].flags |= kBoolDsvFl; break;
  2252. case kFalseTId: a[j].flags |= kBoolDsvFl; break;
  2253. case kStringTId: a[j].flags |= kSymDsvFl; break; // strings are treated as symbols (UInt)
  2254. default:
  2255. { assert(0); }
  2256. }
  2257. // undo the tricky bit with the label types
  2258. lp->typeId = kStringTId;
  2259. }
  2260. // set the null sentinel at the end of the arg array
  2261. memset(a + j,0,sizeof(cmDspVarArg_t));
  2262. return (cmDspMsgList_t*)cmDspInstAlloc(cmDspMsgList_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl);
  2263. }
  2264. cmDspRC_t _cmDspMsgListReload( cmDspCtx_t* ctx, cmDspMsgList_t* p, const cmChar_t* rsrcLabel, const cmChar_t* fn )
  2265. {
  2266. cmDspRC_t rc = kOkDspRC;
  2267. cmJsonH_t jsH = cmJsonNullHandle;
  2268. unsigned colCnt = 0;
  2269. cmJsonNode_t* np = NULL;
  2270. // load the file
  2271. if((rc = _cmDspMsgListLoadFile(ctx,&p->inst.classPtr->err,rsrcLabel,fn,&jsH,&colCnt,&np)) != kOkDspRC )
  2272. return cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"The msg list file '%s' load failed.",fn);
  2273. // verify that the col count is correct
  2274. if( colCnt != p->colCnt )
  2275. return cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"The column count (%i) of msg list file '%s does not match the msg list column count %i.",colCnt,p->colCnt);
  2276. unsigned fixArgCnt = p->inst.varCnt - p->colCnt;
  2277. cmJsonNode_t* lnp = np->u.childPtr->u.childPtr;
  2278. unsigned i,j;
  2279. for(i=0,j=fixArgCnt; i<colCnt; ++i,++j, lnp=lnp->siblingPtr)
  2280. {
  2281. const cmChar_t* labelStr = cmSymTblLabel(ctx->stH,p->inst.varArray[j].symId);
  2282. // the labels of the new file must match the labels of the previous file
  2283. if( strcmp(lnp->u.stringVal,labelStr) )
  2284. return cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"The msg list file '%s' label '%s' does not match the msg list label '%s' in column index %i.",fn,labelStr,lnp->u.stringVal,i);
  2285. // if the msg column is a string ....
  2286. if( (p->inst.varArray[j].flags & kTypeDsvMask) == kStrzDsvFl )
  2287. {
  2288. // ... then the file column must also be a string or null
  2289. if( lnp->typeId != kStringTId && np->typeId != kNullTId )
  2290. return cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"The data type of msg list file '%s' column index %i must be a string or null.",fn,i);
  2291. }
  2292. else // otherwie if the msg column is a number ...
  2293. {
  2294. // ... then the file type can't be a string
  2295. if( lnp->typeId == kStringTId )
  2296. return cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"The data type of msg list file '%s' column index %i cannot be a string.",fn,i);
  2297. // TODO: maybe there are other type conversions to check for here.
  2298. }
  2299. // reset the typeid of the labels
  2300. // (this is a cleanup from the tricky bit at the end of _cmDspListLoadFile())
  2301. lnp->typeId = kStringTId;
  2302. }
  2303. if( cmJsonIsValid( jsH ) )
  2304. {
  2305. if( cmHandlesAreNotEqual(p->jsH,p->dfltJsH) && cmHandlesAreNotEqual(p->jsH,ctx->rsrcJsH) )
  2306. cmJsonFinalize(&p->jsH);
  2307. p->jsH = jsH;
  2308. p->np = np;
  2309. _cmDspMsgListLoadSymbolMtx(ctx,p);
  2310. }
  2311. return rc;
  2312. }
  2313. enum
  2314. {
  2315. kRsrcMlId,
  2316. kFnMlId,
  2317. kSelMlId,
  2318. kListMlId,
  2319. kCntMlId,
  2320. kOutBaseMlId // identify the first output port
  2321. };
  2322. cmDspInst_t* _cmDspMsgListAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  2323. {
  2324. va_list vl2;
  2325. cmDspVarArg_t args[] =
  2326. {
  2327. { "rsrc", kRsrcMlId, 0, 0, kInDsvFl | kReqArgDsvFl | kStrzDsvFl, "Msg list resource label"},
  2328. { "fn", kFnMlId, 0, 0, kInDsvFl | kOptArgDsvFl | kStrzDsvFl, "Msg list file name" },
  2329. { "sel", kSelMlId, 0, 0, kOutDsvFl | kInDsvFl | kOptArgDsvFl | kUIntDsvFl, "Current selection index" },
  2330. { "list", kListMlId, 0, 0, kInDsvFl | kJsonDsvFl, "Msg list data as a JSON array"},
  2331. { "cnt", kCntMlId, 0, 0, kOutDsvFl | kSendDfltDsvFl | kUIntDsvFl , "Count of elements."},
  2332. { NULL, 0, 0, 0, 0 }
  2333. };
  2334. va_copy(vl2,vl);
  2335. if( va_cnt < 1 )
  2336. {
  2337. cmErrMsg(&classPtr->err,kVarArgParseFailDspRC,"The message list constructor must contain at least two arguments.");
  2338. goto errLabel;
  2339. }
  2340. const cmChar_t* rsrcLabel = va_arg(vl,const char*);
  2341. const cmChar_t* fn = va_cnt>1 ? va_arg(vl,const char*) : NULL;
  2342. unsigned colCnt = 0;
  2343. cmJsonH_t jsH = cmJsonNullHandle;
  2344. cmDspMsgList_t* p;
  2345. cmJsonNode_t* np;
  2346. cmDspRC_t rc;
  2347. // be sure the rsrc label contains a valid string
  2348. if( rsrcLabel==NULL || strlen(rsrcLabel)==0 )
  2349. {
  2350. cmErrMsg(&classPtr->err,kVarArgParseFailDspRC,"No msg list resource label was given.");
  2351. goto errLabel;
  2352. }
  2353. // load and validate the JSON file
  2354. if((rc = _cmDspMsgListLoadFile(ctx,&classPtr->err,rsrcLabel,fn,&jsH,&colCnt,&np)) == kOkDspRC )
  2355. {
  2356. unsigned i;
  2357. unsigned typeIdArray[colCnt];
  2358. // allocate the instance
  2359. if((p = _cmDspMsgListCons(ctx,classPtr,storeSymId,instSymId,id,args,jsH,colCnt, np, typeIdArray, va_cnt, vl2 )) != NULL )
  2360. {
  2361. p->jsH = jsH;
  2362. p->np = np;
  2363. p->dfltJsH = jsH;
  2364. p->colCnt = colCnt;
  2365. p->typeIdArray = cmLhAllocZ( ctx->lhH, unsigned, colCnt );
  2366. memcpy(p->typeIdArray,typeIdArray,sizeof(p->typeIdArray[0])*colCnt);
  2367. p->symColCnt = 0;
  2368. for(i=0; i<p->colCnt; ++i)
  2369. if( p->typeIdArray[i] == kStringTId )
  2370. ++p->symColCnt;
  2371. _cmDspMsgListLoadSymbolMtx(ctx,p);
  2372. cmDspSetDefaultStrcz( ctx,&p->inst, kRsrcMlId,NULL, rsrcLabel); // rsrc label
  2373. cmDspSetDefaultStrcz( ctx,&p->inst, kFnMlId, NULL, fn); // file name var
  2374. cmDspSetDefaultJson( ctx,&p->inst, kListMlId,NULL, np); // default tree
  2375. cmDspSetDefaultUInt( ctx,&p->inst, kSelMlId, 0, 0); // selection
  2376. cmDspSetDefaultUInt( ctx,&p->inst, kCntMlId, 0, p->symRowCnt);
  2377. // if there is only one column then signal the UI to create a menu button rather
  2378. // than a list by setting the height to zero.
  2379. unsigned height = p->colCnt == 1 ? 0 : 5;
  2380. // create the list UI element
  2381. cmDspUiMsgListCreate(ctx, &p->inst, height, kListMlId, kSelMlId );
  2382. return &p->inst;
  2383. }
  2384. }
  2385. errLabel:
  2386. return NULL;
  2387. }
  2388. cmDspRC_t _cmDspMsgListFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2389. {
  2390. cmDspMsgList_t* p = (cmDspMsgList_t*)inst;
  2391. // be careful not release the ctx->rsrcJsH handle if is happens to be the p->dflJsH
  2392. if( cmJsonIsValid(p->dfltJsH) && cmHandlesAreNotEqual(p->jsH,p->dfltJsH) && cmHandlesAreNotEqual(p->dfltJsH,ctx->rsrcJsH))
  2393. if( cmJsonFinalize(&p->dfltJsH) != kOkJsRC )
  2394. cmDspInstErr(ctx,inst,kJsonFailDspRC,"JSON default tree finalize failed.");
  2395. // be careful not to release the ctx->rsrcJsH handle if it happens to be the p->jsH
  2396. if( cmJsonIsValid(p->jsH) && cmHandlesAreNotEqual(p->dfltJsH,ctx->rsrcJsH) )
  2397. if( cmJsonFinalize( &p->jsH ) != kOkJsRC )
  2398. cmDspInstErr(ctx,inst,kJsonFailDspRC,"JSON finalization failed.");
  2399. cmLhFree(ctx->lhH,p->typeIdArray);
  2400. p->typeIdArray = NULL;
  2401. cmLhFree(ctx->lhH,p->symM);
  2402. p->symM = NULL;
  2403. return kOkDspRC;
  2404. }
  2405. cmDspRC_t _cmDspMsgListReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2406. {
  2407. cmDspApplyAllDefaults(ctx,inst);
  2408. return kOkDspRC;
  2409. }
  2410. cmDspRC_t _cmDspMsgOnSel( cmDspCtx_t* ctx, cmDspMsgList_t* p, unsigned rowIdx )
  2411. {
  2412. cmDspRC_t rc = kOkDspRC;
  2413. unsigned i,j;
  2414. unsigned symId;
  2415. const cmJsonNode_t* cnp = cmJsonArrayElementC(p->np,rowIdx);
  2416. assert( cnp != NULL );
  2417. // for each output variable (column)
  2418. for(i=0,j=0; i<p->colCnt; ++i)
  2419. {
  2420. unsigned varId = kOutBaseMlId + i; // variable id
  2421. const cmJsonNode_t* np = cmJsonArrayElementC(cnp,i); // json row array
  2422. assert( np != NULL );
  2423. switch( p->typeIdArray[i] )
  2424. {
  2425. case kNullTId:
  2426. break;
  2427. case kIntTId:
  2428. {
  2429. int v;
  2430. if( cmJsonIntValue(np,&v) != kOkJsRC )
  2431. { assert(0); }
  2432. rc = cmDspSetInt(ctx,&p->inst,varId,v);
  2433. }
  2434. break;
  2435. case kRealTId:
  2436. {
  2437. double v;
  2438. if( cmJsonRealValue(np,&v) != kOkJsRC )
  2439. { assert(0); }
  2440. rc = cmDspSetDouble(ctx,&p->inst,varId,v);
  2441. }
  2442. break;
  2443. case kTrueTId:
  2444. case kFalseTId:
  2445. {
  2446. bool v;
  2447. if( cmJsonBoolValue(np,&v) != kOkJsRC )
  2448. { assert(0); }
  2449. rc = cmDspSetBool(ctx,&p->inst,varId,v);
  2450. }
  2451. break;
  2452. case kStringTId:
  2453. {
  2454. assert( j < p->symColCnt );
  2455. if((symId = p->symM[ (j*p->symRowCnt) + rowIdx ]) != cmInvalidId )
  2456. if((rc = cmDspSetSymbol(ctx,&p->inst,varId,symId )) != kOkDspRC )
  2457. break;
  2458. ++j;
  2459. }
  2460. break;
  2461. default:
  2462. { assert(0); }
  2463. } // end switch
  2464. } // end for
  2465. return rc;
  2466. }
  2467. cmDspRC_t _cmDspMsgListRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2468. {
  2469. cmDspRC_t rc = kOkDspRC;
  2470. cmDspMsgList_t* p = (cmDspMsgList_t*)inst;
  2471. switch( evt->dstVarId )
  2472. {
  2473. case kRsrcMlId:
  2474. {
  2475. const cmChar_t* fn = cmDspStrcz(inst,kFnMlId);
  2476. const cmChar_t* rsrcLabel = cmDsvStrz(evt->valuePtr);
  2477. if( rsrcLabel != NULL )
  2478. {
  2479. if((rc = _cmDspMsgListReload(ctx,p,rsrcLabel,fn)) != kOkDspRC )
  2480. return cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"The msg list file '%s' load failed.",fn);
  2481. cmDspSetEvent(ctx,inst,evt);
  2482. cmDspSetJson(ctx,inst,kListMlId,p->np);
  2483. cmDspSetUInt(ctx,inst,kSelMlId,0);
  2484. cmDspSetUInt(ctx,inst,kCntMlId,p->symRowCnt);
  2485. }
  2486. }
  2487. break;
  2488. case kFnMlId:
  2489. // Store the new file name.
  2490. // A new file will not be loaded until the next rsrc label is received.
  2491. cmDspSetEvent(ctx,inst,evt);
  2492. break;
  2493. case kListMlId:
  2494. break;
  2495. case kSelMlId:
  2496. {
  2497. unsigned rowIdx = cmDsvGetUInt(evt->valuePtr);
  2498. assert( rowIdx < p->symRowCnt);
  2499. // set the current selection variable
  2500. if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
  2501. rc = _cmDspMsgOnSel(ctx,p,rowIdx);
  2502. } //end case
  2503. break;
  2504. }
  2505. return rc;
  2506. }
  2507. cmDspRC_t _cmDspMsgListPresetRdWr( cmDspCtx_t* ctx, cmDspInst_t* inst, bool storeFl )
  2508. {
  2509. cmDspRC_t rc = kOkDspRC;
  2510. cmDspMsgList_t* p = (cmDspMsgList_t*)inst;
  2511. if((rc = cmDspVarPresetRdWr(ctx,inst,kSelMlId,storeFl)) == kOkDspRC )
  2512. {
  2513. if( !storeFl )
  2514. rc = _cmDspMsgOnSel(ctx,p, cmDspUInt(inst,kSelMlId) );
  2515. }
  2516. return rc;
  2517. }
  2518. struct cmDspClass_str* cmMsgListClassCons( cmDspCtx_t* ctx )
  2519. {
  2520. cmDspClassSetup(&_cmMsgListDC,ctx,"MsgList",
  2521. NULL,
  2522. _cmDspMsgListAlloc,
  2523. _cmDspMsgListFree,
  2524. _cmDspMsgListReset,
  2525. NULL,
  2526. _cmDspMsgListRecv,
  2527. _cmDspMsgListPresetRdWr,
  2528. NULL,
  2529. "Message list selection control.");
  2530. return &_cmMsgListDC;
  2531. }
  2532. //==========================================================================================================================================
  2533. enum
  2534. {
  2535. kLenWtId,
  2536. kShapeWtId,
  2537. kFnWtId,
  2538. kLoopWtId,
  2539. kBegWtId,
  2540. kEndWtId,
  2541. kCmdWtId,
  2542. kOtWtId,
  2543. kGainWtId,
  2544. kPhsWtId,
  2545. kOutWtId,
  2546. kCntWtId,
  2547. kFIdxWtId,
  2548. kDoneWtId
  2549. };
  2550. enum
  2551. {
  2552. kSilenceWtId, // 0
  2553. kFileWtId, // 1
  2554. kWhiteWtId, // 2
  2555. kPinkWtId, // 3
  2556. kSineWtId, // 4
  2557. kCosWtId, // 5
  2558. kSqrWtId, // 6
  2559. kTriWtId, // 7
  2560. kSawWtId, // 8
  2561. kPulseWtId, // 9
  2562. kImpulseWtId, // 10
  2563. kPhasorWtId, // 11
  2564. kShapeWtCnt
  2565. };
  2566. cmDspClass_t _cmWaveTableDC;
  2567. typedef struct
  2568. {
  2569. cmDspInst_t inst;
  2570. cmSample_t* wt; // wave table memory
  2571. unsigned wti; // next location to write samples into the wavetable
  2572. unsigned wtn; // count of empty samples (avail for writing over) in the wavetable.
  2573. unsigned fi; // absolute index into the file of the next sample to read
  2574. unsigned fn; // length of the file in samples
  2575. unsigned cfi; // absolute index into the file of the beginning of the current audio vector
  2576. unsigned cfn; // when cfi >= cfn and doneFl is set then the 'done' msg is sent
  2577. unsigned loopCnt; // current loop count
  2578. bool doneFl; // the wave table source is exhausted
  2579. cmAudioFileH_t afH; // current audio file handle
  2580. int nxtBegSmpIdx; // the beg/end sample index to use with the next filename to arrive at port 'fn'
  2581. int nxtEndSmpIdx; //
  2582. cmThreadH_t thH;
  2583. bool loadFileFl;
  2584. cmDspCtx_t* ctx;
  2585. cmSample_t phsOffs;
  2586. cmSample_t phsLast;
  2587. unsigned onSymId;
  2588. unsigned offSymId;
  2589. unsigned doneSymId;
  2590. bool useThreadFl;
  2591. unsigned minAfIndexRptCnt; // min count of audio samples between transmitting the current audio file index
  2592. unsigned afIndexRptCnt; // current audio file sample index count
  2593. } cmDspWaveTable_t;
  2594. bool _cmDspWaveTableThreadFunc( void* param);
  2595. cmDspInst_t* _cmDspWaveTableAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  2596. {
  2597. cmDspVarArg_t args[] =
  2598. {
  2599. { "len", kLenWtId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Wave table length in samples" },
  2600. { "shape", kShapeWtId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Wave shape 0=silent 1=file 2=white 3=pink 4=sine, 5=cos 6=sqr 7=tri 8=saw 9=pulse 10=impulse 11=phasor" },
  2601. { "fn", kFnWtId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "Optional audio file name" },
  2602. { "loop", kLoopWtId, 0, 0, kInDsvFl | kIntDsvFl | kOptArgDsvFl, "-1=loop forever >0=loop count (dflt:-1)"},
  2603. { "beg", kBegWtId, 0, 0, kInDsvFl | kIntDsvFl | kOptArgDsvFl, "File begin sample index" },
  2604. { "end", kEndWtId, 0, 0, kInDsvFl | kIntDsvFl | kOptArgDsvFl, "File end sample index (-1=play all)" },
  2605. { "cmd", kCmdWtId, 0, 0, kInDsvFl | kSymDsvFl | kOptArgDsvFl, "Command: on off"},
  2606. { "ot", kOtWtId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Overtone count"},
  2607. { "gain", kGainWtId, 0, 0, kInDsvFl | kDoubleDsvFl|kOptArgDsvFl, "Gain"},
  2608. { "phs", kPhsWtId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Driving phase" },
  2609. { "out", kOutWtId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output" },
  2610. { "cnt", kCntWtId, 0, 0, kOutDsvFl | kIntDsvFl, "Loop count event."},
  2611. { "fidx", kFIdxWtId, 0, 0, kOutDsvFl | kUIntDsvFl, "Current audio file index."},
  2612. { "done", kDoneWtId, 0, 0, kOutDsvFl | kSymDsvFl, "'done' sent after last loop."},
  2613. { NULL, 0, 0, 0, 0 }
  2614. };
  2615. cmDspWaveTable_t* p = cmDspInstAlloc(cmDspWaveTable_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  2616. // get the filename given in the va_list (or NULL if no filename was given)
  2617. const cmChar_t* fn = cmDspDefaultStrcz(&p->inst,kFnWtId);
  2618. p->offSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"off");
  2619. p->onSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"on");
  2620. p->doneSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"done");
  2621. double adCurFileIdxRptPeriodMs = 100.0;
  2622. p->minAfIndexRptCnt = floor(adCurFileIdxRptPeriodMs * cmDspSampleRate(ctx) / 1000.0);
  2623. cmDspSetDefaultUInt( ctx, &p->inst, kLenWtId, 0, cmDspSampleRate(ctx));
  2624. cmDspSetDefaultUInt( ctx, &p->inst, kShapeWtId, 0, kSilenceWtId );
  2625. cmDspSetDefaultStrcz( ctx, &p->inst, kFnWtId, NULL, fn );
  2626. cmDspSetDefaultInt( ctx, &p->inst, kLoopWtId, 0, -1 );
  2627. cmDspSetDefaultInt( ctx, &p->inst, kBegWtId, 0, 0 );
  2628. cmDspSetDefaultInt( ctx, &p->inst, kEndWtId, 0, -1 );
  2629. cmDspSetDefaultSymbol(ctx, &p->inst, kCmdWtId, p->onSymId );
  2630. cmDspSetDefaultUInt( ctx, &p->inst, kOtWtId, 0, 5 );
  2631. cmDspSetDefaultDouble(ctx, &p->inst, kGainWtId, 0, 1.0 );
  2632. cmDspSetDefaultUInt( ctx, &p->inst, kFIdxWtId, 0, 0 );
  2633. p->useThreadFl = false;
  2634. return &p->inst;
  2635. }
  2636. cmDspRC_t _cmDspWaveTableFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2637. {
  2638. cmDspWaveTable_t* p = (cmDspWaveTable_t*)inst;
  2639. if( cmThreadIsValid(p->thH) )
  2640. if( cmThreadDestroy(&p->thH) != kOkThRC )
  2641. cmDspInstErr(ctx,inst,kThreadFailDspRC,"The wavetable file reader thread failed to close.");
  2642. if( cmAudioFileIsValid(p->afH) )
  2643. cmAudioFileDelete(&p->afH);
  2644. cmLhFree(ctx->lhH,p->wt);
  2645. return kOkDspRC;
  2646. }
  2647. // Read the next block of 'rdSmpCnt' samples starting at the absolute file index 'p->fi'
  2648. // into wt[rdSmpCnt].
  2649. // If the end of the file segment marked by absolute file indexes 'begSmpIdx' to 'endSmpIdx'
  2650. // is encountered in the middle of the requested block and the wave table is in loop
  2651. // mode then the the function will automatically begin reading from the begining of the
  2652. // file segment. If the end of the file segment is encountered and the wave table is not
  2653. // in loop mode then the empty portion of wt[] will be set to zero.
  2654. cmDspRC_t _cmDspWaveTableReadBlock( cmDspCtx_t* ctx, cmDspWaveTable_t* p, cmSample_t* wt, unsigned rdSmpCnt, int begSmpIdx, int endSmpIdx, int maxLoopCnt )
  2655. {
  2656. unsigned actFrmCnt = 0;
  2657. unsigned chIdx = 0;
  2658. unsigned chCnt = 1;
  2659. unsigned fn = endSmpIdx - p->fi + 1; // count of samples between p->fi and endSmpIdx
  2660. unsigned n0 = rdSmpCnt;
  2661. unsigned n1 = 0;
  2662. // if the requested sample count will go past the end of the file segment
  2663. if( rdSmpCnt > fn )
  2664. {
  2665. n1 = rdSmpCnt - fn;
  2666. n0 = rdSmpCnt - n1;
  2667. }
  2668. // if we don't have a valid file yet - then
  2669. if( cmAudioFileIsValid(p->afH) == false )
  2670. {
  2671. cmVOS_Zero(wt,n0);
  2672. return kOkDspRC;
  2673. }
  2674. // read the first block of samples
  2675. if( cmAudioFileReadSample(p->afH, n0, chIdx, chCnt, &wt, &actFrmCnt ) != kOkAfRC )
  2676. return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"An error occured while reading the wave table file.");
  2677. // BUG BUG BUG
  2678. // This assertion will fail if the file beg/end markers are not legal given the file length.
  2679. // An error msg would be more appropriate.
  2680. assert( actFrmCnt == n0 );
  2681. // increment the wave table pointer
  2682. wt += n0;
  2683. p->fi += n0;
  2684. // if n1 != 0 then we have encountered the end of the file segment
  2685. if( n1 > 0 )
  2686. {
  2687. ++p->loopCnt;
  2688. // send the loop count event
  2689. cmDspSetInt(ctx,&p->inst,kCntWtId,p->loopCnt);
  2690. // if we have played all the requested loops
  2691. if( maxLoopCnt != -1 && p->loopCnt >= maxLoopCnt )
  2692. {
  2693. p->doneFl = true;
  2694. cmVOS_Zero(wt,n1); // zero to the end of the buffer
  2695. p->cfn = p->cfi + cmDspUInt((cmDspInst_t*)p,kLenWtId) - p->wtn - n0;
  2696. assert( p->cfn >= p->cfi );
  2697. }
  2698. else
  2699. {
  2700. // seek to the first sample indicated by the 'beg' variable
  2701. if( cmAudioFileSeek(p->afH,begSmpIdx) != kOkAfRC )
  2702. return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"Seeking failed on the wave table file.",fn);
  2703. // read the second block of samples
  2704. if( cmAudioFileReadSample(p->afH, n1, chIdx, chCnt, &wt, &actFrmCnt ) != kOkAfRC )
  2705. return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"An error occured while reading the wave table file.");
  2706. assert( actFrmCnt == n1 );
  2707. // reset the file index tracker
  2708. p->fi = begSmpIdx + n1;
  2709. p->cfi = begSmpIdx;
  2710. }
  2711. }
  2712. return kOkDspRC;
  2713. }
  2714. cmDspRC_t _cmDspWaveTableReadAudioFile( cmDspCtx_t* ctx, cmDspWaveTable_t* p, unsigned wtSmpCnt, unsigned rdSmpCnt )
  2715. {
  2716. unsigned n0 = rdSmpCnt;
  2717. unsigned n1 = 0;
  2718. int begSmpIdx = cmDspInt(&p->inst,kBegWtId);
  2719. int endSmpIdx = cmDspInt(&p->inst,kEndWtId);
  2720. int maxLoopCnt= cmDspInt(&p->inst,kLoopWtId);
  2721. if( endSmpIdx < begSmpIdx )
  2722. endSmpIdx = p->fn-1;
  2723. // if this read will wrap to the beginning of the wave table
  2724. if( p->wti + rdSmpCnt > wtSmpCnt )
  2725. {
  2726. n0 = wtSmpCnt - p->wti; // count of samples to read into the end of p->wt[]
  2727. n1 = rdSmpCnt - n0; // count of samples to read into the beg of p->wt[]
  2728. }
  2729. assert(n1<wtSmpCnt);
  2730. // the first read always starts at p->wt + p->wti
  2731. if( p->doneFl )
  2732. cmVOS_Zero(p->wt + p->wti,n0);
  2733. else
  2734. if( _cmDspWaveTableReadBlock(ctx, p, p->wt+p->wti, n0,begSmpIdx,endSmpIdx,maxLoopCnt ) != kOkDspRC )
  2735. return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"An error occured while reading the wave table file.");
  2736. p->wtn -= n0; // decrease the count of available samples
  2737. p->wti += n0;
  2738. if( n1 > 0 )
  2739. {
  2740. // the second read always starts at the beginning of the wave table
  2741. if( p->doneFl )
  2742. cmVOS_Zero(p->wt,n1);
  2743. else
  2744. if( _cmDspWaveTableReadBlock(ctx, p, p->wt, n1,begSmpIdx,endSmpIdx,maxLoopCnt ) != kOkDspRC )
  2745. return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"An error occured while reading the wave table file.");
  2746. p->wtn -= n1; // decrease the count of available samples
  2747. p->wti = n1;
  2748. }
  2749. //p->wtn -= rdSmpCnt; // decrease the count of available samples
  2750. return kOkDspRC;
  2751. }
  2752. cmDspRC_t _cmDspWaveTableInitAudioFile( cmDspCtx_t* ctx, cmDspWaveTable_t* p )
  2753. {
  2754. cmDspRC_t rc = kOkDspRC;
  2755. cmAudioFileH_t afH;
  2756. cmRC_t afRC;
  2757. cmAudioFileInfo_t afInfo;
  2758. const cmChar_t* fn = cmDspStrcz(&p->inst,kFnWtId);
  2759. unsigned wtSmpCnt = cmDspUInt(&p->inst,kLenWtId);
  2760. int begSmpIdx= cmDspInt(&p->inst,kBegWtId);
  2761. // if the file name is valid
  2762. if( fn == NULL || strlen(fn)==0 )
  2763. {
  2764. rc = cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"Audio file loading was requested for the wave table but no file name was given.");
  2765. goto errLabel;
  2766. }
  2767. // open the audio file
  2768. afH = cmAudioFileNewOpen(fn,&afInfo,&afRC,ctx->rpt);
  2769. // check for file open errors
  2770. if( afRC != kOkAfRC )
  2771. {
  2772. rc = cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"The audio file '%s' could not be opened. ('%s').",fn,cmAudioFileErrorMsg(afRC));
  2773. goto errLabel;
  2774. }
  2775. // if the file opened but is invalid
  2776. if( cmAudioFileIsValid(p->afH) )
  2777. cmAudioFileDelete(&p->afH);
  2778. // seek to the first sample indicated by the 'beg' variable
  2779. if( cmAudioFileSeek(afH,begSmpIdx) != kOkAfRC )
  2780. {
  2781. rc = cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"Seeking failed on the audio file '%s'.",fn);
  2782. goto errLabel;
  2783. }
  2784. p->afH = afH;
  2785. p->fi = begSmpIdx;
  2786. p->cfi = begSmpIdx;
  2787. p->fn = afInfo.frameCnt;
  2788. p->wti = 0;
  2789. p->wtn = wtSmpCnt;
  2790. // read the first block of samples
  2791. if((rc= _cmDspWaveTableReadAudioFile(ctx,p,wtSmpCnt,wtSmpCnt))!= kOkDspRC )
  2792. goto errLabel;
  2793. //printf("Wt:%s %i %i\n",fn,begSmpIdx,cmDspInt(&p->inst,kEndWtId));
  2794. // set the shape param to kFileWtId
  2795. //if((rc= cmDspSetUInt(ctx,&p->inst,kShapeWtId,kFileWtId)) != kOkDspRC )
  2796. // goto errLabel;
  2797. errLabel:
  2798. if( rc != kOkDspRC )
  2799. cmDspSetUInt(ctx,&p->inst,kShapeWtId,kSilenceWtId);
  2800. return rc;
  2801. }
  2802. bool _cmDspWaveTableThreadFunc( void* param)
  2803. {
  2804. cmDspWaveTable_t* p = (cmDspWaveTable_t*)param;
  2805. if( p->loadFileFl )
  2806. {
  2807. p->loadFileFl = false;
  2808. if( _cmDspWaveTableInitAudioFile(p->ctx,p) == kOkDspRC )
  2809. {
  2810. p->phsOffs = p->phsLast;
  2811. cmDspSetUInt(p->ctx,&p->inst,kShapeWtId,kFileWtId);
  2812. }
  2813. cmThreadPause(p->thH,kPauseThFl);
  2814. }
  2815. return true;
  2816. }
  2817. // Files are loaded via a background thread.
  2818. cmDspRC_t _cmDspWaveTableStartFileLoadThread( cmDspCtx_t* ctx, cmDspWaveTable_t* p, const cmChar_t* fn )
  2819. {
  2820. cmDspRC_t rc = kOkDspRC;
  2821. if( fn == NULL )
  2822. return rc;
  2823. if( p->loadFileFl )
  2824. return cmDspInstErr(ctx,&p->inst,kInvalidStateDspRC,"The audio file '%s' was not loaded because another file is in the process of being loaded.",cmStringNullGuard(fn));
  2825. if(p->useThreadFl && cmThreadIsValid(p->thH) == false)
  2826. cmThreadCreate(&p->thH,_cmDspWaveTableThreadFunc,p,ctx->rpt);
  2827. if(p->useThreadFl && cmThreadIsValid(p->thH) == false )
  2828. return cmDspInstErr(ctx,&p->inst,kInvalidStateDspRC,"The audio file '%s' was not loaded because the audio load thread is invalid.",cmStringNullGuard(fn));
  2829. p->loadFileFl = true;
  2830. p->ctx = ctx;
  2831. cmDspSetUInt(ctx,&p->inst,kShapeWtId,kSilenceWtId);
  2832. cmDspSetStrcz(ctx,&p->inst,kFnWtId,fn);
  2833. if( p->useThreadFl == false )
  2834. {
  2835. // use non-threaded load
  2836. if((rc = _cmDspWaveTableInitAudioFile(p->ctx,p)) != kOkDspRC )
  2837. return cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"The audio file '%s' was not loaded.",cmStringNullGuard(fn));
  2838. p->phsOffs = p->phsLast;
  2839. cmDspSetUInt(p->ctx,&p->inst,kShapeWtId,kFileWtId);
  2840. p->loadFileFl = false;
  2841. }
  2842. else
  2843. {
  2844. // use threaded load - this works but it isn't really thread safe
  2845. if( cmThreadPause(p->thH,0) != kOkThRC )
  2846. return cmDspInstErr(ctx,&p->inst,kThreadFailDspRC,"The audio file '%s' was not loaded because audio thread enable failed.",cmStringNullGuard(fn));
  2847. }
  2848. return rc;
  2849. }
  2850. // This function is called whenever the source mode variable changes (or a new file name arrives)
  2851. cmDspRC_t _cmDspWaveTableCreateTable( cmDspCtx_t* ctx, cmDspWaveTable_t* p )
  2852. {
  2853. cmDspRC_t rc = kOkDspRC;
  2854. unsigned shapeId = cmDspUInt(&p->inst,kShapeWtId);
  2855. unsigned wtSmpCnt = cmDspUInt(&p->inst,kLenWtId);
  2856. unsigned otCnt = cmDspUInt(&p->inst,kOtWtId);
  2857. cmSample_t gain = 0.9;
  2858. double hz = 1.0;
  2859. double sr = cmDspSampleRate(ctx);
  2860. assert( wtSmpCnt > 0 );
  2861. if( p->wt == NULL )
  2862. p->wt = cmLhResizeNZ(ctx->lhH,cmSample_t,p->wt,wtSmpCnt);
  2863. else
  2864. cmVOS_Zero(p->wt,wtSmpCnt);
  2865. p->wtn = wtSmpCnt; // all samples in the wt are avail for filling
  2866. p->wti = 0; // beginning with the first sample
  2867. p->loopCnt = 0; // we are starting from a new source so set the loop cnt to 0
  2868. p->doneFl = false; // and the doneFl to false
  2869. assert( p->wt != NULL );
  2870. switch( shapeId )
  2871. {
  2872. case kSilenceWtId:
  2873. break;
  2874. case kFileWtId:
  2875. printf("Loading:%i %i %s\n",p->nxtBegSmpIdx,p->nxtEndSmpIdx,cmDspStrcz(&p->inst,kFnWtId));
  2876. rc = _cmDspWaveTableStartFileLoadThread(ctx,p,cmDspStrcz(&p->inst,kFnWtId));
  2877. break;
  2878. case kWhiteWtId:
  2879. cmVOS_Random(p->wt,wtSmpCnt,-gain,gain);
  2880. break;
  2881. case kPinkWtId:
  2882. cmVOS_SynthPinkNoise(p->wt,wtSmpCnt,0.0);
  2883. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2884. break;
  2885. case kSineWtId:
  2886. cmVOS_SynthSine(p->wt,wtSmpCnt,0,sr,hz);
  2887. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2888. break;
  2889. case kCosWtId:
  2890. cmVOS_SynthCosine(p->wt,wtSmpCnt,0,sr,hz);
  2891. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2892. break;
  2893. case kSawWtId:
  2894. cmVOS_SynthSawtooth(p->wt,wtSmpCnt,0,sr,hz,otCnt);
  2895. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2896. break;
  2897. case kSqrWtId:
  2898. cmVOS_SynthSquare( p->wt,wtSmpCnt,0,sr,hz,otCnt );
  2899. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2900. break;
  2901. case kTriWtId:
  2902. cmVOS_SynthTriangle( p->wt,wtSmpCnt,0,sr,hz,otCnt );
  2903. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2904. break;
  2905. case kPulseWtId:
  2906. cmVOS_SynthPulseCos( p->wt,wtSmpCnt,0,sr,hz,otCnt );
  2907. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2908. break;
  2909. case kPhasorWtId:
  2910. cmVOS_SynthPhasor( p->wt,wtSmpCnt,0,sr,hz );
  2911. cmVOS_MultVS(p->wt,wtSmpCnt,gain);
  2912. break;
  2913. }
  2914. return rc;
  2915. }
  2916. cmDspRC_t _cmDspWaveTableReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2917. {
  2918. cmDspWaveTable_t* p = (cmDspWaveTable_t*)inst;
  2919. cmDspApplyAllDefaults(ctx,inst);
  2920. cmDspZeroAudioBuf(ctx,inst,kOutWtId);
  2921. p->nxtBegSmpIdx = cmDspInt(&p->inst,kBegWtId);
  2922. p->nxtEndSmpIdx = cmDspInt(&p->inst,kEndWtId);
  2923. return _cmDspWaveTableCreateTable(ctx,p);
  2924. }
  2925. cmDspRC_t _cmDspWaveTableExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2926. {
  2927. cmDspRC_t rc = kOkDspRC;
  2928. const cmSample_t* phsV = cmDspAudioBuf(ctx,inst,kPhsWtId,0);
  2929. if( phsV == NULL )
  2930. {
  2931. inst->execFunc = NULL; // disable this instance because it has no input
  2932. return kOkDspRC;
  2933. }
  2934. cmDspWaveTable_t* p = (cmDspWaveTable_t*)inst;
  2935. unsigned mode = cmDspSymbol(inst,kCmdWtId);
  2936. unsigned srcId = cmDspUInt(inst,kShapeWtId);
  2937. if( mode == p->offSymId || srcId == kSilenceWtId )
  2938. {
  2939. cmDspZeroAudioBuf(ctx,inst,kOutWtId);
  2940. return kOkDspRC;
  2941. }
  2942. cmSample_t* outV = cmDspAudioBuf(ctx,inst,kOutWtId,0);
  2943. unsigned outCnt = cmDspVarRows(inst,kOutWtId);
  2944. unsigned wtSmpCnt = cmDspUInt(inst,kLenWtId);
  2945. double gain = cmDspDouble(inst,kGainWtId);
  2946. unsigned i;
  2947. // for each output sample
  2948. for(i=0; i<outCnt; ++i)
  2949. {
  2950. // get the wave table location
  2951. //unsigned x = fmodf(phsV[i] - p->phsOffs,wtSmpCnt);
  2952. unsigned x = fmodf(phsV[i],wtSmpCnt);
  2953. // if the wt loctn is passed the end of the table
  2954. /*
  2955. if( x >= wtSmpCnt )
  2956. {
  2957. offs += wtSmpCnt;
  2958. x -= wtSmpCnt;
  2959. }
  2960. */
  2961. outV[i] = gain * p->wt[x];
  2962. }
  2963. p->phsLast = phsV[outCnt-1];
  2964. // if we are reading from a file ...
  2965. if( srcId == kFileWtId )
  2966. {
  2967. unsigned rdSmpCnt = 8192; // file read block sample count
  2968. p->wtn += outCnt;
  2969. // ... and there are rdSmpCnt avail locations in the wave table
  2970. if( p->wtn >= rdSmpCnt )
  2971. rc = _cmDspWaveTableReadAudioFile(ctx, p, wtSmpCnt, rdSmpCnt );
  2972. // send the current audio file index
  2973. if( p->doneFl && p->cfi < p->cfn && p->cfn <= (p->cfi + outCnt) )
  2974. {
  2975. cmDspSetUInt(ctx,inst,kFIdxWtId,p->cfn);
  2976. cmDspSetSymbol(ctx,inst,kDoneWtId,p->doneSymId);
  2977. }
  2978. else
  2979. {
  2980. if( p->afIndexRptCnt >= p->minAfIndexRptCnt )
  2981. {
  2982. p->afIndexRptCnt -= p->minAfIndexRptCnt;
  2983. cmDspSetUInt(ctx,inst,kFIdxWtId,p->cfi);
  2984. }
  2985. }
  2986. p->afIndexRptCnt += outCnt;
  2987. p->cfi += outCnt;
  2988. }
  2989. return rc;
  2990. }
  2991. cmDspRC_t _cmDspWaveTableRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  2992. {
  2993. cmDspRC_t rc = kOkDspRC;
  2994. cmDspWaveTable_t* p = (cmDspWaveTable_t*)inst;
  2995. switch( evt->dstVarId )
  2996. {
  2997. case kFnWtId: // a new file name arrived - this automatically switches the source mode to kFileWtId
  2998. {
  2999. const cmChar_t* fn = cmDsvStrz(evt->valuePtr);
  3000. if( cmFsIsFile( fn )==false )
  3001. cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"'%s' does not exist.",cmStringNullGuard(fn));
  3002. else
  3003. {
  3004. cmDspSetEvent(ctx,inst,evt); // set the file name variable
  3005. cmDspSetInt(ctx,inst,kBegWtId,p->nxtBegSmpIdx); // set the beg/end smp idx var's from the stored nxtBeg/EndSmpIdx values
  3006. cmDspSetInt(ctx,inst,kEndWtId,p->nxtEndSmpIdx); //
  3007. cmDspSetUInt(ctx,inst,kShapeWtId,kFileWtId); // switch to file mode
  3008. rc = _cmDspWaveTableCreateTable(ctx,p); // reload the wavetable
  3009. }
  3010. }
  3011. break;
  3012. case kBegWtId:
  3013. // store for next incoming file name msg
  3014. p->nxtBegSmpIdx = cmDsvGetInt(evt->valuePtr);
  3015. break;
  3016. case kEndWtId:
  3017. // store for next incoming file name msg
  3018. p->nxtEndSmpIdx = cmDsvGetInt(evt->valuePtr);
  3019. break;
  3020. case kShapeWtId:
  3021. if( cmDsvGetUInt(evt->valuePtr) < kShapeWtCnt )
  3022. {
  3023. cmDspSetEvent(ctx,inst,evt); // switch modes
  3024. rc = _cmDspWaveTableCreateTable(ctx,p); // reload the wavetable
  3025. }
  3026. break;
  3027. case kLenWtId: // we don't support table size changes
  3028. break;
  3029. case kPhsWtId:
  3030. break;
  3031. case kCmdWtId:
  3032. if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
  3033. {
  3034. if( cmDspSymbol(inst,kCmdWtId) == p->onSymId )
  3035. {
  3036. //rc = _cmDspWaveTableReset(ctx,inst, evt );
  3037. rc = _cmDspWaveTableCreateTable(ctx,p);
  3038. cmDspSetSymbol(ctx,inst,kCmdWtId,p->onSymId);
  3039. p->phsOffs = 0;
  3040. p->phsLast = 0;
  3041. }
  3042. }
  3043. break;
  3044. case kOtWtId:
  3045. if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
  3046. rc = _cmDspWaveTableCreateTable(ctx,p); // reload the wavetable
  3047. break;
  3048. case kGainWtId:
  3049. rc = cmDspSetEvent(ctx,inst,evt);
  3050. break;
  3051. default:
  3052. { assert(0); }
  3053. }
  3054. return rc;
  3055. }
  3056. struct cmDspClass_str* cmWaveTableClassCons( cmDspCtx_t* ctx )
  3057. {
  3058. cmDspClassSetup(&_cmWaveTableDC,ctx,"WaveTable",
  3059. NULL,
  3060. _cmDspWaveTableAlloc,
  3061. _cmDspWaveTableFree,
  3062. _cmDspWaveTableReset,
  3063. _cmDspWaveTableExec,
  3064. _cmDspWaveTableRecv,
  3065. NULL,
  3066. NULL,
  3067. "Variable frequency and waveshape signal generator." );
  3068. return &_cmWaveTableDC;
  3069. }
  3070. //==========================================================================================================================================
  3071. enum
  3072. {
  3073. kFmtSpId,
  3074. kOutSpId,
  3075. kInSpId,
  3076. kSprintfLabelCharCnt = 15,
  3077. kSprintfDocCharCnt = 31,
  3078. kSprintfBufCharCnt = 1023
  3079. };
  3080. cmDspClass_t _cmSprintfDC;
  3081. typedef struct
  3082. {
  3083. unsigned flags; // dsv type id for this fmt conversion or 0 if it is a literal string
  3084. unsigned fsi; // index into the fmt string where the fmt starts (always points to an '%' char)
  3085. unsigned fsn; // length of the format string
  3086. cmChar_t label[ kSprintfLabelCharCnt+1 ];
  3087. cmChar_t doc[ kSprintfDocCharCnt+1 ];
  3088. } cmDspSprintfFmt_t;
  3089. typedef struct
  3090. {
  3091. cmDspInst_t inst;
  3092. cmChar_t buf[ kSprintfBufCharCnt+1]; // output string buffer
  3093. unsigned inCnt; // count conversion spec's in the fmtStr[]
  3094. cmDspSprintfFmt_t* fmtArray; // fmtArray[inCnt]
  3095. cmChar_t* fmtStr; // C-style printf format string.
  3096. } cmDspSprintf_t;
  3097. cmDspRC_t _cmSprintfGetInputCnt( cmDspCtx_t* ctx, cmDspClass_t* classPtr, const cmChar_t* fmt, unsigned* inCntPtr )
  3098. {
  3099. unsigned i,n;
  3100. unsigned inCnt = 0;
  3101. if( fmt== NULL || (n=strlen(fmt))==0 )
  3102. return cmErrMsg(&classPtr->err,kInvalidArgDspRC,"Empty format string.");
  3103. for(i=0; i<n; ++i)
  3104. {
  3105. // handle the escape character
  3106. if( fmt[i] == '\\' )
  3107. ++i; // for now we will just skip the next character
  3108. else
  3109. if( fmt[i] == '%' )
  3110. ++inCnt;
  3111. }
  3112. *inCntPtr = inCnt;
  3113. return kOkDspRC;
  3114. }
  3115. cmDspRC_t _cmSprintfGetInputTypes( cmDspCtx_t* ctx, cmDspClass_t* classPtr, const cmChar_t* fmt, cmDspSprintfFmt_t fmtArray[], unsigned inCnt )
  3116. {
  3117. unsigned i,j,n;
  3118. if( fmt== NULL || (n=strlen(fmt))==0 )
  3119. return cmErrMsg(&classPtr->err,kInvalidArgDspRC,"Empty format string.");
  3120. n = strlen(fmt);
  3121. for(i=0,j=0; i<n; ++i)
  3122. {
  3123. // handle the escape character
  3124. if( fmt[i] == '\\' )
  3125. ++i; // for now we will just skip the next character
  3126. else
  3127. if( fmt[i] == '%' )
  3128. {
  3129. unsigned fn;
  3130. if((fn = strcspn(fmt+i,"diouxXfeEgGcs")) == 0 )
  3131. return cmErrMsg(&classPtr->err,kInvalidArgDspRC,"Invalid format string on input conversion at index:%i.",j);
  3132. ++fn;
  3133. fmtArray[j].fsi = i;
  3134. fmtArray[j].fsn = fn;
  3135. snprintf(fmtArray[j].label,kSprintfLabelCharCnt,"in-%i",j);
  3136. fmtArray[j].label[kSprintfLabelCharCnt]=0;
  3137. fmtArray[j].doc[kSprintfDocCharCnt] = 0;
  3138. switch( fmt[ i + fn - 1 ] )
  3139. {
  3140. case 'd':
  3141. case 'i':
  3142. fmtArray[j].flags = kIntDsvFl;
  3143. snprintf(fmtArray[j].doc,kSprintfDocCharCnt,"Integer input %i.",j);
  3144. break;
  3145. case 'o':
  3146. case 'u':
  3147. case 'x':
  3148. case 'X':
  3149. fmtArray[j].flags = kUIntDsvFl;
  3150. snprintf(fmtArray[j].doc,kSprintfDocCharCnt,"Unsigned input %i.",j);
  3151. break;
  3152. case 'f':
  3153. case 'e':
  3154. case 'E':
  3155. case 'g':
  3156. case 'G':
  3157. fmtArray[j].flags = kDoubleDsvFl;
  3158. snprintf(fmtArray[j].doc,kSprintfDocCharCnt,"Double input %i.",j);
  3159. break;
  3160. case 'c':
  3161. fmtArray[j].flags = kUCharDsvFl;
  3162. snprintf(fmtArray[j].doc,kSprintfDocCharCnt,"Unsigned char input %i.",j);
  3163. break;
  3164. case 's':
  3165. fmtArray[j].flags = kStrzDsvFl | kSymDsvFl;
  3166. snprintf(fmtArray[j].doc,kSprintfDocCharCnt,"String input %i.",j);
  3167. break;
  3168. default:
  3169. { assert(0); }
  3170. }
  3171. i += fn - 1;
  3172. ++j;
  3173. }
  3174. }
  3175. return kOkDspRC;
  3176. }
  3177. cmDspRC_t _cmSprintfLoadFormat(cmDspSprintf_t** pp, cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned instSymId, unsigned id, unsigned storeSymId, cmDspVarArg_t args[], unsigned va_cnt, va_list vl )
  3178. {
  3179. cmDspRC_t rc;
  3180. unsigned i,j;
  3181. va_list vl2;
  3182. unsigned inCnt = 0;
  3183. unsigned fixedArgCnt = 0;
  3184. cmDspSprintf_t* p = NULL;
  3185. const cmChar_t* fmt = NULL;
  3186. cmDspSprintfFmt_t* fmtArray = NULL;
  3187. va_copy(vl2,vl);
  3188. *pp = NULL;
  3189. if( va_cnt > 0 )
  3190. fmt = va_arg(vl,const char*);
  3191. if( va_cnt < 1 || fmt == NULL )
  3192. return cmErrMsg(&classPtr->err,kVarArgParseFailDspRC,"Expected the format string as the first argument.");
  3193. // calc the number of fixed args
  3194. for(i=0; args[i].label != NULL; ++i)
  3195. ++fixedArgCnt;
  3196. // calc the count of input args
  3197. if((rc = _cmSprintfGetInputCnt(ctx, classPtr, fmt, &inCnt)) != kOkDspRC )
  3198. return rc;
  3199. if( inCnt > 0 )
  3200. {
  3201. fmtArray = cmLhAllocZ( ctx->lhH, cmDspSprintfFmt_t, inCnt );
  3202. rc = _cmSprintfGetInputTypes(ctx, classPtr, fmt, fmtArray, inCnt );
  3203. }
  3204. if( rc == kOkDspRC )
  3205. {
  3206. unsigned argCnt = fixedArgCnt + inCnt;
  3207. cmDspVarArg_t argArray[ argCnt+1 ];
  3208. // copy in fixed args
  3209. for(i=0; i<fixedArgCnt; ++i)
  3210. argArray[i] = args[i];
  3211. // calc input args
  3212. for(j=0; i<argCnt; ++i,++j)
  3213. {
  3214. argArray[i].label = fmtArray[j].label;
  3215. argArray[i].constId = i;
  3216. argArray[i].rn = 0;
  3217. argArray[i].cn = 0;
  3218. argArray[i].flags = kInDsvFl | fmtArray[j].flags;
  3219. argArray[i].doc = fmtArray[j].doc;
  3220. }
  3221. // set the sentinel arg to all zeros
  3222. memset(argArray + argCnt,0,sizeof(cmDspVarArg_t));
  3223. if((p = cmDspInstAlloc(cmDspSprintf_t,ctx,classPtr,argArray,instSymId,id,storeSymId,va_cnt,vl2)) != NULL )
  3224. {
  3225. // make a copy of the format string - we need to be sure that it is in
  3226. // r/w memory in order that _cmDspSprintfGenString() can write to it
  3227. p->fmtStr = cmLhResizeN(ctx->lhH,cmChar_t,p->fmtStr,strlen(fmt)+1);
  3228. strcpy(p->fmtStr,fmt);
  3229. cmLhFree(ctx->lhH,p->fmtArray);
  3230. p->fmtArray = fmtArray;
  3231. p->inCnt = inCnt;
  3232. memset(p->buf,0,kSprintfBufCharCnt+1);
  3233. }
  3234. *pp = p;
  3235. }
  3236. if( cmErrLastRC(&classPtr->err) != kOkDspRC )
  3237. cmLhFree(ctx->lhH,fmtArray);
  3238. return rc;
  3239. }
  3240. cmDspRC_t _cmDspSprintfGenString(cmDspCtx_t* ctx, cmDspSprintf_t* p )
  3241. {
  3242. cmDspRC_t rc = kOkDspRC;
  3243. unsigned fsi = 0; // format string index
  3244. unsigned i = 0; // fmtArray[] index
  3245. unsigned bi = 0; // string buffer index
  3246. unsigned bn = kSprintfBufCharCnt; // available char's in the string buffer
  3247. cmChar_t* fmt = p->fmtStr;
  3248. unsigned fn = strlen(fmt)+1;
  3249. // for each
  3250. for(i=0; i<p->inCnt && bn>0; ++i)
  3251. {
  3252. const cmDspSprintfFmt_t* f = p->fmtArray + i;
  3253. unsigned varId = kInSpId + i;
  3254. const cmDspVar_t* varPtr = cmDspVarIdToCPtr(&p->inst, varId);
  3255. assert(varPtr != NULL);
  3256. // if there are literal char's to copy prior to the format
  3257. if( fsi < f->fsi )
  3258. {
  3259. unsigned cn = cmMin(f->fsi-fsi,bn);
  3260. strncpy(p->buf+bi,fmt+fsi,cn);
  3261. bn -= cn;
  3262. fsi += cn;
  3263. bi += cn;
  3264. }
  3265. if( bn == 0 )
  3266. {
  3267. rc = cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The internal string buffer is too small.");
  3268. break;
  3269. }
  3270. unsigned pn = 0;
  3271. char c = fmt[ f->fsi + f->fsn ];
  3272. // zero terminate the format string for this input
  3273. fmt[ f->fsi + f->fsn] = 0;
  3274. // if the conversion fmt is for a string then the kSymDsvFl will be set
  3275. // which will prevent the switch() from working - so clear the sym flag here.
  3276. unsigned flags = cmClrFlag(f->flags,kSymDsvFl);
  3277. switch(flags)
  3278. {
  3279. case kUCharDsvFl:
  3280. // not implemented - need to implment a uchar variable type or
  3281. // assume a one character strz.
  3282. assert(0);
  3283. break;
  3284. case kIntDsvFl:
  3285. pn = snprintf(p->buf+bi,bn,fmt + fsi, cmDspInt(&p->inst,varId));
  3286. break;
  3287. case kUIntDsvFl:
  3288. pn = snprintf(p->buf+bi,bn,fmt + fsi, cmDspUInt(&p->inst,varId));
  3289. break;
  3290. case kDoubleDsvFl:
  3291. pn = snprintf(p->buf+bi,bn,fmt + fsi, cmDspDouble(&p->inst,varId));
  3292. break;
  3293. case kStrzDsvFl:
  3294. if( cmDspIsSymbol(&p->inst,varId) )
  3295. pn = snprintf(p->buf+bi,bn,fmt + fsi, cmStringNullGuard(cmSymTblLabel(ctx->stH,cmDspSymbol(&p->inst,varId))));
  3296. else
  3297. pn = snprintf(p->buf+bi,bn,fmt + fsi, cmDspStrcz(&p->inst,varId));
  3298. break;
  3299. default:
  3300. { assert(0); }
  3301. }
  3302. // restore the char written over by the termination zero
  3303. fmt[ f->fsi + f->fsn] = c;
  3304. assert(pn<=bn);
  3305. bn -= pn;
  3306. bi += pn;
  3307. fsi += f->fsn;
  3308. }
  3309. // if there is literal text in the format string after the last conversion spec.
  3310. if( fsi < fn )
  3311. {
  3312. unsigned cn = cmMin(fn-fsi,bn);
  3313. strncpy(p->buf+bi,fmt+fsi,cn);
  3314. }
  3315. return rc;
  3316. }
  3317. cmDspInst_t* _cmDspSprintfAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3318. {
  3319. cmDspVarArg_t args[] =
  3320. {
  3321. { "fmt", kFmtSpId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Format string" },
  3322. { "out", kOutSpId, 0, 0, kOutDsvFl | kStrzDsvFl, "Output string" },
  3323. { NULL, 0, 0, 0, 0 }
  3324. };
  3325. cmDspSprintf_t* p = NULL;
  3326. if( _cmSprintfLoadFormat(&p, ctx, classPtr, instSymId, id, storeSymId, args, va_cnt, vl ) == kOkDspRC )
  3327. return &p->inst;
  3328. return NULL;
  3329. }
  3330. cmDspRC_t _cmDspSprintfFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3331. {
  3332. cmDspSprintf_t* p = (cmDspSprintf_t*)inst;
  3333. cmLhFree(ctx->lhH,p->fmtArray);
  3334. cmLhFree(ctx->lhH,p->fmtStr);
  3335. p->fmtArray=NULL;
  3336. return kOkDspRC;
  3337. }
  3338. cmDspRC_t _cmDspSprintfReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3339. {
  3340. return kOkDspRC;
  3341. }
  3342. cmDspRC_t _cmDspSprintfRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3343. {
  3344. cmDspRC_t rc= kOkDspRC;
  3345. cmDspSprintf_t* p = (cmDspSprintf_t*)inst;
  3346. if( kInSpId <= evt->dstVarId && evt->dstVarId < kInSpId + p->inCnt )
  3347. {
  3348. cmDspSetEvent(ctx,inst,evt);
  3349. //if( evt->dstVarId == kInSpId )
  3350. if((rc = _cmDspSprintfGenString(ctx,p)) == kOkDspRC )
  3351. cmDspSetStrcz(ctx,inst,kOutSpId,p->buf);
  3352. }
  3353. return rc;
  3354. }
  3355. struct cmDspClass_str* cmSprintfClassCons( cmDspCtx_t* ctx )
  3356. {
  3357. cmDspClassSetup(&_cmSprintfDC,ctx,"Sprintf",
  3358. NULL,
  3359. _cmDspSprintfAlloc,
  3360. _cmDspSprintfFree,
  3361. _cmDspSprintfReset,
  3362. NULL,
  3363. _cmDspSprintfRecv,
  3364. NULL,NULL,
  3365. "String formatter." );
  3366. return &_cmSprintfDC;
  3367. }
  3368. //==========================================================================================================================================
  3369. enum
  3370. {
  3371. kOutAmId,
  3372. kBaseInAmId
  3373. };
  3374. cmDspClass_t _cmAMixDC;
  3375. typedef struct
  3376. {
  3377. cmDspInst_t inst;
  3378. unsigned inPortCnt;
  3379. unsigned baseGainId;
  3380. unsigned baseMuteId;
  3381. } cmDspAMix_t;
  3382. cmDspInst_t* _cmDspAMixAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3383. {
  3384. if( va_cnt < 1 )
  3385. {
  3386. cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'AMix' constructor must have a count of input ports.");
  3387. return NULL;
  3388. }
  3389. // args:
  3390. // <inPortCnt> <gain0>, <gain1> (the default gains are optional)
  3391. unsigned i;
  3392. int inPortCnt = va_arg(vl,int);
  3393. unsigned baseGainAmId = kBaseInAmId + inPortCnt;
  3394. unsigned baseMuteAmId = baseGainAmId + inPortCnt;
  3395. double dfltGain[ inPortCnt ];
  3396. if( va_cnt == 1 )
  3397. cmVOD_Fill(dfltGain,inPortCnt,1.0);
  3398. else
  3399. if( va_cnt == 2 )
  3400. {
  3401. dfltGain[0] = va_arg(vl,double);
  3402. cmVOD_Fill(dfltGain+1,inPortCnt-1,dfltGain[0]);
  3403. }
  3404. else
  3405. if( va_cnt == inPortCnt + 1 )
  3406. {
  3407. for(i=0; i<inPortCnt; ++i)
  3408. dfltGain[i] = va_arg(vl,double);
  3409. }
  3410. else
  3411. {
  3412. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The mix argument list must contain no default gain values, one default gain value, or all default gain values.");
  3413. return NULL;
  3414. }
  3415. cmDspAMix_t* p = cmDspInstAllocV(cmDspAMix_t,ctx,classPtr,instSymId,id,storeSymId,0,vl,
  3416. 1, "out", kOutAmId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output",
  3417. inPortCnt, "in", kBaseInAmId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input",
  3418. inPortCnt, "gain", baseGainAmId, 0, 0, kInDsvFl | kDoubleDsvFl, "Gain input",
  3419. inPortCnt, "mute", baseMuteAmId, 0, 0, kInDsvFl | kBoolDsvFl, "Mute input",
  3420. 0 );
  3421. p->inPortCnt = inPortCnt;
  3422. p->baseGainId = baseGainAmId;
  3423. p->baseMuteId = baseMuteAmId;
  3424. for(i=0; i<inPortCnt; ++i)
  3425. {
  3426. cmDspSetDefaultDouble( ctx, &p->inst, p->baseGainId + i, 0.0, dfltGain[i]);
  3427. cmDspSetDefaultBool( ctx, &p->inst, p->baseMuteId + i, false, false );
  3428. }
  3429. /*
  3430. // read any default gain settings
  3431. --va_cnt;
  3432. for(i=0; i<inPortCnt; ++i)
  3433. {
  3434. // if excplicit gains are not given then default to 1.0.
  3435. double dflt = 1.0;
  3436. if( i < va_cnt )
  3437. dflt = va_arg(vl,double);
  3438. cmDspSetDefaultDouble( ctx, &p->inst, p->baseGainId + i, 0.0, dflt);
  3439. }
  3440. */
  3441. return &p->inst;
  3442. }
  3443. cmDspRC_t _cmDspAMixReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3444. {
  3445. cmDspRC_t rc = kOkDspRC;
  3446. cmDspApplyAllDefaults(ctx,inst);
  3447. cmDspZeroAudioBuf(ctx,inst,kOutAmId);
  3448. return rc;
  3449. }
  3450. cmDspRC_t _cmDspAMixExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3451. {
  3452. cmDspAMix_t* p = (cmDspAMix_t*)inst;
  3453. unsigned i;
  3454. cmDspZeroAudioBuf(ctx,inst,kOutAmId);
  3455. unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutAmId,0);
  3456. cmSample_t* dp = cmDspAudioBuf(ctx,inst,kOutAmId,0);
  3457. for(i=0; i<p->inPortCnt; ++i)
  3458. {
  3459. const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kBaseInAmId+i,0);
  3460. if( sp != NULL )
  3461. {
  3462. double gain = cmDspDouble(inst,p->baseGainId+i);
  3463. cmVOS_MultSumVVS(dp,n,sp,(cmSample_t)gain);
  3464. }
  3465. }
  3466. return kOkDspRC;
  3467. }
  3468. cmDspRC_t _cmDspAMixRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3469. {
  3470. cmDspRC_t rc = kOkDspRC;
  3471. cmDspAMix_t* p = (cmDspAMix_t*)inst;
  3472. if( p->baseGainId <= evt->dstVarId && evt->dstVarId < p->baseGainId + p->inPortCnt )
  3473. {
  3474. cmDspSetEvent(ctx,inst,evt);
  3475. //printf("rcv:%i %f\n",evt->dstVarId,cmDspDouble(inst,evt->dstVarId));
  3476. }
  3477. return rc;
  3478. }
  3479. struct cmDspClass_str* cmAMixClassCons( cmDspCtx_t* ctx )
  3480. {
  3481. cmDspClassSetup(&_cmAMixDC,ctx,"AMix",
  3482. NULL,
  3483. _cmDspAMixAlloc,
  3484. NULL,
  3485. _cmDspAMixReset,
  3486. _cmDspAMixExec,
  3487. _cmDspAMixRecv,
  3488. NULL,NULL,
  3489. "Audio mixer");
  3490. return &_cmAMixDC;
  3491. }
  3492. //==========================================================================================================================================
  3493. enum
  3494. {
  3495. kInAsId,
  3496. kBaseOutAsId
  3497. };
  3498. cmDspClass_t _cmASplitDC;
  3499. typedef struct
  3500. {
  3501. cmDspInst_t inst;
  3502. unsigned outPortCnt;
  3503. unsigned baseGainId;
  3504. } cmDspASplit_t;
  3505. // A splitter has one audio input port and multiple audio output ports.
  3506. // A gain input is automatically provided for each output port.
  3507. cmDspInst_t* _cmDspASplitAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3508. {
  3509. cmDspVarArg_t args[] =
  3510. {
  3511. { "in", kInAsId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
  3512. { NULL, 0, 0, 0, 0 }
  3513. };
  3514. if( va_cnt < 1 )
  3515. {
  3516. cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'ASplit' constructor must have a count of input ports.");
  3517. return NULL;
  3518. }
  3519. // args:
  3520. // <outPortCnt> <gain0>, <gain1> (the default gains are optional)
  3521. unsigned i,j,k;
  3522. int outPortCnt = va_arg(vl,int);
  3523. unsigned fixArgCnt = sizeof(args)/sizeof(args[0]) - 1;
  3524. unsigned argCnt = fixArgCnt + 2*outPortCnt + 1;
  3525. cmDspVarArg_t argArray[ argCnt ];
  3526. int labelCharCnt = 15;
  3527. cmChar_t label[ labelCharCnt + 1 ];
  3528. label[labelCharCnt] = 0;
  3529. //
  3530. for(i=0; i<fixArgCnt; ++i)
  3531. argArray[i] = args[i];
  3532. // define the audio output port specifications
  3533. for(j=0,k=0; j<outPortCnt; ++i,++j,++k)
  3534. {
  3535. snprintf(label,labelCharCnt,"out-%i",j);
  3536. unsigned symId = cmSymTblRegisterSymbol(ctx->stH,label);
  3537. argArray[i].label = cmSymTblLabel(ctx->stH,symId);
  3538. argArray[i].constId = kBaseOutAsId + k;
  3539. argArray[i].rn = 0;
  3540. argArray[i].cn = 1;
  3541. argArray[i].flags = kOutDsvFl | kAudioBufDsvFl;
  3542. argArray[i].doc = "Audio Input";
  3543. }
  3544. // define the gain input specifications
  3545. for(j=0; j<outPortCnt; ++i,++j,++k)
  3546. {
  3547. snprintf(label,labelCharCnt,"gain-%i",j);
  3548. unsigned symId = cmSymTblRegisterSymbol(ctx->stH,label);
  3549. argArray[i].label = cmSymTblLabel(ctx->stH,symId);
  3550. argArray[i].constId = kBaseOutAsId + k;
  3551. argArray[i].rn = 0;
  3552. argArray[i].cn = 0;
  3553. argArray[i].flags = kInDsvFl | kDoubleDsvFl;
  3554. argArray[i].doc = "Gain input";
  3555. }
  3556. // set the NULL end-of-arg-array sentinel
  3557. memset(argArray + i, 0, sizeof(argArray[0]));
  3558. cmDspASplit_t* p = cmDspInstAlloc(cmDspASplit_t,ctx,classPtr,argArray,instSymId,id,storeSymId,0,vl);
  3559. p->outPortCnt = outPortCnt;
  3560. p->baseGainId = kBaseOutAsId + outPortCnt;
  3561. // read any default gain settings
  3562. --va_cnt;
  3563. for(i=0; i<outPortCnt; ++i)
  3564. {
  3565. double dflt = 1.0;
  3566. if( i < va_cnt )
  3567. dflt = va_arg(vl,double);
  3568. cmDspSetDefaultDouble( ctx, &p->inst, p->baseGainId + i, 0.0, dflt);
  3569. }
  3570. return &p->inst;
  3571. }
  3572. cmDspRC_t _cmDspASplitReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3573. {
  3574. cmDspRC_t rc = kOkDspRC;
  3575. cmDspASplit_t* p = (cmDspASplit_t*)inst;
  3576. int i;
  3577. cmDspApplyAllDefaults(ctx,inst);
  3578. for(i=0; i<p->outPortCnt; ++i)
  3579. cmDspZeroAudioBuf(ctx,inst,kBaseOutAsId+i);
  3580. return rc;
  3581. }
  3582. cmDspRC_t _cmDspASplitExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3583. {
  3584. cmDspASplit_t* p = (cmDspASplit_t*)inst;
  3585. unsigned i;
  3586. unsigned n = cmDspAudioBufSmpCount(ctx,inst,kBaseOutAsId,0);
  3587. const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kInAsId,0);
  3588. for(i=0; i<p->outPortCnt; ++i)
  3589. {
  3590. cmSample_t* dp = cmDspAudioBuf(ctx,inst,kBaseOutAsId+i,0);
  3591. double gain = cmDspDouble(inst,p->baseGainId+i);
  3592. cmVOS_MultVVS(dp,n,sp,(cmSample_t)gain);
  3593. }
  3594. return kOkDspRC;
  3595. }
  3596. cmDspRC_t _cmDspASplitRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3597. {
  3598. cmDspRC_t rc = kOkDspRC;
  3599. cmDspASplit_t* p = (cmDspASplit_t*)inst;
  3600. if( p->baseGainId <= evt->dstVarId && evt->dstVarId < p->baseGainId + p->outPortCnt )
  3601. {
  3602. cmDspSetEvent(ctx,inst,evt);
  3603. }
  3604. return rc;
  3605. }
  3606. struct cmDspClass_str* cmASplitClassCons( cmDspCtx_t* ctx )
  3607. {
  3608. cmDspClassSetup(&_cmASplitDC,ctx,"ASplit",
  3609. NULL,
  3610. _cmDspASplitAlloc,
  3611. NULL,
  3612. _cmDspASplitReset,
  3613. _cmDspASplitExec,
  3614. _cmDspASplitRecv,
  3615. NULL,NULL,
  3616. "Audio splitter");
  3617. return &_cmASplitDC;
  3618. }
  3619. //==========================================================================================================================================
  3620. enum
  3621. {
  3622. kInAmId,
  3623. kMinAmId,
  3624. kMaxAmId,
  3625. kValAmId, // meter value
  3626. kLblAmId,
  3627. };
  3628. #define cmDspMeter_MIN (-100)
  3629. #define cmDspMeter_MAX (0)
  3630. cmDspClass_t _cmAMeterDC;
  3631. typedef struct
  3632. {
  3633. cmDspInst_t inst;
  3634. unsigned bufN;
  3635. unsigned idx;
  3636. cmReal_t sum;
  3637. cmReal_t val;
  3638. } cmDspAMeter_t;
  3639. cmDspInst_t* _cmDspAMeterAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3640. {
  3641. cmDspVarArg_t args[] =
  3642. {
  3643. { "in", kInAmId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input"},
  3644. { "min", kMinAmId, 0, 0, kDoubleDsvFl, "Minimum value"},
  3645. { "max", kMaxAmId, 0, 0, kDoubleDsvFl, "Maximum value"},
  3646. { "val", kValAmId, 0, 0, kDoubleDsvFl, "Meter value"},
  3647. { "label",kLblAmId, 0, 0, kStrzDsvFl, "Label."},
  3648. { NULL, 0, 0, 0, 0 }
  3649. };
  3650. cmDspAMeter_t* p = cmDspInstAlloc(cmDspAMeter_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  3651. double updateMs = 100;
  3652. double sr = cmDspSampleRate(ctx);
  3653. unsigned spc = cmDspSamplesPerCycle(ctx);
  3654. p->bufN = cmMax(1,floor(updateMs * sr/ (1000.0 * spc)));
  3655. cmDspSetDefaultDouble(ctx, &p->inst, kValAmId, 0.0, cmDspMeter_MIN);
  3656. cmDspSetDefaultDouble(ctx, &p->inst, kMinAmId, 0.0, cmDspMeter_MIN);
  3657. cmDspSetDefaultDouble(ctx, &p->inst, kMaxAmId, 0.0, cmDspMeter_MAX);
  3658. // create the UI control
  3659. cmDspUiMeterCreate(ctx,&p->inst,kMinAmId,kMaxAmId,kValAmId,kLblAmId);
  3660. return &p->inst;
  3661. }
  3662. cmDspRC_t _cmDspAMeterReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3663. {
  3664. cmDspAMeter_t* p = (cmDspAMeter_t*)inst;
  3665. cmDspApplyAllDefaults(ctx,inst);
  3666. //cmDspZeroAudioBuf(ctx,inst,kInAmId);
  3667. p->idx = 0;
  3668. p->sum = 0;
  3669. p->val = 0;
  3670. return kOkDspRC;
  3671. }
  3672. cmDspRC_t _cmDspAMeterExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3673. {
  3674. cmDspAMeter_t* p = (cmDspAMeter_t*)inst;
  3675. unsigned n = cmDspAudioBufSmpCount(ctx,inst,kInAmId,0);
  3676. const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kInAmId,0);
  3677. if( sp == NULL )
  3678. {
  3679. inst->execFunc = NULL; // if there is no connected input then disable further callbacks
  3680. return kOkDspRC;
  3681. }
  3682. p->sum += cmVOS_SquaredSum(sp,n);
  3683. ++p->idx;
  3684. if( p->idx == p->bufN )
  3685. {
  3686. cmReal_t coeff = 0.7;
  3687. cmReal_t rms = sqrt(p->sum/(n*p->bufN));
  3688. p->idx = 0;
  3689. p->sum = 0;
  3690. p->val = rms > p->val ? rms : (rms*(1.0-coeff)) + (p->val*coeff);
  3691. double db = cmMax(cmDspMeter_MIN,cmMin(cmDspMeter_MAX,20.0 * log10(p->val)));
  3692. cmDspSetDouble(ctx, inst, kValAmId, db);
  3693. }
  3694. return kOkDspRC;
  3695. }
  3696. struct cmDspClass_str* cmAMeterClassCons( cmDspCtx_t* ctx )
  3697. {
  3698. cmDspClassSetup(&_cmAMeterDC,ctx,"AMeter",
  3699. NULL,
  3700. _cmDspAMeterAlloc,
  3701. NULL,
  3702. _cmDspAMeterReset,
  3703. _cmDspAMeterExec,
  3704. NULL,
  3705. NULL,NULL,
  3706. "Audio meter display.");
  3707. return &_cmAMeterDC;
  3708. }
  3709. //==========================================================================================================================================
  3710. //
  3711. //
  3712. // Read files created by this object with the Octave function cmTextFile().
  3713. //
  3714. //
  3715. enum
  3716. {
  3717. kCntTfId,
  3718. kFnTfId,
  3719. kBaseTfId
  3720. };
  3721. cmDspClass_t _cmTextFileDC;
  3722. typedef struct
  3723. {
  3724. cmDspInst_t inst;
  3725. int inPortCnt;
  3726. cmFileH_t fH;
  3727. } cmDspTextFile_t;
  3728. cmDspInst_t* _cmDspTextFileAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3729. {
  3730. cmDspVarArg_t args[] =
  3731. {
  3732. { "cnt", kCntTfId, 0, 0, kIntDsvFl | kReqArgDsvFl, "Input port count"},
  3733. { "fn", kFnTfId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "File name"},
  3734. { NULL, 0, 0, 0, 0 }
  3735. };
  3736. if( va_cnt < 1 )
  3737. {
  3738. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The text file object must be given a input port count argument.");
  3739. return NULL;
  3740. }
  3741. va_list vl1;
  3742. va_copy(vl1,vl);
  3743. int i,j;
  3744. int inPortCnt = va_arg(vl1,int);
  3745. unsigned fixArgCnt = sizeof(args)/sizeof(args[0]) - 1;
  3746. unsigned argCnt = fixArgCnt + inPortCnt + 1;
  3747. cmDspVarArg_t argArray[ argCnt ];
  3748. if( inPortCnt <= 0 )
  3749. {
  3750. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The text file input port count must be a positive integer.");
  3751. return NULL;
  3752. }
  3753. //
  3754. for(i=0; i<fixArgCnt; ++i)
  3755. argArray[i] = args[i];
  3756. // define the input port specifications
  3757. for(j=0; j<inPortCnt; ++j,++i)
  3758. {
  3759. int labelCharCnt = 15;
  3760. cmChar_t label[ labelCharCnt + 1 ];
  3761. label[labelCharCnt] = 0;
  3762. snprintf(label,labelCharCnt,"in-%i",j);
  3763. unsigned symId = cmSymTblRegisterSymbol(ctx->stH,label);
  3764. argArray[i].label = cmSymTblLabel(ctx->stH,symId);
  3765. argArray[i].constId = kBaseTfId + j;
  3766. argArray[i].rn = 0;
  3767. argArray[i].cn = 0;
  3768. argArray[i].flags = kInDsvFl | kDoubleDsvFl | kSymDsvFl;
  3769. argArray[i].doc = "Data input";
  3770. }
  3771. // set the NULL end-of-arg-array sentinel
  3772. memset(argArray + i, 0, sizeof(argArray[0]));
  3773. cmDspTextFile_t* p = cmDspInstAlloc(cmDspTextFile_t,ctx,classPtr,argArray,instSymId,id,storeSymId,va_cnt,vl);
  3774. p->inPortCnt = inPortCnt;
  3775. p->fH = cmFileNullHandle;
  3776. return &p->inst;
  3777. }
  3778. cmDspRC_t _cmDspTextFileFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3779. {
  3780. cmDspTextFile_t* p = (cmDspTextFile_t*)inst;
  3781. if( cmFileClose(&p->fH) )
  3782. return cmErrMsg(&inst->classPtr->err, kInstFinalFailDspRC, "Text file close failed.");
  3783. return kOkDspRC;
  3784. }
  3785. cmDspRC_t _cmDspTextFileReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3786. {
  3787. cmDspRC_t rc = kOkDspRC;
  3788. cmDspTextFile_t* p = (cmDspTextFile_t*)inst;
  3789. cmDspApplyAllDefaults(ctx,inst);
  3790. const cmChar_t* fn = cmDspStrcz(inst,kFnTfId);
  3791. if( cmFileOpen( &p->fH, fn, kWriteFileFl, ctx->cmCtx->err.rpt ) != kOkFileRC )
  3792. rc = cmErrMsg(&inst->classPtr->err, kInstResetFailDspRC, "Text file open failed.");
  3793. return rc;
  3794. }
  3795. cmDspRC_t _cmDspTextFileRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3796. {
  3797. cmDspTextFile_t* p = (cmDspTextFile_t*)inst;
  3798. cmDspSetEvent(ctx,inst,evt);
  3799. if( cmFileIsValid(p->fH) && evt->dstVarId >= kBaseTfId )
  3800. {
  3801. double secs = ctx->ctx->begSmpIdx / cmDspSampleRate(ctx);
  3802. cmFilePrintf(p->fH,"%f %f %i ", secs, secs, evt->dstVarId-kBaseTfId);
  3803. if( cmIsFlag(evt->valuePtr->flags,kSymDsvFl) )
  3804. cmFilePrintf(p->fH,"%s\n", cmStringNullGuard(cmSymTblLabel(ctx->stH,cmDsvSymbol(evt->valuePtr))));
  3805. else
  3806. cmFilePrintf(p->fH,"%f\n", cmDspDouble(inst,evt->dstVarId));
  3807. }
  3808. return kOkDspRC;
  3809. }
  3810. struct cmDspClass_str* cmTextFileClassCons( cmDspCtx_t* ctx )
  3811. {
  3812. cmDspClassSetup(&_cmTextFileDC,ctx,"TextFile",
  3813. NULL,
  3814. _cmDspTextFileAlloc,
  3815. _cmDspTextFileFree,
  3816. _cmDspTextFileReset,
  3817. NULL,
  3818. _cmDspTextFileRecv,
  3819. NULL,NULL,
  3820. "Time tagged text file.");
  3821. return &_cmTextFileDC;
  3822. }
  3823. //==========================================================================================================================================
  3824. enum
  3825. {
  3826. kRsrcArId,
  3827. kCmdArId,
  3828. kIdxArId,
  3829. kValArId,
  3830. kCntArId,
  3831. kDoneArId,
  3832. kBaseOutArId
  3833. };
  3834. cmDspClass_t _cmArrayDC;
  3835. typedef struct
  3836. {
  3837. cmDspInst_t inst;
  3838. unsigned cnt;
  3839. cmReal_t* array;
  3840. unsigned printSymId;
  3841. unsigned sendSymId;
  3842. unsigned cntSymId;
  3843. unsigned doneSymId;
  3844. } cmDspArray_t;
  3845. cmDspInst_t* _cmDspArrayAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3846. {
  3847. va_list vl1;
  3848. va_copy(vl1,vl);
  3849. if( va_cnt < 1 )
  3850. {
  3851. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The array object must be given a 'rsrc' parameter.");
  3852. return NULL;
  3853. }
  3854. const cmChar_t* rsrcStr = va_arg(vl,cmChar_t*);
  3855. unsigned outPortCnt = 0;
  3856. cmReal_t* array = NULL;
  3857. unsigned doneSymId = cmDspSysRegisterStaticSymbol(ctx->dspH,"done");
  3858. unsigned i;
  3859. if( rsrcStr == NULL )
  3860. {
  3861. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The array object 'rsrc' parameter appears to be missing.");
  3862. return NULL;
  3863. }
  3864. if( cmDspRsrcRealArray( ctx->dspH, &outPortCnt, &array, rsrcStr, NULL ) != kOkDspRC )
  3865. {
  3866. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The array resource '%s' could not be read.",cmStringNullGuard(rsrcStr));
  3867. return NULL;
  3868. }
  3869. cmDspArray_t* p = cmDspInstAllocV(cmDspArray_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
  3870. 1, "rsrc", kRsrcArId, 0, 0, kStrzDsvFl | kInDsvFl | kReqArgDsvFl, "Array data resource label.",
  3871. 1, "cmd", kCmdArId, 0, 0, kSymDsvFl | kInDsvFl, "Command: send | print | count.",
  3872. 1, "idx", kIdxArId, 0, 0, kUIntDsvFl | kInDsvFl, "Send value at index out 'val' port.",
  3873. 1, "val", kValArId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Index output value.",
  3874. 1, "cnt", kCntArId, 0, 0, kUIntDsvFl | kOutDsvFl, "Count output value.",
  3875. 1, "done", kDoneArId, 0, 0, kSymDsvFl | kOutDsvFl, "'done' after last send.",
  3876. outPortCnt, "out", kBaseOutArId, 0, 0, kDoubleDsvFl | kOutDsvFl | kSendDfltDsvFl, "Individual real value outputs.",
  3877. 0 );
  3878. cmDspSetDefaultDouble( ctx, &p->inst, kValArId, 0, 0 );
  3879. cmDspSetDefaultUInt( ctx, &p->inst, kCntArId, 0, outPortCnt );
  3880. cmDspSetDefaultSymbol( ctx, &p->inst, kDoneArId, doneSymId);
  3881. for(i=0; i<outPortCnt; ++i)
  3882. cmDspSetDefaultDouble( ctx, &p->inst, kBaseOutArId+i, 0, array[i] );
  3883. p->array = array;
  3884. p->cnt = outPortCnt;
  3885. p->sendSymId = cmDspSysRegisterStaticSymbol(ctx->dspH,"send");
  3886. p->printSymId = cmDspSysRegisterStaticSymbol(ctx->dspH,"print");
  3887. p->cntSymId = cmDspSysRegisterStaticSymbol(ctx->dspH,"count");
  3888. p->doneSymId = doneSymId;
  3889. return &p->inst;
  3890. }
  3891. cmDspRC_t _cmDspArrayReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3892. {
  3893. cmDspArray_t* p = (cmDspArray_t*)inst;
  3894. cmDspRC_t rc;
  3895. // send 'out' values and then 'done' value
  3896. if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
  3897. cmDspSetSymbol(ctx,inst,kDoneArId,p->doneSymId);
  3898. return rc;
  3899. }
  3900. cmDspRC_t _cmDspArrayRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3901. {
  3902. cmDspRC_t rc;
  3903. cmDspArray_t* p = (cmDspArray_t*)inst;
  3904. if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
  3905. return rc;
  3906. switch( evt->dstVarId )
  3907. {
  3908. case kCmdArId:
  3909. {
  3910. unsigned i;
  3911. unsigned cmdSymId = cmDsvSymbol(evt->valuePtr);
  3912. if( cmdSymId == p->printSymId )
  3913. {
  3914. for(i=0; i<p->cnt; ++i)
  3915. cmRptPrintf(ctx->rpt,"%f ",p->array[i]);
  3916. cmRptPrintf(ctx->rpt,"\n");
  3917. }
  3918. else
  3919. if( cmdSymId == p->sendSymId )
  3920. {
  3921. for(i=0; i<p->cnt; ++i)
  3922. cmDspSetDouble(ctx,inst,kBaseOutArId+i,p->array[i]);
  3923. cmDspSetSymbol(ctx,inst,kDoneArId,p->doneSymId);
  3924. }
  3925. else
  3926. if( cmdSymId == p->cntSymId )
  3927. {
  3928. cmDspSetUInt(ctx,inst,kCntArId,p->cnt);
  3929. }
  3930. }
  3931. break;
  3932. case kIdxArId:
  3933. {
  3934. unsigned idx = cmDsvUInt(evt->valuePtr);
  3935. if( idx < p->cnt )
  3936. cmDspSetDouble(ctx,inst,kValArId,p->array[idx]);
  3937. }
  3938. break;
  3939. }
  3940. return kOkDspRC;
  3941. }
  3942. struct cmDspClass_str* cmArrayClassCons( cmDspCtx_t* ctx )
  3943. {
  3944. cmDspClassSetup(&_cmArrayDC,ctx,"Array",
  3945. NULL,
  3946. _cmDspArrayAlloc,
  3947. NULL,
  3948. _cmDspArrayReset,
  3949. NULL,
  3950. _cmDspArrayRecv,
  3951. NULL,NULL,
  3952. "Time tagged text file.");
  3953. return &_cmArrayDC;
  3954. }
  3955. //==========================================================================================================================================
  3956. enum
  3957. {
  3958. kMidiPcId,
  3959. kHzPcId,
  3960. kOffsPcId,
  3961. kStrPcId,
  3962. kRatioPcId
  3963. };
  3964. cmDspClass_t _cmPitchCvtDC;
  3965. typedef struct
  3966. {
  3967. cmDspInst_t inst;
  3968. int midi;
  3969. double hz;
  3970. } cmDspPitchCvt_t;
  3971. cmDspInst_t* _cmDspPitchCvtAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  3972. {
  3973. va_cnt = 0; // ignore an errant arguments
  3974. cmDspPitchCvt_t* p = cmDspInstAllocV(cmDspPitchCvt_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
  3975. 1, "midi", kMidiPcId, 0, 0, kUIntDsvFl | kInDsvFl | kOutDsvFl | kOptArgDsvFl, "MIDI pitch value input.",
  3976. 1, "hz", kHzPcId, 0, 0, kDoubleDsvFl | kInDsvFl | kOutDsvFl | kOptArgDsvFl, "Hz pitch value.",
  3977. 1, "offs", kOffsPcId, 0, 0, kDoubleDsvFl | kInDsvFl | kInDsvFl | kOptArgDsvFl, "Semitone offset.",
  3978. 1, "str", kStrPcId, 0, 0, kStrzDsvFl | kOutDsvFl, "Pitch string output.",
  3979. 1, "ratio", kRatioPcId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Offset as a ratio",
  3980. 0 );
  3981. cmDspSetDefaultUInt( ctx, &p->inst, kMidiPcId, 0, 0 );
  3982. cmDspSetDefaultDouble( ctx, &p->inst, kHzPcId, 0, 0.0 );
  3983. cmDspSetDefaultDouble( ctx, &p->inst, kOffsPcId, 0, 0 );
  3984. cmDspSetDefaultStrcz( ctx, &p->inst, kStrPcId, NULL, "" );
  3985. cmDspSetDefaultDouble( ctx, &p->inst, kRatioPcId, 0, 0 );
  3986. return &p->inst;
  3987. }
  3988. cmDspRC_t _cmDspPitchCvtReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  3989. {
  3990. cmDspRC_t rc = cmDspApplyAllDefaults(ctx,inst);
  3991. return rc;
  3992. }
  3993. cmDspRC_t _cmDspPitchCvtOutput( cmDspCtx_t* ctx, cmDspPitchCvt_t* p )
  3994. {
  3995. cmDspInst_t* inst = &p->inst;
  3996. double offs = cmDspDouble( inst, kOffsPcId );
  3997. unsigned midi = cmMax(0, p->midi + rint(offs) );
  3998. double ratio= pow(2.0,offs/12.0);
  3999. double hz = p->hz * ratio;
  4000. //cmRptPrintf(ctx->rpt,"%i %i %f %f\n",offs,midi,hz,p->hz);
  4001. cmDspSetStrcz( ctx, inst, kStrPcId, cmMidiToSciPitch(midi,NULL,0));
  4002. cmDspSetUInt( ctx, inst, kMidiPcId, midi);
  4003. cmDspSetDouble( ctx, inst, kHzPcId, hz);
  4004. cmDspSetDouble( ctx, inst, kRatioPcId, ratio );
  4005. return kOkDspRC;
  4006. }
  4007. cmDspRC_t _cmDspPitchCvtRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4008. {
  4009. cmDspRC_t rc = kOkDspRC;
  4010. cmDspPitchCvt_t* p = (cmDspPitchCvt_t*)inst;
  4011. switch( evt->dstVarId )
  4012. {
  4013. case kMidiPcId:
  4014. p->midi = cmMax(0,cmDsvGetInt(evt->valuePtr));
  4015. p->hz = cmMidiToHz(p->midi);
  4016. rc = _cmDspPitchCvtOutput(ctx,p);
  4017. break;
  4018. case kHzPcId:
  4019. p->hz = cmDsvGetDouble(evt->valuePtr);
  4020. p->midi = cmHzToMidi(p->hz);
  4021. rc = _cmDspPitchCvtOutput(ctx,p);
  4022. break;
  4023. case kOffsPcId:
  4024. if((rc = cmDspSetEvent(ctx, inst, evt )) == kOkDspRC )
  4025. rc = _cmDspPitchCvtOutput(ctx,p);
  4026. break;
  4027. }
  4028. return rc;
  4029. }
  4030. struct cmDspClass_str* cmPitchCvtClassCons( cmDspCtx_t* ctx )
  4031. {
  4032. cmDspClassSetup(&_cmPitchCvtDC,ctx,"PitchCvt",
  4033. NULL,
  4034. _cmDspPitchCvtAlloc,
  4035. NULL,
  4036. _cmDspPitchCvtReset,
  4037. NULL,
  4038. _cmDspPitchCvtRecv,
  4039. NULL,NULL,
  4040. "Time tagged text file.");
  4041. return &_cmPitchCvtDC;
  4042. }
  4043. //==========================================================================================================================================
  4044. //
  4045. //
  4046. // Create a file which can be read by readBinFile.m
  4047. //
  4048. //
  4049. enum
  4050. {
  4051. kCntBmId,
  4052. kFnBmId,
  4053. kBaseBmId
  4054. };
  4055. cmDspClass_t _cmBinMtxFileDC;
  4056. typedef struct
  4057. {
  4058. cmDspInst_t inst;
  4059. int inPortCnt;
  4060. cmBinMtxFile_t* bmfp;
  4061. cmReal_t * valArray; // valArray[ inPortCnt ]
  4062. } cmDspBinMtxFile_t;
  4063. cmDspInst_t* _cmDspBinMtxFileAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  4064. {
  4065. cmDspVarArg_t args[] =
  4066. {
  4067. { "cnt", kCntBmId, 0, 0, kIntDsvFl | kReqArgDsvFl, "Input port count"},
  4068. { "fn", kFnBmId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "File name"},
  4069. };
  4070. if( va_cnt < 1 )
  4071. {
  4072. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The binary matrix file object must be given a input port count argument.");
  4073. return NULL;
  4074. }
  4075. va_list vl1;
  4076. va_copy(vl1,vl);
  4077. int inPortCnt = va_arg(vl,int);
  4078. unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
  4079. unsigned argCnt = fixArgCnt + inPortCnt;
  4080. cmDspVarArg_t a[ argCnt+1 ];
  4081. cmDspBinMtxFile_t* p = NULL;
  4082. if( inPortCnt <= 0 )
  4083. {
  4084. cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The binary matrix file must be a positive integer.");
  4085. return NULL;
  4086. }
  4087. cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
  4088. cmDspArgSetupN(ctx, a, argCnt, kBaseBmId, inPortCnt, "in", kBaseBmId, 0, 0, kInDsvFl | kDoubleDsvFl, "input ports");
  4089. cmDspArgSetupNull( a+argCnt );
  4090. if((p = cmDspInstAlloc(cmDspBinMtxFile_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1)) == NULL )
  4091. return NULL;
  4092. p->bmfp = cmBinMtxFileAlloc(ctx->cmProcCtx, NULL, NULL );
  4093. p->inPortCnt = inPortCnt;
  4094. p->valArray = cmMemAllocZ(cmReal_t,inPortCnt);
  4095. return &p->inst;
  4096. }
  4097. cmDspRC_t _cmDspBinMtxFileOpen( cmDspCtx_t* ctx, cmDspInst_t* inst )
  4098. {
  4099. cmDspRC_t rc = kOkDspRC;
  4100. cmDspBinMtxFile_t* p = (cmDspBinMtxFile_t*)inst;
  4101. if( p->bmfp != NULL )
  4102. {
  4103. // finalize the current file
  4104. if( cmBinMtxFileFinal(p->bmfp) != cmOkRC )
  4105. cmDspInstErr(ctx,inst,kFileCloseFailDspRC,"File close failed.");
  4106. // open a new one
  4107. if( cmBinMtxFileInit( p->bmfp, cmDspDefaultStrcz(&p->inst,kFnBmId) ) != cmOkRC)
  4108. rc = cmDspInstErr(ctx,inst,kFileOpenFailDspRC,"File open failed.");
  4109. }
  4110. return rc;
  4111. }
  4112. cmDspRC_t _cmDspBinMtxFileFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4113. {
  4114. cmDspBinMtxFile_t* p = (cmDspBinMtxFile_t*)inst;
  4115. cmBinMtxFileFree(&p->bmfp);
  4116. cmMemFree(p->valArray);
  4117. return kOkDspRC;
  4118. }
  4119. cmDspRC_t _cmDspBinMtxFileReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4120. {
  4121. cmDspApplyAllDefaults(ctx,inst);
  4122. return _cmDspBinMtxFileOpen(ctx,inst);
  4123. }
  4124. cmDspRC_t _cmDspBinMtxFileExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4125. {
  4126. cmDspRC_t rc = kOkDspRC;
  4127. cmDspBinMtxFile_t* p = (cmDspBinMtxFile_t*)inst;
  4128. // write the file
  4129. if( cmBinMtxFileIsValid( p->bmfp ) )
  4130. if( cmBinMtxFileExecR(p->bmfp, p->valArray, p->inPortCnt ) != cmOkRC )
  4131. return cmDspInstErr(ctx,inst,kFileWriteFailDspRC,"File write failure.");
  4132. return rc;
  4133. }
  4134. cmDspRC_t _cmDspBinMtxFileRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4135. {
  4136. cmDspRC_t rc = kOkDspRC;
  4137. cmDspBinMtxFile_t* p = (cmDspBinMtxFile_t*)inst;
  4138. cmDspSetEvent(ctx,inst,evt);
  4139. // new file name - create new output file
  4140. if( evt->dstVarId == kFnBmId )
  4141. {
  4142. rc = _cmDspBinMtxFileOpen(ctx,inst);
  4143. }
  4144. else
  4145. // new value - store in p->valArray[]
  4146. if( kBaseBmId <= evt->dstVarId && evt->dstVarId < kBaseBmId + p->inPortCnt )
  4147. {
  4148. p->valArray[ evt->dstVarId - kBaseBmId ] = cmDspDouble(inst,evt->dstVarId );
  4149. }
  4150. else
  4151. { assert(0); }
  4152. return rc;
  4153. }
  4154. struct cmDspClass_str* cmBinMtxFileClassCons( cmDspCtx_t* ctx )
  4155. {
  4156. cmDspClassSetup(&_cmBinMtxFileDC,ctx,"BinMtxFile",
  4157. NULL,
  4158. _cmDspBinMtxFileAlloc,
  4159. _cmDspBinMtxFileFree,
  4160. _cmDspBinMtxFileReset,
  4161. _cmDspBinMtxFileExec,
  4162. _cmDspBinMtxFileRecv,
  4163. NULL,NULL,
  4164. "Time tagged text file.");
  4165. return &_cmBinMtxFileDC;
  4166. }
  4167. //==========================================================================================================================================
  4168. enum
  4169. {
  4170. kHopMsSbId,
  4171. kWndFactSbId,
  4172. kInSbId,
  4173. kOutSbId
  4174. };
  4175. cmDspClass_t _cmShiftBufDC;
  4176. typedef struct
  4177. {
  4178. cmDspInst_t inst;
  4179. cmShiftBuf* sbp;
  4180. } cmDspShiftBuf_t;
  4181. void _cmDspShiftBufSetup( cmDspCtx_t* ctx, cmDspShiftBuf_t* p )
  4182. {
  4183. double hopMs = cmDspDouble(&p->inst,kHopMsSbId);
  4184. unsigned hopSmpCnt = lround(cmDspSampleRate(ctx) * hopMs / 1000.0 );
  4185. unsigned wndSmpCnt = cmDspUInt(&p->inst,kWndFactSbId) * hopSmpCnt;
  4186. if( p->sbp == NULL || hopSmpCnt != p->sbp->hopSmpCnt || wndSmpCnt != p->sbp->wndSmpCnt )
  4187. {
  4188. cmShiftBufFree(&p->sbp);
  4189. p->sbp = cmShiftBufAlloc(ctx->cmProcCtx, NULL, cmDspSamplesPerCycle(ctx), wndSmpCnt, hopSmpCnt );
  4190. }
  4191. }
  4192. cmDspInst_t* _cmDspShiftBufAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  4193. {
  4194. cmDspVarArg_t args[] =
  4195. {
  4196. { "hopMs", kHopMsSbId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Hop size on milliseconds"},
  4197. { "wndFact", kWndFactSbId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Count of hops contained in each output buffer."},
  4198. { "in", kInSbId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input"},
  4199. { "out", kOutSbId, 0, 0, kOutDsvFl | kAudioBufDsvFl, "Audio output"},
  4200. { NULL, 0, 0, 0, 0 }
  4201. };
  4202. // Note: by setting the column count of the output audio variable to zero
  4203. // we prevent it from being automatically assigned vector memory.
  4204. cmDspShiftBuf_t* p = cmDspInstAlloc(cmDspShiftBuf_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  4205. return &p->inst;
  4206. }
  4207. cmDspRC_t _cmDspShiftBufFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4208. {
  4209. cmDspShiftBuf_t* p = (cmDspShiftBuf_t*)inst;
  4210. cmShiftBufFree(&p->sbp);
  4211. return kOkDspRC;
  4212. }
  4213. cmDspRC_t _cmDspShiftBufReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4214. {
  4215. cmDspRC_t rc = kOkDspRC;
  4216. cmDspShiftBuf_t* p = (cmDspShiftBuf_t*)inst;
  4217. cmDspApplyAllDefaults(ctx,inst);
  4218. _cmDspShiftBufSetup(ctx,p);
  4219. return rc;
  4220. }
  4221. cmDspRC_t _cmDspShiftBufRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4222. {
  4223. cmDspShiftBuf_t* p = (cmDspShiftBuf_t*)inst;
  4224. cmDspSetEvent(ctx,inst,evt);
  4225. switch(evt->dstVarId)
  4226. {
  4227. case kHopMsSbId:
  4228. case kWndFactSbId:
  4229. _cmDspShiftBufSetup( ctx, p );
  4230. break;
  4231. };
  4232. return kOkDspRC;
  4233. }
  4234. cmDspRC_t _cmDspShiftBufExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4235. {
  4236. cmDspShiftBuf_t* p = (cmDspShiftBuf_t*)inst;
  4237. unsigned sn = cmDspAudioBufSmpCount(ctx,inst,kInSbId,0);
  4238. const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kInSbId,0);
  4239. cmDspVar_t* varPtr = cmDspVarIdToPtr(inst,kOutSbId);
  4240. if( cmShiftBufExec(p->sbp, sp, sn ) )
  4241. cmDsvSetSampleMtx( &varPtr->value, p->sbp->outV, p->sbp->outN, 1);
  4242. else
  4243. cmDsvSetSampleMtx( &varPtr->value, NULL, 0, 0);
  4244. return kOkDspRC;
  4245. }
  4246. struct cmDspClass_str* cmShiftBufClassCons( cmDspCtx_t* ctx )
  4247. {
  4248. cmDspClassSetup(&_cmShiftBufDC,ctx,"ShiftBuf",
  4249. NULL,
  4250. _cmDspShiftBufAlloc,
  4251. _cmDspShiftBufFree,
  4252. _cmDspShiftBufReset,
  4253. _cmDspShiftBufExec,
  4254. _cmDspShiftBufRecv,
  4255. NULL,NULL,
  4256. "Time tagged text file.");
  4257. return &_cmShiftBufDC;
  4258. }
  4259. //==========================================================================================================================================
  4260. enum
  4261. {
  4262. kInNsId
  4263. };
  4264. cmDspClass_t _cmNetSendDC;
  4265. typedef struct
  4266. {
  4267. cmDspInst_t inst;
  4268. _cmDspSrcConn_t* srcConnPtr;
  4269. } cmDspNetSend_t;
  4270. cmDspInst_t* _cmDspNetSendAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  4271. {
  4272. cmDspVarArg_t args[] =
  4273. {
  4274. { "in", kInNsId, 0, 0, kInDsvFl | kTypeDsvMask, "Input port" },
  4275. { NULL, 0, 0, 0, 0 }
  4276. };
  4277. assert( va_cnt == 1 );
  4278. _cmDspSrcConn_t* srcConnPtr = va_arg(vl,_cmDspSrcConn_t*);
  4279. assert( srcConnPtr != NULL );
  4280. cmDspNetSend_t* p = cmDspInstAlloc(cmDspNetSend_t,ctx,classPtr,args,instSymId,id,storeSymId,0,vl);
  4281. p->srcConnPtr = srcConnPtr;
  4282. return &p->inst;
  4283. }
  4284. cmDspRC_t _cmDspNetSendReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4285. {
  4286. return kOkDspRC;
  4287. }
  4288. cmDspRC_t _cmDspNetSendRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4289. {
  4290. cmDspNetSend_t* p = (cmDspNetSend_t*)inst;
  4291. return _cmDspSysNetSendEvent(ctx->dspH, p->srcConnPtr->dstNetNodeId, p->srcConnPtr->dstId, evt );
  4292. }
  4293. struct cmDspClass_str* cmNetSendClassCons( cmDspCtx_t* ctx )
  4294. {
  4295. cmDspClassSetup(&_cmNetSendDC,ctx,"NetSend",
  4296. NULL,
  4297. _cmDspNetSendAlloc,
  4298. NULL,
  4299. _cmDspNetSendReset,
  4300. NULL,
  4301. _cmDspNetSendRecv,
  4302. NULL,NULL,
  4303. "Print the value of any event arriving at 'in'.");
  4304. return &_cmNetSendDC;
  4305. }
  4306. //==========================================================================================================================================
  4307. enum
  4308. {
  4309. kBaseInPtsId,
  4310. };
  4311. cmDspClass_t _cmRsrcWrDC;
  4312. typedef struct
  4313. {
  4314. cmDspInst_t inst;
  4315. char** pathArray;
  4316. unsigned pathCnt;
  4317. } cmDspRsrcWr_t;
  4318. cmDspInst_t* _cmDspRsrcWrAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  4319. {
  4320. va_list vl1;
  4321. va_copy(vl1,vl);
  4322. if( va_cnt < 1 )
  4323. {
  4324. cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'RsrcWr' constructor argument list must contain at least one resource path specificiation.");
  4325. return NULL;
  4326. }
  4327. unsigned pathCnt = va_cnt;
  4328. unsigned argCnt = pathCnt;
  4329. cmDspVarArg_t args[argCnt+1];
  4330. char** pathArray = cmMemAllocZ(char*,pathCnt);
  4331. unsigned i;
  4332. for(i=0; i<pathCnt; ++i)
  4333. {
  4334. // get the path
  4335. const cmChar_t* pathLabel = va_arg(vl,const char*);
  4336. assert( pathLabel != NULL );
  4337. // store the path name
  4338. pathArray[i] = cmMemAllocStr(pathLabel);
  4339. cmDspArgSetup(ctx, args+kBaseInPtsId+i, pathLabel, cmInvalidId, kBaseInPtsId+i, 0, 0, kInDsvFl | kTypeDsvMask, cmTsPrintfH(ctx->lhH,"%s Input.",pathLabel) );
  4340. }
  4341. cmDspArgSetupNull(args + argCnt);
  4342. cmDspRsrcWr_t* p = cmDspInstAlloc(cmDspRsrcWr_t,ctx,classPtr,args,instSymId,id,storeSymId,0,vl1);
  4343. p->pathCnt = pathCnt;
  4344. p->pathArray = pathArray;
  4345. return &p->inst;
  4346. }
  4347. cmDspRC_t _cmDspRsrcWrFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4348. {
  4349. cmDspRsrcWr_t* p = (cmDspRsrcWr_t*)inst;
  4350. int i;
  4351. for(i=0; i<p->pathCnt; ++i)
  4352. cmMemFree(p->pathArray[i]);
  4353. cmMemFree(p->pathArray);
  4354. return kOkDspRC;
  4355. }
  4356. cmDspRC_t _cmDspRsrcWrReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4357. {
  4358. return kOkDspRC;
  4359. }
  4360. cmDspRC_t _cmDspRsrcWrRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4361. {
  4362. cmDspRsrcWr_t* p = (cmDspRsrcWr_t*)inst;
  4363. cmDspRC_t rc = kOkDspRC;
  4364. // if a msg of any type is recieved on an input port - send out the associated symbol
  4365. if( kBaseInPtsId <= evt->dstVarId && evt->dstVarId < kBaseInPtsId + p->pathCnt )
  4366. {
  4367. unsigned idx = evt->dstVarId - kBaseInPtsId;
  4368. assert( idx < p->pathCnt );
  4369. if( cmDsvIsStrz(evt->valuePtr) )
  4370. {
  4371. rc = cmDspRsrcWriteString( ctx->dspH, cmDsvStrz(evt->valuePtr), p->pathArray[idx], NULL );
  4372. }
  4373. }
  4374. return rc;
  4375. }
  4376. struct cmDspClass_str* cmRsrcWrClassCons( cmDspCtx_t* ctx )
  4377. {
  4378. cmDspClassSetup(&_cmRsrcWrDC,ctx,"RsrcWr",
  4379. NULL,
  4380. _cmDspRsrcWrAlloc,
  4381. _cmDspRsrcWrFree,
  4382. _cmDspRsrcWrReset,
  4383. NULL,
  4384. _cmDspRsrcWrRecv,
  4385. NULL,
  4386. NULL,
  4387. "Set the value of a resource variable.");
  4388. return &_cmRsrcWrDC;
  4389. }
  4390. //==========================================================================================================================================
  4391. enum
  4392. {
  4393. kModeBeId,
  4394. kAzimBeId,
  4395. kElevBeId,
  4396. kDistBeId,
  4397. kAudioInBeId,
  4398. kAudioOut0BeId,
  4399. kAudioOut1BeId
  4400. };
  4401. typedef struct
  4402. {
  4403. cmDspInst_t inst;
  4404. cmBinEnc* bep;
  4405. } cmDspBinEnc_t;
  4406. cmDspClass_t _cmBeDC;
  4407. cmDspInst_t* _cmDspBinEncAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  4408. {
  4409. cmDspVarArg_t args[] =
  4410. {
  4411. { "mode", kModeBeId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Mode" },
  4412. { "azim", kAzimBeId, 0, 0, kInDsvFl | kDoubleDsvFl, "Azimuth" },
  4413. { "elev", kElevBeId, 0, 0, kInDsvFl | kDoubleDsvFl, "Elevation" },
  4414. { "dist", kDistBeId, 0, 0, kInDsvFl | kDoubleDsvFl, "Distance" },
  4415. { "in", kAudioInBeId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio Input" },
  4416. { "out0", kAudioOut0BeId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio Output 0" },
  4417. { "out1", kAudioOut1BeId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio Output 1" },
  4418. { NULL, 0, 0, 0, 0 }
  4419. };
  4420. cmDspBinEnc_t* p = cmDspInstAlloc(cmDspBinEnc_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  4421. cmDspSetDefaultUInt( ctx,&p->inst, kModeBeId, 0, 0.0 );
  4422. cmDspSetDefaultDouble( ctx,&p->inst, kAzimBeId, 0, 0.0 );
  4423. cmDspSetDefaultDouble( ctx,&p->inst, kElevBeId, 0, 0.0 );
  4424. cmDspSetDefaultDouble( ctx,&p->inst, kDistBeId, 0, 0.0 );
  4425. return &p->inst;
  4426. }
  4427. cmDspRC_t _cmDspBinEncFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4428. {
  4429. cmDspRC_t rc = kOkDspRC;
  4430. cmDspBinEnc_t* p = (cmDspBinEnc_t*)inst;
  4431. cmBinEncFree(&p->bep);
  4432. return rc;
  4433. }
  4434. cmDspRC_t _cmDspBinEncSetup(cmDspCtx_t* ctx, cmDspBinEnc_t* p )
  4435. {
  4436. cmDspRC_t rc = kOkDspRC;
  4437. cmBinEncFree(&p->bep);
  4438. p->bep = cmBinEncAlloc(ctx->cmProcCtx,NULL,cmDspSampleRate(ctx), cmDspSamplesPerCycle(ctx));
  4439. return rc;
  4440. }
  4441. cmDspRC_t _cmDspBinEncReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4442. {
  4443. cmDspBinEnc_t* p = (cmDspBinEnc_t*)inst;
  4444. cmDspRC_t rc;
  4445. if((rc = cmDspApplyAllDefaults(ctx,inst)) != kOkDspRC )
  4446. return rc;
  4447. return _cmDspBinEncSetup(ctx,p);
  4448. }
  4449. cmDspRC_t _cmDspBinEncExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4450. {
  4451. cmDspBinEnc_t* p = (cmDspBinEnc_t*)inst;
  4452. cmDspRC_t rc = kOkDspRC;
  4453. unsigned iChIdx = 0;
  4454. const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kAudioInBeId,iChIdx);
  4455. unsigned iSmpCnt = cmDspVarRows(inst,kAudioInBeId);
  4456. // if no connected
  4457. if( iSmpCnt == 0 )
  4458. return rc;
  4459. unsigned oChIdx = 0;
  4460. cmSample_t* o0p = cmDspAudioBuf(ctx,inst,kAudioOut0BeId,oChIdx);
  4461. unsigned oSmp0Cnt = cmDspVarRows(inst,kAudioOut0BeId);
  4462. cmSample_t* o1p = cmDspAudioBuf(ctx,inst,kAudioOut1BeId,oChIdx);
  4463. unsigned oSmp1Cnt = cmDspVarRows(inst,kAudioOut0BeId);
  4464. assert( iSmpCnt==oSmp0Cnt && iSmpCnt==oSmp1Cnt );
  4465. cmBinEncExec( p->bep, ip, o0p, o1p, iSmpCnt );
  4466. return rc;
  4467. }
  4468. cmDspRC_t _cmDspBinEncRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4469. {
  4470. cmDspBinEnc_t* p = (cmDspBinEnc_t*)inst;
  4471. cmDspRC_t rc = kOkDspRC;
  4472. cmDspSetEvent(ctx,inst,evt);
  4473. switch( evt->dstVarId )
  4474. {
  4475. case kModeBeId:
  4476. cmBinEncSetMode(p->bep, cmDspUInt(inst,kModeBeId));
  4477. break;
  4478. case kAzimBeId:
  4479. case kElevBeId:
  4480. case kDistBeId:
  4481. {
  4482. float azim = cmDspDouble(inst,kAzimBeId);
  4483. float elev = cmDspDouble(inst,kElevBeId);
  4484. float dist = cmDspDouble(inst,kDistBeId);
  4485. cmBinEncSetLoc(p->bep, azim, elev, dist );
  4486. }
  4487. break;
  4488. default:
  4489. { assert(0); }
  4490. }
  4491. return rc;
  4492. }
  4493. struct cmDspClass_str* cmBinEncClassCons( cmDspCtx_t* ctx )
  4494. {
  4495. cmDspClassSetup(&_cmBeDC,ctx,"BinauralEnc",
  4496. NULL,
  4497. _cmDspBinEncAlloc,
  4498. _cmDspBinEncFree,
  4499. _cmDspBinEncReset,
  4500. _cmDspBinEncExec,
  4501. _cmDspBinEncRecv,
  4502. NULL,NULL,
  4503. "Binaural filter.");
  4504. return &_cmBeDC;
  4505. }
  4506. //==========================================================================================================================================
  4507. enum
  4508. {
  4509. kX2dId,
  4510. kY2dId,
  4511. kRadius2dId,
  4512. kAngle2dId
  4513. };
  4514. cmDspClass_t _cm2dDC;
  4515. typedef struct
  4516. {
  4517. cmDspInst_t inst;
  4518. } cmDsp2d_t;
  4519. cmDspInst_t* _cmDsp2dAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  4520. {
  4521. cmDspVarArg_t args[] =
  4522. {
  4523. { "x", kX2dId, 0, 0, kOutDsvFl | kDoubleDsvFl, "X coordinate" },
  4524. { "y", kY2dId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Y coordinate"},
  4525. { "radius",kRadius2dId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Radius"},
  4526. { "angle", kAngle2dId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Angle"},
  4527. { NULL, 0, 0, 0, 0 }
  4528. };
  4529. cmDsp2d_t* p = cmDspInstAlloc(cmDsp2d_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  4530. cmDspSetDefaultDouble(ctx, &p->inst, kX2dId, 0.0, 0.0);
  4531. cmDspSetDefaultDouble(ctx, &p->inst, kY2dId, 0.0, 0.0);
  4532. cmDspSetDefaultDouble(ctx, &p->inst, kRadius2dId, 0.0, 0.0);
  4533. cmDspSetDefaultDouble(ctx, &p->inst, kAngle2dId, 0.0, 0.0);
  4534. // create the UI control
  4535. cmDspUi2dCreate(ctx,&p->inst,kX2dId,kY2dId,kRadius2dId,kAngle2dId);
  4536. return &p->inst;
  4537. }
  4538. cmDspRC_t _cmDsp2dReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4539. {
  4540. cmDspApplyAllDefaults(ctx,inst);
  4541. return kOkDspRC;
  4542. }
  4543. cmDspRC_t _cmDsp2dRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  4544. {
  4545. cmDspSetEvent(ctx,inst,evt);
  4546. return kOkDspRC;
  4547. }
  4548. struct cmDspClass_str* cm2dClassCons( cmDspCtx_t* ctx )
  4549. {
  4550. cmDspClassSetup(&_cm2dDC,ctx,"twod",
  4551. NULL,
  4552. _cmDsp2dAlloc,
  4553. NULL,
  4554. _cmDsp2dReset,
  4555. NULL,
  4556. _cmDsp2dRecv,
  4557. NULL,
  4558. NULL,
  4559. "2d value control.");
  4560. return &_cm2dDC;
  4561. }
  4562. //==========================================================================================================================================
  4563. //==========================================================================================================================================
  4564. cmDspClassConsFunc_t _cmDspClassBuiltInArray[] =
  4565. {
  4566. cmPrinterClassCons,
  4567. cmCounterClassCons,
  4568. cmPhasorClassCons,
  4569. cmMidiOutClassCons,
  4570. cmMidiInClassCons,
  4571. cmAudioInClassCons,
  4572. cmAudioOutClassCons,
  4573. cmAudioFileOutClassCons,
  4574. cmSigGenClassCons,
  4575. cmScalarClassCons,
  4576. cmTextClassCons,
  4577. cmMeterClassCons,
  4578. cmLabelClassCons,
  4579. cmButtonClassCons,
  4580. cmCheckboxClassCons,
  4581. cmReorderClassCons,
  4582. cmFnameClassCons,
  4583. cmMsgListClassCons,
  4584. cmWaveTableClassCons,
  4585. cmSprintfClassCons,
  4586. cmAMixClassCons,
  4587. cmASplitClassCons,
  4588. cmAMeterClassCons,
  4589. cmTextFileClassCons,
  4590. cmBinMtxFileClassCons,
  4591. cmArrayClassCons,
  4592. cmPitchCvtClassCons,
  4593. cmShiftBufClassCons,
  4594. cmNetSendClassCons,
  4595. cmRsrcWrClassCons,
  4596. cmBinEncClassCons,
  4597. cm2dClassCons,
  4598. cmDelayClassCons,
  4599. cmPShiftClassCons,
  4600. cmLoopRecdClassCons,
  4601. cmRectifyClassCons,
  4602. cmGateDetectClassCons,
  4603. cmAutoGainClassCons,
  4604. cmEnvFollowClassCons,
  4605. cmXfaderClassCons,
  4606. cmChCfgClassCons,
  4607. cmChordDetectClassCons,
  4608. cmFaderClassCons,
  4609. cmNoteSelectClassCons,
  4610. cmNetNoteSelectClassCons,
  4611. cmCombFiltClassCons,
  4612. cmScalarOpClassCons,
  4613. cmGroupSelClassCons,
  4614. cmAudioNofMClassCons,
  4615. cmRingModClassCons,
  4616. cmMsgDelayClassCons,
  4617. cmLineClassCons,
  4618. cmAdsrClassCons,
  4619. cmCompressorClassCons,
  4620. cmBiQuadEqClassCons,
  4621. cmDistDsClassCons,
  4622. cmDbToLinClassCons,
  4623. cmMtDelayClassCons,
  4624. cmNofMClassCons,
  4625. cm1ofNClassCons,
  4626. cm1UpClassCons,
  4627. cmGateToSymClassCons,
  4628. cmPortToSymClassCons,
  4629. cmRouterClassCons,
  4630. cmAvailChClassCons,
  4631. cmPresetClassCons,
  4632. cmBcastSymClassCons,
  4633. cmSegLineClassCons,
  4634. cmKrClassCons,
  4635. cmTimeLineClassCons,
  4636. cmScoreClassCons,
  4637. cmMidiFilePlayClassCons,
  4638. cmScFolClassCons,
  4639. cmScModClassCons,
  4640. cmGSwitchClassCons,
  4641. cmScaleRangeClassCons,
  4642. cmActiveMeasClassCons,
  4643. cmAmSyncClassCons,
  4644. cmNanoMapClassCons,
  4645. cmRecdPlayClassCons,
  4646. cmGoertzelClassCons,
  4647. cmSyncRecdClassCons,
  4648. cmTakeSeqBldrClassCons,
  4649. cmTakeSeqRendClassCons,
  4650. cmReflectCalcClassCons,
  4651. cmEchoCancelClassCons,
  4652. NULL,
  4653. };
  4654. cmDspClassConsFunc_t cmDspClassGetBuiltIn( unsigned index )
  4655. {
  4656. unsigned n = sizeof(_cmDspClassBuiltInArray)/sizeof(cmDspClass_t*);
  4657. if( index >= n )
  4658. return NULL;
  4659. return _cmDspClassBuiltInArray[index];
  4660. }