123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243 |
- #ifdef _WIN32
- /*
- * $Id: pa_win_ds.c 1877 2012-11-10 02:55:20Z rbencina $
- * Portable Audio I/O Library DirectSound implementation
- *
- * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
- *
- * 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.
- */
- /** @file
- @ingroup hostapi_src
- */
- /* Until May 2011 PA/DS has used a multimedia timer to perform the callback.
- We're replacing this with a new implementation using a thread and a different timer mechanim.
- Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior.
- */
- //#define PA_WIN_DS_USE_WMME_TIMER
- #include <assert.h>
- #include <stdio.h>
- #include <string.h> /* strlen() */
- #define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */
- #include <initguid.h> /* make sure ds guids get defined */
- #include <windows.h>
- #include <objbase.h>
- /*
- Use the earliest version of DX required, no need to polute the namespace
- */
- #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- #define DIRECTSOUND_VERSION 0x0800
- #else
- #define DIRECTSOUND_VERSION 0x0300
- #endif
- #include <dsound.h>
- #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- #include <dsconf.h>
- #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- #ifndef UNDER_CE
- #include <process.h>
- #endif
- #endif
- #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_win_ds.h"
- #include "pa_win_ds_dynlink.h"
- #include "pa_win_waveformat.h"
- #include "pa_win_wdmks_utils.h"
- #include "pa_win_coinitialize.h"
- #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
- #pragma comment( lib, "dsound.lib" )
- #pragma comment( lib, "winmm.lib" )
- #pragma comment( lib, "kernel32.lib" )
- #endif
- /* use CreateThread for CYGWIN, _beginthreadex for all others */
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- #if !defined(__CYGWIN__) && !defined(UNDER_CE)
- #define CREATE_THREAD (HANDLE)_beginthreadex
- #undef CLOSE_THREAD_HANDLE /* as per documentation we don't call CloseHandle on a thread created with _beginthreadex */
- #define PA_THREAD_FUNC static unsigned WINAPI
- #define PA_THREAD_ID unsigned
- #else
- #define CREATE_THREAD CreateThread
- #define CLOSE_THREAD_HANDLE CloseHandle
- #define PA_THREAD_FUNC static DWORD WINAPI
- #define PA_THREAD_ID DWORD
- #endif
- #if (defined(UNDER_CE))
- #pragma comment(lib, "Coredll.lib")
- #elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
- #pragma comment(lib, "winmm.lib")
- #endif
- PA_THREAD_FUNC ProcessingThreadProc( void *pArg );
- #if !defined(UNDER_CE)
- #define PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT /* use waitable timer where possible, otherwise we use a WaitForSingleObject timeout */
- #endif
- #endif /* !PA_WIN_DS_USE_WMME_TIMER */
- /*
- provided in newer platform sdks and x64
- */
- #ifndef DWORD_PTR
- #if defined(_WIN64)
- #define DWORD_PTR unsigned __int64
- #else
- #define DWORD_PTR unsigned long
- #endif
- #endif
- #define PRINT(x) PA_DEBUG(x);
- #define ERR_RPT(x) PRINT(x)
- #define DBUG(x) PRINT(x)
- #define DBUGX(x) PRINT(x)
- #define PA_USE_HIGH_LATENCY (0)
- #if PA_USE_HIGH_LATENCY
- #define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.500)
- #define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.600)
- #else
- #define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.140)
- #define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.280)
- #endif
- #define PA_DS_WIN_WDM_DEFAULT_LATENCY_ (.120)
- /* we allow the polling period to range between 1 and 100ms.
- prior to August 2011 we limited the minimum polling period to 10ms.
- */
- #define PA_DS_MINIMUM_POLLING_PERIOD_SECONDS (0.001) /* 1ms */
- #define PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS (0.100) /* 100ms */
- #define PA_DS_POLLING_JITTER_SECONDS (0.001) /* 1ms */
- #define SECONDS_PER_MSEC (0.001)
- #define MSECS_PER_SECOND (1000)
- /* prototypes for functions declared in this file */
- #ifdef __cplusplus
- extern "C"
- {
- #endif /* __cplusplus */
- PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
- 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 );
- /* FIXME: should convert hr to a string */
- #define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
- PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
- /************************************************* DX Prototypes **********/
- static BOOL CALLBACK CollectGUIDsProcA(LPGUID lpGUID,
- LPCSTR lpszDesc,
- LPCSTR lpszDrvName,
- LPVOID lpContext );
- /************************************************************************************/
- /********************** Structures **************************************************/
- /************************************************************************************/
- /* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
- typedef struct PaWinDsDeviceInfo
- {
- PaDeviceInfo inheritedDeviceInfo;
- GUID guid;
- GUID *lpGUID;
- double sampleRates[3];
- char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
- char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
- } PaWinDsDeviceInfo;
- typedef struct
- {
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
- PaUtilAllocationGroup *allocations;
- /* implementation specific data goes here */
- PaWinUtilComInitializationResult comInitializationResult;
- } PaWinDsHostApiRepresentation;
- /* PaWinDsStream - a stream data structure specifically for this implementation */
- typedef struct PaWinDsStream
- {
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
- /* DirectSound specific data. */
- #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- LPDIRECTSOUNDFULLDUPLEX8 pDirectSoundFullDuplex8;
- #endif
- /* Output */
- LPDIRECTSOUND pDirectSound;
- LPDIRECTSOUNDBUFFER pDirectSoundPrimaryBuffer;
- LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer;
- DWORD outputBufferWriteOffsetBytes; /* last write position */
- INT outputBufferSizeBytes;
- INT outputFrameSizeBytes;
- /* Try to detect play buffer underflows. */
- LARGE_INTEGER perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
- LARGE_INTEGER previousPlayTime;
- DWORD previousPlayCursor;
- UINT outputUnderflowCount;
- BOOL outputIsRunning;
- INT finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
- /* Input */
- LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
- LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer;
- INT inputFrameSizeBytes;
- UINT readOffset; /* last read position */
- UINT inputBufferSizeBytes;
-
- int hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
- double framesWritten;
- double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
- double pollingPeriodSeconds;
- PaStreamCallbackFlags callbackFlags;
- PaStreamFlags streamFlags;
- int callbackResult;
- HANDLE processingCompleted;
-
- /* FIXME - move all below to PaUtilStreamRepresentation */
- volatile int isStarted;
- volatile int isActive;
- volatile int stopProcessing; /* stop thread once existing buffers have been returned */
- volatile int abortProcessing; /* stop thread immediately */
- UINT systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */
- #ifdef PA_WIN_DS_USE_WMME_TIMER
- MMRESULT timerID;
- #else
- #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- HANDLE waitableTimer;
- #endif
- HANDLE processingThread;
- PA_THREAD_ID processingThreadId;
- HANDLE processingThreadCompleted;
- #endif
- } PaWinDsStream;
- /* Set minimal latency based on the current OS version.
- * NT has higher latency.
- */
- static double PaWinDS_GetMinSystemLatencySeconds( void )
- {
- double minLatencySeconds;
- /* Set minimal latency based on whether NT or other OS.
- * NT has higher latency.
- */
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- GetVersionEx( &osvi );
- DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
- DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
- DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
- /* Check for NT */
- if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
- {
- minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
- }
- else if(osvi.dwMajorVersion >= 5)
- {
- minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
- }
- else
- {
- minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
- }
- return minLatencySeconds;
- }
- /*************************************************************************
- ** Return minimum workable latency required for this host. This is returned
- ** As the default stream latency in PaDeviceInfo.
- ** Latency can be optionally set by user by setting an environment variable.
- ** For example, to set latency to 200 msec, put:
- **
- ** set PA_MIN_LATENCY_MSEC=200
- **
- ** in the AUTOEXEC.BAT file and reboot.
- ** If the environment variable is not set, then the latency will be determined
- ** based on the OS. Windows NT has higher latency than Win95.
- */
- #define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
- #define PA_ENV_BUF_SIZE (32)
- static double PaWinDs_GetMinLatencySeconds( double sampleRate )
- {
- char envbuf[PA_ENV_BUF_SIZE];
- DWORD hresult;
- double minLatencySeconds = 0;
- /* Let user determine minimal latency by setting environment variable. */
- hresult = GetEnvironmentVariableA( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
- if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
- {
- minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC;
- }
- else
- {
- minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds();
- #if PA_USE_HIGH_LATENCY
- PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND ));
- #endif
- }
- return minLatencySeconds;
- }
- /************************************************************************************
- ** Duplicate the input string using the allocations allocator.
- ** A NULL string is converted to a zero length string.
- ** If memory cannot be allocated, NULL is returned.
- **/
- static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
- {
- char *result = 0;
-
- if( src != NULL )
- {
- size_t len = strlen(src);
- result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
- if( result )
- memcpy( (void *) result, src, len+1 );
- }
- else
- {
- result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
- if( result )
- result[0] = '\0';
- }
- return result;
- }
- /************************************************************************************
- ** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
- ** information during device enumeration.
- */
- typedef struct DSDeviceNameAndGUID{
- char *name; // allocated from parent's allocations, never deleted by this structure
- GUID guid;
- LPGUID lpGUID;
- void *pnpInterface; // wchar_t* interface path, allocated using the DS host api's allocation group
- } DSDeviceNameAndGUID;
- typedef struct DSDeviceNameAndGUIDVector{
- PaUtilAllocationGroup *allocations;
- PaError enumerationError;
- int count;
- int free;
- DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
- } DSDeviceNameAndGUIDVector;
- typedef struct DSDeviceNamesAndGUIDs{
- PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
- DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
- } DSDeviceNamesAndGUIDs;
- static PaError InitializeDSDeviceNameAndGUIDVector(
- DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
- {
- PaError result = paNoError;
- guidVector->allocations = allocations;
- guidVector->enumerationError = paNoError;
- guidVector->count = 0;
- guidVector->free = 8;
- guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
- if( guidVector->items == NULL )
- result = paInsufficientMemory;
-
- return result;
- }
- static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
- {
- PaError result = paNoError;
- DSDeviceNameAndGUID *newItems;
- int i;
-
- /* double size of vector */
- int size = guidVector->count + guidVector->free;
- guidVector->free += size;
- newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
- if( newItems == NULL )
- {
- result = paInsufficientMemory;
- }
- else
- {
- for( i=0; i < guidVector->count; ++i )
- {
- newItems[i].name = guidVector->items[i].name;
- if( guidVector->items[i].lpGUID == NULL )
- {
- newItems[i].lpGUID = NULL;
- }
- else
- {
- newItems[i].lpGUID = &newItems[i].guid;
- memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );
- }
- newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
- }
- LocalFree( guidVector->items );
- guidVector->items = newItems;
- }
- return result;
- }
- /*
- it's safe to call DSDeviceNameAndGUIDVector multiple times
- */
- static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
- {
- PaError result = paNoError;
- if( guidVector->items != NULL )
- {
- if( LocalFree( guidVector->items ) != NULL )
- result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
- guidVector->items = NULL;
- }
- return result;
- }
- /************************************************************************************
- ** Collect preliminary device information during DirectSound enumeration
- */
- static BOOL CALLBACK CollectGUIDsProcA(LPGUID lpGUID,
- LPCSTR lpszDesc,
- LPCSTR lpszDrvName,
- LPVOID lpContext )
- {
- DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
- PaError error;
- (void) lpszDrvName; /* unused variable */
- if( namesAndGUIDs->free == 0 )
- {
- error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
- if( error != paNoError )
- {
- namesAndGUIDs->enumerationError = error;
- return FALSE;
- }
- }
-
- /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
- if( lpGUID == NULL )
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
- }
- else
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
- &namesAndGUIDs->items[namesAndGUIDs->count].guid;
-
- memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
- }
- namesAndGUIDs->items[namesAndGUIDs->count].name =
- DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
- if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
- {
- namesAndGUIDs->enumerationError = paInsufficientMemory;
- return FALSE;
- }
- namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
- ++namesAndGUIDs->count;
- --namesAndGUIDs->free;
-
- return TRUE;
- }
- #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
- {
- size_t len;
- wchar_t *result;
- len = wcslen( source );
- result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
- wcscpy( result, source );
- return result;
- }
- static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
- {
- int i;
- DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
- /*
- Apparently data->Interface can be NULL in some cases.
- Possibly virtual devices without hardware.
- So we check for NULLs now. See mailing list message November 10, 2012:
- "[Portaudio] portaudio initialization crash in KsPropertySetEnumerateCallback(pa_win_ds.c)"
- */
- if( data->Interface )
- {
- if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
- {
- for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
- {
- if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
- && memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
- {
- deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
- (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
- break;
- }
- }
- }
- else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
- {
- for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
- {
- if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
- && memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
- {
- deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
- (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
- break;
- }
- }
- }
- }
- return TRUE;
- }
- static GUID pawin_CLSID_DirectSoundPrivate =
- { 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
- static GUID pawin_DSPROPSETID_DirectSoundDevice =
- { 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
- static GUID pawin_IID_IKsPropertySet =
- { 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
- /*
- FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
- with UNICODE file paths to the devices. The DS documentation mentions
- at least two techniques by which these Interface paths can be found using IKsPropertySet on
- the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
- property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
- I tried both methods and only the second worked. I found two postings on the
- net from people who had the same problem with the first method, so I think the method used here is
- more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
- but the fields of the device description are not filled in.
- The mechanism we use works by registering an enumeration callback which is called for
- every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
- with the matching GUID and copies the pointer to the Interface path.
- Note that we could have used this enumeration callback to perform the original
- device enumeration, however we choose not to so we can disable this step easily.
- Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
- http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
- -- rossb
- */
- static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
- {
- IClassFactory *pClassFactory;
-
- if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
- IKsPropertySet *pPropertySet;
- if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
-
- DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
- ULONG bytesReturned;
- data.Callback = KsPropertySetEnumerateCallback;
- data.Context = deviceNamesAndGUIDs;
- IKsPropertySet_Get( pPropertySet,
- &pawin_DSPROPSETID_DirectSoundDevice,
- DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
- NULL,
- 0,
- &data,
- sizeof(data),
- &bytesReturned
- );
-
- IKsPropertySet_Release( pPropertySet );
- }
- pClassFactory->lpVtbl->Release( pClassFactory );
- }
- /*
- The following code fragment, which I chose not to use, queries for the
- device interface for a device with a specific GUID:
- ULONG BytesReturned;
- DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
- memset (&Property, 0, sizeof(Property));
- Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
- Property.DeviceId = *lpGUID;
- hr = IKsPropertySet_Get( pPropertySet,
- &pawin_DSPROPSETID_DirectSoundDevice,
- DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
- NULL,
- 0,
- &Property,
- sizeof(Property),
- &BytesReturned
- );
- if( hr == S_OK )
- {
- //pnpInterface = Property.Interface;
- }
- */
- }
- #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
- /*
- GUIDs for emulated devices which we blacklist below.
- are there more than two of them??
- */
- GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
- GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
- #define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
- static double 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 };
- /************************************************************************************
- ** Extract capabilities from an output device, and add it to the device info list
- ** if successful. This function assumes that there is enough room in the
- ** device info list to accomodate all entries.
- **
- ** The device will not be added to the device list if any errors are encountered.
- */
- static PaError AddOutputDeviceInfoFromDirectSound(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
- {
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
- PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
- HRESULT hr;
- LPDIRECTSOUND lpDirectSound;
- DSCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
- int i;
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- }
-
- if( lpGUID )
- {
- if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
- IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
- {
- PA_DEBUG(("BLACKLISTED: %s \n",name));
- return paNoError;
- }
- }
- /* Create a DirectSound object for the specified GUID
- Note that using CoCreateInstance doesn't work on windows CE.
- */
- hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSound, (void**)&lpDirectSound );
- if( hr == S_OK )
- {
- hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
- }
- */
-
- if( hr != DS_OK )
- {
- if (hr == DSERR_ALLOCATED)
- PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
- DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
- if (lpGUID)
- DBUG(("%s's GUID: {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, 0x%x} \n",
- name,
- lpGUID->Data1,
- lpGUID->Data2,
- lpGUID->Data3,
- lpGUID->Data4[0],
- lpGUID->Data4[1],
- lpGUID->Data4[2],
- lpGUID->Data4[3],
- lpGUID->Data4[4],
- lpGUID->Data4[5],
- lpGUID->Data4[6],
- lpGUID->Data4[7]));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSound_GetCaps( lpDirectSound, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- #if PA_USE_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
- #endif
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = 0;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
- /* DS output capabilities only indicate supported number of channels
- using two flags which indicate mono and/or stereo.
- We assume that stereo devices may support more than 2 channels
- (as is the case with 5.1 devices for example) and so
- set deviceOutputChannelCountIsKnown to 0 (unknown).
- In this case OpenStream will try to open the device
- when the user requests more than 2 channels, rather than
- returning an error.
- */
- if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
- {
- deviceInfo->maxOutputChannels = 2;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
- }
- else
- {
- deviceInfo->maxOutputChannels = 1;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
- /* Guess channels count from speaker configuration. We do it only when
- pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
- */
- #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( !pnpInterface )
- #endif
- {
- DWORD spkrcfg;
- if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
- {
- int count = 0;
- switch (DSSPEAKER_CONFIG(spkrcfg))
- {
- case DSSPEAKER_HEADPHONE: count = 2; break;
- case DSSPEAKER_MONO: count = 1; break;
- case DSSPEAKER_QUAD: count = 4; break;
- case DSSPEAKER_STEREO: count = 2; break;
- case DSSPEAKER_SURROUND: count = 4; break;
- case DSSPEAKER_5POINT1: count = 6; break;
- case DSSPEAKER_7POINT1: count = 8; break;
- #ifndef DSSPEAKER_7POINT1_SURROUND
- #define DSSPEAKER_7POINT1_SURROUND 0x00000008
- #endif
- case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
- #ifndef DSSPEAKER_5POINT1_SURROUND
- #define DSSPEAKER_5POINT1_SURROUND 0x00000009
- #endif
- case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
- }
- if( count )
- {
- deviceInfo->maxOutputChannels = count;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
- }
- }
- #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( pnpInterface )
- {
- int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
- if( count > 0 )
- {
- deviceInfo->maxOutputChannels = count;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- }
- }
- #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
- /* initialize defaultSampleRate */
-
- if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
- {
- /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
- {
- if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
- && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
- {
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
- break;
- }
- }
- }
- else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
- {
- if( caps.dwMinSecondarySampleRate == 0 )
- {
- /*
- ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
- ** But it supports continuous sampling.
- ** So fake range of rates, and hope it really supports it.
- */
- deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
- DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
- }
- else
- {
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- }
- }
- else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
- {
- /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
- ** But we know that they really support a range of rates!
- ** So when we see a ridiculous set of rates, assume it is a range.
- */
- deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
- DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
- }
- else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
- // dwFlags | DSCAPS_CONTINUOUSRATE
- deviceInfo->defaultLowInputLatency = 0.;
- deviceInfo->defaultHighInputLatency = 0.;
- deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
- }
- }
- IDirectSound_Release( lpDirectSound );
- }
- if( deviceOK )
- {
- deviceInfo->name = name;
- if( lpGUID == NULL )
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
- return result;
- }
- /************************************************************************************
- ** Extract capabilities from an input device, and add it to the device info list
- ** if successful. This function assumes that there is enough room in the
- ** device info list to accomodate all entries.
- **
- ** The device will not be added to the device list if any errors are encountered.
- */
- static PaError AddInputDeviceInfoFromDirectSoundCapture(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
- {
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
- PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
- HRESULT hr;
- LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
- DSCCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- }
- hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
- */
- if( hr != DS_OK )
- {
- DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- #if PA_USE_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
- #endif
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = caps.dwChannels;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
- deviceInfo->maxOutputChannels = 0;
- winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
- #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( pnpInterface )
- {
- int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
- if( count > 0 )
- {
- deviceInfo->maxInputChannels = count;
- winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
- }
- }
- #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
- /* constants from a WINE patch by Francois Gouget, see:
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
- ---
- Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
- From: Francois Gouget <fgouget@ ... .fr>
- To: Ross Bencina <rbencina@ ... .au>
- Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
- [snip]
- I give you permission to use the patch below under the BSD license.
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
- [snip]
- */
- #ifndef WAVE_FORMAT_48M08
- #define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
- #define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
- #define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
- #define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
- #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
- #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
- #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
- #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
- #endif
- /* defaultSampleRate */
- if( caps.dwChannels == 2 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4S16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48S16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2S16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1S16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96S16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- }
- else if( caps.dwChannels == 1 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4M16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48M16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2M16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1M16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96M16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- }
- else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
- deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
- deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
-
- deviceInfo->defaultLowOutputLatency = 0.;
- deviceInfo->defaultHighOutputLatency = 0.;
- }
- }
-
- IDirectSoundCapture_Release( lpDirectSoundCapture );
- }
- if( deviceOK )
- {
- deviceInfo->name = name;
- if( lpGUID == NULL )
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
- hostApi->info.deviceCount++;
- }
- return result;
- }
- /***********************************************************************************/
- PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
- {
- PaError result = paNoError;
- int i, deviceCount;
- PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
- PaWinDsDeviceInfo *deviceInfoArray;
- PaWinDs_InitializeDSoundEntryPoints();
- /* initialise guid vectors so they can be safely deleted on error */
- deviceNamesAndGUIDs.winDsHostApi = NULL;
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
- winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
- if( !winDsHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
- memset( winDsHostApi, 0, sizeof(PaWinDsHostApiRepresentation) ); /* ensure all fields are zeroed. especially winDsHostApi->allocations */
- result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
- if( result != paNoError )
- {
- goto error;
- }
- winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !winDsHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
- *hostApi = &winDsHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paDirectSound;
- (*hostApi)->info.name = "Windows DirectSound";
-
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
-
- /* DSound - enumerate devices to count them and to gather their GUIDs */
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
- result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
- paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACKA)CollectGUIDsProcA, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
- paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACKA)CollectGUIDsProcA, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
- if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
- goto error;
- }
- if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
- goto error;
- }
- deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
- #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
- if( deviceCount > 0 )
- {
- deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
- FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
- }
- #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
- if( deviceCount > 0 )
- {
- /* allocate array for pointers to PaDeviceInfo structs */
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
- /* allocate all PaDeviceInfo structs in a contiguous block */
- deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
- for( i=0; i < deviceCount; ++i )
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0;
- (*hostApi)->deviceInfos[i] = deviceInfo;
- }
- for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
- {
- result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
- deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
- if( result != paNoError )
- goto error;
- }
- for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
- {
- result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
- deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
- if( result != paNoError )
- goto error;
- }
- }
- result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
- result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
- PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
- PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
- return result;
- error:
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
- TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
- Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
- return result;
- }
- /***********************************************************************************/
- static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
- {
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
- if( winDsHostApi ){
- if( winDsHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winDsHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
- }
- PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
- PaUtil_FreeMemory( winDsHostApi );
- }
- PaWinDs_TerminateDSoundEntryPoints();
- }
- static PaError ValidateWinDirectSoundSpecificStreamInfo(
- const PaStreamParameters *streamParameters,
- const PaWinDirectSoundStreamInfo *streamInfo )
- {
- if( streamInfo )
- {
- if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
- || streamInfo->version != 2 )
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
- {
- if( streamInfo->framesPerBuffer <= 0 )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- }
- return paNoError;
- }
- /***********************************************************************************/
- static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
- {
- PaError result;
- PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
- PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
- if( inputParameters )
- {
- inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
- inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- /* check that input device can support inputChannelCount */
- if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
- && inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
- /* validate inputStreamInfo */
- inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
- if( result != paNoError ) return result;
- }
- else
- {
- inputChannelCount = 0;
- }
- if( outputParameters )
- {
- outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
- outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- /* check that output device can support inputChannelCount */
- if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
- && outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
- /* validate outputStreamInfo */
- outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
- if( result != paNoError ) return result;
- }
- else
- {
- outputChannelCount = 0;
- }
-
- /*
- IMPLEMENT ME:
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported if necessary
- - check that the device supports sampleRate
- Because the buffer adapter handles conversion between all standard
- sample formats, the following checks are only required if paCustomFormat
- is implemented, or under some other unusual conditions.
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- */
- return paFormatIsSupported;
- }
- #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
- PaWinDsDeviceInfo *inputDevice,
- PaSampleFormat hostInputSampleFormat,
- WORD inputChannelCount,
- int bytesPerInputBuffer,
- PaWinWaveFormatChannelMask inputChannelMask,
- PaWinDsDeviceInfo *outputDevice,
- PaSampleFormat hostOutputSampleFormat,
- WORD outputChannelCount,
- int bytesPerOutputBuffer,
- PaWinWaveFormatChannelMask outputChannelMask,
- unsigned long nFrameRate
- )
- {
- HRESULT hr;
- DSCBUFFERDESC captureDesc;
- PaWinWaveFormat captureWaveFormat;
- DSBUFFERDESC secondaryRenderDesc;
- PaWinWaveFormat renderWaveFormat;
- LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
- LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
- // capture buffer description
- // only try wave format extensible. assume it's available on all ds 8 systems
- PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
- hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
- nFrameRate, inputChannelMask );
- ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
- captureDesc.dwSize = sizeof(DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = bytesPerInputBuffer;
- captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
- // render buffer description
- PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
- hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
- nFrameRate, outputChannelMask );
- ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
- secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
- secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
- secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
- /* note that we don't create a primary buffer here at all */
- hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
- inputDevice->lpGUID, outputDevice->lpGUID,
- &captureDesc, &secondaryRenderDesc,
- GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
- DSSCL_EXCLUSIVE,
- &stream->pDirectSoundFullDuplex8,
- &pCaptureBuffer8,
- &pRenderBuffer8,
- NULL /* pUnkOuter must be NULL */
- );
- if( hr == DS_OK )
- {
- PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
- /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
- hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
-
- if( hr == DS_OK )
- hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
- /* release the ds 8 interfaces, we don't need them */
- IUnknown_Release( pCaptureBuffer8 );
- IUnknown_Release( pRenderBuffer8 );
- if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
- /* couldn't get pre ds 8 interfaces for some reason. clean up. */
- if( stream->pDirectSoundInputBuffer )
- {
- IUnknown_Release( stream->pDirectSoundInputBuffer );
- stream->pDirectSoundInputBuffer = NULL;
- }
- if( stream->pDirectSoundOutputBuffer )
- {
- IUnknown_Release( stream->pDirectSoundOutputBuffer );
- stream->pDirectSoundOutputBuffer = NULL;
- }
-
- IUnknown_Release( stream->pDirectSoundFullDuplex8 );
- stream->pDirectSoundFullDuplex8 = NULL;
- }
- }
- else
- {
- PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
- }
- return hr;
- }
- #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
- static HRESULT InitInputBuffer( PaWinDsStream *stream,
- PaWinDsDeviceInfo *device,
- PaSampleFormat sampleFormat,
- unsigned long nFrameRate,
- WORD nChannels,
- int bytesPerBuffer,
- PaWinWaveFormatChannelMask channelMask )
- {
- DSCBUFFERDESC captureDesc;
- PaWinWaveFormat waveFormat;
- HRESULT result;
-
- if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
- device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
- ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
- return result;
- }
- // Setup the secondary buffer description
- ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
- captureDesc.dwSize = sizeof(DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = bytesPerBuffer;
- captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
-
- // Create the capture buffer
- // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
- PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
- sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
- nFrameRate, channelMask );
- if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
- &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
- {
- PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
- PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
- if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
- &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
- }
- stream->readOffset = 0; // reset last read position to start of buffer
- return DS_OK;
- }
- static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
- PaSampleFormat sampleFormat, unsigned long nFrameRate,
- WORD nChannels, int bytesPerBuffer,
- PaWinWaveFormatChannelMask channelMask )
- {
- HRESULT result;
- HWND hWnd;
- HRESULT hr;
- PaWinWaveFormat waveFormat;
- DSBUFFERDESC primaryDesc;
- DSBUFFERDESC secondaryDesc;
-
- if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
- device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
- ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
- return hr;
- }
- // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
- // applications's window. Also if that window is closed before the Buffer is closed
- // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
- // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
- // hWnd = GetForegroundWindow();
- //
- // FIXME: The example code I have on the net creates a hidden window that
- // is managed by our code - I think we should do that - one hidden
- // window for the whole of Pa_DS
- //
- hWnd = GetDesktopWindow();
- // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
- // exclusive also prevents unexpected sounds from other apps during a performance.
- if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
- hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
- {
- return hr;
- }
- // -----------------------------------------------------------------------
- // Create primary buffer and set format just so we can specify our custom format.
- // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
- // Setup the primary buffer description
- ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
- primaryDesc.dwSize = sizeof(DSBUFFERDESC);
- primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
- primaryDesc.dwBufferBytes = 0;
- primaryDesc.lpwfxFormat = NULL;
- // Create the buffer
- if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
- &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
- goto error;
- // Set the primary buffer's format
- // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
- PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
- sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
- nFrameRate, channelMask );
- if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
- {
- PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
- PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
- if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
- goto error;
- }
- // ----------------------------------------------------------------------
- // Setup the secondary buffer description
- ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
- secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
- secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- secondaryDesc.dwBufferBytes = bytesPerBuffer;
- secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
- // Create the secondary buffer
- if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
- &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
- goto error;
-
- return DS_OK;
- error:
- if( stream->pDirectSoundPrimaryBuffer )
- {
- IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
- stream->pDirectSoundPrimaryBuffer = NULL;
- }
- return result;
- }
- static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
- unsigned long *pollingPeriodFrames,
- int isFullDuplex,
- unsigned long suggestedInputLatencyFrames,
- unsigned long suggestedOutputLatencyFrames,
- double sampleRate, unsigned long userFramesPerBuffer )
- {
- unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
- unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
- unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
-
- if( userFramesPerBuffer == paFramesPerBufferUnspecified )
- {
- unsigned long targetBufferingLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
- *pollingPeriodFrames = targetBufferingLatencyFrames / 4;
- if( *pollingPeriodFrames < minimumPollingPeriodFrames )
- {
- *pollingPeriodFrames = minimumPollingPeriodFrames;
- }
- else if( *pollingPeriodFrames > maximumPollingPeriodFrames )
- {
- *pollingPeriodFrames = maximumPollingPeriodFrames;
- }
- *hostBufferSizeFrames = *pollingPeriodFrames
- + max( *pollingPeriodFrames + pollingJitterFrames, targetBufferingLatencyFrames);
- }
- else
- {
- unsigned long targetBufferingLatencyFrames = suggestedInputLatencyFrames;
- if( isFullDuplex )
- {
- /* In full duplex streams we know that the buffer adapter adds userFramesPerBuffer
- extra fixed latency. so we subtract it here as a fixed latency before computing
- the buffer size. being careful not to produce an unrepresentable negative result.
-
- Note: this only works as expected if output latency is greater than input latency.
- Otherwise we use input latency anyway since we do max(in,out).
- */
- if( userFramesPerBuffer < suggestedOutputLatencyFrames )
- {
- unsigned long adjustedSuggestedOutputLatencyFrames =
- suggestedOutputLatencyFrames - userFramesPerBuffer;
- /* maximum of input and adjusted output suggested latency */
- if( adjustedSuggestedOutputLatencyFrames > targetBufferingLatencyFrames )
- targetBufferingLatencyFrames = adjustedSuggestedOutputLatencyFrames;
- }
- }
- else
- {
- /* maximum of input and output suggested latency */
- if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
- targetBufferingLatencyFrames = suggestedOutputLatencyFrames;
- }
- *hostBufferSizeFrames = userFramesPerBuffer
- + max( userFramesPerBuffer + pollingJitterFrames, targetBufferingLatencyFrames);
- *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), targetBufferingLatencyFrames / 16 );
- if( *pollingPeriodFrames > maximumPollingPeriodFrames )
- {
- *pollingPeriodFrames = maximumPollingPeriodFrames;
- }
- }
- }
- static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
- unsigned long *pollingPeriodFrames,
- double sampleRate, unsigned long userFramesPerBuffer )
- {
- unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
- unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
- unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
- *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 );
- if( *pollingPeriodFrames > maximumPollingPeriodFrames )
- {
- *pollingPeriodFrames = maximumPollingPeriodFrames;
- }
- }
- static void SetStreamInfoLatencies( PaWinDsStream *stream,
- unsigned long userFramesPerBuffer,
- unsigned long pollingPeriodFrames,
- double sampleRate )
- {
- /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames,
- and the configuration of the buffer processor */
- unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
- ? pollingPeriodFrames
- : userFramesPerBuffer;
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- /* stream info input latency is the minimum buffering latency
- (unlike suggested and default which are *maximums*) */
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
- + effectiveFramesPerBuffer) / sampleRate;
- }
- else
- {
- stream->streamRepresentation.streamInfo.inputLatency = 0;
- }
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
- + (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate;
- }
- else
- {
- stream->streamRepresentation.streamInfo.outputLatency = 0;
- }
- }
- /***********************************************************************************/
- /* 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;
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
- PaWinDsStream *stream = 0;
- int bufferProcessorIsInitialized = 0;
- int streamRepresentationIsInitialized = 0;
- PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
- PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
- int userRequestedHostInputBufferSizeFrames = 0;
- int userRequestedHostOutputBufferSizeFrames = 0;
- unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
- PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
- PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
- unsigned long pollingPeriodFrames = 0;
- if( inputParameters )
- {
- inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
- inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
- /* IDEA: the following 3 checks could be performed by default by pa_front
- unless some flag indicated otherwise */
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- /* check that input device can support inputChannelCount */
- if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
- && inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
- if( result != paNoError ) return result;
- if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
- userRequestedHostInputBufferSizeFrames = inputStreamInfo->framesPerBuffer;
- if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
- inputChannelMask = inputStreamInfo->channelMask;
- else
- inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = 0;
- suggestedInputLatencyFrames = 0;
- }
- if( outputParameters )
- {
- outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
- outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
- /* check that output device can support outputChannelCount */
- if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
- && outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
- /* validate hostApiSpecificStreamInfo */
- outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
- if( result != paNoError ) return result;
- if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
- userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;
- if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
- outputChannelMask = outputStreamInfo->channelMask;
- else
- outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = 0;
- suggestedOutputLatencyFrames = 0;
- }
- /*
- If low level host buffer size is specified for both input and output
- the current code requires the sizes to match.
- */
- if( (userRequestedHostInputBufferSizeFrames > 0 && userRequestedHostOutputBufferSizeFrames > 0)
- && userRequestedHostInputBufferSizeFrames != userRequestedHostOutputBufferSizeFrames )
- return paIncompatibleHostApiSpecificStreamInfo;
- /*
- IMPLEMENT ME:
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
- - check that the device supports sampleRate
- - alter sampleRate to a close allowable rate if possible / necessary
- - validate suggestedInputLatency and suggestedOutputLatency parameters,
- use default values where necessary
- */
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
- stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
- memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
- if( streamCallback )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->callbackStreamInterface, streamCallback, userData );
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->blockingStreamInterface, streamCallback, userData );
- }
-
- streamRepresentationIsInitialized = 1;
- stream->streamFlags = streamFlags;
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
- if( inputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- PaSampleFormat nativeInputFormats = paInt16;
- /* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
- }
- else
- {
- hostInputSampleFormat = 0;
- }
- if( outputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- PaSampleFormat nativeOutputFormats = paInt16;
- /* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
- }
- else
- {
- hostOutputSampleFormat = 0;
- }
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- 0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
- /* This next mode is required because DS can split the host buffer when it wraps around. */
- paUtilVariableHostBufferSizePartialUsageAllowed,
- streamCallback, userData );
- if( result != paNoError )
- goto error;
- bufferProcessorIsInitialized = 1;
-
- /* DirectSound specific initialization */
- {
- HRESULT hr;
- unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
-
- stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
- if( stream->processingCompleted == NULL )
- {
- result = paInsufficientMemory;
- goto error;
- }
- #ifdef PA_WIN_DS_USE_WMME_TIMER
- stream->timerID = 0;
- #endif
- #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL );
- if( stream->waitableTimer == NULL )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
- #endif
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- stream->processingThreadCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
- if( stream->processingThreadCompleted == NULL )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
- #endif
- /* set up i/o parameters */
- if( userRequestedHostInputBufferSizeFrames > 0 || userRequestedHostOutputBufferSizeFrames > 0 )
- {
- /* use low level parameters */
- /* since we use the same host buffer size for input and output
- we choose the highest user specified value.
- */
- stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );
- CalculatePollingPeriodFrames(
- stream->hostBufferSizeFrames, &pollingPeriodFrames,
- sampleRate, framesPerBuffer );
- }
- else
- {
- CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames,
- /* isFullDuplex = */ (inputParameters && outputParameters),
- suggestedInputLatencyFrames,
- suggestedOutputLatencyFrames,
- sampleRate, framesPerBuffer );
- }
- stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
- DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
- stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));
- /* ------------------ OUTPUT */
- if( outputParameters )
- {
- LARGE_INTEGER counterFrequency;
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
- */
-
- int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
- stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
- stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes;
- if( stream->outputBufferSizeBytes < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( stream->outputBufferSizeBytes > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
- /* Calculate value used in latency calculation to avoid real-time divides. */
- stream->secondsPerHostByte = 1.0 /
- (stream->bufferProcessor.bytesPerHostOutputSample *
- outputChannelCount * sampleRate);
- stream->outputIsRunning = FALSE;
- stream->outputUnderflowCount = 0;
-
- /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
- if( QueryPerformanceFrequency( &counterFrequency ) )
- {
- stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate;
- }
- else
- {
- stream->perfCounterTicksPerBuffer.QuadPart = 0;
- }
- }
- /* ------------------ INPUT */
- if( inputParameters )
- {
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
- */
-
- int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
- stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
- stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes;
- if( stream->inputBufferSizeBytes < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( stream->inputBufferSizeBytes > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
- }
- /* open/create the DirectSound buffers */
- /* interface ptrs should be zeroed when stream is zeroed. */
- assert( stream->pDirectSoundCapture == NULL );
- assert( stream->pDirectSoundInputBuffer == NULL );
- assert( stream->pDirectSound == NULL );
- assert( stream->pDirectSoundPrimaryBuffer == NULL );
- assert( stream->pDirectSoundOutputBuffer == NULL );
-
- if( inputParameters && outputParameters )
- {
- #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- /* try to use the full-duplex DX8 API to create the buffers.
- if that fails we fall back to the half-duplex API below */
- hr = InitFullDuplexInputOutputBuffers( stream,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
- hostInputSampleFormat,
- (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
- inputChannelMask,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
- hostOutputSampleFormat,
- (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
- outputChannelMask,
- integerSampleRate
- );
- DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
- /* ignore any error returned by InitFullDuplexInputOutputBuffers.
- we retry opening the buffers below */
- #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
- }
- /* create half duplex buffers. also used for full-duplex streams which didn't
- succeed when using the full duplex API. that could happen because
- DX8 or greater isnt installed, the i/o devices aren't the same
- physical device. etc.
- */
- if( outputParameters && !stream->pDirectSoundOutputBuffer )
- {
- hr = InitOutputBuffer( stream,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
- hostOutputSampleFormat,
- integerSampleRate,
- (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
- outputChannelMask );
- DBUG(("InitOutputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
- if( inputParameters && !stream->pDirectSoundInputBuffer )
- {
- hr = InitInputBuffer( stream,
- (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
- hostInputSampleFormat,
- integerSampleRate,
- (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
- inputChannelMask );
- DBUG(("InitInputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
- }
- SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate );
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
- *s = (PaStream*)stream;
- return result;
- error:
- if( stream )
- {
- if( stream->processingCompleted != NULL )
- CloseHandle( stream->processingCompleted );
- #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- if( stream->waitableTimer != NULL )
- CloseHandle( stream->waitableTimer );
- #endif
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- if( stream->processingThreadCompleted != NULL )
- CloseHandle( stream->processingThreadCompleted );
- #endif
- if( stream->pDirectSoundOutputBuffer )
- {
- IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
- stream->pDirectSoundOutputBuffer = NULL;
- }
- if( stream->pDirectSoundPrimaryBuffer )
- {
- IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
- stream->pDirectSoundPrimaryBuffer = NULL;
- }
- if( stream->pDirectSoundInputBuffer )
- {
- IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
- IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
- stream->pDirectSoundInputBuffer = NULL;
- }
- if( stream->pDirectSoundCapture )
- {
- IDirectSoundCapture_Release( stream->pDirectSoundCapture );
- stream->pDirectSoundCapture = NULL;
- }
- if( stream->pDirectSound )
- {
- IDirectSound_Release( stream->pDirectSound );
- stream->pDirectSound = NULL;
- }
- #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- if( stream->pDirectSoundFullDuplex8 )
- {
- IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
- stream->pDirectSoundFullDuplex8 = NULL;
- }
- #endif
- if( bufferProcessorIsInitialized )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- if( streamRepresentationIsInitialized )
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
- }
- return result;
- }
- /************************************************************************************
- * Determine how much space can be safely written to in DS buffer.
- * Detect underflows and overflows.
- * Does not allow writing into safety gap maintained by DirectSound.
- */
- static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
- {
- HRESULT hr;
- DWORD playCursor;
- DWORD writeCursor;
- long numBytesEmpty;
- long playWriteGap;
- // Query to see how much room is in buffer.
- hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
- &playCursor, &writeCursor );
- if( hr != DS_OK )
- {
- return hr;
- }
- // Determine size of gap between playIndex and WriteIndex that we cannot write into.
- playWriteGap = writeCursor - playCursor;
- if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
- /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
- /* Attempt to detect playCursor wrap-around and correct it. */
- if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
- {
- /* How much time has elapsed since last check. */
- LARGE_INTEGER currentTime;
- LARGE_INTEGER elapsedTime;
- long bytesPlayed;
- long bytesExpected;
- long buffersWrapped;
- QueryPerformanceCounter( ¤tTime );
- elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
- stream->previousPlayTime = currentTime;
- /* How many bytes does DirectSound say have been played. */
- bytesPlayed = playCursor - stream->previousPlayCursor;
- if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
- stream->previousPlayCursor = playCursor;
- /* Calculate how many bytes we would have expected to been played by now. */
- bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
- buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
- if( buffersWrapped > 0 )
- {
- playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
- bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
- }
- }
- numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
- if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
- /* Have we underflowed? */
- if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
- {
- if( stream->outputIsRunning )
- {
- stream->outputUnderflowCount += 1;
- }
- /*
- From MSDN:
- The write cursor indicates the position at which it is safe
- to write new data to the buffer. The write cursor always leads the
- play cursor, typically by about 15 milliseconds' worth of audio
- data.
- It is always safe to change data that is behind the position
- indicated by the lpdwCurrentPlayCursor parameter.
- */
- stream->outputBufferWriteOffsetBytes = writeCursor;
- numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
- }
- *bytesEmpty = numBytesEmpty;
- return hr;
- }
- /***********************************************************************************/
- static int TimeSlice( PaWinDsStream *stream )
- {
- long numFrames = 0;
- long bytesEmpty = 0;
- long bytesFilled = 0;
- long bytesToXfer = 0;
- long framesToXfer = 0; /* the number of frames we'll process this tick */
- long numInFramesReady = 0;
- long numOutFramesReady = 0;
- long bytesProcessed;
- HRESULT hresult;
- double outputLatency = 0;
- double inputLatency = 0;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0};
-
- /* Input */
- LPBYTE lpInBuf1 = NULL;
- LPBYTE lpInBuf2 = NULL;
- DWORD dwInSize1 = 0;
- DWORD dwInSize2 = 0;
- /* Output */
- LPBYTE lpOutBuf1 = NULL;
- LPBYTE lpOutBuf2 = NULL;
- DWORD dwOutSize1 = 0;
- DWORD dwOutSize2 = 0;
- /* How much input data is available? */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- HRESULT hr;
- DWORD capturePos;
- DWORD readPos;
- long filled = 0;
- // Query to see how much data is in buffer.
- // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
- // so let's pass a pointer just to be safe.
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
- if( hr == DS_OK )
- {
- filled = readPos - stream->readOffset;
- if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset
- bytesFilled = filled;
- inputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
- }
- // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
- framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
- /** @todo Check for overflow */
- }
- /* How much output room is available? */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- UINT previousUnderflowCount = stream->outputUnderflowCount;
- QueryOutputSpace( stream, &bytesEmpty );
- framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
- /* Check for underflow */
- /* FIXME QueryOutputSpace should not adjust underflow count as a side effect.
- A query function should be a const operator on the stream and return a flag on underflow. */
- if( stream->outputUnderflowCount != previousUnderflowCount )
- stream->callbackFlags |= paOutputUnderflow;
- /* We are about to compute audio into the first byte of empty space in the output buffer.
- This audio will reach the DAC after all of the current (non-empty) audio
- in the buffer has played. Therefore the output time is the current time
- plus the time it takes to play the non-empty bytes in the buffer,
- computed here:
- */
- outputLatency = ((double)(stream->outputBufferSizeBytes - bytesEmpty)) * stream->secondsPerHostByte;
- }
- /* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */
- if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 )
- {
- framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
- }
- if( framesToXfer > 0 )
- {
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
- /* The outputBufferDacTime parameter should indicates the time at which
- the first sample of the output buffer is heard at the DACs. */
- timeInfo.currentTime = PaUtil_GetTime();
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
- stream->callbackFlags = 0;
-
- /* Input */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency;
- bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
- hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
- stream->readOffset, bytesToXfer,
- (void **) &lpInBuf1, &dwInSize1,
- (void **) &lpInBuf2, &dwInSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
- /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
- stream->callbackResult = paComplete;
- goto error2;
- }
- numFrames = dwInSize1 / stream->inputFrameSizeBytes;
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
- /* Is input split into two regions. */
- if( dwInSize2 > 0 )
- {
- numFrames = dwInSize2 / stream->inputFrameSizeBytes;
- PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
- }
- }
- /* Output */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /*
- We don't currently add outputLatency here because it appears to produce worse
- results than not adding it. Need to do more testing to verify this.
- */
- /* timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; */
- timeInfo.outputBufferDacTime = timeInfo.currentTime;
- bytesToXfer = framesToXfer * stream->outputFrameSizeBytes;
- hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
- stream->outputBufferWriteOffsetBytes, bytesToXfer,
- (void **) &lpOutBuf1, &dwOutSize1,
- (void **) &lpOutBuf2, &dwOutSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
- /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
- stream->callbackResult = paComplete;
- goto error1;
- }
- numFrames = dwOutSize1 / stream->outputFrameSizeBytes;
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
- /* Is output split into two regions. */
- if( dwOutSize2 > 0 )
- {
- numFrames = dwOutSize2 / stream->outputFrameSizeBytes;
- PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
- }
- }
- numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
- stream->framesWritten += numFrames;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /* FIXME: an underflow could happen here */
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * stream->outputFrameSizeBytes;
- stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
- IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
- }
- error1:
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- /* FIXME: an overflow could happen here */
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * stream->inputFrameSizeBytes;
- stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes;
- IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
- }
- error2:
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
- }
- if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
- {
- /* don't return completed until the buffer processor has been drained */
- return paContinue;
- }
- else
- {
- return stream->callbackResult;
- }
- }
- /*******************************************************************/
- static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
- {
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- long bytesEmpty;
- hr = QueryOutputSpace( stream, &bytesEmpty );
- if (hr != DS_OK) return hr;
- if( bytesEmpty == 0 ) return DS_OK;
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
- bytesEmpty, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy the buffer into the DS
- ZeroMemory(lpbuf1, dwsize1);
- if(lpbuf2 != NULL)
- {
- ZeroMemory(lpbuf2, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
- IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- stream->finalZeroBytesWritten += dwsize1 + dwsize2;
- }
- return hr;
- }
- static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
- {
- PaWinDsStream *stream;
- int isFinished = 0;
- /* suppress unused variable warnings */
- (void) uID;
- (void) uMsg;
- (void) dw1;
- (void) dw2;
-
- stream = (PaWinDsStream *) dwUser;
- if( stream == NULL ) return;
- if( stream->isActive )
- {
- if( stream->abortProcessing )
- {
- isFinished = 1;
- }
- else if( stream->stopProcessing )
- {
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- ZeroAvailableOutputSpace( stream );
- if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
- {
- /* once we've flushed the whole output buffer with zeros we know all data has been played */
- isFinished = 1;
- }
- }
- else
- {
- isFinished = 1;
- }
- }
- else
- {
- int callbackResult = TimeSlice( stream );
- if( callbackResult != paContinue )
- {
- /* FIXME implement handling of paComplete and paAbort if possible
- At the moment this should behave as if paComplete was called and
- flush the buffer.
- */
- stream->stopProcessing = 1;
- }
- }
- if( isFinished )
- {
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- stream->isActive = 0; /* don't set this until the stream really is inactive */
- SetEvent( stream->processingCompleted );
- }
- }
- }
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- static void CALLBACK WaitableTimerAPCProc(
- LPVOID lpArg, // Data value
- DWORD dwTimerLowValue, // Timer low value
- DWORD dwTimerHighValue ) // Timer high value
- {
- (void)dwTimerLowValue;
- (void)dwTimerHighValue;
- TimerCallback( 0, 0, (DWORD_PTR)lpArg, 0, 0 );
- }
- #endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
- PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
- {
- PaWinDsStream *stream = (PaWinDsStream *)pArg;
- LARGE_INTEGER dueTime;
- int timerPeriodMs;
- timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
- if( timerPeriodMs < 1 )
- timerPeriodMs = 1;
- #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- assert( stream->waitableTimer != NULL );
- /* invoke first timeout immediately */
- dueTime.LowPart = timerPeriodMs * 1000 * 10;
- dueTime.HighPart = 0;
- /* tick using waitable timer */
- if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
- {
- DWORD wfsoResult = 0;
- do
- {
- /* wait for processingCompleted to be signaled or our timer APC to be called */
- wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE );
- }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
- }
- CancelWaitableTimer( stream->waitableTimer );
- #else
- /* tick using WaitForSingleObject timout */
- while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT )
- {
- TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 );
- }
- #endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
- SetEvent( stream->processingThreadCompleted );
- return 0;
- }
- #endif /* !PA_WIN_DS_USE_WMME_TIMER */
- /***********************************************************************************
- 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;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- CloseHandle( stream->processingCompleted );
- #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
- if( stream->waitableTimer != NULL )
- CloseHandle( stream->waitableTimer );
- #endif
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- CloseHandle( stream->processingThreadCompleted );
- #endif
- // Cleanup the sound buffers
- if( stream->pDirectSoundOutputBuffer )
- {
- IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
- stream->pDirectSoundOutputBuffer = NULL;
- }
- if( stream->pDirectSoundPrimaryBuffer )
- {
- IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
- stream->pDirectSoundPrimaryBuffer = NULL;
- }
- if( stream->pDirectSoundInputBuffer )
- {
- IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
- IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
- stream->pDirectSoundInputBuffer = NULL;
- }
- if( stream->pDirectSoundCapture )
- {
- IDirectSoundCapture_Release( stream->pDirectSoundCapture );
- stream->pDirectSoundCapture = NULL;
- }
- if( stream->pDirectSound )
- {
- IDirectSound_Release( stream->pDirectSound );
- stream->pDirectSound = NULL;
- }
- #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
- if( stream->pDirectSoundFullDuplex8 )
- {
- IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
- stream->pDirectSoundFullDuplex8 = NULL;
- }
- #endif
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
- return result;
- }
- /***********************************************************************************/
- static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
- {
- PaError result = paNoError;
- unsigned char* pDSBuffData;
- DWORD dwDataLen;
- HRESULT hr;
- hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
- DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- return hr;
- // Lock the DS buffer
- if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
- &dwDataLen, NULL, 0, 0)) != DS_OK )
- return hr;
- // Zero the DS buffer
- ZeroMemory(pDSBuffData, dwDataLen);
- // Unlock the DS buffer
- if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
- return hr;
-
- // Let DSound set the starting write position because if we set it to zero, it looks like the
- // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
- if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
- &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
- return hr;
- /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
- return DS_OK;
- }
- static PaError StartStream( PaStream *s )
- {
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
-
- stream->callbackResult = paContinue;
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- ResetEvent( stream->processingCompleted );
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- ResetEvent( stream->processingThreadCompleted );
- #endif
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- // Start the buffer capture
- if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
- {
- hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
- }
- DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
- stream->framesWritten = 0;
- stream->callbackFlags = 0;
- stream->abortProcessing = 0;
- stream->stopProcessing = 0;
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- QueryPerformanceCounter( &stream->previousPlayTime );
- stream->finalZeroBytesWritten = 0;
- hr = ClearOutputBuffer( stream );
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
- {
- stream->callbackFlags = paPrimingOutput;
- TimeSlice( stream );
- /* we ignore the return value from TimeSlice here and start the stream as usual.
- The first timer callback will detect if the callback has completed. */
- stream->callbackFlags = 0;
- }
- // Start the buffer playback in a loop.
- if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
- {
- hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
- DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- stream->outputIsRunning = TRUE;
- }
- }
- if( stream->streamRepresentation.streamCallback )
- {
- TIMECAPS timecaps;
- int timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
- if( timerPeriodMs < 1 )
- timerPeriodMs = 1;
- /* set windows scheduler granularity only as fine as needed, no finer */
- /* Although this is not fully documented by MS, it appears that
- timeBeginPeriod() affects the scheduling granulatity of all timers
- including Waitable Timer Objects. So we always call timeBeginPeriod, whether
- we're using an MM timer callback via timeSetEvent or not.
- */
- assert( stream->systemTimerResolutionPeriodMs == 0 );
- if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) ) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 )
- {
- /* aim for resolution 4 times higher than polling rate */
- stream->systemTimerResolutionPeriodMs = (UINT)((stream->pollingPeriodSeconds * MSECS_PER_SECOND) * .25);
- if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
- stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
- if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax )
- stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax;
- if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR )
- stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */
- }
- #ifdef PA_WIN_DS_USE_WMME_TIMER
- /* Create timer that will wake us up so we can fill the DSound buffer. */
- /* We have deprecated timeSetEvent because all MM timer callbacks
- are serialised onto a single thread. Which creates problems with multiple
- PA streams, or when also using timers for other time critical tasks
- */
- stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
- (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
-
- if( stream->timerID == 0 )
- {
- stream->isActive = 0;
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
- #else
- /* Create processing thread which calls TimerCallback */
- stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
- if( !stream->processingThread )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
- if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
- goto error;
- }
- #endif
- }
- stream->isActive = 1;
- stream->isStarted = 1;
- assert( result == paNoError );
- return result;
- error:
- if( stream->pDirectSoundOutputBuffer != NULL && stream->outputIsRunning )
- IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- stream->outputIsRunning = FALSE;
- #ifndef PA_WIN_DS_USE_WMME_TIMER
- if( stream->processingThread )
- {
- #ifdef CLOSE_THREAD_HANDLE
- CLOSE_THREAD_HANDLE( stream->processingThread ); /* Delete thread. */
- #endif
- stream->processingThread = NULL;
- }
- #endif
- return result;
- }
- /***********************************************************************************/
- static PaError StopStream( PaStream *s )
- {
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
- int timeoutMsec;
- if( stream->streamRepresentation.streamCallback )
- {
- stream->stopProcessing = 1;
- /* Set timeout at 4 times maximum time we might wait. */
- timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate));
- WaitForSingleObject( stream->processingCompleted, timeoutMsec );
- }
- #ifdef PA_WIN_DS_USE_WMME_TIMER
- if( stream->timerID != 0 )
- {
- timeKillEvent(stream->timerID); /* Stop callback timer. */
- stream->timerID = 0;
- }
- #else
- if( stream->processingThread )
- {
- if( WaitForSingleObject( stream->processingThreadCompleted, 30*100 ) == WAIT_TIMEOUT )
- return paUnanticipatedHostError;
- #ifdef CLOSE_THREAD_HANDLE
- CloseHandle( stream->processingThread ); /* Delete thread. */
- stream->processingThread = NULL;
- #endif
- }
- #endif
- if( stream->systemTimerResolutionPeriodMs > 0 ){
- timeEndPeriod( stream->systemTimerResolutionPeriodMs );
- stream->systemTimerResolutionPeriodMs = 0;
- }
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- // Stop the buffer playback
- if( stream->pDirectSoundOutputBuffer != NULL )
- {
- stream->outputIsRunning = FALSE;
- // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
- hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
- if( stream->pDirectSoundPrimaryBuffer )
- IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
- }
- }
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- // Stop the buffer capture
- if( stream->pDirectSoundInputBuffer != NULL )
- {
- // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
- hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
- }
- }
- stream->isStarted = 0;
- return result;
- }
- /***********************************************************************************/
- static PaError AbortStream( PaStream *s )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- stream->abortProcessing = 1;
- return StopStream( s );
- }
- /***********************************************************************************/
- static PaError IsStreamStopped( PaStream *s )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- return !stream->isStarted;
- }
- /***********************************************************************************/
- static PaError IsStreamActive( PaStream *s )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- return stream->isActive;
- }
- /***********************************************************************************/
- static PaTime GetStreamTime( PaStream *s )
- {
- /* suppress unused variable warnings */
- (void) s;
- return PaUtil_GetTime();
- }
- /***********************************************************************************/
- static double GetStreamCpuLoad( PaStream* s )
- {
- PaWinDsStream *stream = (PaWinDsStream*)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 )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- return paNoError;
- }
- /***********************************************************************************/
- static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- return paNoError;
- }
- /***********************************************************************************/
- static signed long GetStreamReadAvailable( PaStream* s )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- /* suppress unused variable warnings */
- (void) stream;
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- return 0;
- }
- /***********************************************************************************/
- static signed long GetStreamWriteAvailable( PaStream* s )
- {
- PaWinDsStream *stream = (PaWinDsStream*)s;
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- return 0;
- }
- #endif
|