pa_win_ds.c 121 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243
  1. #ifdef _WIN32
  2. /*
  3. * $Id: pa_win_ds.c 1877 2012-11-10 02:55:20Z rbencina $
  4. * Portable Audio I/O Library DirectSound implementation
  5. *
  6. * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
  7. * Based on the Open Source API proposed by Ross Bencina
  8. * Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining
  11. * a copy of this software and associated documentation files
  12. * (the "Software"), to deal in the Software without restriction,
  13. * including without limitation the rights to use, copy, modify, merge,
  14. * publish, distribute, sublicense, and/or sell copies of the Software,
  15. * and to permit persons to whom the Software is furnished to do so,
  16. * subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be
  19. * included in all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  24. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
  25. * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  26. * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. */
  29. /*
  30. * The text above constitutes the entire PortAudio license; however,
  31. * the PortAudio community also makes the following non-binding requests:
  32. *
  33. * Any person wishing to distribute modifications to the Software is
  34. * requested to send the modifications to the original developer so that
  35. * they can be incorporated into the canonical version. It is also
  36. * requested that these non-binding requests be included along with the
  37. * license above.
  38. */
  39. /** @file
  40. @ingroup hostapi_src
  41. */
  42. /* Until May 2011 PA/DS has used a multimedia timer to perform the callback.
  43. We're replacing this with a new implementation using a thread and a different timer mechanim.
  44. Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior.
  45. */
  46. //#define PA_WIN_DS_USE_WMME_TIMER
  47. #include <assert.h>
  48. #include <stdio.h>
  49. #include <string.h> /* strlen() */
  50. #define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */
  51. #include <initguid.h> /* make sure ds guids get defined */
  52. #include <windows.h>
  53. #include <objbase.h>
  54. /*
  55. Use the earliest version of DX required, no need to polute the namespace
  56. */
  57. #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
  58. #define DIRECTSOUND_VERSION 0x0800
  59. #else
  60. #define DIRECTSOUND_VERSION 0x0300
  61. #endif
  62. #include <dsound.h>
  63. #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
  64. #include <dsconf.h>
  65. #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
  66. #ifndef PA_WIN_DS_USE_WMME_TIMER
  67. #ifndef UNDER_CE
  68. #include <process.h>
  69. #endif
  70. #endif
  71. #include "pa_util.h"
  72. #include "pa_allocation.h"
  73. #include "pa_hostapi.h"
  74. #include "pa_stream.h"
  75. #include "pa_cpuload.h"
  76. #include "pa_process.h"
  77. #include "pa_debugprint.h"
  78. #include "pa_win_ds.h"
  79. #include "pa_win_ds_dynlink.h"
  80. #include "pa_win_waveformat.h"
  81. #include "pa_win_wdmks_utils.h"
  82. #include "pa_win_coinitialize.h"
  83. #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
  84. #pragma comment( lib, "dsound.lib" )
  85. #pragma comment( lib, "winmm.lib" )
  86. #pragma comment( lib, "kernel32.lib" )
  87. #endif
  88. /* use CreateThread for CYGWIN, _beginthreadex for all others */
  89. #ifndef PA_WIN_DS_USE_WMME_TIMER
  90. #if !defined(__CYGWIN__) && !defined(UNDER_CE)
  91. #define CREATE_THREAD (HANDLE)_beginthreadex
  92. #undef CLOSE_THREAD_HANDLE /* as per documentation we don't call CloseHandle on a thread created with _beginthreadex */
  93. #define PA_THREAD_FUNC static unsigned WINAPI
  94. #define PA_THREAD_ID unsigned
  95. #else
  96. #define CREATE_THREAD CreateThread
  97. #define CLOSE_THREAD_HANDLE CloseHandle
  98. #define PA_THREAD_FUNC static DWORD WINAPI
  99. #define PA_THREAD_ID DWORD
  100. #endif
  101. #if (defined(UNDER_CE))
  102. #pragma comment(lib, "Coredll.lib")
  103. #elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
  104. #pragma comment(lib, "winmm.lib")
  105. #endif
  106. PA_THREAD_FUNC ProcessingThreadProc( void *pArg );
  107. #if !defined(UNDER_CE)
  108. #define PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT /* use waitable timer where possible, otherwise we use a WaitForSingleObject timeout */
  109. #endif
  110. #endif /* !PA_WIN_DS_USE_WMME_TIMER */
  111. /*
  112. provided in newer platform sdks and x64
  113. */
  114. #ifndef DWORD_PTR
  115. #if defined(_WIN64)
  116. #define DWORD_PTR unsigned __int64
  117. #else
  118. #define DWORD_PTR unsigned long
  119. #endif
  120. #endif
  121. #define PRINT(x) PA_DEBUG(x);
  122. #define ERR_RPT(x) PRINT(x)
  123. #define DBUG(x) PRINT(x)
  124. #define DBUGX(x) PRINT(x)
  125. #define PA_USE_HIGH_LATENCY (0)
  126. #if PA_USE_HIGH_LATENCY
  127. #define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.500)
  128. #define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.600)
  129. #else
  130. #define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.140)
  131. #define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.280)
  132. #endif
  133. #define PA_DS_WIN_WDM_DEFAULT_LATENCY_ (.120)
  134. /* we allow the polling period to range between 1 and 100ms.
  135. prior to August 2011 we limited the minimum polling period to 10ms.
  136. */
  137. #define PA_DS_MINIMUM_POLLING_PERIOD_SECONDS (0.001) /* 1ms */
  138. #define PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS (0.100) /* 100ms */
  139. #define PA_DS_POLLING_JITTER_SECONDS (0.001) /* 1ms */
  140. #define SECONDS_PER_MSEC (0.001)
  141. #define MSECS_PER_SECOND (1000)
  142. /* prototypes for functions declared in this file */
  143. #ifdef __cplusplus
  144. extern "C"
  145. {
  146. #endif /* __cplusplus */
  147. PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
  148. #ifdef __cplusplus
  149. }
  150. #endif /* __cplusplus */
  151. static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
  152. static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
  153. PaStream** s,
  154. const PaStreamParameters *inputParameters,
  155. const PaStreamParameters *outputParameters,
  156. double sampleRate,
  157. unsigned long framesPerBuffer,
  158. PaStreamFlags streamFlags,
  159. PaStreamCallback *streamCallback,
  160. void *userData );
  161. static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
  162. const PaStreamParameters *inputParameters,
  163. const PaStreamParameters *outputParameters,
  164. double sampleRate );
  165. static PaError CloseStream( PaStream* stream );
  166. static PaError StartStream( PaStream *stream );
  167. static PaError StopStream( PaStream *stream );
  168. static PaError AbortStream( PaStream *stream );
  169. static PaError IsStreamStopped( PaStream *s );
  170. static PaError IsStreamActive( PaStream *stream );
  171. static PaTime GetStreamTime( PaStream *stream );
  172. static double GetStreamCpuLoad( PaStream* stream );
  173. static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
  174. static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
  175. static signed long GetStreamReadAvailable( PaStream* stream );
  176. static signed long GetStreamWriteAvailable( PaStream* stream );
  177. /* FIXME: should convert hr to a string */
  178. #define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
  179. PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
  180. /************************************************* DX Prototypes **********/
  181. static BOOL CALLBACK CollectGUIDsProcA(LPGUID lpGUID,
  182. LPCSTR lpszDesc,
  183. LPCSTR lpszDrvName,
  184. LPVOID lpContext );
  185. /************************************************************************************/
  186. /********************** Structures **************************************************/
  187. /************************************************************************************/
  188. /* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
  189. typedef struct PaWinDsDeviceInfo
  190. {
  191. PaDeviceInfo inheritedDeviceInfo;
  192. GUID guid;
  193. GUID *lpGUID;
  194. double sampleRates[3];
  195. char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
  196. char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
  197. } PaWinDsDeviceInfo;
  198. typedef struct
  199. {
  200. PaUtilHostApiRepresentation inheritedHostApiRep;
  201. PaUtilStreamInterface callbackStreamInterface;
  202. PaUtilStreamInterface blockingStreamInterface;
  203. PaUtilAllocationGroup *allocations;
  204. /* implementation specific data goes here */
  205. PaWinUtilComInitializationResult comInitializationResult;
  206. } PaWinDsHostApiRepresentation;
  207. /* PaWinDsStream - a stream data structure specifically for this implementation */
  208. typedef struct PaWinDsStream
  209. {
  210. PaUtilStreamRepresentation streamRepresentation;
  211. PaUtilCpuLoadMeasurer cpuLoadMeasurer;
  212. PaUtilBufferProcessor bufferProcessor;
  213. /* DirectSound specific data. */
  214. #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
  215. LPDIRECTSOUNDFULLDUPLEX8 pDirectSoundFullDuplex8;
  216. #endif
  217. /* Output */
  218. LPDIRECTSOUND pDirectSound;
  219. LPDIRECTSOUNDBUFFER pDirectSoundPrimaryBuffer;
  220. LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer;
  221. DWORD outputBufferWriteOffsetBytes; /* last write position */
  222. INT outputBufferSizeBytes;
  223. INT outputFrameSizeBytes;
  224. /* Try to detect play buffer underflows. */
  225. LARGE_INTEGER perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
  226. LARGE_INTEGER previousPlayTime;
  227. DWORD previousPlayCursor;
  228. UINT outputUnderflowCount;
  229. BOOL outputIsRunning;
  230. INT finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
  231. /* Input */
  232. LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
  233. LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer;
  234. INT inputFrameSizeBytes;
  235. UINT readOffset; /* last read position */
  236. UINT inputBufferSizeBytes;
  237. int hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
  238. double framesWritten;
  239. double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
  240. double pollingPeriodSeconds;
  241. PaStreamCallbackFlags callbackFlags;
  242. PaStreamFlags streamFlags;
  243. int callbackResult;
  244. HANDLE processingCompleted;
  245. /* FIXME - move all below to PaUtilStreamRepresentation */
  246. volatile int isStarted;
  247. volatile int isActive;
  248. volatile int stopProcessing; /* stop thread once existing buffers have been returned */
  249. volatile int abortProcessing; /* stop thread immediately */
  250. UINT systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */
  251. #ifdef PA_WIN_DS_USE_WMME_TIMER
  252. MMRESULT timerID;
  253. #else
  254. #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
  255. HANDLE waitableTimer;
  256. #endif
  257. HANDLE processingThread;
  258. PA_THREAD_ID processingThreadId;
  259. HANDLE processingThreadCompleted;
  260. #endif
  261. } PaWinDsStream;
  262. /* Set minimal latency based on the current OS version.
  263. * NT has higher latency.
  264. */
  265. static double PaWinDS_GetMinSystemLatencySeconds( void )
  266. {
  267. double minLatencySeconds;
  268. /* Set minimal latency based on whether NT or other OS.
  269. * NT has higher latency.
  270. */
  271. OSVERSIONINFO osvi;
  272. osvi.dwOSVersionInfoSize = sizeof( osvi );
  273. GetVersionEx( &osvi );
  274. DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
  275. DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
  276. DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
  277. /* Check for NT */
  278. if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
  279. {
  280. minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
  281. }
  282. else if(osvi.dwMajorVersion >= 5)
  283. {
  284. minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
  285. }
  286. else
  287. {
  288. minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
  289. }
  290. return minLatencySeconds;
  291. }
  292. /*************************************************************************
  293. ** Return minimum workable latency required for this host. This is returned
  294. ** As the default stream latency in PaDeviceInfo.
  295. ** Latency can be optionally set by user by setting an environment variable.
  296. ** For example, to set latency to 200 msec, put:
  297. **
  298. ** set PA_MIN_LATENCY_MSEC=200
  299. **
  300. ** in the AUTOEXEC.BAT file and reboot.
  301. ** If the environment variable is not set, then the latency will be determined
  302. ** based on the OS. Windows NT has higher latency than Win95.
  303. */
  304. #define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
  305. #define PA_ENV_BUF_SIZE (32)
  306. static double PaWinDs_GetMinLatencySeconds( double sampleRate )
  307. {
  308. char envbuf[PA_ENV_BUF_SIZE];
  309. DWORD hresult;
  310. double minLatencySeconds = 0;
  311. /* Let user determine minimal latency by setting environment variable. */
  312. hresult = GetEnvironmentVariableA( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
  313. if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
  314. {
  315. minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC;
  316. }
  317. else
  318. {
  319. minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds();
  320. #if PA_USE_HIGH_LATENCY
  321. PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND ));
  322. #endif
  323. }
  324. return minLatencySeconds;
  325. }
  326. /************************************************************************************
  327. ** Duplicate the input string using the allocations allocator.
  328. ** A NULL string is converted to a zero length string.
  329. ** If memory cannot be allocated, NULL is returned.
  330. **/
  331. static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
  332. {
  333. char *result = 0;
  334. if( src != NULL )
  335. {
  336. size_t len = strlen(src);
  337. result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
  338. if( result )
  339. memcpy( (void *) result, src, len+1 );
  340. }
  341. else
  342. {
  343. result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
  344. if( result )
  345. result[0] = '\0';
  346. }
  347. return result;
  348. }
  349. /************************************************************************************
  350. ** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
  351. ** information during device enumeration.
  352. */
  353. typedef struct DSDeviceNameAndGUID{
  354. char *name; // allocated from parent's allocations, never deleted by this structure
  355. GUID guid;
  356. LPGUID lpGUID;
  357. void *pnpInterface; // wchar_t* interface path, allocated using the DS host api's allocation group
  358. } DSDeviceNameAndGUID;
  359. typedef struct DSDeviceNameAndGUIDVector{
  360. PaUtilAllocationGroup *allocations;
  361. PaError enumerationError;
  362. int count;
  363. int free;
  364. DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
  365. } DSDeviceNameAndGUIDVector;
  366. typedef struct DSDeviceNamesAndGUIDs{
  367. PaWinDsHostApiRepresentation *winDsHostApi;
  368. DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
  369. DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
  370. } DSDeviceNamesAndGUIDs;
  371. static PaError InitializeDSDeviceNameAndGUIDVector(
  372. DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
  373. {
  374. PaError result = paNoError;
  375. guidVector->allocations = allocations;
  376. guidVector->enumerationError = paNoError;
  377. guidVector->count = 0;
  378. guidVector->free = 8;
  379. guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
  380. if( guidVector->items == NULL )
  381. result = paInsufficientMemory;
  382. return result;
  383. }
  384. static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
  385. {
  386. PaError result = paNoError;
  387. DSDeviceNameAndGUID *newItems;
  388. int i;
  389. /* double size of vector */
  390. int size = guidVector->count + guidVector->free;
  391. guidVector->free += size;
  392. newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
  393. if( newItems == NULL )
  394. {
  395. result = paInsufficientMemory;
  396. }
  397. else
  398. {
  399. for( i=0; i < guidVector->count; ++i )
  400. {
  401. newItems[i].name = guidVector->items[i].name;
  402. if( guidVector->items[i].lpGUID == NULL )
  403. {
  404. newItems[i].lpGUID = NULL;
  405. }
  406. else
  407. {
  408. newItems[i].lpGUID = &newItems[i].guid;
  409. memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );
  410. }
  411. newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
  412. }
  413. LocalFree( guidVector->items );
  414. guidVector->items = newItems;
  415. }
  416. return result;
  417. }
  418. /*
  419. it's safe to call DSDeviceNameAndGUIDVector multiple times
  420. */
  421. static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
  422. {
  423. PaError result = paNoError;
  424. if( guidVector->items != NULL )
  425. {
  426. if( LocalFree( guidVector->items ) != NULL )
  427. result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
  428. guidVector->items = NULL;
  429. }
  430. return result;
  431. }
  432. /************************************************************************************
  433. ** Collect preliminary device information during DirectSound enumeration
  434. */
  435. static BOOL CALLBACK CollectGUIDsProcA(LPGUID lpGUID,
  436. LPCSTR lpszDesc,
  437. LPCSTR lpszDrvName,
  438. LPVOID lpContext )
  439. {
  440. DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
  441. PaError error;
  442. (void) lpszDrvName; /* unused variable */
  443. if( namesAndGUIDs->free == 0 )
  444. {
  445. error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
  446. if( error != paNoError )
  447. {
  448. namesAndGUIDs->enumerationError = error;
  449. return FALSE;
  450. }
  451. }
  452. /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
  453. if( lpGUID == NULL )
  454. {
  455. namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
  456. }
  457. else
  458. {
  459. namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
  460. &namesAndGUIDs->items[namesAndGUIDs->count].guid;
  461. memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
  462. }
  463. namesAndGUIDs->items[namesAndGUIDs->count].name =
  464. DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
  465. if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
  466. {
  467. namesAndGUIDs->enumerationError = paInsufficientMemory;
  468. return FALSE;
  469. }
  470. namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
  471. ++namesAndGUIDs->count;
  472. --namesAndGUIDs->free;
  473. return TRUE;
  474. }
  475. #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
  476. static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
  477. {
  478. size_t len;
  479. wchar_t *result;
  480. len = wcslen( source );
  481. result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
  482. wcscpy( result, source );
  483. return result;
  484. }
  485. static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
  486. {
  487. int i;
  488. DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
  489. /*
  490. Apparently data->Interface can be NULL in some cases.
  491. Possibly virtual devices without hardware.
  492. So we check for NULLs now. See mailing list message November 10, 2012:
  493. "[Portaudio] portaudio initialization crash in KsPropertySetEnumerateCallback(pa_win_ds.c)"
  494. */
  495. if( data->Interface )
  496. {
  497. if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
  498. {
  499. for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
  500. {
  501. if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
  502. && memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
  503. {
  504. deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
  505. (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
  506. break;
  507. }
  508. }
  509. }
  510. else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
  511. {
  512. for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
  513. {
  514. if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
  515. && memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
  516. {
  517. deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
  518. (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
  519. break;
  520. }
  521. }
  522. }
  523. }
  524. return TRUE;
  525. }
  526. static GUID pawin_CLSID_DirectSoundPrivate =
  527. { 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
  528. static GUID pawin_DSPROPSETID_DirectSoundDevice =
  529. { 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
  530. static GUID pawin_IID_IKsPropertySet =
  531. { 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
  532. /*
  533. FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
  534. with UNICODE file paths to the devices. The DS documentation mentions
  535. at least two techniques by which these Interface paths can be found using IKsPropertySet on
  536. the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
  537. property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
  538. I tried both methods and only the second worked. I found two postings on the
  539. net from people who had the same problem with the first method, so I think the method used here is
  540. more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
  541. but the fields of the device description are not filled in.
  542. The mechanism we use works by registering an enumeration callback which is called for
  543. every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
  544. with the matching GUID and copies the pointer to the Interface path.
  545. Note that we could have used this enumeration callback to perform the original
  546. device enumeration, however we choose not to so we can disable this step easily.
  547. Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
  548. http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
  549. -- rossb
  550. */
  551. static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
  552. {
  553. IClassFactory *pClassFactory;
  554. if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
  555. IKsPropertySet *pPropertySet;
  556. if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
  557. DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
  558. ULONG bytesReturned;
  559. data.Callback = KsPropertySetEnumerateCallback;
  560. data.Context = deviceNamesAndGUIDs;
  561. IKsPropertySet_Get( pPropertySet,
  562. &pawin_DSPROPSETID_DirectSoundDevice,
  563. DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
  564. NULL,
  565. 0,
  566. &data,
  567. sizeof(data),
  568. &bytesReturned
  569. );
  570. IKsPropertySet_Release( pPropertySet );
  571. }
  572. pClassFactory->lpVtbl->Release( pClassFactory );
  573. }
  574. /*
  575. The following code fragment, which I chose not to use, queries for the
  576. device interface for a device with a specific GUID:
  577. ULONG BytesReturned;
  578. DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
  579. memset (&Property, 0, sizeof(Property));
  580. Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
  581. Property.DeviceId = *lpGUID;
  582. hr = IKsPropertySet_Get( pPropertySet,
  583. &pawin_DSPROPSETID_DirectSoundDevice,
  584. DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
  585. NULL,
  586. 0,
  587. &Property,
  588. sizeof(Property),
  589. &BytesReturned
  590. );
  591. if( hr == S_OK )
  592. {
  593. //pnpInterface = Property.Interface;
  594. }
  595. */
  596. }
  597. #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
  598. /*
  599. GUIDs for emulated devices which we blacklist below.
  600. are there more than two of them??
  601. */
  602. GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
  603. GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
  604. #define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
  605. static double defaultSampleRateSearchOrder_[] =
  606. { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
  607. 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
  608. /************************************************************************************
  609. ** Extract capabilities from an output device, and add it to the device info list
  610. ** if successful. This function assumes that there is enough room in the
  611. ** device info list to accomodate all entries.
  612. **
  613. ** The device will not be added to the device list if any errors are encountered.
  614. */
  615. static PaError AddOutputDeviceInfoFromDirectSound(
  616. PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
  617. {
  618. PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
  619. PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
  620. PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
  621. HRESULT hr;
  622. LPDIRECTSOUND lpDirectSound;
  623. DSCAPS caps;
  624. int deviceOK = TRUE;
  625. PaError result = paNoError;
  626. int i;
  627. /* Copy GUID to the device info structure. Set pointer. */
  628. if( lpGUID == NULL )
  629. {
  630. winDsDeviceInfo->lpGUID = NULL;
  631. }
  632. else
  633. {
  634. memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
  635. winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
  636. }
  637. if( lpGUID )
  638. {
  639. if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
  640. IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
  641. {
  642. PA_DEBUG(("BLACKLISTED: %s \n",name));
  643. return paNoError;
  644. }
  645. }
  646. /* Create a DirectSound object for the specified GUID
  647. Note that using CoCreateInstance doesn't work on windows CE.
  648. */
  649. hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
  650. /** try using CoCreateInstance because DirectSoundCreate was hanging under
  651. some circumstances - note this was probably related to the
  652. #define BOOL short bug which has now been fixed
  653. @todo delete this comment and the following code once we've ensured
  654. there is no bug.
  655. */
  656. /*
  657. hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
  658. &IID_IDirectSound, (void**)&lpDirectSound );
  659. if( hr == S_OK )
  660. {
  661. hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
  662. }
  663. */
  664. if( hr != DS_OK )
  665. {
  666. if (hr == DSERR_ALLOCATED)
  667. PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
  668. DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
  669. if (lpGUID)
  670. 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",
  671. name,
  672. lpGUID->Data1,
  673. lpGUID->Data2,
  674. lpGUID->Data3,
  675. lpGUID->Data4[0],
  676. lpGUID->Data4[1],
  677. lpGUID->Data4[2],
  678. lpGUID->Data4[3],
  679. lpGUID->Data4[4],
  680. lpGUID->Data4[5],
  681. lpGUID->Data4[6],
  682. lpGUID->Data4[7]));
  683. deviceOK = FALSE;
  684. }
  685. else
  686. {
  687. /* Query device characteristics. */
  688. memset( &caps, 0, sizeof(caps) );
  689. caps.dwSize = sizeof(caps);
  690. hr = IDirectSound_GetCaps( lpDirectSound, &caps );
  691. if( hr != DS_OK )
  692. {
  693. DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
  694. deviceOK = FALSE;
  695. }
  696. else
  697. {
  698. #if PA_USE_WMME
  699. if( caps.dwFlags & DSCAPS_EMULDRIVER )
  700. {
  701. /* If WMME supported, then reject Emulated drivers because they are lousy. */
  702. deviceOK = FALSE;
  703. }
  704. #endif
  705. if( deviceOK )
  706. {
  707. deviceInfo->maxInputChannels = 0;
  708. winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
  709. /* DS output capabilities only indicate supported number of channels
  710. using two flags which indicate mono and/or stereo.
  711. We assume that stereo devices may support more than 2 channels
  712. (as is the case with 5.1 devices for example) and so
  713. set deviceOutputChannelCountIsKnown to 0 (unknown).
  714. In this case OpenStream will try to open the device
  715. when the user requests more than 2 channels, rather than
  716. returning an error.
  717. */
  718. if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
  719. {
  720. deviceInfo->maxOutputChannels = 2;
  721. winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
  722. }
  723. else
  724. {
  725. deviceInfo->maxOutputChannels = 1;
  726. winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
  727. }
  728. /* Guess channels count from speaker configuration. We do it only when
  729. pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
  730. */
  731. #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
  732. if( !pnpInterface )
  733. #endif
  734. {
  735. DWORD spkrcfg;
  736. if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
  737. {
  738. int count = 0;
  739. switch (DSSPEAKER_CONFIG(spkrcfg))
  740. {
  741. case DSSPEAKER_HEADPHONE: count = 2; break;
  742. case DSSPEAKER_MONO: count = 1; break;
  743. case DSSPEAKER_QUAD: count = 4; break;
  744. case DSSPEAKER_STEREO: count = 2; break;
  745. case DSSPEAKER_SURROUND: count = 4; break;
  746. case DSSPEAKER_5POINT1: count = 6; break;
  747. case DSSPEAKER_7POINT1: count = 8; break;
  748. #ifndef DSSPEAKER_7POINT1_SURROUND
  749. #define DSSPEAKER_7POINT1_SURROUND 0x00000008
  750. #endif
  751. case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
  752. #ifndef DSSPEAKER_5POINT1_SURROUND
  753. #define DSSPEAKER_5POINT1_SURROUND 0x00000009
  754. #endif
  755. case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
  756. }
  757. if( count )
  758. {
  759. deviceInfo->maxOutputChannels = count;
  760. winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
  761. }
  762. }
  763. }
  764. #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
  765. if( pnpInterface )
  766. {
  767. int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
  768. if( count > 0 )
  769. {
  770. deviceInfo->maxOutputChannels = count;
  771. winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
  772. }
  773. }
  774. #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
  775. /* initialize defaultSampleRate */
  776. if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
  777. {
  778. /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
  779. deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
  780. for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
  781. {
  782. if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
  783. && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
  784. {
  785. deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
  786. break;
  787. }
  788. }
  789. }
  790. else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
  791. {
  792. if( caps.dwMinSecondarySampleRate == 0 )
  793. {
  794. /*
  795. ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
  796. ** But it supports continuous sampling.
  797. ** So fake range of rates, and hope it really supports it.
  798. */
  799. deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
  800. DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
  801. }
  802. else
  803. {
  804. deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
  805. }
  806. }
  807. else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
  808. {
  809. /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
  810. ** But we know that they really support a range of rates!
  811. ** So when we see a ridiculous set of rates, assume it is a range.
  812. */
  813. deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
  814. DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
  815. }
  816. else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
  817. //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
  818. // dwFlags | DSCAPS_CONTINUOUSRATE
  819. deviceInfo->defaultLowInputLatency = 0.;
  820. deviceInfo->defaultHighInputLatency = 0.;
  821. deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
  822. deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
  823. }
  824. }
  825. IDirectSound_Release( lpDirectSound );
  826. }
  827. if( deviceOK )
  828. {
  829. deviceInfo->name = name;
  830. if( lpGUID == NULL )
  831. hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
  832. hostApi->info.deviceCount++;
  833. }
  834. return result;
  835. }
  836. /************************************************************************************
  837. ** Extract capabilities from an input device, and add it to the device info list
  838. ** if successful. This function assumes that there is enough room in the
  839. ** device info list to accomodate all entries.
  840. **
  841. ** The device will not be added to the device list if any errors are encountered.
  842. */
  843. static PaError AddInputDeviceInfoFromDirectSoundCapture(
  844. PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
  845. {
  846. PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
  847. PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
  848. PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
  849. HRESULT hr;
  850. LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
  851. DSCCAPS caps;
  852. int deviceOK = TRUE;
  853. PaError result = paNoError;
  854. /* Copy GUID to the device info structure. Set pointer. */
  855. if( lpGUID == NULL )
  856. {
  857. winDsDeviceInfo->lpGUID = NULL;
  858. }
  859. else
  860. {
  861. winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
  862. memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
  863. }
  864. hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
  865. /** try using CoCreateInstance because DirectSoundCreate was hanging under
  866. some circumstances - note this was probably related to the
  867. #define BOOL short bug which has now been fixed
  868. @todo delete this comment and the following code once we've ensured
  869. there is no bug.
  870. */
  871. /*
  872. hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
  873. &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
  874. */
  875. if( hr != DS_OK )
  876. {
  877. DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
  878. deviceOK = FALSE;
  879. }
  880. else
  881. {
  882. /* Query device characteristics. */
  883. memset( &caps, 0, sizeof(caps) );
  884. caps.dwSize = sizeof(caps);
  885. hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
  886. if( hr != DS_OK )
  887. {
  888. DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
  889. deviceOK = FALSE;
  890. }
  891. else
  892. {
  893. #if PA_USE_WMME
  894. if( caps.dwFlags & DSCAPS_EMULDRIVER )
  895. {
  896. /* If WMME supported, then reject Emulated drivers because they are lousy. */
  897. deviceOK = FALSE;
  898. }
  899. #endif
  900. if( deviceOK )
  901. {
  902. deviceInfo->maxInputChannels = caps.dwChannels;
  903. winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
  904. deviceInfo->maxOutputChannels = 0;
  905. winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
  906. #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
  907. if( pnpInterface )
  908. {
  909. int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
  910. if( count > 0 )
  911. {
  912. deviceInfo->maxInputChannels = count;
  913. winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
  914. }
  915. }
  916. #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
  917. /* constants from a WINE patch by Francois Gouget, see:
  918. http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
  919. ---
  920. Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
  921. From: Francois Gouget <fgouget@ ... .fr>
  922. To: Ross Bencina <rbencina@ ... .au>
  923. Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
  924. [snip]
  925. I give you permission to use the patch below under the BSD license.
  926. http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
  927. [snip]
  928. */
  929. #ifndef WAVE_FORMAT_48M08
  930. #define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
  931. #define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
  932. #define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
  933. #define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
  934. #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
  935. #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
  936. #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
  937. #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
  938. #endif
  939. /* defaultSampleRate */
  940. if( caps.dwChannels == 2 )
  941. {
  942. if( caps.dwFormats & WAVE_FORMAT_4S16 )
  943. deviceInfo->defaultSampleRate = 44100.0;
  944. else if( caps.dwFormats & WAVE_FORMAT_48S16 )
  945. deviceInfo->defaultSampleRate = 48000.0;
  946. else if( caps.dwFormats & WAVE_FORMAT_2S16 )
  947. deviceInfo->defaultSampleRate = 22050.0;
  948. else if( caps.dwFormats & WAVE_FORMAT_1S16 )
  949. deviceInfo->defaultSampleRate = 11025.0;
  950. else if( caps.dwFormats & WAVE_FORMAT_96S16 )
  951. deviceInfo->defaultSampleRate = 96000.0;
  952. else
  953. deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
  954. }
  955. else if( caps.dwChannels == 1 )
  956. {
  957. if( caps.dwFormats & WAVE_FORMAT_4M16 )
  958. deviceInfo->defaultSampleRate = 44100.0;
  959. else if( caps.dwFormats & WAVE_FORMAT_48M16 )
  960. deviceInfo->defaultSampleRate = 48000.0;
  961. else if( caps.dwFormats & WAVE_FORMAT_2M16 )
  962. deviceInfo->defaultSampleRate = 22050.0;
  963. else if( caps.dwFormats & WAVE_FORMAT_1M16 )
  964. deviceInfo->defaultSampleRate = 11025.0;
  965. else if( caps.dwFormats & WAVE_FORMAT_96M16 )
  966. deviceInfo->defaultSampleRate = 96000.0;
  967. else
  968. deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
  969. }
  970. else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
  971. deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
  972. deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
  973. deviceInfo->defaultLowOutputLatency = 0.;
  974. deviceInfo->defaultHighOutputLatency = 0.;
  975. }
  976. }
  977. IDirectSoundCapture_Release( lpDirectSoundCapture );
  978. }
  979. if( deviceOK )
  980. {
  981. deviceInfo->name = name;
  982. if( lpGUID == NULL )
  983. hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
  984. hostApi->info.deviceCount++;
  985. }
  986. return result;
  987. }
  988. /***********************************************************************************/
  989. PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
  990. {
  991. PaError result = paNoError;
  992. int i, deviceCount;
  993. PaWinDsHostApiRepresentation *winDsHostApi;
  994. DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
  995. PaWinDsDeviceInfo *deviceInfoArray;
  996. PaWinDs_InitializeDSoundEntryPoints();
  997. /* initialise guid vectors so they can be safely deleted on error */
  998. deviceNamesAndGUIDs.winDsHostApi = NULL;
  999. deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
  1000. deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
  1001. winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
  1002. if( !winDsHostApi )
  1003. {
  1004. result = paInsufficientMemory;
  1005. goto error;
  1006. }
  1007. memset( winDsHostApi, 0, sizeof(PaWinDsHostApiRepresentation) ); /* ensure all fields are zeroed. especially winDsHostApi->allocations */
  1008. result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
  1009. if( result != paNoError )
  1010. {
  1011. goto error;
  1012. }
  1013. winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
  1014. if( !winDsHostApi->allocations )
  1015. {
  1016. result = paInsufficientMemory;
  1017. goto error;
  1018. }
  1019. *hostApi = &winDsHostApi->inheritedHostApiRep;
  1020. (*hostApi)->info.structVersion = 1;
  1021. (*hostApi)->info.type = paDirectSound;
  1022. (*hostApi)->info.name = "Windows DirectSound";
  1023. (*hostApi)->info.deviceCount = 0;
  1024. (*hostApi)->info.defaultInputDevice = paNoDevice;
  1025. (*hostApi)->info.defaultOutputDevice = paNoDevice;
  1026. /* DSound - enumerate devices to count them and to gather their GUIDs */
  1027. result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
  1028. if( result != paNoError )
  1029. goto error;
  1030. result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
  1031. if( result != paNoError )
  1032. goto error;
  1033. paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACKA)CollectGUIDsProcA, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
  1034. paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACKA)CollectGUIDsProcA, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
  1035. if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
  1036. {
  1037. result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
  1038. goto error;
  1039. }
  1040. if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
  1041. {
  1042. result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
  1043. goto error;
  1044. }
  1045. deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
  1046. #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
  1047. if( deviceCount > 0 )
  1048. {
  1049. deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
  1050. FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
  1051. }
  1052. #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
  1053. if( deviceCount > 0 )
  1054. {
  1055. /* allocate array for pointers to PaDeviceInfo structs */
  1056. (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
  1057. winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
  1058. if( !(*hostApi)->deviceInfos )
  1059. {
  1060. result = paInsufficientMemory;
  1061. goto error;
  1062. }
  1063. /* allocate all PaDeviceInfo structs in a contiguous block */
  1064. deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
  1065. winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
  1066. if( !deviceInfoArray )
  1067. {
  1068. result = paInsufficientMemory;
  1069. goto error;
  1070. }
  1071. for( i=0; i < deviceCount; ++i )
  1072. {
  1073. PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
  1074. deviceInfo->structVersion = 2;
  1075. deviceInfo->hostApi = hostApiIndex;
  1076. deviceInfo->name = 0;
  1077. (*hostApi)->deviceInfos[i] = deviceInfo;
  1078. }
  1079. for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
  1080. {
  1081. result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
  1082. deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
  1083. deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
  1084. deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
  1085. if( result != paNoError )
  1086. goto error;
  1087. }
  1088. for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
  1089. {
  1090. result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
  1091. deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
  1092. deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
  1093. deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
  1094. if( result != paNoError )
  1095. goto error;
  1096. }
  1097. }
  1098. result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
  1099. if( result != paNoError )
  1100. goto error;
  1101. result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
  1102. if( result != paNoError )
  1103. goto error;
  1104. (*hostApi)->Terminate = Terminate;
  1105. (*hostApi)->OpenStream = OpenStream;
  1106. (*hostApi)->IsFormatSupported = IsFormatSupported;
  1107. PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
  1108. StopStream, AbortStream, IsStreamStopped, IsStreamActive,
  1109. GetStreamTime, GetStreamCpuLoad,
  1110. PaUtil_DummyRead, PaUtil_DummyWrite,
  1111. PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
  1112. PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
  1113. StopStream, AbortStream, IsStreamStopped, IsStreamActive,
  1114. GetStreamTime, PaUtil_DummyGetCpuLoad,
  1115. ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
  1116. return result;
  1117. error:
  1118. TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
  1119. TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
  1120. Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
  1121. return result;
  1122. }
  1123. /***********************************************************************************/
  1124. static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
  1125. {
  1126. PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
  1127. if( winDsHostApi ){
  1128. if( winDsHostApi->allocations )
  1129. {
  1130. PaUtil_FreeAllAllocations( winDsHostApi->allocations );
  1131. PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
  1132. }
  1133. PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
  1134. PaUtil_FreeMemory( winDsHostApi );
  1135. }
  1136. PaWinDs_TerminateDSoundEntryPoints();
  1137. }
  1138. static PaError ValidateWinDirectSoundSpecificStreamInfo(
  1139. const PaStreamParameters *streamParameters,
  1140. const PaWinDirectSoundStreamInfo *streamInfo )
  1141. {
  1142. if( streamInfo )
  1143. {
  1144. if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
  1145. || streamInfo->version != 2 )
  1146. {
  1147. return paIncompatibleHostApiSpecificStreamInfo;
  1148. }
  1149. if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
  1150. {
  1151. if( streamInfo->framesPerBuffer <= 0 )
  1152. return paIncompatibleHostApiSpecificStreamInfo;
  1153. }
  1154. }
  1155. return paNoError;
  1156. }
  1157. /***********************************************************************************/
  1158. static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
  1159. const PaStreamParameters *inputParameters,
  1160. const PaStreamParameters *outputParameters,
  1161. double sampleRate )
  1162. {
  1163. PaError result;
  1164. PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
  1165. PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
  1166. int inputChannelCount, outputChannelCount;
  1167. PaSampleFormat inputSampleFormat, outputSampleFormat;
  1168. PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
  1169. if( inputParameters )
  1170. {
  1171. inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
  1172. inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
  1173. inputChannelCount = inputParameters->channelCount;
  1174. inputSampleFormat = inputParameters->sampleFormat;
  1175. /* unless alternate device specification is supported, reject the use of
  1176. paUseHostApiSpecificDeviceSpecification */
  1177. if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
  1178. return paInvalidDevice;
  1179. /* check that input device can support inputChannelCount */
  1180. if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
  1181. && inputChannelCount > inputDeviceInfo->maxInputChannels )
  1182. return paInvalidChannelCount;
  1183. /* validate inputStreamInfo */
  1184. inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
  1185. result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
  1186. if( result != paNoError ) return result;
  1187. }
  1188. else
  1189. {
  1190. inputChannelCount = 0;
  1191. }
  1192. if( outputParameters )
  1193. {
  1194. outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
  1195. outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
  1196. outputChannelCount = outputParameters->channelCount;
  1197. outputSampleFormat = outputParameters->sampleFormat;
  1198. /* unless alternate device specification is supported, reject the use of
  1199. paUseHostApiSpecificDeviceSpecification */
  1200. if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
  1201. return paInvalidDevice;
  1202. /* check that output device can support inputChannelCount */
  1203. if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
  1204. && outputChannelCount > outputDeviceInfo->maxOutputChannels )
  1205. return paInvalidChannelCount;
  1206. /* validate outputStreamInfo */
  1207. outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
  1208. result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
  1209. if( result != paNoError ) return result;
  1210. }
  1211. else
  1212. {
  1213. outputChannelCount = 0;
  1214. }
  1215. /*
  1216. IMPLEMENT ME:
  1217. - if a full duplex stream is requested, check that the combination
  1218. of input and output parameters is supported if necessary
  1219. - check that the device supports sampleRate
  1220. Because the buffer adapter handles conversion between all standard
  1221. sample formats, the following checks are only required if paCustomFormat
  1222. is implemented, or under some other unusual conditions.
  1223. - check that input device can support inputSampleFormat, or that
  1224. we have the capability to convert from outputSampleFormat to
  1225. a native format
  1226. - check that output device can support outputSampleFormat, or that
  1227. we have the capability to convert from outputSampleFormat to
  1228. a native format
  1229. */
  1230. return paFormatIsSupported;
  1231. }
  1232. #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
  1233. static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
  1234. PaWinDsDeviceInfo *inputDevice,
  1235. PaSampleFormat hostInputSampleFormat,
  1236. WORD inputChannelCount,
  1237. int bytesPerInputBuffer,
  1238. PaWinWaveFormatChannelMask inputChannelMask,
  1239. PaWinDsDeviceInfo *outputDevice,
  1240. PaSampleFormat hostOutputSampleFormat,
  1241. WORD outputChannelCount,
  1242. int bytesPerOutputBuffer,
  1243. PaWinWaveFormatChannelMask outputChannelMask,
  1244. unsigned long nFrameRate
  1245. )
  1246. {
  1247. HRESULT hr;
  1248. DSCBUFFERDESC captureDesc;
  1249. PaWinWaveFormat captureWaveFormat;
  1250. DSBUFFERDESC secondaryRenderDesc;
  1251. PaWinWaveFormat renderWaveFormat;
  1252. LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
  1253. LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
  1254. // capture buffer description
  1255. // only try wave format extensible. assume it's available on all ds 8 systems
  1256. PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
  1257. hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
  1258. nFrameRate, inputChannelMask );
  1259. ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
  1260. captureDesc.dwSize = sizeof(DSCBUFFERDESC);
  1261. captureDesc.dwFlags = 0;
  1262. captureDesc.dwBufferBytes = bytesPerInputBuffer;
  1263. captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
  1264. // render buffer description
  1265. PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
  1266. hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
  1267. nFrameRate, outputChannelMask );
  1268. ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
  1269. secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
  1270. secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
  1271. secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
  1272. secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
  1273. /* note that we don't create a primary buffer here at all */
  1274. hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
  1275. inputDevice->lpGUID, outputDevice->lpGUID,
  1276. &captureDesc, &secondaryRenderDesc,
  1277. GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
  1278. DSSCL_EXCLUSIVE,
  1279. &stream->pDirectSoundFullDuplex8,
  1280. &pCaptureBuffer8,
  1281. &pRenderBuffer8,
  1282. NULL /* pUnkOuter must be NULL */
  1283. );
  1284. if( hr == DS_OK )
  1285. {
  1286. PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
  1287. /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
  1288. hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
  1289. if( hr == DS_OK )
  1290. hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
  1291. /* release the ds 8 interfaces, we don't need them */
  1292. IUnknown_Release( pCaptureBuffer8 );
  1293. IUnknown_Release( pRenderBuffer8 );
  1294. if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
  1295. /* couldn't get pre ds 8 interfaces for some reason. clean up. */
  1296. if( stream->pDirectSoundInputBuffer )
  1297. {
  1298. IUnknown_Release( stream->pDirectSoundInputBuffer );
  1299. stream->pDirectSoundInputBuffer = NULL;
  1300. }
  1301. if( stream->pDirectSoundOutputBuffer )
  1302. {
  1303. IUnknown_Release( stream->pDirectSoundOutputBuffer );
  1304. stream->pDirectSoundOutputBuffer = NULL;
  1305. }
  1306. IUnknown_Release( stream->pDirectSoundFullDuplex8 );
  1307. stream->pDirectSoundFullDuplex8 = NULL;
  1308. }
  1309. }
  1310. else
  1311. {
  1312. PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
  1313. }
  1314. return hr;
  1315. }
  1316. #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
  1317. static HRESULT InitInputBuffer( PaWinDsStream *stream,
  1318. PaWinDsDeviceInfo *device,
  1319. PaSampleFormat sampleFormat,
  1320. unsigned long nFrameRate,
  1321. WORD nChannels,
  1322. int bytesPerBuffer,
  1323. PaWinWaveFormatChannelMask channelMask )
  1324. {
  1325. DSCBUFFERDESC captureDesc;
  1326. PaWinWaveFormat waveFormat;
  1327. HRESULT result;
  1328. if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
  1329. device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
  1330. ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
  1331. return result;
  1332. }
  1333. // Setup the secondary buffer description
  1334. ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
  1335. captureDesc.dwSize = sizeof(DSCBUFFERDESC);
  1336. captureDesc.dwFlags = 0;
  1337. captureDesc.dwBufferBytes = bytesPerBuffer;
  1338. captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
  1339. // Create the capture buffer
  1340. // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
  1341. PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
  1342. sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
  1343. nFrameRate, channelMask );
  1344. if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
  1345. &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
  1346. {
  1347. PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
  1348. PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
  1349. if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
  1350. &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
  1351. }
  1352. stream->readOffset = 0; // reset last read position to start of buffer
  1353. return DS_OK;
  1354. }
  1355. static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
  1356. PaSampleFormat sampleFormat, unsigned long nFrameRate,
  1357. WORD nChannels, int bytesPerBuffer,
  1358. PaWinWaveFormatChannelMask channelMask )
  1359. {
  1360. HRESULT result;
  1361. HWND hWnd;
  1362. HRESULT hr;
  1363. PaWinWaveFormat waveFormat;
  1364. DSBUFFERDESC primaryDesc;
  1365. DSBUFFERDESC secondaryDesc;
  1366. if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
  1367. device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
  1368. ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
  1369. return hr;
  1370. }
  1371. // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
  1372. // applications's window. Also if that window is closed before the Buffer is closed
  1373. // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
  1374. // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
  1375. // hWnd = GetForegroundWindow();
  1376. //
  1377. // FIXME: The example code I have on the net creates a hidden window that
  1378. // is managed by our code - I think we should do that - one hidden
  1379. // window for the whole of Pa_DS
  1380. //
  1381. hWnd = GetDesktopWindow();
  1382. // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
  1383. // exclusive also prevents unexpected sounds from other apps during a performance.
  1384. if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
  1385. hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
  1386. {
  1387. return hr;
  1388. }
  1389. // -----------------------------------------------------------------------
  1390. // Create primary buffer and set format just so we can specify our custom format.
  1391. // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
  1392. // Setup the primary buffer description
  1393. ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
  1394. primaryDesc.dwSize = sizeof(DSBUFFERDESC);
  1395. primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
  1396. primaryDesc.dwBufferBytes = 0;
  1397. primaryDesc.lpwfxFormat = NULL;
  1398. // Create the buffer
  1399. if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
  1400. &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
  1401. goto error;
  1402. // Set the primary buffer's format
  1403. // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
  1404. PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
  1405. sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
  1406. nFrameRate, channelMask );
  1407. if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
  1408. {
  1409. PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
  1410. PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
  1411. if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
  1412. goto error;
  1413. }
  1414. // ----------------------------------------------------------------------
  1415. // Setup the secondary buffer description
  1416. ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
  1417. secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
  1418. secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
  1419. secondaryDesc.dwBufferBytes = bytesPerBuffer;
  1420. secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
  1421. // Create the secondary buffer
  1422. if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
  1423. &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
  1424. goto error;
  1425. return DS_OK;
  1426. error:
  1427. if( stream->pDirectSoundPrimaryBuffer )
  1428. {
  1429. IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
  1430. stream->pDirectSoundPrimaryBuffer = NULL;
  1431. }
  1432. return result;
  1433. }
  1434. static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
  1435. unsigned long *pollingPeriodFrames,
  1436. int isFullDuplex,
  1437. unsigned long suggestedInputLatencyFrames,
  1438. unsigned long suggestedOutputLatencyFrames,
  1439. double sampleRate, unsigned long userFramesPerBuffer )
  1440. {
  1441. unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
  1442. unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
  1443. unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
  1444. if( userFramesPerBuffer == paFramesPerBufferUnspecified )
  1445. {
  1446. unsigned long targetBufferingLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
  1447. *pollingPeriodFrames = targetBufferingLatencyFrames / 4;
  1448. if( *pollingPeriodFrames < minimumPollingPeriodFrames )
  1449. {
  1450. *pollingPeriodFrames = minimumPollingPeriodFrames;
  1451. }
  1452. else if( *pollingPeriodFrames > maximumPollingPeriodFrames )
  1453. {
  1454. *pollingPeriodFrames = maximumPollingPeriodFrames;
  1455. }
  1456. *hostBufferSizeFrames = *pollingPeriodFrames
  1457. + max( *pollingPeriodFrames + pollingJitterFrames, targetBufferingLatencyFrames);
  1458. }
  1459. else
  1460. {
  1461. unsigned long targetBufferingLatencyFrames = suggestedInputLatencyFrames;
  1462. if( isFullDuplex )
  1463. {
  1464. /* In full duplex streams we know that the buffer adapter adds userFramesPerBuffer
  1465. extra fixed latency. so we subtract it here as a fixed latency before computing
  1466. the buffer size. being careful not to produce an unrepresentable negative result.
  1467. Note: this only works as expected if output latency is greater than input latency.
  1468. Otherwise we use input latency anyway since we do max(in,out).
  1469. */
  1470. if( userFramesPerBuffer < suggestedOutputLatencyFrames )
  1471. {
  1472. unsigned long adjustedSuggestedOutputLatencyFrames =
  1473. suggestedOutputLatencyFrames - userFramesPerBuffer;
  1474. /* maximum of input and adjusted output suggested latency */
  1475. if( adjustedSuggestedOutputLatencyFrames > targetBufferingLatencyFrames )
  1476. targetBufferingLatencyFrames = adjustedSuggestedOutputLatencyFrames;
  1477. }
  1478. }
  1479. else
  1480. {
  1481. /* maximum of input and output suggested latency */
  1482. if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
  1483. targetBufferingLatencyFrames = suggestedOutputLatencyFrames;
  1484. }
  1485. *hostBufferSizeFrames = userFramesPerBuffer
  1486. + max( userFramesPerBuffer + pollingJitterFrames, targetBufferingLatencyFrames);
  1487. *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), targetBufferingLatencyFrames / 16 );
  1488. if( *pollingPeriodFrames > maximumPollingPeriodFrames )
  1489. {
  1490. *pollingPeriodFrames = maximumPollingPeriodFrames;
  1491. }
  1492. }
  1493. }
  1494. static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
  1495. unsigned long *pollingPeriodFrames,
  1496. double sampleRate, unsigned long userFramesPerBuffer )
  1497. {
  1498. unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
  1499. unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
  1500. unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
  1501. *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 );
  1502. if( *pollingPeriodFrames > maximumPollingPeriodFrames )
  1503. {
  1504. *pollingPeriodFrames = maximumPollingPeriodFrames;
  1505. }
  1506. }
  1507. static void SetStreamInfoLatencies( PaWinDsStream *stream,
  1508. unsigned long userFramesPerBuffer,
  1509. unsigned long pollingPeriodFrames,
  1510. double sampleRate )
  1511. {
  1512. /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames,
  1513. and the configuration of the buffer processor */
  1514. unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
  1515. ? pollingPeriodFrames
  1516. : userFramesPerBuffer;
  1517. if( stream->bufferProcessor.inputChannelCount > 0 )
  1518. {
  1519. /* stream info input latency is the minimum buffering latency
  1520. (unlike suggested and default which are *maximums*) */
  1521. stream->streamRepresentation.streamInfo.inputLatency =
  1522. (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
  1523. + effectiveFramesPerBuffer) / sampleRate;
  1524. }
  1525. else
  1526. {
  1527. stream->streamRepresentation.streamInfo.inputLatency = 0;
  1528. }
  1529. if( stream->bufferProcessor.outputChannelCount > 0 )
  1530. {
  1531. stream->streamRepresentation.streamInfo.outputLatency =
  1532. (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
  1533. + (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate;
  1534. }
  1535. else
  1536. {
  1537. stream->streamRepresentation.streamInfo.outputLatency = 0;
  1538. }
  1539. }
  1540. /***********************************************************************************/
  1541. /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
  1542. static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
  1543. PaStream** s,
  1544. const PaStreamParameters *inputParameters,
  1545. const PaStreamParameters *outputParameters,
  1546. double sampleRate,
  1547. unsigned long framesPerBuffer,
  1548. PaStreamFlags streamFlags,
  1549. PaStreamCallback *streamCallback,
  1550. void *userData )
  1551. {
  1552. PaError result = paNoError;
  1553. PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
  1554. PaWinDsStream *stream = 0;
  1555. int bufferProcessorIsInitialized = 0;
  1556. int streamRepresentationIsInitialized = 0;
  1557. PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
  1558. PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
  1559. int inputChannelCount, outputChannelCount;
  1560. PaSampleFormat inputSampleFormat, outputSampleFormat;
  1561. PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
  1562. int userRequestedHostInputBufferSizeFrames = 0;
  1563. int userRequestedHostOutputBufferSizeFrames = 0;
  1564. unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
  1565. PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
  1566. PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
  1567. unsigned long pollingPeriodFrames = 0;
  1568. if( inputParameters )
  1569. {
  1570. inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
  1571. inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
  1572. inputChannelCount = inputParameters->channelCount;
  1573. inputSampleFormat = inputParameters->sampleFormat;
  1574. suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
  1575. /* IDEA: the following 3 checks could be performed by default by pa_front
  1576. unless some flag indicated otherwise */
  1577. /* unless alternate device specification is supported, reject the use of
  1578. paUseHostApiSpecificDeviceSpecification */
  1579. if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
  1580. return paInvalidDevice;
  1581. /* check that input device can support inputChannelCount */
  1582. if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
  1583. && inputChannelCount > inputDeviceInfo->maxInputChannels )
  1584. return paInvalidChannelCount;
  1585. /* validate hostApiSpecificStreamInfo */
  1586. inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
  1587. result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
  1588. if( result != paNoError ) return result;
  1589. if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
  1590. userRequestedHostInputBufferSizeFrames = inputStreamInfo->framesPerBuffer;
  1591. if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
  1592. inputChannelMask = inputStreamInfo->channelMask;
  1593. else
  1594. inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
  1595. }
  1596. else
  1597. {
  1598. inputChannelCount = 0;
  1599. inputSampleFormat = 0;
  1600. suggestedInputLatencyFrames = 0;
  1601. }
  1602. if( outputParameters )
  1603. {
  1604. outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
  1605. outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
  1606. outputChannelCount = outputParameters->channelCount;
  1607. outputSampleFormat = outputParameters->sampleFormat;
  1608. suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
  1609. /* unless alternate device specification is supported, reject the use of
  1610. paUseHostApiSpecificDeviceSpecification */
  1611. if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
  1612. return paInvalidDevice;
  1613. /* check that output device can support outputChannelCount */
  1614. if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
  1615. && outputChannelCount > outputDeviceInfo->maxOutputChannels )
  1616. return paInvalidChannelCount;
  1617. /* validate hostApiSpecificStreamInfo */
  1618. outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
  1619. result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
  1620. if( result != paNoError ) return result;
  1621. if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
  1622. userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;
  1623. if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
  1624. outputChannelMask = outputStreamInfo->channelMask;
  1625. else
  1626. outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
  1627. }
  1628. else
  1629. {
  1630. outputChannelCount = 0;
  1631. outputSampleFormat = 0;
  1632. suggestedOutputLatencyFrames = 0;
  1633. }
  1634. /*
  1635. If low level host buffer size is specified for both input and output
  1636. the current code requires the sizes to match.
  1637. */
  1638. if( (userRequestedHostInputBufferSizeFrames > 0 && userRequestedHostOutputBufferSizeFrames > 0)
  1639. && userRequestedHostInputBufferSizeFrames != userRequestedHostOutputBufferSizeFrames )
  1640. return paIncompatibleHostApiSpecificStreamInfo;
  1641. /*
  1642. IMPLEMENT ME:
  1643. ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
  1644. - check that input device can support inputSampleFormat, or that
  1645. we have the capability to convert from outputSampleFormat to
  1646. a native format
  1647. - check that output device can support outputSampleFormat, or that
  1648. we have the capability to convert from outputSampleFormat to
  1649. a native format
  1650. - if a full duplex stream is requested, check that the combination
  1651. of input and output parameters is supported
  1652. - check that the device supports sampleRate
  1653. - alter sampleRate to a close allowable rate if possible / necessary
  1654. - validate suggestedInputLatency and suggestedOutputLatency parameters,
  1655. use default values where necessary
  1656. */
  1657. /* validate platform specific flags */
  1658. if( (streamFlags & paPlatformSpecificFlags) != 0 )
  1659. return paInvalidFlag; /* unexpected platform specific flag */
  1660. stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
  1661. if( !stream )
  1662. {
  1663. result = paInsufficientMemory;
  1664. goto error;
  1665. }
  1666. memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
  1667. if( streamCallback )
  1668. {
  1669. PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
  1670. &winDsHostApi->callbackStreamInterface, streamCallback, userData );
  1671. }
  1672. else
  1673. {
  1674. PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
  1675. &winDsHostApi->blockingStreamInterface, streamCallback, userData );
  1676. }
  1677. streamRepresentationIsInitialized = 1;
  1678. stream->streamFlags = streamFlags;
  1679. PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
  1680. if( inputParameters )
  1681. {
  1682. /* IMPLEMENT ME - establish which host formats are available */
  1683. PaSampleFormat nativeInputFormats = paInt16;
  1684. /* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
  1685. hostInputSampleFormat =
  1686. PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
  1687. }
  1688. else
  1689. {
  1690. hostInputSampleFormat = 0;
  1691. }
  1692. if( outputParameters )
  1693. {
  1694. /* IMPLEMENT ME - establish which host formats are available */
  1695. PaSampleFormat nativeOutputFormats = paInt16;
  1696. /* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
  1697. hostOutputSampleFormat =
  1698. PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
  1699. }
  1700. else
  1701. {
  1702. hostOutputSampleFormat = 0;
  1703. }
  1704. result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
  1705. inputChannelCount, inputSampleFormat, hostInputSampleFormat,
  1706. outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
  1707. sampleRate, streamFlags, framesPerBuffer,
  1708. 0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
  1709. /* This next mode is required because DS can split the host buffer when it wraps around. */
  1710. paUtilVariableHostBufferSizePartialUsageAllowed,
  1711. streamCallback, userData );
  1712. if( result != paNoError )
  1713. goto error;
  1714. bufferProcessorIsInitialized = 1;
  1715. /* DirectSound specific initialization */
  1716. {
  1717. HRESULT hr;
  1718. unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
  1719. stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
  1720. if( stream->processingCompleted == NULL )
  1721. {
  1722. result = paInsufficientMemory;
  1723. goto error;
  1724. }
  1725. #ifdef PA_WIN_DS_USE_WMME_TIMER
  1726. stream->timerID = 0;
  1727. #endif
  1728. #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
  1729. stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL );
  1730. if( stream->waitableTimer == NULL )
  1731. {
  1732. result = paUnanticipatedHostError;
  1733. PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
  1734. goto error;
  1735. }
  1736. #endif
  1737. #ifndef PA_WIN_DS_USE_WMME_TIMER
  1738. stream->processingThreadCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
  1739. if( stream->processingThreadCompleted == NULL )
  1740. {
  1741. result = paUnanticipatedHostError;
  1742. PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
  1743. goto error;
  1744. }
  1745. #endif
  1746. /* set up i/o parameters */
  1747. if( userRequestedHostInputBufferSizeFrames > 0 || userRequestedHostOutputBufferSizeFrames > 0 )
  1748. {
  1749. /* use low level parameters */
  1750. /* since we use the same host buffer size for input and output
  1751. we choose the highest user specified value.
  1752. */
  1753. stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );
  1754. CalculatePollingPeriodFrames(
  1755. stream->hostBufferSizeFrames, &pollingPeriodFrames,
  1756. sampleRate, framesPerBuffer );
  1757. }
  1758. else
  1759. {
  1760. CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames,
  1761. /* isFullDuplex = */ (inputParameters && outputParameters),
  1762. suggestedInputLatencyFrames,
  1763. suggestedOutputLatencyFrames,
  1764. sampleRate, framesPerBuffer );
  1765. }
  1766. stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
  1767. DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
  1768. stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));
  1769. /* ------------------ OUTPUT */
  1770. if( outputParameters )
  1771. {
  1772. LARGE_INTEGER counterFrequency;
  1773. /*
  1774. PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
  1775. DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
  1776. */
  1777. int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
  1778. stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
  1779. stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes;
  1780. if( stream->outputBufferSizeBytes < DSBSIZE_MIN )
  1781. {
  1782. result = paBufferTooSmall;
  1783. goto error;
  1784. }
  1785. else if( stream->outputBufferSizeBytes > DSBSIZE_MAX )
  1786. {
  1787. result = paBufferTooBig;
  1788. goto error;
  1789. }
  1790. /* Calculate value used in latency calculation to avoid real-time divides. */
  1791. stream->secondsPerHostByte = 1.0 /
  1792. (stream->bufferProcessor.bytesPerHostOutputSample *
  1793. outputChannelCount * sampleRate);
  1794. stream->outputIsRunning = FALSE;
  1795. stream->outputUnderflowCount = 0;
  1796. /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
  1797. if( QueryPerformanceFrequency( &counterFrequency ) )
  1798. {
  1799. stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate;
  1800. }
  1801. else
  1802. {
  1803. stream->perfCounterTicksPerBuffer.QuadPart = 0;
  1804. }
  1805. }
  1806. /* ------------------ INPUT */
  1807. if( inputParameters )
  1808. {
  1809. /*
  1810. PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
  1811. DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
  1812. */
  1813. int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
  1814. stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
  1815. stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes;
  1816. if( stream->inputBufferSizeBytes < DSBSIZE_MIN )
  1817. {
  1818. result = paBufferTooSmall;
  1819. goto error;
  1820. }
  1821. else if( stream->inputBufferSizeBytes > DSBSIZE_MAX )
  1822. {
  1823. result = paBufferTooBig;
  1824. goto error;
  1825. }
  1826. }
  1827. /* open/create the DirectSound buffers */
  1828. /* interface ptrs should be zeroed when stream is zeroed. */
  1829. assert( stream->pDirectSoundCapture == NULL );
  1830. assert( stream->pDirectSoundInputBuffer == NULL );
  1831. assert( stream->pDirectSound == NULL );
  1832. assert( stream->pDirectSoundPrimaryBuffer == NULL );
  1833. assert( stream->pDirectSoundOutputBuffer == NULL );
  1834. if( inputParameters && outputParameters )
  1835. {
  1836. #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
  1837. /* try to use the full-duplex DX8 API to create the buffers.
  1838. if that fails we fall back to the half-duplex API below */
  1839. hr = InitFullDuplexInputOutputBuffers( stream,
  1840. (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
  1841. hostInputSampleFormat,
  1842. (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
  1843. inputChannelMask,
  1844. (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
  1845. hostOutputSampleFormat,
  1846. (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
  1847. outputChannelMask,
  1848. integerSampleRate
  1849. );
  1850. DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
  1851. /* ignore any error returned by InitFullDuplexInputOutputBuffers.
  1852. we retry opening the buffers below */
  1853. #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
  1854. }
  1855. /* create half duplex buffers. also used for full-duplex streams which didn't
  1856. succeed when using the full duplex API. that could happen because
  1857. DX8 or greater isnt installed, the i/o devices aren't the same
  1858. physical device. etc.
  1859. */
  1860. if( outputParameters && !stream->pDirectSoundOutputBuffer )
  1861. {
  1862. hr = InitOutputBuffer( stream,
  1863. (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
  1864. hostOutputSampleFormat,
  1865. integerSampleRate,
  1866. (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
  1867. outputChannelMask );
  1868. DBUG(("InitOutputBuffer() returns %x\n", hr));
  1869. if( hr != DS_OK )
  1870. {
  1871. result = paUnanticipatedHostError;
  1872. PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
  1873. goto error;
  1874. }
  1875. }
  1876. if( inputParameters && !stream->pDirectSoundInputBuffer )
  1877. {
  1878. hr = InitInputBuffer( stream,
  1879. (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
  1880. hostInputSampleFormat,
  1881. integerSampleRate,
  1882. (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
  1883. inputChannelMask );
  1884. DBUG(("InitInputBuffer() returns %x\n", hr));
  1885. if( hr != DS_OK )
  1886. {
  1887. ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
  1888. result = paUnanticipatedHostError;
  1889. PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
  1890. goto error;
  1891. }
  1892. }
  1893. }
  1894. SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate );
  1895. stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
  1896. *s = (PaStream*)stream;
  1897. return result;
  1898. error:
  1899. if( stream )
  1900. {
  1901. if( stream->processingCompleted != NULL )
  1902. CloseHandle( stream->processingCompleted );
  1903. #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
  1904. if( stream->waitableTimer != NULL )
  1905. CloseHandle( stream->waitableTimer );
  1906. #endif
  1907. #ifndef PA_WIN_DS_USE_WMME_TIMER
  1908. if( stream->processingThreadCompleted != NULL )
  1909. CloseHandle( stream->processingThreadCompleted );
  1910. #endif
  1911. if( stream->pDirectSoundOutputBuffer )
  1912. {
  1913. IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
  1914. IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
  1915. stream->pDirectSoundOutputBuffer = NULL;
  1916. }
  1917. if( stream->pDirectSoundPrimaryBuffer )
  1918. {
  1919. IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
  1920. stream->pDirectSoundPrimaryBuffer = NULL;
  1921. }
  1922. if( stream->pDirectSoundInputBuffer )
  1923. {
  1924. IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
  1925. IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
  1926. stream->pDirectSoundInputBuffer = NULL;
  1927. }
  1928. if( stream->pDirectSoundCapture )
  1929. {
  1930. IDirectSoundCapture_Release( stream->pDirectSoundCapture );
  1931. stream->pDirectSoundCapture = NULL;
  1932. }
  1933. if( stream->pDirectSound )
  1934. {
  1935. IDirectSound_Release( stream->pDirectSound );
  1936. stream->pDirectSound = NULL;
  1937. }
  1938. #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
  1939. if( stream->pDirectSoundFullDuplex8 )
  1940. {
  1941. IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
  1942. stream->pDirectSoundFullDuplex8 = NULL;
  1943. }
  1944. #endif
  1945. if( bufferProcessorIsInitialized )
  1946. PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
  1947. if( streamRepresentationIsInitialized )
  1948. PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
  1949. PaUtil_FreeMemory( stream );
  1950. }
  1951. return result;
  1952. }
  1953. /************************************************************************************
  1954. * Determine how much space can be safely written to in DS buffer.
  1955. * Detect underflows and overflows.
  1956. * Does not allow writing into safety gap maintained by DirectSound.
  1957. */
  1958. static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
  1959. {
  1960. HRESULT hr;
  1961. DWORD playCursor;
  1962. DWORD writeCursor;
  1963. long numBytesEmpty;
  1964. long playWriteGap;
  1965. // Query to see how much room is in buffer.
  1966. hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
  1967. &playCursor, &writeCursor );
  1968. if( hr != DS_OK )
  1969. {
  1970. return hr;
  1971. }
  1972. // Determine size of gap between playIndex and WriteIndex that we cannot write into.
  1973. playWriteGap = writeCursor - playCursor;
  1974. if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
  1975. /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
  1976. /* Attempt to detect playCursor wrap-around and correct it. */
  1977. if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
  1978. {
  1979. /* How much time has elapsed since last check. */
  1980. LARGE_INTEGER currentTime;
  1981. LARGE_INTEGER elapsedTime;
  1982. long bytesPlayed;
  1983. long bytesExpected;
  1984. long buffersWrapped;
  1985. QueryPerformanceCounter( &currentTime );
  1986. elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
  1987. stream->previousPlayTime = currentTime;
  1988. /* How many bytes does DirectSound say have been played. */
  1989. bytesPlayed = playCursor - stream->previousPlayCursor;
  1990. if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
  1991. stream->previousPlayCursor = playCursor;
  1992. /* Calculate how many bytes we would have expected to been played by now. */
  1993. bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
  1994. buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
  1995. if( buffersWrapped > 0 )
  1996. {
  1997. playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
  1998. bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
  1999. }
  2000. }
  2001. numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
  2002. if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
  2003. /* Have we underflowed? */
  2004. if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
  2005. {
  2006. if( stream->outputIsRunning )
  2007. {
  2008. stream->outputUnderflowCount += 1;
  2009. }
  2010. /*
  2011. From MSDN:
  2012. The write cursor indicates the position at which it is safe
  2013. to write new data to the buffer. The write cursor always leads the
  2014. play cursor, typically by about 15 milliseconds' worth of audio
  2015. data.
  2016. It is always safe to change data that is behind the position
  2017. indicated by the lpdwCurrentPlayCursor parameter.
  2018. */
  2019. stream->outputBufferWriteOffsetBytes = writeCursor;
  2020. numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
  2021. }
  2022. *bytesEmpty = numBytesEmpty;
  2023. return hr;
  2024. }
  2025. /***********************************************************************************/
  2026. static int TimeSlice( PaWinDsStream *stream )
  2027. {
  2028. long numFrames = 0;
  2029. long bytesEmpty = 0;
  2030. long bytesFilled = 0;
  2031. long bytesToXfer = 0;
  2032. long framesToXfer = 0; /* the number of frames we'll process this tick */
  2033. long numInFramesReady = 0;
  2034. long numOutFramesReady = 0;
  2035. long bytesProcessed;
  2036. HRESULT hresult;
  2037. double outputLatency = 0;
  2038. double inputLatency = 0;
  2039. PaStreamCallbackTimeInfo timeInfo = {0,0,0};
  2040. /* Input */
  2041. LPBYTE lpInBuf1 = NULL;
  2042. LPBYTE lpInBuf2 = NULL;
  2043. DWORD dwInSize1 = 0;
  2044. DWORD dwInSize2 = 0;
  2045. /* Output */
  2046. LPBYTE lpOutBuf1 = NULL;
  2047. LPBYTE lpOutBuf2 = NULL;
  2048. DWORD dwOutSize1 = 0;
  2049. DWORD dwOutSize2 = 0;
  2050. /* How much input data is available? */
  2051. if( stream->bufferProcessor.inputChannelCount > 0 )
  2052. {
  2053. HRESULT hr;
  2054. DWORD capturePos;
  2055. DWORD readPos;
  2056. long filled = 0;
  2057. // Query to see how much data is in buffer.
  2058. // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
  2059. // so let's pass a pointer just to be safe.
  2060. hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
  2061. if( hr == DS_OK )
  2062. {
  2063. filled = readPos - stream->readOffset;
  2064. if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset
  2065. bytesFilled = filled;
  2066. inputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
  2067. }
  2068. // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
  2069. framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
  2070. /** @todo Check for overflow */
  2071. }
  2072. /* How much output room is available? */
  2073. if( stream->bufferProcessor.outputChannelCount > 0 )
  2074. {
  2075. UINT previousUnderflowCount = stream->outputUnderflowCount;
  2076. QueryOutputSpace( stream, &bytesEmpty );
  2077. framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
  2078. /* Check for underflow */
  2079. /* FIXME QueryOutputSpace should not adjust underflow count as a side effect.
  2080. A query function should be a const operator on the stream and return a flag on underflow. */
  2081. if( stream->outputUnderflowCount != previousUnderflowCount )
  2082. stream->callbackFlags |= paOutputUnderflow;
  2083. /* We are about to compute audio into the first byte of empty space in the output buffer.
  2084. This audio will reach the DAC after all of the current (non-empty) audio
  2085. in the buffer has played. Therefore the output time is the current time
  2086. plus the time it takes to play the non-empty bytes in the buffer,
  2087. computed here:
  2088. */
  2089. outputLatency = ((double)(stream->outputBufferSizeBytes - bytesEmpty)) * stream->secondsPerHostByte;
  2090. }
  2091. /* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */
  2092. if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 )
  2093. {
  2094. framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
  2095. }
  2096. if( framesToXfer > 0 )
  2097. {
  2098. PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
  2099. /* The outputBufferDacTime parameter should indicates the time at which
  2100. the first sample of the output buffer is heard at the DACs. */
  2101. timeInfo.currentTime = PaUtil_GetTime();
  2102. PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
  2103. stream->callbackFlags = 0;
  2104. /* Input */
  2105. if( stream->bufferProcessor.inputChannelCount > 0 )
  2106. {
  2107. timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency;
  2108. bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
  2109. hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
  2110. stream->readOffset, bytesToXfer,
  2111. (void **) &lpInBuf1, &dwInSize1,
  2112. (void **) &lpInBuf2, &dwInSize2, 0);
  2113. if (hresult != DS_OK)
  2114. {
  2115. ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
  2116. /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
  2117. PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
  2118. stream->callbackResult = paComplete;
  2119. goto error2;
  2120. }
  2121. numFrames = dwInSize1 / stream->inputFrameSizeBytes;
  2122. PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
  2123. PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
  2124. /* Is input split into two regions. */
  2125. if( dwInSize2 > 0 )
  2126. {
  2127. numFrames = dwInSize2 / stream->inputFrameSizeBytes;
  2128. PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
  2129. PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
  2130. }
  2131. }
  2132. /* Output */
  2133. if( stream->bufferProcessor.outputChannelCount > 0 )
  2134. {
  2135. /*
  2136. We don't currently add outputLatency here because it appears to produce worse
  2137. results than not adding it. Need to do more testing to verify this.
  2138. */
  2139. /* timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; */
  2140. timeInfo.outputBufferDacTime = timeInfo.currentTime;
  2141. bytesToXfer = framesToXfer * stream->outputFrameSizeBytes;
  2142. hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
  2143. stream->outputBufferWriteOffsetBytes, bytesToXfer,
  2144. (void **) &lpOutBuf1, &dwOutSize1,
  2145. (void **) &lpOutBuf2, &dwOutSize2, 0);
  2146. if (hresult != DS_OK)
  2147. {
  2148. ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
  2149. /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
  2150. PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
  2151. stream->callbackResult = paComplete;
  2152. goto error1;
  2153. }
  2154. numFrames = dwOutSize1 / stream->outputFrameSizeBytes;
  2155. PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
  2156. PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
  2157. /* Is output split into two regions. */
  2158. if( dwOutSize2 > 0 )
  2159. {
  2160. numFrames = dwOutSize2 / stream->outputFrameSizeBytes;
  2161. PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
  2162. PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
  2163. }
  2164. }
  2165. numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
  2166. stream->framesWritten += numFrames;
  2167. if( stream->bufferProcessor.outputChannelCount > 0 )
  2168. {
  2169. /* FIXME: an underflow could happen here */
  2170. /* Update our buffer offset and unlock sound buffer */
  2171. bytesProcessed = numFrames * stream->outputFrameSizeBytes;
  2172. stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
  2173. IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
  2174. }
  2175. error1:
  2176. if( stream->bufferProcessor.inputChannelCount > 0 )
  2177. {
  2178. /* FIXME: an overflow could happen here */
  2179. /* Update our buffer offset and unlock sound buffer */
  2180. bytesProcessed = numFrames * stream->inputFrameSizeBytes;
  2181. stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes;
  2182. IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
  2183. }
  2184. error2:
  2185. PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
  2186. }
  2187. if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
  2188. {
  2189. /* don't return completed until the buffer processor has been drained */
  2190. return paContinue;
  2191. }
  2192. else
  2193. {
  2194. return stream->callbackResult;
  2195. }
  2196. }
  2197. /*******************************************************************/
  2198. static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
  2199. {
  2200. HRESULT hr;
  2201. LPBYTE lpbuf1 = NULL;
  2202. LPBYTE lpbuf2 = NULL;
  2203. DWORD dwsize1 = 0;
  2204. DWORD dwsize2 = 0;
  2205. long bytesEmpty;
  2206. hr = QueryOutputSpace( stream, &bytesEmpty );
  2207. if (hr != DS_OK) return hr;
  2208. if( bytesEmpty == 0 ) return DS_OK;
  2209. // Lock free space in the DS
  2210. hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
  2211. bytesEmpty, (void **) &lpbuf1, &dwsize1,
  2212. (void **) &lpbuf2, &dwsize2, 0);
  2213. if (hr == DS_OK)
  2214. {
  2215. // Copy the buffer into the DS
  2216. ZeroMemory(lpbuf1, dwsize1);
  2217. if(lpbuf2 != NULL)
  2218. {
  2219. ZeroMemory(lpbuf2, dwsize2);
  2220. }
  2221. // Update our buffer offset and unlock sound buffer
  2222. stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
  2223. IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
  2224. stream->finalZeroBytesWritten += dwsize1 + dwsize2;
  2225. }
  2226. return hr;
  2227. }
  2228. static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
  2229. {
  2230. PaWinDsStream *stream;
  2231. int isFinished = 0;
  2232. /* suppress unused variable warnings */
  2233. (void) uID;
  2234. (void) uMsg;
  2235. (void) dw1;
  2236. (void) dw2;
  2237. stream = (PaWinDsStream *) dwUser;
  2238. if( stream == NULL ) return;
  2239. if( stream->isActive )
  2240. {
  2241. if( stream->abortProcessing )
  2242. {
  2243. isFinished = 1;
  2244. }
  2245. else if( stream->stopProcessing )
  2246. {
  2247. if( stream->bufferProcessor.outputChannelCount > 0 )
  2248. {
  2249. ZeroAvailableOutputSpace( stream );
  2250. if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
  2251. {
  2252. /* once we've flushed the whole output buffer with zeros we know all data has been played */
  2253. isFinished = 1;
  2254. }
  2255. }
  2256. else
  2257. {
  2258. isFinished = 1;
  2259. }
  2260. }
  2261. else
  2262. {
  2263. int callbackResult = TimeSlice( stream );
  2264. if( callbackResult != paContinue )
  2265. {
  2266. /* FIXME implement handling of paComplete and paAbort if possible
  2267. At the moment this should behave as if paComplete was called and
  2268. flush the buffer.
  2269. */
  2270. stream->stopProcessing = 1;
  2271. }
  2272. }
  2273. if( isFinished )
  2274. {
  2275. if( stream->streamRepresentation.streamFinishedCallback != 0 )
  2276. stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
  2277. stream->isActive = 0; /* don't set this until the stream really is inactive */
  2278. SetEvent( stream->processingCompleted );
  2279. }
  2280. }
  2281. }
  2282. #ifndef PA_WIN_DS_USE_WMME_TIMER
  2283. #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
  2284. static void CALLBACK WaitableTimerAPCProc(
  2285. LPVOID lpArg, // Data value
  2286. DWORD dwTimerLowValue, // Timer low value
  2287. DWORD dwTimerHighValue ) // Timer high value
  2288. {
  2289. (void)dwTimerLowValue;
  2290. (void)dwTimerHighValue;
  2291. TimerCallback( 0, 0, (DWORD_PTR)lpArg, 0, 0 );
  2292. }
  2293. #endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
  2294. PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
  2295. {
  2296. PaWinDsStream *stream = (PaWinDsStream *)pArg;
  2297. LARGE_INTEGER dueTime;
  2298. int timerPeriodMs;
  2299. timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
  2300. if( timerPeriodMs < 1 )
  2301. timerPeriodMs = 1;
  2302. #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
  2303. assert( stream->waitableTimer != NULL );
  2304. /* invoke first timeout immediately */
  2305. dueTime.LowPart = timerPeriodMs * 1000 * 10;
  2306. dueTime.HighPart = 0;
  2307. /* tick using waitable timer */
  2308. if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
  2309. {
  2310. DWORD wfsoResult = 0;
  2311. do
  2312. {
  2313. /* wait for processingCompleted to be signaled or our timer APC to be called */
  2314. wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE );
  2315. }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
  2316. }
  2317. CancelWaitableTimer( stream->waitableTimer );
  2318. #else
  2319. /* tick using WaitForSingleObject timout */
  2320. while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT )
  2321. {
  2322. TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 );
  2323. }
  2324. #endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
  2325. SetEvent( stream->processingThreadCompleted );
  2326. return 0;
  2327. }
  2328. #endif /* !PA_WIN_DS_USE_WMME_TIMER */
  2329. /***********************************************************************************
  2330. When CloseStream() is called, the multi-api layer ensures that
  2331. the stream has already been stopped or aborted.
  2332. */
  2333. static PaError CloseStream( PaStream* s )
  2334. {
  2335. PaError result = paNoError;
  2336. PaWinDsStream *stream = (PaWinDsStream*)s;
  2337. CloseHandle( stream->processingCompleted );
  2338. #ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
  2339. if( stream->waitableTimer != NULL )
  2340. CloseHandle( stream->waitableTimer );
  2341. #endif
  2342. #ifndef PA_WIN_DS_USE_WMME_TIMER
  2343. CloseHandle( stream->processingThreadCompleted );
  2344. #endif
  2345. // Cleanup the sound buffers
  2346. if( stream->pDirectSoundOutputBuffer )
  2347. {
  2348. IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
  2349. IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
  2350. stream->pDirectSoundOutputBuffer = NULL;
  2351. }
  2352. if( stream->pDirectSoundPrimaryBuffer )
  2353. {
  2354. IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
  2355. stream->pDirectSoundPrimaryBuffer = NULL;
  2356. }
  2357. if( stream->pDirectSoundInputBuffer )
  2358. {
  2359. IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
  2360. IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
  2361. stream->pDirectSoundInputBuffer = NULL;
  2362. }
  2363. if( stream->pDirectSoundCapture )
  2364. {
  2365. IDirectSoundCapture_Release( stream->pDirectSoundCapture );
  2366. stream->pDirectSoundCapture = NULL;
  2367. }
  2368. if( stream->pDirectSound )
  2369. {
  2370. IDirectSound_Release( stream->pDirectSound );
  2371. stream->pDirectSound = NULL;
  2372. }
  2373. #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
  2374. if( stream->pDirectSoundFullDuplex8 )
  2375. {
  2376. IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
  2377. stream->pDirectSoundFullDuplex8 = NULL;
  2378. }
  2379. #endif
  2380. PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
  2381. PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
  2382. PaUtil_FreeMemory( stream );
  2383. return result;
  2384. }
  2385. /***********************************************************************************/
  2386. static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
  2387. {
  2388. PaError result = paNoError;
  2389. unsigned char* pDSBuffData;
  2390. DWORD dwDataLen;
  2391. HRESULT hr;
  2392. hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
  2393. DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
  2394. if( hr != DS_OK )
  2395. return hr;
  2396. // Lock the DS buffer
  2397. if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
  2398. &dwDataLen, NULL, 0, 0)) != DS_OK )
  2399. return hr;
  2400. // Zero the DS buffer
  2401. ZeroMemory(pDSBuffData, dwDataLen);
  2402. // Unlock the DS buffer
  2403. if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
  2404. return hr;
  2405. // Let DSound set the starting write position because if we set it to zero, it looks like the
  2406. // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
  2407. if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
  2408. &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
  2409. return hr;
  2410. /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
  2411. return DS_OK;
  2412. }
  2413. static PaError StartStream( PaStream *s )
  2414. {
  2415. PaError result = paNoError;
  2416. PaWinDsStream *stream = (PaWinDsStream*)s;
  2417. HRESULT hr;
  2418. stream->callbackResult = paContinue;
  2419. PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
  2420. ResetEvent( stream->processingCompleted );
  2421. #ifndef PA_WIN_DS_USE_WMME_TIMER
  2422. ResetEvent( stream->processingThreadCompleted );
  2423. #endif
  2424. if( stream->bufferProcessor.inputChannelCount > 0 )
  2425. {
  2426. // Start the buffer capture
  2427. if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
  2428. {
  2429. hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
  2430. }
  2431. DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
  2432. if( hr != DS_OK )
  2433. {
  2434. result = paUnanticipatedHostError;
  2435. PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
  2436. goto error;
  2437. }
  2438. }
  2439. stream->framesWritten = 0;
  2440. stream->callbackFlags = 0;
  2441. stream->abortProcessing = 0;
  2442. stream->stopProcessing = 0;
  2443. if( stream->bufferProcessor.outputChannelCount > 0 )
  2444. {
  2445. QueryPerformanceCounter( &stream->previousPlayTime );
  2446. stream->finalZeroBytesWritten = 0;
  2447. hr = ClearOutputBuffer( stream );
  2448. if( hr != DS_OK )
  2449. {
  2450. result = paUnanticipatedHostError;
  2451. PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
  2452. goto error;
  2453. }
  2454. if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
  2455. {
  2456. stream->callbackFlags = paPrimingOutput;
  2457. TimeSlice( stream );
  2458. /* we ignore the return value from TimeSlice here and start the stream as usual.
  2459. The first timer callback will detect if the callback has completed. */
  2460. stream->callbackFlags = 0;
  2461. }
  2462. // Start the buffer playback in a loop.
  2463. if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
  2464. {
  2465. hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
  2466. DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
  2467. if( hr != DS_OK )
  2468. {
  2469. result = paUnanticipatedHostError;
  2470. PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
  2471. goto error;
  2472. }
  2473. stream->outputIsRunning = TRUE;
  2474. }
  2475. }
  2476. if( stream->streamRepresentation.streamCallback )
  2477. {
  2478. TIMECAPS timecaps;
  2479. int timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
  2480. if( timerPeriodMs < 1 )
  2481. timerPeriodMs = 1;
  2482. /* set windows scheduler granularity only as fine as needed, no finer */
  2483. /* Although this is not fully documented by MS, it appears that
  2484. timeBeginPeriod() affects the scheduling granulatity of all timers
  2485. including Waitable Timer Objects. So we always call timeBeginPeriod, whether
  2486. we're using an MM timer callback via timeSetEvent or not.
  2487. */
  2488. assert( stream->systemTimerResolutionPeriodMs == 0 );
  2489. if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) ) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 )
  2490. {
  2491. /* aim for resolution 4 times higher than polling rate */
  2492. stream->systemTimerResolutionPeriodMs = (UINT)((stream->pollingPeriodSeconds * MSECS_PER_SECOND) * .25);
  2493. if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
  2494. stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
  2495. if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax )
  2496. stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax;
  2497. if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR )
  2498. stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */
  2499. }
  2500. #ifdef PA_WIN_DS_USE_WMME_TIMER
  2501. /* Create timer that will wake us up so we can fill the DSound buffer. */
  2502. /* We have deprecated timeSetEvent because all MM timer callbacks
  2503. are serialised onto a single thread. Which creates problems with multiple
  2504. PA streams, or when also using timers for other time critical tasks
  2505. */
  2506. stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
  2507. (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
  2508. if( stream->timerID == 0 )
  2509. {
  2510. stream->isActive = 0;
  2511. result = paUnanticipatedHostError;
  2512. PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
  2513. goto error;
  2514. }
  2515. #else
  2516. /* Create processing thread which calls TimerCallback */
  2517. stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
  2518. if( !stream->processingThread )
  2519. {
  2520. result = paUnanticipatedHostError;
  2521. PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
  2522. goto error;
  2523. }
  2524. if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) )
  2525. {
  2526. result = paUnanticipatedHostError;
  2527. PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
  2528. goto error;
  2529. }
  2530. #endif
  2531. }
  2532. stream->isActive = 1;
  2533. stream->isStarted = 1;
  2534. assert( result == paNoError );
  2535. return result;
  2536. error:
  2537. if( stream->pDirectSoundOutputBuffer != NULL && stream->outputIsRunning )
  2538. IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
  2539. stream->outputIsRunning = FALSE;
  2540. #ifndef PA_WIN_DS_USE_WMME_TIMER
  2541. if( stream->processingThread )
  2542. {
  2543. #ifdef CLOSE_THREAD_HANDLE
  2544. CLOSE_THREAD_HANDLE( stream->processingThread ); /* Delete thread. */
  2545. #endif
  2546. stream->processingThread = NULL;
  2547. }
  2548. #endif
  2549. return result;
  2550. }
  2551. /***********************************************************************************/
  2552. static PaError StopStream( PaStream *s )
  2553. {
  2554. PaError result = paNoError;
  2555. PaWinDsStream *stream = (PaWinDsStream*)s;
  2556. HRESULT hr;
  2557. int timeoutMsec;
  2558. if( stream->streamRepresentation.streamCallback )
  2559. {
  2560. stream->stopProcessing = 1;
  2561. /* Set timeout at 4 times maximum time we might wait. */
  2562. timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate));
  2563. WaitForSingleObject( stream->processingCompleted, timeoutMsec );
  2564. }
  2565. #ifdef PA_WIN_DS_USE_WMME_TIMER
  2566. if( stream->timerID != 0 )
  2567. {
  2568. timeKillEvent(stream->timerID); /* Stop callback timer. */
  2569. stream->timerID = 0;
  2570. }
  2571. #else
  2572. if( stream->processingThread )
  2573. {
  2574. if( WaitForSingleObject( stream->processingThreadCompleted, 30*100 ) == WAIT_TIMEOUT )
  2575. return paUnanticipatedHostError;
  2576. #ifdef CLOSE_THREAD_HANDLE
  2577. CloseHandle( stream->processingThread ); /* Delete thread. */
  2578. stream->processingThread = NULL;
  2579. #endif
  2580. }
  2581. #endif
  2582. if( stream->systemTimerResolutionPeriodMs > 0 ){
  2583. timeEndPeriod( stream->systemTimerResolutionPeriodMs );
  2584. stream->systemTimerResolutionPeriodMs = 0;
  2585. }
  2586. if( stream->bufferProcessor.outputChannelCount > 0 )
  2587. {
  2588. // Stop the buffer playback
  2589. if( stream->pDirectSoundOutputBuffer != NULL )
  2590. {
  2591. stream->outputIsRunning = FALSE;
  2592. // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
  2593. hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
  2594. if( stream->pDirectSoundPrimaryBuffer )
  2595. IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
  2596. }
  2597. }
  2598. if( stream->bufferProcessor.inputChannelCount > 0 )
  2599. {
  2600. // Stop the buffer capture
  2601. if( stream->pDirectSoundInputBuffer != NULL )
  2602. {
  2603. // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
  2604. hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
  2605. }
  2606. }
  2607. stream->isStarted = 0;
  2608. return result;
  2609. }
  2610. /***********************************************************************************/
  2611. static PaError AbortStream( PaStream *s )
  2612. {
  2613. PaWinDsStream *stream = (PaWinDsStream*)s;
  2614. stream->abortProcessing = 1;
  2615. return StopStream( s );
  2616. }
  2617. /***********************************************************************************/
  2618. static PaError IsStreamStopped( PaStream *s )
  2619. {
  2620. PaWinDsStream *stream = (PaWinDsStream*)s;
  2621. return !stream->isStarted;
  2622. }
  2623. /***********************************************************************************/
  2624. static PaError IsStreamActive( PaStream *s )
  2625. {
  2626. PaWinDsStream *stream = (PaWinDsStream*)s;
  2627. return stream->isActive;
  2628. }
  2629. /***********************************************************************************/
  2630. static PaTime GetStreamTime( PaStream *s )
  2631. {
  2632. /* suppress unused variable warnings */
  2633. (void) s;
  2634. return PaUtil_GetTime();
  2635. }
  2636. /***********************************************************************************/
  2637. static double GetStreamCpuLoad( PaStream* s )
  2638. {
  2639. PaWinDsStream *stream = (PaWinDsStream*)s;
  2640. return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
  2641. }
  2642. /***********************************************************************************
  2643. As separate stream interfaces are used for blocking and callback
  2644. streams, the following functions can be guaranteed to only be called
  2645. for blocking streams.
  2646. */
  2647. static PaError ReadStream( PaStream* s,
  2648. void *buffer,
  2649. unsigned long frames )
  2650. {
  2651. PaWinDsStream *stream = (PaWinDsStream*)s;
  2652. /* suppress unused variable warnings */
  2653. (void) buffer;
  2654. (void) frames;
  2655. (void) stream;
  2656. /* IMPLEMENT ME, see portaudio.h for required behavior*/
  2657. return paNoError;
  2658. }
  2659. /***********************************************************************************/
  2660. static PaError WriteStream( PaStream* s,
  2661. const void *buffer,
  2662. unsigned long frames )
  2663. {
  2664. PaWinDsStream *stream = (PaWinDsStream*)s;
  2665. /* suppress unused variable warnings */
  2666. (void) buffer;
  2667. (void) frames;
  2668. (void) stream;
  2669. /* IMPLEMENT ME, see portaudio.h for required behavior*/
  2670. return paNoError;
  2671. }
  2672. /***********************************************************************************/
  2673. static signed long GetStreamReadAvailable( PaStream* s )
  2674. {
  2675. PaWinDsStream *stream = (PaWinDsStream*)s;
  2676. /* suppress unused variable warnings */
  2677. (void) stream;
  2678. /* IMPLEMENT ME, see portaudio.h for required behavior*/
  2679. return 0;
  2680. }
  2681. /***********************************************************************************/
  2682. static signed long GetStreamWriteAvailable( PaStream* s )
  2683. {
  2684. PaWinDsStream *stream = (PaWinDsStream*)s;
  2685. /* suppress unused variable warnings */
  2686. (void) stream;
  2687. /* IMPLEMENT ME, see portaudio.h for required behavior*/
  2688. return 0;
  2689. }
  2690. #endif