1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252 |
- /*
- * $Id: pa_asio.cpp 1890 2013-05-02 01:06:01Z rbencina $
- * Portable Audio I/O Library for ASIO Drivers
- *
- * Author: Stephane Letz
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina
- * Blocking i/o implementation by Sven Fischer, Institute of Hearing
- * Technology and Audiology (www.hoertechnik-audiologie.de)
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /*
- * The text above constitutes the entire PortAudio license; however,
- * the PortAudio community also makes the following non-binding requests:
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version. It is also
- * requested that these non-binding requests be included along with the
- * license above.
- */
- /* Modification History
- 08-03-01 First version : Stephane Letz
- 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
- 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
- 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
- 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
- 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
- the stream is stopped : Stephane Letz
- 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
- the stream is stopped : Stephane Letz
- 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
- respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
- 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
- 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
- 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
- 10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
- 11-06-01 Rename functions : Stephane Letz
- 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
- 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
- 01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
- 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz
- 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz
- 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz
- 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk
- 13-04-02 Removes another compiler warning : Stephane Letz
- 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz
- 12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina
- 18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B.
- 21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina
- ** NOTE maintanance history is now stored in CVS **
- */
- /** @file
- @ingroup hostapi_src
- Note that specific support for paInputUnderflow, paOutputOverflow and
- paNeverDropInput is not necessary or possible with this driver due to the
- synchronous full duplex double-buffered architecture of ASIO.
- */
- #include <stdio.h>
- #include <assert.h>
- #include <string.h>
- //#include <values.h>
- #include <new>
- #include <windows.h>
- #include <mmsystem.h>
- #include "portaudio.h"
- #include "pa_asio.h"
- #include "pa_util.h"
- #include "pa_allocation.h"
- #include "pa_hostapi.h"
- #include "pa_stream.h"
- #include "pa_cpuload.h"
- #include "pa_process.h"
- #include "pa_debugprint.h"
- #include "pa_ringbuffer.h"
- #include "pa_win_coinitialize.h"
- /* This version of pa_asio.cpp is currently only targetted at Win32,
- It would require a few tweaks to work with pre-OS X Macintosh.
- To make configuration easier, we define WIN32 here to make sure
- that the ASIO SDK knows this is Win32.
- */
- #ifndef WIN32
- #define WIN32
- #endif
- #include "asiosys.h"
- #include "asio.h"
- #include "asiodrivers.h"
- #include "iasiothiscallresolver.h"
- /*
- #if MAC
- #include <Devices.h>
- #include <Timer.h>
- #include <Math64.h>
- #else
- */
- /*
- #include <math.h>
- #include <windows.h>
- #include <mmsystem.h>
- */
- /*
- #endif
- */
- /* winmm.lib is needed for timeGetTime() (this is in winmm.a if you're using gcc) */
- #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
- #pragma comment(lib, "winmm.lib")
- #endif
- /* external reference to ASIO SDK's asioDrivers.
- This is a bit messy because we want to explicitly manage
- allocation/deallocation of this structure, but some layers of the SDK
- which we currently use (eg the implementation in asio.cpp) still
- use this global version.
- For now we keep it in sync with our local instance in the host
- API representation structure, but later we should be able to remove
- all dependence on it.
- */
- extern AsioDrivers* asioDrivers;
- /* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
- /* not tested at all since new V19 code was introduced. */
- #define CARBON_COMPATIBLE (0)
- /* prototypes for functions declared in this file */
- extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
- static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
- static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
- static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
- static PaError CloseStream( PaStream* stream );
- static PaError StartStream( PaStream *stream );
- static PaError StopStream( PaStream *stream );
- static PaError AbortStream( PaStream *stream );
- static PaError IsStreamStopped( PaStream *s );
- static PaError IsStreamActive( PaStream *stream );
- static PaTime GetStreamTime( PaStream *stream );
- static double GetStreamCpuLoad( PaStream* stream );
- static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
- static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
- static signed long GetStreamReadAvailable( PaStream* stream );
- static signed long GetStreamWriteAvailable( PaStream* stream );
- /* Blocking i/o callback function. */
- static int BlockingIoPaCallback(const void *inputBuffer ,
- void *outputBuffer ,
- unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo *timeInfo ,
- PaStreamCallbackFlags statusFlags ,
- void *userData );
- /* our ASIO callback functions */
- static void bufferSwitch(long index, ASIOBool processNow);
- static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
- static void sampleRateChanged(ASIOSampleRate sRate);
- static long asioMessages(long selector, long value, void* message, double* opt);
- static ASIOCallbacks asioCallbacks_ =
- { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo };
- #define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \
- PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText )
- static void PaAsio_SetLastSystemError( DWORD errorCode )
- {
- LPVOID lpMsgBuf;
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- errorCode,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf,
- 0,
- NULL
- );
- PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf );
- LocalFree( lpMsgBuf );
- }
- #define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \
- PaAsio_SetLastSystemError( errorCode )
- static const char* PaAsio_GetAsioErrorText( ASIOError asioError )
- {
- const char *result;
- switch( asioError ){
- case ASE_OK:
- case ASE_SUCCESS: result = "Success"; break;
- case ASE_NotPresent: result = "Hardware input or output is not present or available"; break;
- case ASE_HWMalfunction: result = "Hardware is malfunctioning"; break;
- case ASE_InvalidParameter: result = "Input parameter invalid"; break;
- case ASE_InvalidMode: result = "Hardware is in a bad mode or used in a bad mode"; break;
- case ASE_SPNotAdvancing: result = "Hardware is not running when sample position is inquired"; break;
- case ASE_NoClock: result = "Sample clock or rate cannot be determined or is not present"; break;
- case ASE_NoMemory: result = "Not enough memory for completing the request"; break;
- default: result = "Unknown ASIO error"; break;
- }
- return result;
- }
- #define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \
- PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) )
- // Atomic increment and decrement operations
- #if MAC
- /* need to be implemented on Mac */
- inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
- inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
- #elif WINDOWS
- inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
- inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
- #endif
- typedef struct PaAsioDriverInfo
- {
- ASIODriverInfo asioDriverInfo;
- long inputChannelCount, outputChannelCount;
- long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity;
- bool postOutput;
- }
- PaAsioDriverInfo;
- /* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */
- typedef struct
- {
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
- PaUtilAllocationGroup *allocations;
- PaWinUtilComInitializationResult comInitializationResult;
- AsioDrivers *asioDrivers;
- void *systemSpecific;
-
- /* the ASIO C API only allows one ASIO driver to be open at a time,
- so we keep track of whether we have the driver open here, and
- use this information to return errors from OpenStream if the
- driver is already open.
- openAsioDeviceIndex will be PaNoDevice if there is no device open
- and a valid pa_asio (not global) device index otherwise.
- openAsioDriverInfo is populated with the driver info for the
- currently open device (if any)
- */
- PaDeviceIndex openAsioDeviceIndex;
- PaAsioDriverInfo openAsioDriverInfo;
- }
- PaAsioHostApiRepresentation;
- /*
- Retrieve <driverCount> driver names from ASIO, returned in a char**
- allocated in <group>.
- */
- static char **GetAsioDriverNames( PaAsioHostApiRepresentation *asioHostApi, PaUtilAllocationGroup *group, long driverCount )
- {
- char **result = 0;
- int i;
- result =(char**)PaUtil_GroupAllocateMemory(
- group, sizeof(char*) * driverCount );
- if( !result )
- goto error;
- result[0] = (char*)PaUtil_GroupAllocateMemory(
- group, 32 * driverCount );
- if( !result[0] )
- goto error;
- for( i=0; i<driverCount; ++i )
- result[i] = result[0] + (32 * i);
- asioHostApi->asioDrivers->getDriverNames( result, driverCount );
- error:
- return result;
- }
- static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type)
- {
- switch (type) {
- case ASIOSTInt16MSB:
- case ASIOSTInt16LSB:
- return paInt16;
- case ASIOSTFloat32MSB:
- case ASIOSTFloat32LSB:
- case ASIOSTFloat64MSB:
- case ASIOSTFloat64LSB:
- return paFloat32;
- case ASIOSTInt32MSB:
- case ASIOSTInt32LSB:
- case ASIOSTInt32MSB16:
- case ASIOSTInt32LSB16:
- case ASIOSTInt32MSB18:
- case ASIOSTInt32MSB20:
- case ASIOSTInt32MSB24:
- case ASIOSTInt32LSB18:
- case ASIOSTInt32LSB20:
- case ASIOSTInt32LSB24:
- return paInt32;
- case ASIOSTInt24MSB:
- case ASIOSTInt24LSB:
- return paInt24;
- default:
- return paCustomFormat;
- }
- }
- void AsioSampleTypeLOG(ASIOSampleType type)
- {
- switch (type) {
- case ASIOSTInt16MSB: PA_DEBUG(("ASIOSTInt16MSB\n")); break;
- case ASIOSTInt16LSB: PA_DEBUG(("ASIOSTInt16LSB\n")); break;
- case ASIOSTFloat32MSB:PA_DEBUG(("ASIOSTFloat32MSB\n"));break;
- case ASIOSTFloat32LSB:PA_DEBUG(("ASIOSTFloat32LSB\n"));break;
- case ASIOSTFloat64MSB:PA_DEBUG(("ASIOSTFloat64MSB\n"));break;
- case ASIOSTFloat64LSB:PA_DEBUG(("ASIOSTFloat64LSB\n"));break;
- case ASIOSTInt32MSB: PA_DEBUG(("ASIOSTInt32MSB\n")); break;
- case ASIOSTInt32LSB: PA_DEBUG(("ASIOSTInt32LSB\n")); break;
- case ASIOSTInt32MSB16:PA_DEBUG(("ASIOSTInt32MSB16\n"));break;
- case ASIOSTInt32LSB16:PA_DEBUG(("ASIOSTInt32LSB16\n"));break;
- case ASIOSTInt32MSB18:PA_DEBUG(("ASIOSTInt32MSB18\n"));break;
- case ASIOSTInt32MSB20:PA_DEBUG(("ASIOSTInt32MSB20\n"));break;
- case ASIOSTInt32MSB24:PA_DEBUG(("ASIOSTInt32MSB24\n"));break;
- case ASIOSTInt32LSB18:PA_DEBUG(("ASIOSTInt32LSB18\n"));break;
- case ASIOSTInt32LSB20:PA_DEBUG(("ASIOSTInt32LSB20\n"));break;
- case ASIOSTInt32LSB24:PA_DEBUG(("ASIOSTInt32LSB24\n"));break;
- case ASIOSTInt24MSB: PA_DEBUG(("ASIOSTInt24MSB\n")); break;
- case ASIOSTInt24LSB: PA_DEBUG(("ASIOSTInt24LSB\n")); break;
- default: PA_DEBUG(("Custom Format%d\n",type));break;
- }
- }
- static int BytesPerAsioSample( ASIOSampleType sampleType )
- {
- switch (sampleType) {
- case ASIOSTInt16MSB:
- case ASIOSTInt16LSB:
- return 2;
- case ASIOSTFloat64MSB:
- case ASIOSTFloat64LSB:
- return 8;
- case ASIOSTFloat32MSB:
- case ASIOSTFloat32LSB:
- case ASIOSTInt32MSB:
- case ASIOSTInt32LSB:
- case ASIOSTInt32MSB16:
- case ASIOSTInt32LSB16:
- case ASIOSTInt32MSB18:
- case ASIOSTInt32MSB20:
- case ASIOSTInt32MSB24:
- case ASIOSTInt32LSB18:
- case ASIOSTInt32LSB20:
- case ASIOSTInt32LSB24:
- return 4;
- case ASIOSTInt24MSB:
- case ASIOSTInt24LSB:
- return 3;
- default:
- return 0;
- }
- }
- static void Swap16( void *buffer, long shift, long count )
- {
- unsigned short *p = (unsigned short*)buffer;
- unsigned short temp;
- (void) shift; /* unused parameter */
- while( count-- )
- {
- temp = *p;
- *p++ = (unsigned short)((temp<<8) | (temp>>8));
- }
- }
- static void Swap24( void *buffer, long shift, long count )
- {
- unsigned char *p = (unsigned char*)buffer;
- unsigned char temp;
- (void) shift; /* unused parameter */
- while( count-- )
- {
- temp = *p;
- *p = *(p+2);
- *(p+2) = temp;
- p += 3;
- }
- }
- #define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24));
- static void Swap32( void *buffer, long shift, long count )
- {
- unsigned long *p = (unsigned long*)buffer;
- unsigned long temp;
- (void) shift; /* unused parameter */
- while( count-- )
- {
- temp = *p;
- *p++ = PA_SWAP32_( temp);
- }
- }
- static void SwapShiftLeft32( void *buffer, long shift, long count )
- {
- unsigned long *p = (unsigned long*)buffer;
- unsigned long temp;
- while( count-- )
- {
- temp = *p;
- temp = PA_SWAP32_( temp);
- *p++ = temp << shift;
- }
- }
- static void ShiftRightSwap32( void *buffer, long shift, long count )
- {
- unsigned long *p = (unsigned long*)buffer;
- unsigned long temp;
- while( count-- )
- {
- temp = *p >> shift;
- *p++ = PA_SWAP32_( temp);
- }
- }
- static void ShiftLeft32( void *buffer, long shift, long count )
- {
- unsigned long *p = (unsigned long*)buffer;
- unsigned long temp;
- while( count-- )
- {
- temp = *p;
- *p++ = temp << shift;
- }
- }
- static void ShiftRight32( void *buffer, long shift, long count )
- {
- unsigned long *p = (unsigned long*)buffer;
- unsigned long temp;
- while( count-- )
- {
- temp = *p;
- *p++ = temp >> shift;
- }
- }
- #define PA_SWAP_( x, y ) temp=x; x = y; y = temp;
- static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count )
- {
- double *in = (double*)buffer;
- float *out = (float*)buffer;
- unsigned char *p;
- unsigned char temp;
- (void) shift; /* unused parameter */
- while( count-- )
- {
- p = (unsigned char*)in;
- PA_SWAP_( p[0], p[7] );
- PA_SWAP_( p[1], p[6] );
- PA_SWAP_( p[2], p[5] );
- PA_SWAP_( p[3], p[4] );
- *out++ = (float) (*in++);
- }
- }
- static void ConvertFloat64ToFloat32( void *buffer, long shift, long count )
- {
- double *in = (double*)buffer;
- float *out = (float*)buffer;
- (void) shift; /* unused parameter */
- while( count-- )
- *out++ = (float) (*in++);
- }
- static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count )
- {
- float *in = ((float*)buffer) + (count-1);
- double *out = ((double*)buffer) + (count-1);
- unsigned char *p;
- unsigned char temp;
- (void) shift; /* unused parameter */
- while( count-- )
- {
- *out = *in--;
- p = (unsigned char*)out;
- PA_SWAP_( p[0], p[7] );
- PA_SWAP_( p[1], p[6] );
- PA_SWAP_( p[2], p[5] );
- PA_SWAP_( p[3], p[4] );
- out--;
- }
- }
- static void ConvertFloat32ToFloat64( void *buffer, long shift, long count )
- {
- float *in = ((float*)buffer) + (count-1);
- double *out = ((double*)buffer) + (count-1);
- (void) shift; /* unused parameter */
- while( count-- )
- *out-- = *in--;
- }
- #ifdef MAC
- #define PA_MSB_IS_NATIVE_
- #undef PA_LSB_IS_NATIVE_
- #endif
- #ifdef WINDOWS
- #undef PA_MSB_IS_NATIVE_
- #define PA_LSB_IS_NATIVE_
- #endif
- typedef void PaAsioBufferConverter( void *, long, long );
- static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
- {
- *shift = 0;
- *converter = 0;
- switch (type) {
- case ASIOSTInt16MSB:
- /* dest: paInt16, no conversion necessary, possible byte swap*/
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap16;
- #endif
- break;
- case ASIOSTInt16LSB:
- /* dest: paInt16, no conversion necessary, possible byte swap*/
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap16;
- #endif
- break;
- case ASIOSTFloat32MSB:
- /* dest: paFloat32, no conversion necessary, possible byte swap*/
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTFloat32LSB:
- /* dest: paFloat32, no conversion necessary, possible byte swap*/
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTFloat64MSB:
- /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap64ConvertFloat64ToFloat32;
- #else
- *converter = ConvertFloat64ToFloat32;
- #endif
- break;
- case ASIOSTFloat64LSB:
- /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap64ConvertFloat64ToFloat32;
- #else
- *converter = ConvertFloat64ToFloat32;
- #endif
- break;
- case ASIOSTInt32MSB:
- /* dest: paInt32, no conversion necessary, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTInt32LSB:
- /* dest: paInt32, no conversion necessary, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTInt32MSB16:
- /* dest: paInt32, 16 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 16;
- break;
- case ASIOSTInt32MSB18:
- /* dest: paInt32, 14 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 14;
- break;
- case ASIOSTInt32MSB20:
- /* dest: paInt32, 12 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 12;
- break;
- case ASIOSTInt32MSB24:
- /* dest: paInt32, 8 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 8;
- break;
- case ASIOSTInt32LSB16:
- /* dest: paInt32, 16 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 16;
- break;
- case ASIOSTInt32LSB18:
- /* dest: paInt32, 14 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 14;
- break;
- case ASIOSTInt32LSB20:
- /* dest: paInt32, 12 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 12;
- break;
- case ASIOSTInt32LSB24:
- /* dest: paInt32, 8 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = SwapShiftLeft32;
- #else
- *converter = ShiftLeft32;
- #endif
- *shift = 8;
- break;
- case ASIOSTInt24MSB:
- /* dest: paInt24, no conversion necessary, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap24;
- #endif
- break;
- case ASIOSTInt24LSB:
- /* dest: paInt24, no conversion necessary, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap24;
- #endif
- break;
- }
- }
- static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
- {
- *shift = 0;
- *converter = 0;
- switch (type) {
- case ASIOSTInt16MSB:
- /* src: paInt16, no conversion necessary, possible byte swap*/
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap16;
- #endif
- break;
- case ASIOSTInt16LSB:
- /* src: paInt16, no conversion necessary, possible byte swap*/
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap16;
- #endif
- break;
- case ASIOSTFloat32MSB:
- /* src: paFloat32, no conversion necessary, possible byte swap*/
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTFloat32LSB:
- /* src: paFloat32, no conversion necessary, possible byte swap*/
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTFloat64MSB:
- /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
- #ifdef PA_LSB_IS_NATIVE_
- *converter = ConvertFloat32ToFloat64Swap64;
- #else
- *converter = ConvertFloat32ToFloat64;
- #endif
- break;
- case ASIOSTFloat64LSB:
- /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
- #ifdef PA_MSB_IS_NATIVE_
- *converter = ConvertFloat32ToFloat64Swap64;
- #else
- *converter = ConvertFloat32ToFloat64;
- #endif
- break;
- case ASIOSTInt32MSB:
- /* src: paInt32, no conversion necessary, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTInt32LSB:
- /* src: paInt32, no conversion necessary, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap32;
- #endif
- break;
- case ASIOSTInt32MSB16:
- /* src: paInt32, 16 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 16;
- break;
- case ASIOSTInt32MSB18:
- /* src: paInt32, 14 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 14;
- break;
- case ASIOSTInt32MSB20:
- /* src: paInt32, 12 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 12;
- break;
- case ASIOSTInt32MSB24:
- /* src: paInt32, 8 bit shift, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 8;
- break;
- case ASIOSTInt32LSB16:
- /* src: paInt32, 16 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 16;
- break;
- case ASIOSTInt32LSB18:
- /* src: paInt32, 14 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 14;
- break;
- case ASIOSTInt32LSB20:
- /* src: paInt32, 12 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 12;
- break;
- case ASIOSTInt32LSB24:
- /* src: paInt32, 8 bit shift, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = ShiftRightSwap32;
- #else
- *converter = ShiftRight32;
- #endif
- *shift = 8;
- break;
- case ASIOSTInt24MSB:
- /* src: paInt24, no conversion necessary, possible byte swap */
- #ifdef PA_LSB_IS_NATIVE_
- *converter = Swap24;
- #endif
- break;
- case ASIOSTInt24LSB:
- /* src: paInt24, no conversion necessary, possible byte swap */
- #ifdef PA_MSB_IS_NATIVE_
- *converter = Swap24;
- #endif
- break;
- }
- }
- typedef struct PaAsioDeviceInfo
- {
- PaDeviceInfo commonDeviceInfo;
- long minBufferSize;
- long maxBufferSize;
- long preferredBufferSize;
- long bufferGranularity;
- ASIOChannelInfo *asioChannelInfos;
- }
- PaAsioDeviceInfo;
- PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device,
- long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity )
- {
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiDevice;
- result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
- if( result == paNoError )
- {
- result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
- if( result == paNoError )
- {
- PaAsioDeviceInfo *asioDeviceInfo =
- (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
- *minBufferSizeFrames = asioDeviceInfo->minBufferSize;
- *maxBufferSizeFrames = asioDeviceInfo->maxBufferSize;
- *preferredBufferSizeFrames = asioDeviceInfo->preferredBufferSize;
- *granularity = asioDeviceInfo->bufferGranularity;
- }
- }
- return result;
- }
- /* Unload whatever we loaded in LoadAsioDriver().
- */
- static void UnloadAsioDriver( void )
- {
- ASIOExit();
- }
- /*
- load the asio driver named by <driverName> and return statistics about
- the driver in info. If no error occurred, the driver will remain open
- and must be closed by the called by calling UnloadAsioDriver() - if an error
- is returned the driver will already be unloaded.
- */
- static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const char *driverName,
- PaAsioDriverInfo *driverInfo, void *systemSpecific )
- {
- PaError result = paNoError;
- ASIOError asioError;
- int asioIsInitialized = 0;
- if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
- goto error;
- }
- memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) );
- driverInfo->asioDriverInfo.asioVersion = 2;
- driverInfo->asioDriverInfo.sysRef = systemSpecific;
- if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
- }
- else
- {
- asioIsInitialized = 1;
- }
- if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount,
- &driverInfo->outputChannelCount)) != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
- }
- if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize,
- &driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize,
- &driverInfo->bufferGranularity)) != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
- }
- if( ASIOOutputReady() == ASE_OK )
- driverInfo->postOutput = true;
- else
- driverInfo->postOutput = false;
- return result;
- error:
- if( asioIsInitialized )
- {
- ASIOExit();
- }
- return result;
- }
- #define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ 13 /* must be the same number of elements as in the array below */
- static ASIOSampleRate defaultSampleRateSearchOrder_[]
- = {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0,
- 192000.0, 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
- static PaError InitPaDeviceInfoFromAsioDriver( PaAsioHostApiRepresentation *asioHostApi,
- const char *driverName, int driverIndex,
- PaDeviceInfo *deviceInfo, PaAsioDeviceInfo *asioDeviceInfo )
- {
- PaError result = paNoError;
- /* Due to the headless design of the ASIO API, drivers are free to write over data given to them (like M-Audio
- drivers f.i.). This is an attempt to overcome that. */
- union _tag_local {
- PaAsioDriverInfo info;
- char _padding[4096];
- } paAsioDriver;
- asioDeviceInfo->asioChannelInfos = 0; /* we check this below to handle error cleanup */
- result = LoadAsioDriver( asioHostApi, driverName, &paAsioDriver.info, asioHostApi->systemSpecific );
- if( result == paNoError )
- {
- PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", driverIndex,deviceInfo->name));
- PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", driverIndex, paAsioDriver.info.inputChannelCount));
- PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", driverIndex, paAsioDriver.info.outputChannelCount));
- PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", driverIndex, paAsioDriver.info.bufferMinSize));
- PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", driverIndex, paAsioDriver.info.bufferMaxSize));
- PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", driverIndex, paAsioDriver.info.bufferPreferredSize));
- PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", driverIndex, paAsioDriver.info.bufferGranularity));
- deviceInfo->maxInputChannels = paAsioDriver.info.inputChannelCount;
- deviceInfo->maxOutputChannels = paAsioDriver.info.outputChannelCount;
- deviceInfo->defaultSampleRate = 0.;
- bool foundDefaultSampleRate = false;
- for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j )
- {
- ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] );
- if( asioError != ASE_NoClock && asioError != ASE_NotPresent )
- {
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j];
- foundDefaultSampleRate = true;
- break;
- }
- }
- PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", driverIndex, deviceInfo->defaultSampleRate));
- if( foundDefaultSampleRate ){
- /* calculate default latency values from bufferPreferredSize
- for default low latency, and bufferMaxSize
- for default high latency.
- use the default sample rate to convert from samples to
- seconds. Without knowing what sample rate the user will
- use this is the best we can do.
- */
- double defaultLowLatency =
- paAsioDriver.info.bufferPreferredSize / deviceInfo->defaultSampleRate;
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- double defaultHighLatency =
- paAsioDriver.info.bufferMaxSize / deviceInfo->defaultSampleRate;
- if( defaultHighLatency < defaultLowLatency )
- defaultHighLatency = defaultLowLatency; /* just in case the driver returns something strange */
-
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
-
- }else{
- deviceInfo->defaultLowInputLatency = 0.;
- deviceInfo->defaultLowOutputLatency = 0.;
- deviceInfo->defaultHighInputLatency = 0.;
- deviceInfo->defaultHighOutputLatency = 0.;
- }
- PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", driverIndex, deviceInfo->defaultLowInputLatency));
- PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", driverIndex, deviceInfo->defaultLowOutputLatency));
- PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", driverIndex, deviceInfo->defaultHighInputLatency));
- PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", driverIndex, deviceInfo->defaultHighOutputLatency));
- asioDeviceInfo->minBufferSize = paAsioDriver.info.bufferMinSize;
- asioDeviceInfo->maxBufferSize = paAsioDriver.info.bufferMaxSize;
- asioDeviceInfo->preferredBufferSize = paAsioDriver.info.bufferPreferredSize;
- asioDeviceInfo->bufferGranularity = paAsioDriver.info.bufferGranularity;
- asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory(
- asioHostApi->allocations,
- sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels
- + deviceInfo->maxOutputChannels) );
- if( !asioDeviceInfo->asioChannelInfos )
- {
- result = paInsufficientMemory;
- goto error_unload;
- }
- int a;
- for( a=0; a < deviceInfo->maxInputChannels; ++a ){
- asioDeviceInfo->asioChannelInfos[a].channel = a;
- asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue;
- ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[a] );
- if( asioError != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error_unload;
- }
- }
- for( a=0; a < deviceInfo->maxOutputChannels; ++a ){
- int b = deviceInfo->maxInputChannels + a;
- asioDeviceInfo->asioChannelInfos[b].channel = a;
- asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse;
- ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[b] );
- if( asioError != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error_unload;
- }
- }
- /* unload the driver */
- UnloadAsioDriver();
- }
- return result;
- error_unload:
- UnloadAsioDriver();
- if( asioDeviceInfo->asioChannelInfos ){
- PaUtil_GroupFreeMemory( asioHostApi->allocations, asioDeviceInfo->asioChannelInfos );
- asioDeviceInfo->asioChannelInfos = 0;
- }
- return result;
- }
- /* we look up IsDebuggerPresent at runtime incase it isn't present (on Win95 for example) */
- typedef BOOL (WINAPI *IsDebuggerPresentPtr)(VOID);
- IsDebuggerPresentPtr IsDebuggerPresent_ = 0;
- //FARPROC IsDebuggerPresent_ = 0; // this is the current way to do it apparently according to davidv
- PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
- {
- PaError result = paNoError;
- int i, driverCount;
- PaAsioHostApiRepresentation *asioHostApi;
- PaAsioDeviceInfo *deviceInfoArray;
- char **names;
- asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) );
- if( !asioHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
- memset( asioHostApi, 0, sizeof(PaAsioHostApiRepresentation) ); /* ensure all fields are zeroed. especially asioHostApi->allocations */
- /*
- We initialize COM ourselves here and uninitialize it in Terminate().
- This should be the only COM initialization needed in this module.
- The ASIO SDK may also initialize COM but since we want to reduce dependency
- on the ASIO SDK we manage COM initialization ourselves.
- There used to be code that initialized COM in other situations
- such as when creating a Stream. This made PA work when calling Pa_CreateStream
- from a non-main thread. However we currently consider initialization
- of COM in non-main threads to be the caller's responsibility.
- */
- result = PaWinUtil_CoInitialize( paASIO, &asioHostApi->comInitializationResult );
- if( result != paNoError )
- {
- goto error;
- }
- asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
- asioHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !asioHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
- /* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
- try
- {
- asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */
- }
- catch (std::bad_alloc)
- {
- asioHostApi->asioDrivers = 0;
- }
- /* some implementations of new (ie MSVC, see http://support.microsoft.com/?kbid=167733)
- don't throw std::bad_alloc, so we also explicitly test for a null return. */
- if( asioHostApi->asioDrivers == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
- asioDrivers = asioHostApi->asioDrivers; /* keep SDK global in sync until we stop depending on it */
- asioHostApi->systemSpecific = 0;
- asioHostApi->openAsioDeviceIndex = paNoDevice;
- *hostApi = &asioHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paASIO;
- (*hostApi)->info.name = "ASIO";
- (*hostApi)->info.deviceCount = 0;
- #ifdef WINDOWS
- /* use desktop window as system specific ptr */
- asioHostApi->systemSpecific = GetDesktopWindow();
- #endif
- /* driverCount is the number of installed drivers - not necessarily
- the number of installed physical devices. */
- #if MAC
- driverCount = asioHostApi->asioDrivers->getNumFragments();
- #elif WINDOWS
- driverCount = asioHostApi->asioDrivers->asioGetNumDev();
- #endif
- if( driverCount > 0 )
- {
- names = GetAsioDriverNames( asioHostApi, asioHostApi->allocations, driverCount );
- if( !names )
- {
- result = paInsufficientMemory;
- goto error;
- }
- /* allocate enough space for all drivers, even if some aren't installed */
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory(
- asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
- IsDebuggerPresent_ = (IsDebuggerPresentPtr)GetProcAddress( LoadLibraryA( "Kernel32.dll" ), "IsDebuggerPresent" );
- for( i=0; i < driverCount; ++i )
- {
- PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i]));
- // Since portaudio opens ALL ASIO drivers, and no one else does that,
- // we face fact that some drivers were not meant for it, drivers which act
- // like shells on top of REAL drivers, for instance.
- // so we get duplicate handles, locks and other problems.
- // so lets NOT try to load any such wrappers.
- // The ones i [davidv] know of so far are:
- if ( strcmp (names[i],"ASIO DirectX Full Duplex Driver") == 0
- || strcmp (names[i],"ASIO Multimedia Driver") == 0
- || strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0"
- || strncmp(names[i],"Adobe",5) == 0 //"Adobe Default Windows Sound 1.5"
- )
- {
- PA_DEBUG(("BLACKLISTED!!!\n"));
- continue;
- }
- if( IsDebuggerPresent_ && IsDebuggerPresent_() )
- {
- /* ASIO Digidesign Driver uses PACE copy protection which quits out
- if a debugger is running. So we don't load it if a debugger is running. */
- if( strcmp(names[i], "ASIO Digidesign Driver") == 0 )
- {
- PA_DEBUG(("BLACKLISTED!!! ASIO Digidesign Driver would quit the debugger\n"));
- continue;
- }
- }
- /* Attempt to init device info from the asio driver... */
- {
- PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
- PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = names[i];
- if( InitPaDeviceInfoFromAsioDriver( asioHostApi, names[i], i, deviceInfo, asioDeviceInfo ) == paNoError )
- {
- (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
- ++(*hostApi)->info.deviceCount;
- }
- else
- {
- PA_DEBUG(("Skipping ASIO device:%s\n",names[i]));
- continue;
- }
- }
- }
- }
- if( (*hostApi)->info.deviceCount > 0 )
- {
- (*hostApi)->info.defaultInputDevice = 0;
- (*hostApi)->info.defaultOutputDevice = 0;
- }
- else
- {
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
- }
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
- PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
- PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
- return result;
- error:
- if( asioHostApi )
- {
- if( asioHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( asioHostApi->allocations );
- PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
- }
- delete asioHostApi->asioDrivers;
- asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
- PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
- PaUtil_FreeMemory( asioHostApi );
- }
- return result;
- }
- static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
- {
- PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
- /*
- IMPLEMENT ME:
- - clean up any resources not handled by the allocation group (need to review if there are any)
- */
- if( asioHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( asioHostApi->allocations );
- PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
- }
- delete asioHostApi->asioDrivers;
- asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
- PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
- PaUtil_FreeMemory( asioHostApi );
- }
- static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
- {
- PaError result = paNoError;
- PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
- PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaDeviceIndex asioDeviceIndex;
- ASIOError asioError;
-
- if( inputParameters && outputParameters )
- {
- /* full duplex ASIO stream must use the same device for input and output */
- if( inputParameters->device != outputParameters->device )
- return paBadIODeviceCombination;
- }
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( inputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- asioDeviceIndex = inputParameters->device;
- /* validate inputStreamInfo */
- /** @todo do more validation here */
- // if( inputParameters->hostApiSpecificStreamInfo )
- // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( outputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- asioDeviceIndex = outputParameters->device;
- /* validate outputStreamInfo */
- /** @todo do more validation here */
- // if( outputParameters->hostApiSpecificStreamInfo )
- // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
- /* if an ASIO device is open we can only get format information for the currently open device */
- if( asioHostApi->openAsioDeviceIndex != paNoDevice
- && asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
- {
- return paDeviceUnavailable;
- }
- /* NOTE: we load the driver and use its current settings
- rather than the ones in our device info structure which may be stale */
- /* open the device if it's not already open */
- if( asioHostApi->openAsioDeviceIndex == paNoDevice )
- {
- result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
- driverInfo, asioHostApi->systemSpecific );
- if( result != paNoError )
- return result;
- }
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > 0 )
- {
- if( inputChannelCount > driverInfo->inputChannelCount )
- {
- result = paInvalidChannelCount;
- goto done;
- }
- }
- /* check that output device can support outputChannelCount */
- if( outputChannelCount )
- {
- if( outputChannelCount > driverInfo->outputChannelCount )
- {
- result = paInvalidChannelCount;
- goto done;
- }
- }
-
- /* query for sample rate support */
- asioError = ASIOCanSampleRate( sampleRate );
- if( asioError == ASE_NoClock || asioError == ASE_NotPresent )
- {
- result = paInvalidSampleRate;
- goto done;
- }
- done:
- /* close the device if it wasn't already open */
- if( asioHostApi->openAsioDeviceIndex == paNoDevice )
- {
- UnloadAsioDriver(); /* not sure if we should check for errors here */
- }
- if( result == paNoError )
- return paFormatIsSupported;
- else
- return result;
- }
- /** A data structure specifically for storing blocking i/o related data. */
- typedef struct PaAsioStreamBlockingState
- {
- int stopFlag; /**< Flag indicating that block processing is to be stopped. */
- unsigned long writeBuffersRequested; /**< The number of available output buffers, requested by the #WriteStream() function. */
- unsigned long readFramesRequested; /**< The number of available input frames, requested by the #ReadStream() function. */
- int writeBuffersRequestedFlag; /**< Flag to indicate that #WriteStream() has requested more output buffers to be available. */
- int readFramesRequestedFlag; /**< Flag to indicate that #ReadStream() requires more input frames to be available. */
- HANDLE writeBuffersReadyEvent; /**< Event to signal that requested output buffers are available. */
- HANDLE readFramesReadyEvent; /**< Event to signal that requested input frames are available. */
- void *writeRingBufferData; /**< The actual ring buffer memory, used by the output ring buffer. */
- void *readRingBufferData; /**< The actual ring buffer memory, used by the input ring buffer. */
- PaUtilRingBuffer writeRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store output data (interleaved user format). */
- PaUtilRingBuffer readRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store input data (interleaved user format). */
- long writeRingBufferInitialFrames; /**< The initial number of silent frames within the output ring buffer. */
- const void **writeStreamBuffer; /**< Temp buffer, used by #WriteStream() for handling non-interleaved data. */
- void **readStreamBuffer; /**< Temp buffer, used by #ReadStream() for handling non-interleaved data. */
- PaUtilBufferProcessor bufferProcessor; /**< Buffer processor, used to handle the blocking i/o ring buffers. */
- int outputUnderflowFlag; /**< Flag to signal an output underflow from within the callback function. */
- int inputOverflowFlag; /**< Flag to signal an input overflow from within the callback function. */
- }
- PaAsioStreamBlockingState;
- /* PaAsioStream - a stream data structure specifically for this implementation */
- typedef struct PaAsioStream
- {
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
- PaAsioHostApiRepresentation *asioHostApi;
- unsigned long framesPerHostCallback;
- /* ASIO driver info - these may not be needed for the life of the stream,
- but store them here until we work out how format conversion is going
- to work. */
- ASIOBufferInfo *asioBufferInfos;
- ASIOChannelInfo *asioChannelInfos;
- long asioInputLatencyFrames, asioOutputLatencyFrames; // actual latencies returned by asio
- long inputChannelCount, outputChannelCount;
- bool postOutput;
- void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */
- void **inputBufferPtrs[2];
- void **outputBufferPtrs[2];
- PaAsioBufferConverter *inputBufferConverter;
- long inputShift;
- PaAsioBufferConverter *outputBufferConverter;
- long outputShift;
- volatile bool stopProcessing;
- int stopPlayoutCount;
- HANDLE completedBuffersPlayedEvent;
- bool streamFinishedCallbackCalled;
- int isStopped;
- volatile int isActive;
- volatile bool zeroOutput; /* all future calls to the callback will output silence */
- volatile long reenterCount;
- volatile long reenterError;
- PaStreamCallbackFlags callbackFlags;
- PaAsioStreamBlockingState *blockingState; /**< Blocking i/o data struct, or NULL when using callback interface. */
- }
- PaAsioStream;
- static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */
- static void ZeroOutputBuffers( PaAsioStream *stream, long index )
- {
- int i;
- for( i=0; i < stream->outputChannelCount; ++i )
- {
- void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index];
- int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type );
- memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample );
- }
- }
- /* return the next power of two >= x.
- Returns the input parameter if it is already a power of two.
- http://stackoverflow.com/questions/364985/algorithm-for-finding-the-smallest-power-of-two-thats-greater-or-equal-to-a-giv
- */
- static unsigned long NextPowerOfTwo( unsigned long x )
- {
- --x;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- /* If you needed to deal with numbers > 2^32 the following would be needed.
- For latencies, we don't deal with values this large.
- x |= x >> 16;
- */
- return x + 1;
- }
- static unsigned long SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer(
- unsigned long targetBufferingLatencyFrames, PaAsioDriverInfo *driverInfo )
- {
- /* Choose a host buffer size based only on targetBufferingLatencyFrames and the
- device's supported buffer sizes. Always returns a valid value.
- */
- unsigned long result;
- if( targetBufferingLatencyFrames <= (unsigned long)driverInfo->bufferMinSize )
- {
- result = driverInfo->bufferMinSize;
- }
- else if( targetBufferingLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize )
- {
- result = driverInfo->bufferMaxSize;
- }
- else
- {
- if( driverInfo->bufferGranularity == 0 ) /* single fixed host buffer size */
- {
- /* The documentation states that bufferGranularity should be zero
- when bufferMinSize, bufferMaxSize and bufferPreferredSize are the
- same. We assume that is the case.
- */
- result = driverInfo->bufferPreferredSize;
- }
- else if( driverInfo->bufferGranularity == -1 ) /* power-of-two */
- {
- /* We assume bufferMinSize and bufferMaxSize are powers of two. */
- result = NextPowerOfTwo( targetBufferingLatencyFrames );
- if( result < (unsigned long)driverInfo->bufferMinSize )
- result = driverInfo->bufferMinSize;
- if( result > (unsigned long)driverInfo->bufferMaxSize )
- result = driverInfo->bufferMaxSize;
- }
- else /* modulo bufferGranularity */
- {
- /* round up to the next multiple of granularity */
- unsigned long n = (targetBufferingLatencyFrames + driverInfo->bufferGranularity - 1)
- / driverInfo->bufferGranularity;
-
- result = n * driverInfo->bufferGranularity;
- if( result < (unsigned long)driverInfo->bufferMinSize )
- result = driverInfo->bufferMinSize;
- if( result > (unsigned long)driverInfo->bufferMaxSize )
- result = driverInfo->bufferMaxSize;
- }
- }
- return result;
- }
- static unsigned long SelectHostBufferSizeForSpecifiedUserFramesPerBuffer(
- unsigned long targetBufferingLatencyFrames, unsigned long userFramesPerBuffer,
- PaAsioDriverInfo *driverInfo )
- {
- /* Select a host buffer size conforming to targetBufferingLatencyFrames
- and the device's supported buffer sizes.
- The return value will always be a multiple of userFramesPerBuffer.
- If a valid buffer size can not be found the function returns 0.
- The current implementation uses a simple iterative search for clarity.
- Feel free to suggest a closed form solution.
- */
- unsigned long result = 0;
- assert( userFramesPerBuffer != 0 );
-
- if( driverInfo->bufferGranularity == 0 ) /* single fixed host buffer size */
- {
- /* The documentation states that bufferGranularity should be zero
- when bufferMinSize, bufferMaxSize and bufferPreferredSize are the
- same. We assume that is the case.
- */
- if( (driverInfo->bufferPreferredSize % userFramesPerBuffer) == 0 )
- result = driverInfo->bufferPreferredSize;
- }
- else if( driverInfo->bufferGranularity == -1 ) /* power-of-two */
- {
- /* We assume bufferMinSize and bufferMaxSize are powers of two. */
- /* Search all powers of two in the range [bufferMinSize,bufferMaxSize]
- for multiples of userFramesPerBuffer. We prefer the first multiple
- that is equal or greater than targetBufferingLatencyFrames, or
- failing that, the largest multiple less than
- targetBufferingLatencyFrames.
- */
- unsigned long x = (unsigned long)driverInfo->bufferMinSize;
- do {
- if( (x % userFramesPerBuffer) == 0 )
- {
- /* any multiple of userFramesPerBuffer is acceptable */
- result = x;
- if( result >= targetBufferingLatencyFrames )
- break; /* stop. a value >= to targetBufferingLatencyFrames is ideal. */
- }
- x *= 2;
- } while( x <= (unsigned long)driverInfo->bufferMaxSize );
- }
- else /* modulo granularity */
- {
- /* We assume bufferMinSize is a multiple of bufferGranularity. */
- /* Search all multiples of bufferGranularity in the range
- [bufferMinSize,bufferMaxSize] for multiples of userFramesPerBuffer.
- We prefer the first multiple that is equal or greater than
- targetBufferingLatencyFrames, or failing that, the largest multiple
- less than targetBufferingLatencyFrames.
- */
- unsigned long x = (unsigned long)driverInfo->bufferMinSize;
- do {
- if( (x % userFramesPerBuffer) == 0 )
- {
- /* any multiple of userFramesPerBuffer is acceptable */
- result = x;
- if( result >= targetBufferingLatencyFrames )
- break; /* stop. a value >= to targetBufferingLatencyFrames is ideal. */
- }
- x += driverInfo->bufferGranularity;
- } while( x <= (unsigned long)driverInfo->bufferMaxSize );
- }
- return result;
- }
- static unsigned long SelectHostBufferSize(
- unsigned long targetBufferingLatencyFrames,
- unsigned long userFramesPerBuffer, PaAsioDriverInfo *driverInfo )
- {
- unsigned long result = 0;
- /* We select a host buffer size based on the following requirements
- (in priority order):
- 1. The host buffer size must be permissible according to the ASIO
- driverInfo buffer size constraints (min, max, granularity or
- powers-of-two).
- 2. If the user specifies a non-zero framesPerBuffer parameter
- (userFramesPerBuffer here) the host buffer should be a multiple of
- this (subject to the constraints in (1) above).
- [NOTE: Where no permissible host buffer size is a multiple of
- userFramesPerBuffer, we choose a value as if userFramesPerBuffer were
- zero (i.e. we ignore it). This strategy is open for review ~ perhaps
- there are still "more optimal" buffer sizes related to
- userFramesPerBuffer that we could use.]
- 3. The host buffer size should be greater than or equal to
- targetBufferingLatencyFrames, subject to (1) and (2) above. Where it
- is not possible to select a host buffer size equal or greater than
- targetBufferingLatencyFrames, the highest buffer size conforming to
- (1) and (2) should be chosen.
- */
- if( userFramesPerBuffer != 0 )
- {
- /* userFramesPerBuffer is specified, try to find a buffer size that's
- a multiple of it */
- result = SelectHostBufferSizeForSpecifiedUserFramesPerBuffer(
- targetBufferingLatencyFrames, userFramesPerBuffer, driverInfo );
- }
- if( result == 0 )
- {
- /* either userFramesPerBuffer was not specified, or we couldn't find a
- host buffer size that is a multiple of it. Select a host buffer size
- according to targetBufferingLatencyFrames and the ASIO driverInfo
- buffer size constraints.
- */
- result = SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer(
- targetBufferingLatencyFrames, driverInfo );
- }
- return result;
- }
- /* returns channelSelectors if present */
- static PaError ValidateAsioSpecificStreamInfo(
- const PaStreamParameters *streamParameters,
- const PaAsioStreamInfo *streamInfo,
- int deviceChannelCount,
- int **channelSelectors )
- {
- if( streamInfo )
- {
- if( streamInfo->size != sizeof( PaAsioStreamInfo )
- || streamInfo->version != 1 )
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- if( streamInfo->flags & paAsioUseChannelSelectors )
- *channelSelectors = streamInfo->channelSelectors;
- if( !(*channelSelectors) )
- return paIncompatibleHostApiSpecificStreamInfo;
- for( int i=0; i < streamParameters->channelCount; ++i ){
- if( (*channelSelectors)[i] < 0
- || (*channelSelectors)[i] >= deviceChannelCount ){
- return paInvalidChannelCount;
- }
- }
- }
- return paNoError;
- }
- static bool IsUsingExternalClockSource()
- {
- bool result = false;
- ASIOError asioError;
- ASIOClockSource clocks[32];
- long numSources=32;
- /* davidv: listing ASIO Clock sources. there is an ongoing investigation by
- me about whether or not to call ASIOSetSampleRate if an external Clock is
- used. A few drivers expected different things here */
-
- asioError = ASIOGetClockSources(clocks, &numSources);
- if( asioError != ASE_OK ){
- PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- }else{
- PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
- for (int i=0;i<numSources;++i){
- PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i, clocks[i].name, clocks[i].isCurrentSource ));
-
- if (clocks[i].isCurrentSource)
- result = true;
- }
- }
- return result;
- }
- static PaError ValidateAndSetSampleRate( double sampleRate )
- {
- PaError result = paNoError;
- ASIOError asioError;
- // check that the device supports the requested sample rate
- asioError = ASIOCanSampleRate( sampleRate );
- PA_DEBUG(("ASIOCanSampleRate(%f):%d\n", sampleRate, asioError ));
- if( asioError != ASE_OK )
- {
- result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- goto error;
- }
- // retrieve the current sample rate, we only change to the requested
- // sample rate if the device is not already in that rate.
- ASIOSampleRate oldRate;
- asioError = ASIOGetSampleRate(&oldRate);
- if( asioError != ASE_OK )
- {
- result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- goto error;
- }
- PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate));
- if (oldRate != sampleRate){
- /* Set sample rate */
- PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
- /*
- If you have problems with some drivers when externally clocked,
- try switching on the following line and commenting out the one after it.
- See IsUsingExternalClockSource() for more info.
- */
- //if( IsUsingExternalClockSource() ){
- if( false ){
- asioError = ASIOSetSampleRate( 0 );
- }else{
- asioError = ASIOSetSampleRate( sampleRate );
- }
- if( asioError != ASE_OK )
- {
- result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- goto error;
- }
- PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
- }
- else
- {
- PA_DEBUG(("No Need to change SR\n"));
- }
- error:
- return result;
- }
- /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
- static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
- {
- PaError result = paNoError;
- PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
- PaAsioStream *stream = 0;
- PaAsioStreamInfo *inputStreamInfo, *outputStreamInfo;
- unsigned long framesPerHostBuffer;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
- unsigned long suggestedInputLatencyFrames;
- unsigned long suggestedOutputLatencyFrames;
- PaDeviceIndex asioDeviceIndex;
- ASIOError asioError;
- int asioIsInitialized = 0;
- int asioBuffersCreated = 0;
- int completedBuffersPlayedEventInited = 0;
- int i;
- PaAsioDriverInfo *driverInfo;
- int *inputChannelSelectors = 0;
- int *outputChannelSelectors = 0;
- /* Are we using blocking i/o interface? */
- int usingBlockingIo = ( !streamCallback ) ? TRUE : FALSE;
- /* Blocking i/o stuff */
- long lBlockingBufferSize = 0; /* Desired ring buffer size in samples. */
- long lBlockingBufferSizePow2 = 0; /* Power-of-2 rounded ring buffer size. */
- long lBytesPerFrame = 0; /* Number of bytes per input/output frame. */
- int blockingWriteBuffersReadyEventInitialized = 0; /* Event init flag. */
- int blockingReadFramesReadyEventInitialized = 0; /* Event init flag. */
- int callbackBufferProcessorInited = FALSE;
- int blockingBufferProcessorInited = FALSE;
- /* unless we move to using lower level ASIO calls, we can only have
- one device open at a time */
- if( asioHostApi->openAsioDeviceIndex != paNoDevice )
- {
- PA_DEBUG(("OpenStream paDeviceUnavailable\n"));
- return paDeviceUnavailable;
- }
- assert( theAsioStream == 0 );
- if( inputParameters && outputParameters )
- {
- /* full duplex ASIO stream must use the same device for input and output */
- if( inputParameters->device != outputParameters->device )
- {
- PA_DEBUG(("OpenStream paBadIODeviceCombination\n"));
- return paBadIODeviceCombination;
- }
- }
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatencyFrames = (unsigned long)((inputParameters->suggestedLatency * sampleRate)+0.5f);
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- asioDeviceIndex = inputParameters->device;
- PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
- /* validate hostApiSpecificStreamInfo */
- inputStreamInfo = (PaAsioStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateAsioSpecificStreamInfo( inputParameters, inputStreamInfo,
- asioDeviceInfo->commonDeviceInfo.maxInputChannels,
- &inputChannelSelectors
- );
- if( result != paNoError ) return result;
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = 0;
- suggestedInputLatencyFrames = 0;
- }
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatencyFrames = (unsigned long)((outputParameters->suggestedLatency * sampleRate)+0.5f);
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- asioDeviceIndex = outputParameters->device;
- PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
- /* validate hostApiSpecificStreamInfo */
- outputStreamInfo = (PaAsioStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateAsioSpecificStreamInfo( outputParameters, outputStreamInfo,
- asioDeviceInfo->commonDeviceInfo.maxOutputChannels,
- &outputChannelSelectors
- );
- if( result != paNoError ) return result;
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = 0;
- suggestedOutputLatencyFrames = 0;
- }
- driverInfo = &asioHostApi->openAsioDriverInfo;
- /* NOTE: we load the driver and use its current settings
- rather than the ones in our device info structure which may be stale */
- result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
- driverInfo, asioHostApi->systemSpecific );
- if( result == paNoError )
- asioIsInitialized = 1;
- else{
- PA_DEBUG(("OpenStream ERROR1 - LoadAsioDriver returned %d\n", result));
- goto error;
- }
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > 0 )
- {
- if( inputChannelCount > driverInfo->inputChannelCount )
- {
- result = paInvalidChannelCount;
- PA_DEBUG(("OpenStream ERROR2\n"));
- goto error;
- }
- }
- /* check that output device can support outputChannelCount */
- if( outputChannelCount )
- {
- if( outputChannelCount > driverInfo->outputChannelCount )
- {
- result = paInvalidChannelCount;
- PA_DEBUG(("OpenStream ERROR3\n"));
- goto error;
- }
- }
- result = ValidateAndSetSampleRate( sampleRate );
- if( result != paNoError )
- goto error;
- /*
- IMPLEMENT ME:
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
- */
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 ){
- PA_DEBUG(("OpenStream invalid flags!!\n"));
- return paInvalidFlag; /* unexpected platform specific flag */
- }
- stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("OpenStream ERROR5\n"));
- goto error;
- }
- stream->blockingState = NULL; /* Blocking i/o not initialized, yet. */
- stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
- if( stream->completedBuffersPlayedEvent == NULL )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- PA_DEBUG(("OpenStream ERROR6\n"));
- goto error;
- }
- completedBuffersPlayedEventInited = 1;
- stream->asioBufferInfos = 0; /* for deallocation in error */
- stream->asioChannelInfos = 0; /* for deallocation in error */
- stream->bufferPtrs = 0; /* for deallocation in error */
- /* Using blocking i/o interface... */
- if( usingBlockingIo )
- {
- /* Blocking i/o is implemented by running callback mode, using a special blocking i/o callback. */
- streamCallback = BlockingIoPaCallback; /* Setup PA to use the ASIO blocking i/o callback. */
- userData = &theAsioStream; /* The callback user data will be the PA ASIO stream. */
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &asioHostApi->blockingStreamInterface, streamCallback, userData );
- }
- else /* Using callback interface... */
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &asioHostApi->callbackStreamInterface, streamCallback, userData );
- }
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
- stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory(
- sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) );
- if( !stream->asioBufferInfos )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("OpenStream ERROR7\n"));
- goto error;
- }
- for( i=0; i < inputChannelCount; ++i )
- {
- ASIOBufferInfo *info = &stream->asioBufferInfos[i];
- info->isInput = ASIOTrue;
- if( inputChannelSelectors ){
- // inputChannelSelectors values have already been validated in
- // ValidateAsioSpecificStreamInfo() above
- info->channelNum = inputChannelSelectors[i];
- }else{
- info->channelNum = i;
- }
- info->buffers[0] = info->buffers[1] = 0;
- }
- for( i=0; i < outputChannelCount; ++i ){
- ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i];
- info->isInput = ASIOFalse;
- if( outputChannelSelectors ){
- // outputChannelSelectors values have already been validated in
- // ValidateAsioSpecificStreamInfo() above
- info->channelNum = outputChannelSelectors[i];
- }else{
- info->channelNum = i;
- }
-
- info->buffers[0] = info->buffers[1] = 0;
- }
- /* Using blocking i/o interface... */
- if( usingBlockingIo )
- {
- /** @todo REVIEW selection of host buffer size for blocking i/o */
- framesPerHostBuffer = SelectHostBufferSize( 0, framesPerBuffer, driverInfo );
- }
- else /* Using callback interface... */
- {
- /* Select the host buffer size based on user framesPerBuffer and the
- maximum of suggestedInputLatencyFrames and
- suggestedOutputLatencyFrames.
- We should subtract any fixed known driver latency from
- suggestedLatencyFrames before computing the host buffer size.
- However, the ASIO API doesn't provide a method for determining fixed
- latencies independent of the host buffer size. ASIOGetLatencies()
- only returns latencies after the buffer size has been configured, so
- we can't reliably use it to determine fixed latencies here.
- We could set the preferred buffer size and then subtract it from
- the values returned from ASIOGetLatencies, but this would not be 100%
- reliable, so we don't do it.
- */
- unsigned long targetBufferingLatencyFrames =
- (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
- ? suggestedInputLatencyFrames
- : suggestedOutputLatencyFrames);
- framesPerHostBuffer = SelectHostBufferSize( targetBufferingLatencyFrames,
- framesPerBuffer, driverInfo );
- }
- PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer));
- asioError = ASIOCreateBuffers( stream->asioBufferInfos,
- inputChannelCount+outputChannelCount,
- framesPerHostBuffer, &asioCallbacks_ );
- if( asioError != ASE_OK
- && framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize )
- {
- PA_DEBUG(("ERROR: ASIOCreateBuffers: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- /*
- Some buggy drivers (like the Hoontech DSP24) give incorrect
- [min, preferred, max] values They should work with the preferred size
- value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize
- computed in SelectHostBufferSize, we try again with the preferred size.
- */
- framesPerHostBuffer = driverInfo->bufferPreferredSize;
- PA_DEBUG(("PaAsioOpenStream: CORRECTED framesPerHostBuffer :%d\n", framesPerHostBuffer));
- ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos,
- inputChannelCount+outputChannelCount,
- framesPerHostBuffer, &asioCallbacks_ );
- if( asioError2 == ASE_OK )
- asioError = ASE_OK;
- }
- if( asioError != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- PA_DEBUG(("OpenStream ERROR9\n"));
- goto error;
- }
- asioBuffersCreated = 1;
- stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory(
- sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) );
- if( !stream->asioChannelInfos )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("OpenStream ERROR10\n"));
- goto error;
- }
- for( i=0; i < inputChannelCount + outputChannelCount; ++i )
- {
- stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum;
- stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput;
- asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] );
- if( asioError != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- PA_DEBUG(("OpenStream ERROR11\n"));
- goto error;
- }
- }
- stream->bufferPtrs = (void**)PaUtil_AllocateMemory(
- 2 * sizeof(void*) * (inputChannelCount + outputChannelCount) );
- if( !stream->bufferPtrs )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("OpenStream ERROR12\n"));
- goto error;
- }
- if( inputChannelCount > 0 )
- {
- stream->inputBufferPtrs[0] = stream-> bufferPtrs;
- stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount];
- for( i=0; i<inputChannelCount; ++i )
- {
- stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0];
- stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1];
- }
- }
- else
- {
- stream->inputBufferPtrs[0] = 0;
- stream->inputBufferPtrs[1] = 0;
- }
- if( outputChannelCount > 0 )
- {
- stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2];
- stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount];
- for( i=0; i<outputChannelCount; ++i )
- {
- stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0];
- stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1];
- }
- }
- else
- {
- stream->outputBufferPtrs[0] = 0;
- stream->outputBufferPtrs[1] = 0;
- }
- if( inputChannelCount > 0 )
- {
- /* FIXME: assume all channels use the same type for now
-
- see: "ASIO devices with multiple sample formats are unsupported"
- http://www.portaudio.com/trac/ticket/106
- */
- ASIOSampleType inputType = stream->asioChannelInfos[0].type;
- PA_DEBUG(("ASIO Input type:%d",inputType));
- AsioSampleTypeLOG(inputType);
- hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType );
- SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift );
- }
- else
- {
- hostInputSampleFormat = 0;
- stream->inputBufferConverter = 0;
- }
- if( outputChannelCount > 0 )
- {
- /* FIXME: assume all channels use the same type for now
-
- see: "ASIO devices with multiple sample formats are unsupported"
- http://www.portaudio.com/trac/ticket/106
- */
- ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type;
- PA_DEBUG(("ASIO Output type:%d",outputType));
- AsioSampleTypeLOG(outputType);
- hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType );
- SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift );
- }
- else
- {
- hostOutputSampleFormat = 0;
- stream->outputBufferConverter = 0;
- }
- /* Values returned by ASIOGetLatencies() include the latency introduced by
- the ASIO double buffer. */
- ASIOGetLatencies( &stream->asioInputLatencyFrames, &stream->asioOutputLatencyFrames );
- /* Using blocking i/o interface... */
- if( usingBlockingIo )
- {
- /* Allocate the blocking i/o input ring buffer memory. */
- stream->blockingState = (PaAsioStreamBlockingState*)PaUtil_AllocateMemory( sizeof(PaAsioStreamBlockingState) );
- if( !stream->blockingState )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("ERROR! Blocking i/o interface struct allocation failed in OpenStream()\n"));
- goto error;
- }
- /* Initialize blocking i/o interface struct. */
- stream->blockingState->readFramesReadyEvent = NULL; /* Uninitialized, yet. */
- stream->blockingState->writeBuffersReadyEvent = NULL; /* Uninitialized, yet. */
- stream->blockingState->readRingBufferData = NULL; /* Uninitialized, yet. */
- stream->blockingState->writeRingBufferData = NULL; /* Uninitialized, yet. */
- stream->blockingState->readStreamBuffer = NULL; /* Uninitialized, yet. */
- stream->blockingState->writeStreamBuffer = NULL; /* Uninitialized, yet. */
- stream->blockingState->stopFlag = TRUE; /* Not started, yet. */
- /* If the user buffer is unspecified */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- /* Make the user buffer the same size as the host buffer. */
- framesPerBuffer = framesPerHostBuffer;
- }
- /* Initialize callback buffer processor. */
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor ,
- inputChannelCount ,
- inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
- (hostInputSampleFormat | paNonInterleaved), /* Host format. */
- outputChannelCount ,
- outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
- (hostOutputSampleFormat | paNonInterleaved), /* Host format. */
- sampleRate ,
- streamFlags ,
- framesPerBuffer , /* Frames per ring buffer block. */
- framesPerHostBuffer , /* Frames per asio buffer. */
- paUtilFixedHostBufferSize ,
- streamCallback ,
- userData );
- if( result != paNoError ){
- PA_DEBUG(("OpenStream ERROR13\n"));
- goto error;
- }
- callbackBufferProcessorInited = TRUE;
- /* Initialize the blocking i/o buffer processor. */
- result = PaUtil_InitializeBufferProcessor(&stream->blockingState->bufferProcessor,
- inputChannelCount ,
- inputSampleFormat , /* User format. */
- inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
- outputChannelCount ,
- outputSampleFormat , /* User format. */
- outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
- sampleRate ,
- paClipOff | paDitherOff , /* Don't use dither nor clipping. */
- framesPerBuffer , /* Frames per user buffer. */
- framesPerBuffer , /* Frames per ring buffer block. */
- paUtilBoundedHostBufferSize ,
- NULL, NULL );/* No callback! */
- if( result != paNoError ){
- PA_DEBUG(("ERROR! Blocking i/o buffer processor initialization failed in OpenStream()\n"));
- goto error;
- }
- blockingBufferProcessorInited = TRUE;
- /* If input is requested. */
- if( inputChannelCount )
- {
- /* Create the callback sync-event. */
- stream->blockingState->readFramesReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- if( stream->blockingState->readFramesReadyEvent == NULL )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- PA_DEBUG(("ERROR! Blocking i/o \"read frames ready\" event creation failed in OpenStream()\n"));
- goto error;
- }
- blockingReadFramesReadyEventInitialized = 1;
- /* Create pointer buffer to access non-interleaved data in ReadStream() */
- stream->blockingState->readStreamBuffer = (void**)PaUtil_AllocateMemory( sizeof(void*) * inputChannelCount );
- if( !stream->blockingState->readStreamBuffer )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("ERROR! Blocking i/o read stream buffer allocation failed in OpenStream()\n"));
- goto error;
- }
- /* The ring buffer should store as many data blocks as needed
- to achieve the requested latency. Whereas it must be large
- enough to store at least two complete data blocks.
- 1) Determine the amount of latency to be added to the
- prefered ASIO latency.
- 2) Make sure we have at lest one additional latency frame.
- 3) Divide the number of frames by the desired block size to
- get the number (rounded up to pure integer) of blocks to
- be stored in the buffer.
- 4) Add one additional block for block processing and convert
- to samples frames.
- 5) Get the next larger (or equal) power-of-two buffer size.
- */
- lBlockingBufferSize = suggestedInputLatencyFrames - stream->asioInputLatencyFrames;
- lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
- lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
- lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
- /* Get the next larger or equal power-of-two buffersize. */
- lBlockingBufferSizePow2 = 1;
- while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
- lBlockingBufferSize = lBlockingBufferSizePow2;
- /* Compute total intput latency in seconds */
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor )
- + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor)
- + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
- + stream->asioInputLatencyFrames )
- / sampleRate;
- /* The code below prints the ASIO latency which doesn't include
- the buffer processor latency nor the blocking i/o latency. It
- reports the added latency separately.
- */
- PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
- stream->asioInputLatencyFrames,
- (long)( stream->asioInputLatencyFrames * (1000.0 / sampleRate) ),
- PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor),
- (long)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
- PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
- (long)( (PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
- ));
- /* Determine the size of ring buffer in bytes. */
- lBytesPerFrame = inputChannelCount * Pa_GetSampleSize(inputSampleFormat );
- /* Allocate the blocking i/o input ring buffer memory. */
- stream->blockingState->readRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
- if( !stream->blockingState->readRingBufferData )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("ERROR! Blocking i/o input ring buffer allocation failed in OpenStream()\n"));
- goto error;
- }
- /* Initialize the input ring buffer struct. */
- PaUtil_InitializeRingBuffer( &stream->blockingState->readRingBuffer ,
- lBytesPerFrame ,
- lBlockingBufferSize ,
- stream->blockingState->readRingBufferData );
- }
- /* If output is requested. */
- if( outputChannelCount )
- {
- stream->blockingState->writeBuffersReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- if( stream->blockingState->writeBuffersReadyEvent == NULL )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- PA_DEBUG(("ERROR! Blocking i/o \"write buffers ready\" event creation failed in OpenStream()\n"));
- goto error;
- }
- blockingWriteBuffersReadyEventInitialized = 1;
- /* Create pointer buffer to access non-interleaved data in WriteStream() */
- stream->blockingState->writeStreamBuffer = (const void**)PaUtil_AllocateMemory( sizeof(const void*) * outputChannelCount );
- if( !stream->blockingState->writeStreamBuffer )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("ERROR! Blocking i/o write stream buffer allocation failed in OpenStream()\n"));
- goto error;
- }
- /* The ring buffer should store as many data blocks as needed
- to achieve the requested latency. Whereas it must be large
- enough to store at least two complete data blocks.
- 1) Determine the amount of latency to be added to the
- prefered ASIO latency.
- 2) Make sure we have at lest one additional latency frame.
- 3) Divide the number of frames by the desired block size to
- get the number (rounded up to pure integer) of blocks to
- be stored in the buffer.
- 4) Add one additional block for block processing and convert
- to samples frames.
- 5) Get the next larger (or equal) power-of-two buffer size.
- */
- lBlockingBufferSize = suggestedOutputLatencyFrames - stream->asioOutputLatencyFrames;
- lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
- lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
- lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
- /* The buffer size (without the additional block) corresponds
- to the initial number of silent samples in the output ring
- buffer. */
- stream->blockingState->writeRingBufferInitialFrames = lBlockingBufferSize - framesPerBuffer;
- /* Get the next larger or equal power-of-two buffersize. */
- lBlockingBufferSizePow2 = 1;
- while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
- lBlockingBufferSize = lBlockingBufferSizePow2;
- /* Compute total output latency in seconds */
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
- + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor)
- + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
- + stream->asioOutputLatencyFrames )
- / sampleRate;
- /* The code below prints the ASIO latency which doesn't include
- the buffer processor latency nor the blocking i/o latency. It
- reports the added latency separately.
- */
- PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
- stream->asioOutputLatencyFrames,
- (long)( stream->asioOutputLatencyFrames * (1000.0 / sampleRate) ),
- PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor),
- (long)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
- PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
- (long)( (PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
- ));
- /* Determine the size of ring buffer in bytes. */
- lBytesPerFrame = outputChannelCount * Pa_GetSampleSize(outputSampleFormat);
- /* Allocate the blocking i/o output ring buffer memory. */
- stream->blockingState->writeRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
- if( !stream->blockingState->writeRingBufferData )
- {
- result = paInsufficientMemory;
- PA_DEBUG(("ERROR! Blocking i/o output ring buffer allocation failed in OpenStream()\n"));
- goto error;
- }
- /* Initialize the output ring buffer struct. */
- PaUtil_InitializeRingBuffer( &stream->blockingState->writeRingBuffer ,
- lBytesPerFrame ,
- lBlockingBufferSize ,
- stream->blockingState->writeRingBufferData );
- }
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
- }
- else /* Using callback interface... */
- {
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, (hostInputSampleFormat | paNonInterleaved),
- outputChannelCount, outputSampleFormat, (hostOutputSampleFormat | paNonInterleaved),
- sampleRate, streamFlags, framesPerBuffer,
- framesPerHostBuffer, paUtilFixedHostBufferSize,
- streamCallback, userData );
- if( result != paNoError ){
- PA_DEBUG(("OpenStream ERROR13\n"));
- goto error;
- }
- callbackBufferProcessorInited = TRUE;
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
- + stream->asioInputLatencyFrames) / sampleRate; // seconds
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
- + stream->asioOutputLatencyFrames) / sampleRate; // seconds
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
- // the code below prints the ASIO latency which doesn't include the
- // buffer processor latency. it reports the added latency separately
- PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
- stream->asioInputLatencyFrames,
- (long)((stream->asioInputLatencyFrames*1000)/ sampleRate),
- PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor),
- (long)((PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate)
- ));
- PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
- stream->asioOutputLatencyFrames,
- (long)((stream->asioOutputLatencyFrames*1000)/ sampleRate),
- PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor),
- (long)((PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate)
- ));
- }
- stream->asioHostApi = asioHostApi;
- stream->framesPerHostCallback = framesPerHostBuffer;
- stream->inputChannelCount = inputChannelCount;
- stream->outputChannelCount = outputChannelCount;
- stream->postOutput = driverInfo->postOutput;
- stream->isStopped = 1;
- stream->isActive = 0;
-
- asioHostApi->openAsioDeviceIndex = asioDeviceIndex;
- theAsioStream = stream;
- *s = (PaStream*)stream;
- return result;
- error:
- PA_DEBUG(("goto errored\n"));
- if( stream )
- {
- if( stream->blockingState )
- {
- if( blockingBufferProcessorInited )
- PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
- if( stream->blockingState->writeRingBufferData )
- PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
- if( stream->blockingState->writeStreamBuffer )
- PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
- if( blockingWriteBuffersReadyEventInitialized )
- CloseHandle( stream->blockingState->writeBuffersReadyEvent );
- if( stream->blockingState->readRingBufferData )
- PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
- if( stream->blockingState->readStreamBuffer )
- PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
- if( blockingReadFramesReadyEventInitialized )
- CloseHandle( stream->blockingState->readFramesReadyEvent );
- PaUtil_FreeMemory( stream->blockingState );
- }
- if( callbackBufferProcessorInited )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- if( completedBuffersPlayedEventInited )
- CloseHandle( stream->completedBuffersPlayedEvent );
- if( stream->asioBufferInfos )
- PaUtil_FreeMemory( stream->asioBufferInfos );
- if( stream->asioChannelInfos )
- PaUtil_FreeMemory( stream->asioChannelInfos );
- if( stream->bufferPtrs )
- PaUtil_FreeMemory( stream->bufferPtrs );
- PaUtil_FreeMemory( stream );
- }
- if( asioBuffersCreated )
- ASIODisposeBuffers();
- if( asioIsInitialized )
- {
- UnloadAsioDriver();
- }
- return result;
- }
- /*
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
- */
- static PaError CloseStream( PaStream* s )
- {
- PaError result = paNoError;
- PaAsioStream *stream = (PaAsioStream*)s;
- /*
- IMPLEMENT ME:
- - additional stream closing + cleanup
- */
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- stream->asioHostApi->openAsioDeviceIndex = paNoDevice;
- CloseHandle( stream->completedBuffersPlayedEvent );
- /* Using blocking i/o interface... */
- if( stream->blockingState )
- {
- PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
- if( stream->inputChannelCount ) {
- PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
- PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
- CloseHandle( stream->blockingState->readFramesReadyEvent );
- }
- if( stream->outputChannelCount ) {
- PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
- PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
- CloseHandle( stream->blockingState->writeBuffersReadyEvent );
- }
- PaUtil_FreeMemory( stream->blockingState );
- }
- PaUtil_FreeMemory( stream->asioBufferInfos );
- PaUtil_FreeMemory( stream->asioChannelInfos );
- PaUtil_FreeMemory( stream->bufferPtrs );
- PaUtil_FreeMemory( stream );
- ASIODisposeBuffers();
- UnloadAsioDriver();
- theAsioStream = 0;
- return result;
- }
- static void bufferSwitch(long index, ASIOBool directProcess)
- {
- //TAKEN FROM THE ASIO SDK
- // the actual processing callback.
- // Beware that this is normally in a seperate thread, hence be sure that
- // you take care about thread synchronization. This is omitted here for
- // simplicity.
- // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs
- // to be created though it will only set the timeInfo.samplePosition and
- // timeInfo.systemTime fields and the according flags
- ASIOTime timeInfo;
- memset( &timeInfo, 0, sizeof (timeInfo) );
- // get the time stamp of the buffer, not necessary if no
- // synchronization to other media is required
- if( ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
- timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
- // Call the real callback
- bufferSwitchTimeInfo( &timeInfo, index, directProcess );
- }
- // conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
- #if NATIVE_INT64
- #define ASIO64toDouble(a) (a)
- #else
- const double twoRaisedTo32 = 4294967296.;
- #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
- #endif
- static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess )
- {
- // the actual processing callback.
- // Beware that this is normally in a seperate thread, hence be sure that
- // you take care about thread synchronization.
- /* The SDK says the following about the directProcess flag:
- suggests to the host whether it should immediately start processing
- (directProcess == ASIOTrue), or whether its process should be deferred
- because the call comes from a very low level (for instance, a high level
- priority interrupt), and direct processing would cause timing instabilities for
- the rest of the system. If in doubt, directProcess should be set to ASIOFalse.
- We just ignore directProcess. This could cause incompatibilities with
- drivers which really don't want the audio processing to occur in this
- callback, but none have been identified yet.
- */
- (void) directProcess; /* suppress unused parameter warning */
- #if 0
- // store the timeInfo for later use
- asioDriverInfo.tInfo = *timeInfo;
- // get the time stamp of the buffer, not necessary if no
- // synchronization to other media is required
- if (timeInfo->timeInfo.flags & kSystemTimeValid)
- asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
- else
- asioDriverInfo.nanoSeconds = 0;
- if (timeInfo->timeInfo.flags & kSamplePositionValid)
- asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
- else
- asioDriverInfo.samples = 0;
- if (timeInfo->timeCode.flags & kTcValid)
- asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
- else
- asioDriverInfo.tcSamples = 0;
- // get the system reference time
- asioDriverInfo.sysRefTime = get_sys_reference_time();
- #endif
- #if 0
- // a few debug messages for the Windows device driver developer
- // tells you the time when driver got its interrupt and the delay until the app receives
- // the event notification.
- static double last_samples = 0;
- char tmp[128];
- sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
- OutputDebugString (tmp);
- last_samples = asioDriverInfo.samples;
- #endif
- if( !theAsioStream )
- return 0L;
- // protect against reentrancy
- if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) )
- {
- theAsioStream->reenterError++;
- //DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError));
- return 0L;
- }
- int buffersDone = 0;
-
- do
- {
- if( buffersDone > 0 )
- {
- // this is a reentered buffer, we missed processing it on time
- // set the input overflow and output underflow flags as appropriate
-
- if( theAsioStream->inputChannelCount > 0 )
- theAsioStream->callbackFlags |= paInputOverflow;
-
- if( theAsioStream->outputChannelCount > 0 )
- theAsioStream->callbackFlags |= paOutputUnderflow;
- }
- else
- {
- if( theAsioStream->zeroOutput )
- {
- ZeroOutputBuffers( theAsioStream, index );
- // Finally if the driver supports the ASIOOutputReady() optimization,
- // do it here, all data are in place
- if( theAsioStream->postOutput )
- ASIOOutputReady();
- if( theAsioStream->stopProcessing )
- {
- if( theAsioStream->stopPlayoutCount < 2 )
- {
- ++theAsioStream->stopPlayoutCount;
- if( theAsioStream->stopPlayoutCount == 2 )
- {
- theAsioStream->isActive = 0;
- if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
- theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
- theAsioStream->streamFinishedCallbackCalled = true;
- SetEvent( theAsioStream->completedBuffersPlayedEvent );
- }
- }
- }
- }
- else
- {
- #if 0
- /*
- see: "ASIO callback underflow/overflow buffer slip detection doesn't work"
- http://www.portaudio.com/trac/ticket/110
- */
- // test code to try to detect slip conditions... these may work on some systems
- // but neither of them work on the RME Digi96
- // check that sample delta matches buffer size (otherwise we must have skipped
- // a buffer.
- static double last_samples = -512;
- double samples;
- //if( timeInfo->timeCode.flags & kTcValid )
- // samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
- //else
- samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
- int delta = samples - last_samples;
- //printf( "%d\n", delta);
- last_samples = samples;
- if( delta > theAsioStream->framesPerHostCallback )
- {
- if( theAsioStream->inputChannelCount > 0 )
- theAsioStream->callbackFlags |= paInputOverflow;
- if( theAsioStream->outputChannelCount > 0 )
- theAsioStream->callbackFlags |= paOutputUnderflow;
- }
- // check that the buffer index is not the previous index (which would indicate
- // that a buffer was skipped.
- static int previousIndex = 1;
- if( index == previousIndex )
- {
- if( theAsioStream->inputChannelCount > 0 )
- theAsioStream->callbackFlags |= paInputOverflow;
- if( theAsioStream->outputChannelCount > 0 )
- theAsioStream->callbackFlags |= paOutputUnderflow;
- }
- previousIndex = index;
- #endif
- int i;
- PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer );
- PaStreamCallbackTimeInfo paTimeInfo;
- // asio systemTime is supposed to be measured according to the same
- // clock as timeGetTime
- paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001);
- /* patch from Paul Boege */
- paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime -
- ((double)theAsioStream->asioInputLatencyFrames/theAsioStream->streamRepresentation.streamInfo.sampleRate);
- paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime +
- ((double)theAsioStream->asioOutputLatencyFrames/theAsioStream->streamRepresentation.streamInfo.sampleRate);
- /* old version is buggy because the buffer processor also adds in its latency to the time parameters
- paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency;
- paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency;
- */
- /* Disabled! Stopping and re-starting the stream causes an input overflow / output underflow. S.Fischer */
- #if 0
- // detect underflows by checking inter-callback time > 2 buffer period
- static double previousTime = -1;
- if( previousTime > 0 ){
- double delta = paTimeInfo.currentTime - previousTime;
- if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){
- if( theAsioStream->inputChannelCount > 0 )
- theAsioStream->callbackFlags |= paInputOverflow;
- if( theAsioStream->outputChannelCount > 0 )
- theAsioStream->callbackFlags |= paOutputUnderflow;
- }
- }
- previousTime = paTimeInfo.currentTime;
- #endif
- // note that the above input and output times do not need to be
- // adjusted for the latency of the buffer processor -- the buffer
- // processor handles that.
- if( theAsioStream->inputBufferConverter )
- {
- for( i=0; i<theAsioStream->inputChannelCount; i++ )
- {
- theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i],
- theAsioStream->inputShift, theAsioStream->framesPerHostCallback );
- }
- }
- PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags );
- /* reset status flags once they've been passed to the callback */
- theAsioStream->callbackFlags = 0;
- PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
- for( i=0; i<theAsioStream->inputChannelCount; ++i )
- PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] );
- PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
- for( i=0; i<theAsioStream->outputChannelCount; ++i )
- PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] );
- int callbackResult;
- if( theAsioStream->stopProcessing )
- callbackResult = paComplete;
- else
- callbackResult = paContinue;
- unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult );
- if( theAsioStream->outputBufferConverter )
- {
- for( i=0; i<theAsioStream->outputChannelCount; i++ )
- {
- theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i],
- theAsioStream->outputShift, theAsioStream->framesPerHostCallback );
- }
- }
- PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed );
- // Finally if the driver supports the ASIOOutputReady() optimization,
- // do it here, all data are in place
- if( theAsioStream->postOutput )
- ASIOOutputReady();
- if( callbackResult == paContinue )
- {
- /* nothing special to do */
- }
- else if( callbackResult == paAbort )
- {
- /* finish playback immediately */
- theAsioStream->isActive = 0;
- if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
- theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
- theAsioStream->streamFinishedCallbackCalled = true;
- SetEvent( theAsioStream->completedBuffersPlayedEvent );
- theAsioStream->zeroOutput = true;
- }
- else /* paComplete or other non-zero value indicating complete */
- {
- /* Finish playback once currently queued audio has completed. */
- theAsioStream->stopProcessing = true;
- if( PaUtil_IsBufferProcessorOutputEmpty( &theAsioStream->bufferProcessor ) )
- {
- theAsioStream->zeroOutput = true;
- theAsioStream->stopPlayoutCount = 0;
- }
- }
- }
- }
-
- ++buffersDone;
- }while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 );
- return 0L;
- }
- static void sampleRateChanged(ASIOSampleRate sRate)
- {
- // TAKEN FROM THE ASIO SDK
- // do whatever you need to do if the sample rate changed
- // usually this only happens during external sync.
- // Audio processing is not stopped by the driver, actual sample rate
- // might not have even changed, maybe only the sample rate status of an
- // AES/EBU or S/PDIF digital input at the audio device.
- // You might have to update time/sample related conversion routines, etc.
- (void) sRate; /* unused parameter */
- PA_DEBUG( ("sampleRateChanged : %d \n", sRate));
- }
- static long asioMessages(long selector, long value, void* message, double* opt)
- {
- // TAKEN FROM THE ASIO SDK
- // currently the parameters "value", "message" and "opt" are not used.
- long ret = 0;
- (void) message; /* unused parameters */
- (void) opt;
- PA_DEBUG( ("asioMessages : %d , %d \n", selector, value));
- switch(selector)
- {
- case kAsioSelectorSupported:
- if(value == kAsioResetRequest
- || value == kAsioEngineVersion
- || value == kAsioResyncRequest
- || value == kAsioLatenciesChanged
- // the following three were added for ASIO 2.0, you don't necessarily have to support them
- || value == kAsioSupportsTimeInfo
- || value == kAsioSupportsTimeCode
- || value == kAsioSupportsInputMonitor)
- ret = 1L;
- break;
- case kAsioBufferSizeChange:
- //printf("kAsioBufferSizeChange \n");
- break;
- case kAsioResetRequest:
- // defer the task and perform the reset of the driver during the next "safe" situation
- // You cannot reset the driver right now, as this code is called from the driver.
- // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
- // Afterwards you initialize the driver again.
- /*FIXME: commented the next line out
- see: "PA/ASIO ignores some driver notifications it probably shouldn't"
- http://www.portaudio.com/trac/ticket/108
- */
- //asioDriverInfo.stopped; // In this sample the processing will just stop
- ret = 1L;
- break;
- case kAsioResyncRequest:
- // This informs the application, that the driver encountered some non fatal data loss.
- // It is used for synchronization purposes of different media.
- // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
- // Windows Multimedia system, which could loose data because the Mutex was hold too long
- // by another thread.
- // However a driver can issue it in other situations, too.
- ret = 1L;
- break;
- case kAsioLatenciesChanged:
- // This will inform the host application that the drivers were latencies changed.
- // Beware, it this does not mean that the buffer sizes have changed!
- // You might need to update internal delay data.
- ret = 1L;
- //printf("kAsioLatenciesChanged \n");
- break;
- case kAsioEngineVersion:
- // return the supported ASIO version of the host application
- // If a host applications does not implement this selector, ASIO 1.0 is assumed
- // by the driver
- ret = 2L;
- break;
- case kAsioSupportsTimeInfo:
- // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
- // is supported.
- // For compatibility with ASIO 1.0 drivers the host application should always support
- // the "old" bufferSwitch method, too.
- ret = 1;
- break;
- case kAsioSupportsTimeCode:
- // informs the driver wether application is interested in time code info.
- // If an application does not need to know about time code, the driver has less work
- // to do.
- ret = 0;
- break;
- }
- return ret;
- }
- static PaError StartStream( PaStream *s )
- {
- PaError result = paNoError;
- PaAsioStream *stream = (PaAsioStream*)s;
- PaAsioStreamBlockingState *blockingState = stream->blockingState;
- ASIOError asioError;
- if( stream->outputChannelCount > 0 )
- {
- ZeroOutputBuffers( stream, 0 );
- ZeroOutputBuffers( stream, 1 );
- }
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
- stream->stopProcessing = false;
- stream->zeroOutput = false;
- /* Reentrancy counter initialisation */
- stream->reenterCount = -1;
- stream->reenterError = 0;
- stream->callbackFlags = 0;
- if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- /* Using blocking i/o interface... */
- if( blockingState )
- {
- /* Reset blocking i/o buffer processor. */
- PaUtil_ResetBufferProcessor( &blockingState->bufferProcessor );
- /* If we're about to process some input data. */
- if( stream->inputChannelCount )
- {
- /* Reset callback-ReadStream sync event. */
- if( ResetEvent( blockingState->readFramesReadyEvent ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- /* Flush blocking i/o ring buffer. */
- PaUtil_FlushRingBuffer( &blockingState->readRingBuffer );
- (*blockingState->bufferProcessor.inputZeroer)( blockingState->readRingBuffer.buffer, 1, blockingState->bufferProcessor.inputChannelCount * blockingState->readRingBuffer.bufferSize );
- }
- /* If we're about to process some output data. */
- if( stream->outputChannelCount )
- {
- /* Reset callback-WriteStream sync event. */
- if( ResetEvent( blockingState->writeBuffersReadyEvent ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- /* Flush blocking i/o ring buffer. */
- PaUtil_FlushRingBuffer( &blockingState->writeRingBuffer );
- (*blockingState->bufferProcessor.outputZeroer)( blockingState->writeRingBuffer.buffer, 1, blockingState->bufferProcessor.outputChannelCount * blockingState->writeRingBuffer.bufferSize );
- /* Initialize the output ring buffer to "silence". */
- PaUtil_AdvanceRingBufferWriteIndex( &blockingState->writeRingBuffer, blockingState->writeRingBufferInitialFrames );
- }
- /* Clear requested frames / buffers count. */
- blockingState->writeBuffersRequested = 0;
- blockingState->readFramesRequested = 0;
- blockingState->writeBuffersRequestedFlag = FALSE;
- blockingState->readFramesRequestedFlag = FALSE;
- blockingState->outputUnderflowFlag = FALSE;
- blockingState->inputOverflowFlag = FALSE;
- blockingState->stopFlag = FALSE;
- }
- if( result == paNoError )
- {
- assert( theAsioStream == stream ); /* theAsioStream should be set correctly in OpenStream */
- /* initialize these variables before the callback has a chance to be invoked */
- stream->isStopped = 0;
- stream->isActive = 1;
- stream->streamFinishedCallbackCalled = false;
- asioError = ASIOStart();
- if( asioError != ASE_OK )
- {
- stream->isStopped = 1;
- stream->isActive = 0;
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- }
- }
- return result;
- }
- static void EnsureCallbackHasCompleted( PaAsioStream *stream )
- {
- // make sure that the callback is not still in-flight after ASIOStop()
- // returns. This has been observed to happen on the Hoontech DSP24 for
- // example.
- int count = 2000; // only wait for 2 seconds, rather than hanging.
- while( stream->reenterCount != -1 && count > 0 )
- {
- Sleep(1);
- --count;
- }
- }
- static PaError StopStream( PaStream *s )
- {
- PaError result = paNoError;
- PaAsioStream *stream = (PaAsioStream*)s;
- PaAsioStreamBlockingState *blockingState = stream->blockingState;
- ASIOError asioError;
- if( stream->isActive )
- {
- /* If blocking i/o output is in use */
- if( blockingState && stream->outputChannelCount )
- {
- /* Request the whole output buffer to be available. */
- blockingState->writeBuffersRequested = blockingState->writeRingBuffer.bufferSize;
- /* Signalize that additional buffers are need. */
- blockingState->writeBuffersRequestedFlag = TRUE;
- /* Set flag to indicate the playback is to be stopped. */
- blockingState->stopFlag = TRUE;
- /* Wait until requested number of buffers has been freed. Time
- out after twice the blocking i/o ouput buffer could have
- been consumed. */
- DWORD timeout = (DWORD)( 2 * blockingState->writeRingBuffer.bufferSize * 1000
- / stream->streamRepresentation.streamInfo.sampleRate );
- DWORD waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
- /* If something seriously went wrong... */
- if( waitResult == WAIT_FAILED )
- {
- PA_DEBUG(("WaitForSingleObject() failed in StopStream()\n"));
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n"));
- result = paTimedOut;
- }
- }
- stream->stopProcessing = true;
- /* wait for the stream to finish playing out enqueued buffers.
- timeout after four times the stream latency.
- @todo should use a better time out value - if the user buffer
- length is longer than the asio buffer size then that should
- be taken into account.
- */
- if( WaitForSingleObject( stream->completedBuffersPlayedEvent,
- (DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) )
- == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" ));
- }
- }
- asioError = ASIOStop();
- if( asioError == ASE_OK )
- {
- EnsureCallbackHasCompleted( stream );
- }
- else
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- }
- stream->isStopped = 1;
- stream->isActive = 0;
- if( !stream->streamFinishedCallbackCalled )
- {
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
- return result;
- }
- static PaError AbortStream( PaStream *s )
- {
- PaError result = paNoError;
- PaAsioStream *stream = (PaAsioStream*)s;
- ASIOError asioError;
- stream->zeroOutput = true;
- asioError = ASIOStop();
- if( asioError == ASE_OK )
- {
- EnsureCallbackHasCompleted( stream );
- }
- else
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- }
- stream->isStopped = 1;
- stream->isActive = 0;
- if( !stream->streamFinishedCallbackCalled )
- {
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
- return result;
- }
- static PaError IsStreamStopped( PaStream *s )
- {
- PaAsioStream *stream = (PaAsioStream*)s;
-
- return stream->isStopped;
- }
- static PaError IsStreamActive( PaStream *s )
- {
- PaAsioStream *stream = (PaAsioStream*)s;
- return stream->isActive;
- }
- static PaTime GetStreamTime( PaStream *s )
- {
- (void) s; /* unused parameter */
- return (double)timeGetTime() * .001;
- }
- static double GetStreamCpuLoad( PaStream* s )
- {
- PaAsioStream *stream = (PaAsioStream*)s;
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
- }
- /*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
- */
- static PaError ReadStream( PaStream *s ,
- void *buffer,
- unsigned long frames )
- {
- PaError result = paNoError; /* Initial return value. */
- PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
- /* Pointer to the blocking i/o data struct. */
- PaAsioStreamBlockingState *blockingState = stream->blockingState;
- /* Get blocking i/o buffer processor and ring buffer pointers. */
- PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
- PaUtilRingBuffer *pRb = &blockingState->readRingBuffer;
- /* Ring buffer segment(s) used for writing. */
- void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
- void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
- /* Number of frames per ring buffer segment. */
- long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
- long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
- /* Get number of frames to be processed per data block. */
- unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
- /* Actual number of frames that has been copied into the ring buffer. */
- unsigned long lFramesCopied = 0;
- /* The number of remaining unprocessed dtat frames. */
- unsigned long lFramesRemaining = frames;
- /* Copy the input argument to avoid pointer increment! */
- const void *userBuffer;
- unsigned int i; /* Just a counter. */
- /* About the time, needed to process 8 data blocks. */
- DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
- DWORD waitResult = 0;
- /* Check if the stream is still available ready to gather new data. */
- if( blockingState->stopFlag || !stream->isActive )
- {
- PA_DEBUG(("Warning! Stream no longer available for reading in ReadStream()\n"));
- result = paStreamIsStopped;
- return result;
- }
- /* If the stream is a input stream. */
- if( stream->inputChannelCount )
- {
- /* Prepare buffer access. */
- if( !pBp->userOutputIsInterleaved )
- {
- userBuffer = blockingState->readStreamBuffer;
- for( i = 0; i<pBp->inputChannelCount; ++i )
- {
- ((void**)userBuffer)[i] = ((void**)buffer)[i];
- }
- } /* Use the unchanged buffer. */
- else { userBuffer = buffer; }
- do /* Internal block processing for too large user data buffers. */
- {
- /* Get the size of the current data block to be processed. */
- lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
- ? lFramesPerBlock : lFramesRemaining;
- /* Use predefined block size for as long there are enough
- buffers available, thereafter reduce the processing block
- size to match the number of remaining buffers. So the final
- data block is processed although it may be incomplete. */
- /* If the available amount of data frames is insufficient. */
- if( PaUtil_GetRingBufferReadAvailable(pRb) < (long) lFramesPerBlock )
- {
- /* Make sure, the event isn't already set! */
- /* ResetEvent( blockingState->readFramesReadyEvent ); */
- /* Set the number of requested buffers. */
- blockingState->readFramesRequested = lFramesPerBlock;
- /* Signalize that additional buffers are need. */
- blockingState->readFramesRequestedFlag = TRUE;
- /* Wait until requested number of buffers has been freed. */
- waitResult = WaitForSingleObject( blockingState->readFramesReadyEvent, timeout );
- /* If something seriously went wrong... */
- if( waitResult == WAIT_FAILED )
- {
- PA_DEBUG(("WaitForSingleObject() failed in ReadStream()\n"));
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- return result;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WaitForSingleObject() timed out in ReadStream()\n"));
- /* If block processing has stopped, abort! */
- if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
- /* If a timeout is encountered, give up eventually. */
- return result = paTimedOut;
- }
- }
- /* Now, the ring buffer contains the required amount of data
- frames.
- (Therefor we don't need to check the return argument of
- PaUtil_GetRingBufferReadRegions(). ;-) )
- */
- /* Retrieve pointer(s) to the ring buffer's current write
- position(s). If the first buffer segment is too small to
- store the requested number of bytes, an additional second
- segment is returned. Otherwise, i.e. if the first segment
- is large enough, the second segment's pointer will be NULL.
- */
- PaUtil_GetRingBufferReadRegions(pRb ,
- lFramesPerBlock ,
- &pRingBufferData1st,
- &lRingBufferSize1st,
- &pRingBufferData2nd,
- &lRingBufferSize2nd);
- /* Set number of frames to be copied from the ring buffer. */
- PaUtil_SetInputFrameCount( pBp, lRingBufferSize1st );
- /* Setup ring buffer access. */
- PaUtil_SetInterleavedInputChannels(pBp , /* Buffer processor. */
- 0 , /* The first channel's index. */
- pRingBufferData1st, /* First ring buffer segment. */
- 0 ); /* Use all available channels. */
- /* If a second ring buffer segment is required. */
- if( lRingBufferSize2nd ) {
- /* Set number of frames to be copied from the ring buffer. */
- PaUtil_Set2ndInputFrameCount( pBp, lRingBufferSize2nd );
- /* Setup ring buffer access. */
- PaUtil_Set2ndInterleavedInputChannels(pBp , /* Buffer processor. */
- 0 , /* The first channel's index. */
- pRingBufferData2nd, /* Second ring buffer segment. */
- 0 ); /* Use all available channels. */
- }
- /* Let the buffer processor handle "copy and conversion" and
- update the ring buffer indices manually. */
- lFramesCopied = PaUtil_CopyInput( pBp, &buffer, lFramesPerBlock );
- PaUtil_AdvanceRingBufferReadIndex( pRb, lFramesCopied );
- /* Decrease number of unprocessed frames. */
- lFramesRemaining -= lFramesCopied;
- } /* Continue with the next data chunk. */
- while( lFramesRemaining );
- /* If there has been an input overflow within the callback */
- if( blockingState->inputOverflowFlag )
- {
- blockingState->inputOverflowFlag = FALSE;
- /* Return the corresponding error code. */
- result = paInputOverflowed;
- }
- } /* If this is not an input stream. */
- else {
- result = paCanNotReadFromAnOutputOnlyStream;
- }
- return result;
- }
- static PaError WriteStream( PaStream *s ,
- const void *buffer,
- unsigned long frames )
- {
- PaError result = paNoError; /* Initial return value. */
- PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
- /* Pointer to the blocking i/o data struct. */
- PaAsioStreamBlockingState *blockingState = stream->blockingState;
- /* Get blocking i/o buffer processor and ring buffer pointers. */
- PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
- PaUtilRingBuffer *pRb = &blockingState->writeRingBuffer;
- /* Ring buffer segment(s) used for writing. */
- void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
- void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
- /* Number of frames per ring buffer segment. */
- long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
- long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
- /* Get number of frames to be processed per data block. */
- unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
- /* Actual number of frames that has been copied into the ring buffer. */
- unsigned long lFramesCopied = 0;
- /* The number of remaining unprocessed dtat frames. */
- unsigned long lFramesRemaining = frames;
- /* About the time, needed to process 8 data blocks. */
- DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
- DWORD waitResult = 0;
- /* Copy the input argument to avoid pointer increment! */
- const void *userBuffer;
- unsigned int i; /* Just a counter. */
- /* Check if the stream ist still available ready to recieve new data. */
- if( blockingState->stopFlag || !stream->isActive )
- {
- PA_DEBUG(("Warning! Stream no longer available for writing in WriteStream()\n"));
- result = paStreamIsStopped;
- return result;
- }
- /* If the stream is a output stream. */
- if( stream->outputChannelCount )
- {
- /* Prepare buffer access. */
- if( !pBp->userOutputIsInterleaved )
- {
- userBuffer = blockingState->writeStreamBuffer;
- for( i = 0; i<pBp->outputChannelCount; ++i )
- {
- ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
- }
- } /* Use the unchanged buffer. */
- else { userBuffer = buffer; }
- do /* Internal block processing for too large user data buffers. */
- {
- /* Get the size of the current data block to be processed. */
- lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
- ? lFramesPerBlock : lFramesRemaining;
- /* Use predefined block size for as long there are enough
- frames available, thereafter reduce the processing block
- size to match the number of remaining frames. So the final
- data block is processed although it may be incomplete. */
- /* If the available amount of buffers is insufficient. */
- if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) lFramesPerBlock )
- {
- /* Make sure, the event isn't already set! */
- /* ResetEvent( blockingState->writeBuffersReadyEvent ); */
- /* Set the number of requested buffers. */
- blockingState->writeBuffersRequested = lFramesPerBlock;
- /* Signalize that additional buffers are need. */
- blockingState->writeBuffersRequestedFlag = TRUE;
- /* Wait until requested number of buffers has been freed. */
- waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
- /* If something seriously went wrong... */
- if( waitResult == WAIT_FAILED )
- {
- PA_DEBUG(("WaitForSingleObject() failed in WriteStream()\n"));
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
- return result;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WaitForSingleObject() timed out in WriteStream()\n"));
- /* If block processing has stopped, abort! */
- if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
-
- /* If a timeout is encountered, give up eventually. */
- return result = paTimedOut;
- }
- }
- /* Now, the ring buffer contains the required amount of free
- space to store the provided number of data frames.
- (Therefor we don't need to check the return argument of
- PaUtil_GetRingBufferWriteRegions(). ;-) )
- */
- /* Retrieve pointer(s) to the ring buffer's current write
- position(s). If the first buffer segment is too small to
- store the requested number of bytes, an additional second
- segment is returned. Otherwise, i.e. if the first segment
- is large enough, the second segment's pointer will be NULL.
- */
- PaUtil_GetRingBufferWriteRegions(pRb ,
- lFramesPerBlock ,
- &pRingBufferData1st,
- &lRingBufferSize1st,
- &pRingBufferData2nd,
- &lRingBufferSize2nd);
- /* Set number of frames to be copied to the ring buffer. */
- PaUtil_SetOutputFrameCount( pBp, lRingBufferSize1st );
- /* Setup ring buffer access. */
- PaUtil_SetInterleavedOutputChannels(pBp , /* Buffer processor. */
- 0 , /* The first channel's index. */
- pRingBufferData1st, /* First ring buffer segment. */
- 0 ); /* Use all available channels. */
- /* If a second ring buffer segment is required. */
- if( lRingBufferSize2nd ) {
- /* Set number of frames to be copied to the ring buffer. */
- PaUtil_Set2ndOutputFrameCount( pBp, lRingBufferSize2nd );
- /* Setup ring buffer access. */
- PaUtil_Set2ndInterleavedOutputChannels(pBp , /* Buffer processor. */
- 0 , /* The first channel's index. */
- pRingBufferData2nd, /* Second ring buffer segment. */
- 0 ); /* Use all available channels. */
- }
- /* Let the buffer processor handle "copy and conversion" and
- update the ring buffer indices manually. */
- lFramesCopied = PaUtil_CopyOutput( pBp, &userBuffer, lFramesPerBlock );
- PaUtil_AdvanceRingBufferWriteIndex( pRb, lFramesCopied );
- /* Decrease number of unprocessed frames. */
- lFramesRemaining -= lFramesCopied;
- } /* Continue with the next data chunk. */
- while( lFramesRemaining );
- /* If there has been an output underflow within the callback */
- if( blockingState->outputUnderflowFlag )
- {
- blockingState->outputUnderflowFlag = FALSE;
- /* Return the corresponding error code. */
- result = paOutputUnderflowed;
- }
- } /* If this is not an output stream. */
- else
- {
- result = paCanNotWriteToAnInputOnlyStream;
- }
- return result;
- }
- static signed long GetStreamReadAvailable( PaStream* s )
- {
- PaAsioStream *stream = (PaAsioStream*)s;
- /* Call buffer utility routine to get the number of available frames. */
- return PaUtil_GetRingBufferReadAvailable( &stream->blockingState->readRingBuffer );
- }
- static signed long GetStreamWriteAvailable( PaStream* s )
- {
- PaAsioStream *stream = (PaAsioStream*)s;
- /* Call buffer utility routine to get the number of empty buffers. */
- return PaUtil_GetRingBufferWriteAvailable( &stream->blockingState->writeRingBuffer );
- }
- /* This routine will be called by the PortAudio engine when audio is needed.
- ** It may called at interrupt level on some machines so don't do anything
- ** that could mess up the system like calling malloc() or free().
- */
- static int BlockingIoPaCallback(const void *inputBuffer ,
- void *outputBuffer ,
- unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo *timeInfo ,
- PaStreamCallbackFlags statusFlags ,
- void *userData )
- {
- PaError result = paNoError; /* Initial return value. */
- PaAsioStream *stream = *(PaAsioStream**)userData; /* The PA ASIO stream. */
- PaAsioStreamBlockingState *blockingState = stream->blockingState; /* Persume blockingState is valid, otherwise the callback wouldn't be running. */
- /* Get a pointer to the stream's blocking i/o buffer processor. */
- PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
- PaUtilRingBuffer *pRb = NULL;
- /* If output data has been requested. */
- if( stream->outputChannelCount )
- {
- /* If the callback input argument signalizes a output underflow,
- make sure the WriteStream() function knows about it, too! */
- if( statusFlags & paOutputUnderflowed ) {
- blockingState->outputUnderflowFlag = TRUE;
- }
- /* Access the corresponding ring buffer. */
- pRb = &blockingState->writeRingBuffer;
- /* If the blocking i/o buffer contains enough output data, */
- if( PaUtil_GetRingBufferReadAvailable(pRb) >= (long) framesPerBuffer )
- {
- /* Extract the requested data from the ring buffer. */
- PaUtil_ReadRingBuffer( pRb, outputBuffer, framesPerBuffer );
- }
- else /* If no output data is available :-( */
- {
- /* Signalize a write-buffer underflow. */
- blockingState->outputUnderflowFlag = TRUE;
- /* Fill the output buffer with silence. */
- (*pBp->outputZeroer)( outputBuffer, 1, pBp->outputChannelCount * framesPerBuffer );
- /* If playback is to be stopped */
- if( blockingState->stopFlag && PaUtil_GetRingBufferReadAvailable(pRb) < (long) framesPerBuffer )
- {
- /* Extract all the remaining data from the ring buffer,
- whether it is a complete data block or not. */
- PaUtil_ReadRingBuffer( pRb, outputBuffer, PaUtil_GetRingBufferReadAvailable(pRb) );
- }
- }
- /* Set blocking i/o event? */
- if( blockingState->writeBuffersRequestedFlag && PaUtil_GetRingBufferWriteAvailable(pRb) >= (long) blockingState->writeBuffersRequested )
- {
- /* Reset buffer request. */
- blockingState->writeBuffersRequestedFlag = FALSE;
- blockingState->writeBuffersRequested = 0;
- /* Signalize that requested buffers are ready. */
- SetEvent( blockingState->writeBuffersReadyEvent );
- /* What do we do if SetEvent() returns zero, i.e. the event
- could not be set? How to return errors from within the
- callback? - S.Fischer */
- }
- }
- /* If input data has been supplied. */
- if( stream->inputChannelCount )
- {
- /* If the callback input argument signalizes a input overflow,
- make sure the ReadStream() function knows about it, too! */
- if( statusFlags & paInputOverflowed ) {
- blockingState->inputOverflowFlag = TRUE;
- }
- /* Access the corresponding ring buffer. */
- pRb = &blockingState->readRingBuffer;
- /* If the blocking i/o buffer contains not enough input buffers */
- if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) framesPerBuffer )
- {
- /* Signalize a read-buffer overflow. */
- blockingState->inputOverflowFlag = TRUE;
- /* Remove some old data frames from the buffer. */
- PaUtil_AdvanceRingBufferReadIndex( pRb, framesPerBuffer );
- }
- /* Insert the current input data into the ring buffer. */
- PaUtil_WriteRingBuffer( pRb, inputBuffer, framesPerBuffer );
- /* Set blocking i/o event? */
- if( blockingState->readFramesRequestedFlag && PaUtil_GetRingBufferReadAvailable(pRb) >= (long) blockingState->readFramesRequested )
- {
- /* Reset buffer request. */
- blockingState->readFramesRequestedFlag = FALSE;
- blockingState->readFramesRequested = 0;
- /* Signalize that requested buffers are ready. */
- SetEvent( blockingState->readFramesReadyEvent );
- /* What do we do if SetEvent() returns zero, i.e. the event
- could not be set? How to return errors from within the
- callback? - S.Fischer */
- /** @todo report an error with PA_DEBUG */
- }
- }
- return paContinue;
- }
- PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
- {
- PaError result = paNoError;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiDevice;
- ASIODriverInfo asioDriverInfo;
- ASIOError asioError;
- int asioIsInitialized = 0;
- PaAsioHostApiRepresentation *asioHostApi;
- PaAsioDeviceInfo *asioDeviceInfo;
- PaWinUtilComInitializationResult comInitializationResult;
- /* initialize COM again here, we might be in another thread */
- result = PaWinUtil_CoInitialize( paASIO, &comInitializationResult );
- if( result != paNoError )
- return result;
- result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
- if( result != paNoError )
- goto error;
- result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
- if( result != paNoError )
- goto error;
- /*
- In theory we could proceed if the currently open device was the same
- one for which the control panel was requested, however because the
- window pointer is not available until this function is called we
- currently need to call ASIOInit() again here, which of course can't be
- done safely while a stream is open.
- */
- asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
- if( asioHostApi->openAsioDeviceIndex != paNoDevice )
- {
- result = paDeviceUnavailable;
- goto error;
- }
- asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
- if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
- {
- result = paUnanticipatedHostError;
- goto error;
- }
- /* CRUCIAL!!! */
- memset( &asioDriverInfo, 0, sizeof(ASIODriverInfo) );
- asioDriverInfo.asioVersion = 2;
- asioDriverInfo.sysRef = systemSpecific;
- asioError = ASIOInit( &asioDriverInfo );
- if( asioError != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
- }
- else
- {
- asioIsInitialized = 1;
- }
- PA_DEBUG(("PaAsio_ShowControlPanel: ASIOInit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
- PA_DEBUG(("asioVersion: ASIOInit(): %ld\n", asioDriverInfo.asioVersion ));
- PA_DEBUG(("driverVersion: ASIOInit(): %ld\n", asioDriverInfo.driverVersion ));
- PA_DEBUG(("Name: ASIOInit(): %s\n", asioDriverInfo.name ));
- PA_DEBUG(("ErrorMessage: ASIOInit(): %s\n", asioDriverInfo.errorMessage ));
- asioError = ASIOControlPanel();
- if( asioError != ASE_OK )
- {
- PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
- }
- PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
- asioError = ASIOExit();
- if( asioError != ASE_OK )
- {
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- asioIsInitialized = 0;
- goto error;
- }
- PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
- return result;
- error:
- if( asioIsInitialized )
- {
- ASIOExit();
- }
- PaWinUtil_CoUninitialize( paASIO, &comInitializationResult );
- return result;
- }
- PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex,
- const char** channelName )
- {
- PaError result = paNoError;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiDevice;
- PaAsioDeviceInfo *asioDeviceInfo;
- result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
- if( result != paNoError )
- goto error;
- result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
- if( result != paNoError )
- goto error;
- asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
- if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxInputChannels ){
- result = paInvalidChannelCount;
- goto error;
- }
- *channelName = asioDeviceInfo->asioChannelInfos[channelIndex].name;
- return paNoError;
-
- error:
- return result;
- }
- PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex,
- const char** channelName )
- {
- PaError result = paNoError;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiDevice;
- PaAsioDeviceInfo *asioDeviceInfo;
- result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
- if( result != paNoError )
- goto error;
- result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
- if( result != paNoError )
- goto error;
- asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
- if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxOutputChannels ){
- result = paInvalidChannelCount;
- goto error;
- }
- *channelName = asioDeviceInfo->asioChannelInfos[
- asioDeviceInfo->commonDeviceInfo.maxInputChannels + channelIndex].name;
- return paNoError;
-
- error:
- return result;
- }
- /* NOTE: the following functions are ASIO-stream specific, and are called directly
- by client code. We need to check for many more error conditions here because
- we don't have the benefit of pa_front.c's parameter checking.
- */
- static PaError GetAsioStreamPointer( PaAsioStream **stream, PaStream *s )
- {
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaAsioHostApiRepresentation *asioHostApi;
-
- result = PaUtil_ValidateStreamPointer( s );
- if( result != paNoError )
- return result;
- result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
- if( result != paNoError )
- return result;
- asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
-
- if( PA_STREAM_REP( s )->streamInterface == &asioHostApi->callbackStreamInterface
- || PA_STREAM_REP( s )->streamInterface == &asioHostApi->blockingStreamInterface )
- {
- /* s is an ASIO stream */
- *stream = (PaAsioStream *)s;
- return paNoError;
- }
- else
- {
- return paIncompatibleStreamHostApi;
- }
- }
- PaError PaAsio_SetStreamSampleRate( PaStream* s, double sampleRate )
- {
- PaAsioStream *stream;
- PaError result = GetAsioStreamPointer( &stream, s );
- if( result != paNoError )
- return result;
- if( stream != theAsioStream )
- return paBadStreamPtr;
- return ValidateAndSetSampleRate( sampleRate );
- }
|