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.

cmDspBuiltIn.c 159KB

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