Added an author header to the WASAPI section
[rtaudio-cdist.git] / RtAudio.cpp
1 /************************************************************************/\r
2 /*! \class RtAudio\r
3     \brief Realtime audio i/o C++ classes.\r
4 \r
5     RtAudio provides a common API (Application Programming Interface)\r
6     for realtime audio input/output across Linux (native ALSA, Jack,\r
7     and OSS), Macintosh OS X (CoreAudio and Jack), and Windows\r
8     (DirectSound, ASIO and WASAPI) operating systems.\r
9 \r
10     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
11 \r
12     RtAudio: realtime audio i/o C++ classes\r
13     Copyright (c) 2001-2014 Gary P. Scavone\r
14 \r
15     Permission is hereby granted, free of charge, to any person\r
16     obtaining a copy of this software and associated documentation files\r
17     (the "Software"), to deal in the Software without restriction,\r
18     including without limitation the rights to use, copy, modify, merge,\r
19     publish, distribute, sublicense, and/or sell copies of the Software,\r
20     and to permit persons to whom the Software is furnished to do so,\r
21     subject to the following conditions:\r
22 \r
23     The above copyright notice and this permission notice shall be\r
24     included in all copies or substantial portions of the Software.\r
25 \r
26     Any person wishing to distribute modifications to the Software is\r
27     asked to send the modifications to the original developer so that\r
28     they can be incorporated into the canonical version.  This is,\r
29     however, not a binding provision of this license.\r
30 \r
31     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
32     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
33     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
34     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
35     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
36     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
37     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
38 */\r
39 /************************************************************************/\r
40 \r
41 // RtAudio: Version 4.1.1pre\r
42 \r
43 #include "RtAudio.h"\r
44 #include <iostream>\r
45 #include <cstdlib>\r
46 #include <cstring>\r
47 #include <climits>\r
48 \r
49 // Static variable definitions.\r
50 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
51 const unsigned int RtApi::SAMPLE_RATES[] = {\r
52   4000, 5512, 8000, 9600, 11025, 16000, 22050,\r
53   32000, 44100, 48000, 88200, 96000, 176400, 192000\r
54 };\r
55 \r
56 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)\r
57   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
58   #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)\r
59   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
60   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)\r
61 #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
62   // pthread API\r
63   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
64   #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)\r
65   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)\r
66   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)\r
67 #else\r
68   #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions\r
69   #define MUTEX_DESTROY(A)    abs(*A) // dummy definitions\r
70 #endif\r
71 \r
72 // *************************************************** //\r
73 //\r
74 // RtAudio definitions.\r
75 //\r
76 // *************************************************** //\r
77 \r
78 std::string RtAudio :: getVersion( void ) throw()\r
79 {\r
80   return RTAUDIO_VERSION;\r
81 }\r
82 \r
83 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()\r
84 {\r
85   apis.clear();\r
86 \r
87   // The order here will control the order of RtAudio's API search in\r
88   // the constructor.\r
89 #if defined(__UNIX_JACK__)\r
90   apis.push_back( UNIX_JACK );\r
91 #endif\r
92 #if defined(__LINUX_ALSA__)\r
93   apis.push_back( LINUX_ALSA );\r
94 #endif\r
95 #if defined(__LINUX_PULSE__)\r
96   apis.push_back( LINUX_PULSE );\r
97 #endif\r
98 #if defined(__LINUX_OSS__)\r
99   apis.push_back( LINUX_OSS );\r
100 #endif\r
101 #if defined(__WINDOWS_ASIO__)\r
102   apis.push_back( WINDOWS_ASIO );\r
103 #endif\r
104 #if defined(__WINDOWS_WASAPI__)\r
105   apis.push_back( WINDOWS_WASAPI );\r
106 #endif\r
107 #if defined(__WINDOWS_DS__)\r
108   apis.push_back( WINDOWS_DS );\r
109 #endif\r
110 #if defined(__MACOSX_CORE__)\r
111   apis.push_back( MACOSX_CORE );\r
112 #endif\r
113 #if defined(__RTAUDIO_DUMMY__)\r
114   apis.push_back( RTAUDIO_DUMMY );\r
115 #endif\r
116 }\r
117 \r
118 void RtAudio :: openRtApi( RtAudio::Api api )\r
119 {\r
120   if ( rtapi_ )\r
121     delete rtapi_;\r
122   rtapi_ = 0;\r
123 \r
124 #if defined(__UNIX_JACK__)\r
125   if ( api == UNIX_JACK )\r
126     rtapi_ = new RtApiJack();\r
127 #endif\r
128 #if defined(__LINUX_ALSA__)\r
129   if ( api == LINUX_ALSA )\r
130     rtapi_ = new RtApiAlsa();\r
131 #endif\r
132 #if defined(__LINUX_PULSE__)\r
133   if ( api == LINUX_PULSE )\r
134     rtapi_ = new RtApiPulse();\r
135 #endif\r
136 #if defined(__LINUX_OSS__)\r
137   if ( api == LINUX_OSS )\r
138     rtapi_ = new RtApiOss();\r
139 #endif\r
140 #if defined(__WINDOWS_ASIO__)\r
141   if ( api == WINDOWS_ASIO )\r
142     rtapi_ = new RtApiAsio();\r
143 #endif\r
144 #if defined(__WINDOWS_WASAPI__)\r
145   if ( api == WINDOWS_WASAPI )\r
146     rtapi_ = new RtApiWasapi();\r
147 #endif\r
148 #if defined(__WINDOWS_DS__)\r
149   if ( api == WINDOWS_DS )\r
150     rtapi_ = new RtApiDs();\r
151 #endif\r
152 #if defined(__MACOSX_CORE__)\r
153   if ( api == MACOSX_CORE )\r
154     rtapi_ = new RtApiCore();\r
155 #endif\r
156 #if defined(__RTAUDIO_DUMMY__)\r
157   if ( api == RTAUDIO_DUMMY )\r
158     rtapi_ = new RtApiDummy();\r
159 #endif\r
160 }\r
161 \r
162 RtAudio :: RtAudio( RtAudio::Api api )\r
163 {\r
164   rtapi_ = 0;\r
165 \r
166   if ( api != UNSPECIFIED ) {\r
167     // Attempt to open the specified API.\r
168     openRtApi( api );\r
169     if ( rtapi_ ) return;\r
170 \r
171     // No compiled support for specified API value.  Issue a debug\r
172     // warning and continue as if no API was specified.\r
173     std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;\r
174   }\r
175 \r
176   // Iterate through the compiled APIs and return as soon as we find\r
177   // one with at least one device or we reach the end of the list.\r
178   std::vector< RtAudio::Api > apis;\r
179   getCompiledApi( apis );\r
180   for ( unsigned int i=0; i<apis.size(); i++ ) {\r
181     openRtApi( apis[i] );\r
182     if ( rtapi_->getDeviceCount() ) break;\r
183   }\r
184 \r
185   if ( rtapi_ ) return;\r
186 \r
187   // It should not be possible to get here because the preprocessor\r
188   // definition __RTAUDIO_DUMMY__ is automatically defined if no\r
189   // API-specific definitions are passed to the compiler. But just in\r
190   // case something weird happens, we'll thow an error.\r
191   std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
192   throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );\r
193 }\r
194 \r
195 RtAudio :: ~RtAudio() throw()\r
196 {\r
197   if ( rtapi_ )\r
198     delete rtapi_;\r
199 }\r
200 \r
201 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,\r
202                             RtAudio::StreamParameters *inputParameters,\r
203                             RtAudioFormat format, unsigned int sampleRate,\r
204                             unsigned int *bufferFrames,\r
205                             RtAudioCallback callback, void *userData,\r
206                             RtAudio::StreamOptions *options,\r
207                             RtAudioErrorCallback errorCallback )\r
208 {\r
209   return rtapi_->openStream( outputParameters, inputParameters, format,\r
210                              sampleRate, bufferFrames, callback,\r
211                              userData, options, errorCallback );\r
212 }\r
213 \r
214 // *************************************************** //\r
215 //\r
216 // Public RtApi definitions (see end of file for\r
217 // private or protected utility functions).\r
218 //\r
219 // *************************************************** //\r
220 \r
221 RtApi :: RtApi()\r
222 {\r
223   stream_.state = STREAM_CLOSED;\r
224   stream_.mode = UNINITIALIZED;\r
225   stream_.apiHandle = 0;\r
226   stream_.userBuffer[0] = 0;\r
227   stream_.userBuffer[1] = 0;\r
228   MUTEX_INITIALIZE( &stream_.mutex );\r
229   showWarnings_ = true;\r
230   firstErrorOccurred_ = false;\r
231 }\r
232 \r
233 RtApi :: ~RtApi()\r
234 {\r
235   MUTEX_DESTROY( &stream_.mutex );\r
236 }\r
237 \r
238 void RtApi :: openStream( RtAudio::StreamParameters *oParams,\r
239                           RtAudio::StreamParameters *iParams,\r
240                           RtAudioFormat format, unsigned int sampleRate,\r
241                           unsigned int *bufferFrames,\r
242                           RtAudioCallback callback, void *userData,\r
243                           RtAudio::StreamOptions *options,\r
244                           RtAudioErrorCallback errorCallback )\r
245 {\r
246   if ( stream_.state != STREAM_CLOSED ) {\r
247     errorText_ = "RtApi::openStream: a stream is already open!";\r
248     error( RtAudioError::INVALID_USE );\r
249     return;\r
250   }\r
251 \r
252   // Clear stream information potentially left from a previously open stream.\r
253   clearStreamInfo();\r
254 \r
255   if ( oParams && oParams->nChannels < 1 ) {\r
256     errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";\r
257     error( RtAudioError::INVALID_USE );\r
258     return;\r
259   }\r
260 \r
261   if ( iParams && iParams->nChannels < 1 ) {\r
262     errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";\r
263     error( RtAudioError::INVALID_USE );\r
264     return;\r
265   }\r
266 \r
267   if ( oParams == NULL && iParams == NULL ) {\r
268     errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";\r
269     error( RtAudioError::INVALID_USE );\r
270     return;\r
271   }\r
272 \r
273   if ( formatBytes(format) == 0 ) {\r
274     errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";\r
275     error( RtAudioError::INVALID_USE );\r
276     return;\r
277   }\r
278 \r
279   unsigned int nDevices = getDeviceCount();\r
280   unsigned int oChannels = 0;\r
281   if ( oParams ) {\r
282     oChannels = oParams->nChannels;\r
283     if ( oParams->deviceId >= nDevices ) {\r
284       errorText_ = "RtApi::openStream: output device parameter value is invalid.";\r
285       error( RtAudioError::INVALID_USE );\r
286       return;\r
287     }\r
288   }\r
289 \r
290   unsigned int iChannels = 0;\r
291   if ( iParams ) {\r
292     iChannels = iParams->nChannels;\r
293     if ( iParams->deviceId >= nDevices ) {\r
294       errorText_ = "RtApi::openStream: input device parameter value is invalid.";\r
295       error( RtAudioError::INVALID_USE );\r
296       return;\r
297     }\r
298   }\r
299 \r
300   bool result;\r
301 \r
302   if ( oChannels > 0 ) {\r
303 \r
304     result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,\r
305                               sampleRate, format, bufferFrames, options );\r
306     if ( result == false ) {\r
307       error( RtAudioError::SYSTEM_ERROR );\r
308       return;\r
309     }\r
310   }\r
311 \r
312   if ( iChannels > 0 ) {\r
313 \r
314     result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,\r
315                               sampleRate, format, bufferFrames, options );\r
316     if ( result == false ) {\r
317       if ( oChannels > 0 ) closeStream();\r
318       error( RtAudioError::SYSTEM_ERROR );\r
319       return;\r
320     }\r
321   }\r
322 \r
323   stream_.callbackInfo.callback = (void *) callback;\r
324   stream_.callbackInfo.userData = userData;\r
325   stream_.callbackInfo.errorCallback = (void *) errorCallback;\r
326 \r
327   if ( options ) options->numberOfBuffers = stream_.nBuffers;\r
328   stream_.state = STREAM_STOPPED;\r
329 }\r
330 \r
331 unsigned int RtApi :: getDefaultInputDevice( void )\r
332 {\r
333   // Should be implemented in subclasses if possible.\r
334   return 0;\r
335 }\r
336 \r
337 unsigned int RtApi :: getDefaultOutputDevice( void )\r
338 {\r
339   // Should be implemented in subclasses if possible.\r
340   return 0;\r
341 }\r
342 \r
343 void RtApi :: closeStream( void )\r
344 {\r
345   // MUST be implemented in subclasses!\r
346   return;\r
347 }\r
348 \r
349 bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,\r
350                                unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,\r
351                                RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,\r
352                                RtAudio::StreamOptions * /*options*/ )\r
353 {\r
354   // MUST be implemented in subclasses!\r
355   return FAILURE;\r
356 }\r
357 \r
358 void RtApi :: tickStreamTime( void )\r
359 {\r
360   // Subclasses that do not provide their own implementation of\r
361   // getStreamTime should call this function once per buffer I/O to\r
362   // provide basic stream time support.\r
363 \r
364   stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );\r
365 \r
366 #if defined( HAVE_GETTIMEOFDAY )\r
367   gettimeofday( &stream_.lastTickTimestamp, NULL );\r
368 #endif\r
369 }\r
370 \r
371 long RtApi :: getStreamLatency( void )\r
372 {\r
373   verifyStream();\r
374 \r
375   long totalLatency = 0;\r
376   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
377     totalLatency = stream_.latency[0];\r
378   if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
379     totalLatency += stream_.latency[1];\r
380 \r
381   return totalLatency;\r
382 }\r
383 \r
384 double RtApi :: getStreamTime( void )\r
385 {\r
386   verifyStream();\r
387 \r
388 #if defined( HAVE_GETTIMEOFDAY )\r
389   // Return a very accurate estimate of the stream time by\r
390   // adding in the elapsed time since the last tick.\r
391   struct timeval then;\r
392   struct timeval now;\r
393 \r
394   if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )\r
395     return stream_.streamTime;\r
396 \r
397   gettimeofday( &now, NULL );\r
398   then = stream_.lastTickTimestamp;\r
399   return stream_.streamTime +\r
400     ((now.tv_sec + 0.000001 * now.tv_usec) -\r
401      (then.tv_sec + 0.000001 * then.tv_usec));     \r
402 #else\r
403   return stream_.streamTime;\r
404 #endif\r
405 }\r
406 \r
407 unsigned int RtApi :: getStreamSampleRate( void )\r
408 {\r
409  verifyStream();\r
410 \r
411  return stream_.sampleRate;\r
412 }\r
413 \r
414 \r
415 // *************************************************** //\r
416 //\r
417 // OS/API-specific methods.\r
418 //\r
419 // *************************************************** //\r
420 \r
421 #if defined(__MACOSX_CORE__)\r
422 \r
423 // The OS X CoreAudio API is designed to use a separate callback\r
424 // procedure for each of its audio devices.  A single RtAudio duplex\r
425 // stream using two different devices is supported here, though it\r
426 // cannot be guaranteed to always behave correctly because we cannot\r
427 // synchronize these two callbacks.\r
428 //\r
429 // A property listener is installed for over/underrun information.\r
430 // However, no functionality is currently provided to allow property\r
431 // listeners to trigger user handlers because it is unclear what could\r
432 // be done if a critical stream parameter (buffer size, sample rate,\r
433 // device disconnect) notification arrived.  The listeners entail\r
434 // quite a bit of extra code and most likely, a user program wouldn't\r
435 // be prepared for the result anyway.  However, we do provide a flag\r
436 // to the client callback function to inform of an over/underrun.\r
437 \r
438 // A structure to hold various information related to the CoreAudio API\r
439 // implementation.\r
440 struct CoreHandle {\r
441   AudioDeviceID id[2];    // device ids\r
442 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
443   AudioDeviceIOProcID procId[2];\r
444 #endif\r
445   UInt32 iStream[2];      // device stream index (or first if using multiple)\r
446   UInt32 nStreams[2];     // number of streams to use\r
447   bool xrun[2];\r
448   char *deviceBuffer;\r
449   pthread_cond_t condition;\r
450   int drainCounter;       // Tracks callback counts when draining\r
451   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
452 \r
453   CoreHandle()\r
454     :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
455 };\r
456 \r
457 RtApiCore:: RtApiCore()\r
458 {\r
459 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
460   // This is a largely undocumented but absolutely necessary\r
461   // requirement starting with OS-X 10.6.  If not called, queries and\r
462   // updates to various audio device properties are not handled\r
463   // correctly.\r
464   CFRunLoopRef theRunLoop = NULL;\r
465   AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
466                                           kAudioObjectPropertyScopeGlobal,\r
467                                           kAudioObjectPropertyElementMaster };\r
468   OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
469   if ( result != noErr ) {\r
470     errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
471     error( RtAudioError::WARNING );\r
472   }\r
473 #endif\r
474 }\r
475 \r
476 RtApiCore :: ~RtApiCore()\r
477 {\r
478   // The subclass destructor gets called before the base class\r
479   // destructor, so close an existing stream before deallocating\r
480   // apiDeviceId memory.\r
481   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
482 }\r
483 \r
484 unsigned int RtApiCore :: getDeviceCount( void )\r
485 {\r
486   // Find out how many audio devices there are, if any.\r
487   UInt32 dataSize;\r
488   AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
489   OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );\r
490   if ( result != noErr ) {\r
491     errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";\r
492     error( RtAudioError::WARNING );\r
493     return 0;\r
494   }\r
495 \r
496   return dataSize / sizeof( AudioDeviceID );\r
497 }\r
498 \r
499 unsigned int RtApiCore :: getDefaultInputDevice( void )\r
500 {\r
501   unsigned int nDevices = getDeviceCount();\r
502   if ( nDevices <= 1 ) return 0;\r
503 \r
504   AudioDeviceID id;\r
505   UInt32 dataSize = sizeof( AudioDeviceID );\r
506   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
507   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
508   if ( result != noErr ) {\r
509     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";\r
510     error( RtAudioError::WARNING );\r
511     return 0;\r
512   }\r
513 \r
514   dataSize *= nDevices;\r
515   AudioDeviceID deviceList[ nDevices ];\r
516   property.mSelector = kAudioHardwarePropertyDevices;\r
517   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
518   if ( result != noErr ) {\r
519     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";\r
520     error( RtAudioError::WARNING );\r
521     return 0;\r
522   }\r
523 \r
524   for ( unsigned int i=0; i<nDevices; i++ )\r
525     if ( id == deviceList[i] ) return i;\r
526 \r
527   errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";\r
528   error( RtAudioError::WARNING );\r
529   return 0;\r
530 }\r
531 \r
532 unsigned int RtApiCore :: getDefaultOutputDevice( void )\r
533 {\r
534   unsigned int nDevices = getDeviceCount();\r
535   if ( nDevices <= 1 ) return 0;\r
536 \r
537   AudioDeviceID id;\r
538   UInt32 dataSize = sizeof( AudioDeviceID );\r
539   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
540   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
541   if ( result != noErr ) {\r
542     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";\r
543     error( RtAudioError::WARNING );\r
544     return 0;\r
545   }\r
546 \r
547   dataSize = sizeof( AudioDeviceID ) * nDevices;\r
548   AudioDeviceID deviceList[ nDevices ];\r
549   property.mSelector = kAudioHardwarePropertyDevices;\r
550   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
551   if ( result != noErr ) {\r
552     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";\r
553     error( RtAudioError::WARNING );\r
554     return 0;\r
555   }\r
556 \r
557   for ( unsigned int i=0; i<nDevices; i++ )\r
558     if ( id == deviceList[i] ) return i;\r
559 \r
560   errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";\r
561   error( RtAudioError::WARNING );\r
562   return 0;\r
563 }\r
564 \r
565 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )\r
566 {\r
567   RtAudio::DeviceInfo info;\r
568   info.probed = false;\r
569 \r
570   // Get device ID\r
571   unsigned int nDevices = getDeviceCount();\r
572   if ( nDevices == 0 ) {\r
573     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
574     error( RtAudioError::INVALID_USE );\r
575     return info;\r
576   }\r
577 \r
578   if ( device >= nDevices ) {\r
579     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
580     error( RtAudioError::INVALID_USE );\r
581     return info;\r
582   }\r
583 \r
584   AudioDeviceID deviceList[ nDevices ];\r
585   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
586   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
587                                           kAudioObjectPropertyScopeGlobal,\r
588                                           kAudioObjectPropertyElementMaster };\r
589   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
590                                                 0, NULL, &dataSize, (void *) &deviceList );\r
591   if ( result != noErr ) {\r
592     errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";\r
593     error( RtAudioError::WARNING );\r
594     return info;\r
595   }\r
596 \r
597   AudioDeviceID id = deviceList[ device ];\r
598 \r
599   // Get the device name.\r
600   info.name.erase();\r
601   CFStringRef cfname;\r
602   dataSize = sizeof( CFStringRef );\r
603   property.mSelector = kAudioObjectPropertyManufacturer;\r
604   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
605   if ( result != noErr ) {\r
606     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";\r
607     errorText_ = errorStream_.str();\r
608     error( RtAudioError::WARNING );\r
609     return info;\r
610   }\r
611 \r
612   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
613   int length = CFStringGetLength(cfname);\r
614   char *mname = (char *)malloc(length * 3 + 1);\r
615 #if defined( UNICODE ) || defined( _UNICODE )\r
616   CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
617 #else\r
618   CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
619 #endif\r
620   info.name.append( (const char *)mname, strlen(mname) );\r
621   info.name.append( ": " );\r
622   CFRelease( cfname );\r
623   free(mname);\r
624 \r
625   property.mSelector = kAudioObjectPropertyName;\r
626   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
627   if ( result != noErr ) {\r
628     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";\r
629     errorText_ = errorStream_.str();\r
630     error( RtAudioError::WARNING );\r
631     return info;\r
632   }\r
633 \r
634   //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
635   length = CFStringGetLength(cfname);\r
636   char *name = (char *)malloc(length * 3 + 1);\r
637 #if defined( UNICODE ) || defined( _UNICODE )\r
638   CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
639 #else\r
640   CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
641 #endif\r
642   info.name.append( (const char *)name, strlen(name) );\r
643   CFRelease( cfname );\r
644   free(name);\r
645 \r
646   // Get the output stream "configuration".\r
647   AudioBufferList       *bufferList = nil;\r
648   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
649   property.mScope = kAudioDevicePropertyScopeOutput;\r
650   //  property.mElement = kAudioObjectPropertyElementWildcard;\r
651   dataSize = 0;\r
652   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
653   if ( result != noErr || dataSize == 0 ) {\r
654     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";\r
655     errorText_ = errorStream_.str();\r
656     error( RtAudioError::WARNING );\r
657     return info;\r
658   }\r
659 \r
660   // Allocate the AudioBufferList.\r
661   bufferList = (AudioBufferList *) malloc( dataSize );\r
662   if ( bufferList == NULL ) {\r
663     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";\r
664     error( RtAudioError::WARNING );\r
665     return info;\r
666   }\r
667 \r
668   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
669   if ( result != noErr || dataSize == 0 ) {\r
670     free( bufferList );\r
671     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";\r
672     errorText_ = errorStream_.str();\r
673     error( RtAudioError::WARNING );\r
674     return info;\r
675   }\r
676 \r
677   // Get output channel information.\r
678   unsigned int i, nStreams = bufferList->mNumberBuffers;\r
679   for ( i=0; i<nStreams; i++ )\r
680     info.outputChannels += bufferList->mBuffers[i].mNumberChannels;\r
681   free( bufferList );\r
682 \r
683   // Get the input stream "configuration".\r
684   property.mScope = kAudioDevicePropertyScopeInput;\r
685   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
686   if ( result != noErr || dataSize == 0 ) {\r
687     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";\r
688     errorText_ = errorStream_.str();\r
689     error( RtAudioError::WARNING );\r
690     return info;\r
691   }\r
692 \r
693   // Allocate the AudioBufferList.\r
694   bufferList = (AudioBufferList *) malloc( dataSize );\r
695   if ( bufferList == NULL ) {\r
696     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";\r
697     error( RtAudioError::WARNING );\r
698     return info;\r
699   }\r
700 \r
701   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
702   if (result != noErr || dataSize == 0) {\r
703     free( bufferList );\r
704     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";\r
705     errorText_ = errorStream_.str();\r
706     error( RtAudioError::WARNING );\r
707     return info;\r
708   }\r
709 \r
710   // Get input channel information.\r
711   nStreams = bufferList->mNumberBuffers;\r
712   for ( i=0; i<nStreams; i++ )\r
713     info.inputChannels += bufferList->mBuffers[i].mNumberChannels;\r
714   free( bufferList );\r
715 \r
716   // If device opens for both playback and capture, we determine the channels.\r
717   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
718     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
719 \r
720   // Probe the device sample rates.\r
721   bool isInput = false;\r
722   if ( info.outputChannels == 0 ) isInput = true;\r
723 \r
724   // Determine the supported sample rates.\r
725   property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\r
726   if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;\r
727   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
728   if ( result != kAudioHardwareNoError || dataSize == 0 ) {\r
729     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";\r
730     errorText_ = errorStream_.str();\r
731     error( RtAudioError::WARNING );\r
732     return info;\r
733   }\r
734 \r
735   UInt32 nRanges = dataSize / sizeof( AudioValueRange );\r
736   AudioValueRange rangeList[ nRanges ];\r
737   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );\r
738   if ( result != kAudioHardwareNoError ) {\r
739     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";\r
740     errorText_ = errorStream_.str();\r
741     error( RtAudioError::WARNING );\r
742     return info;\r
743   }\r
744 \r
745   // The sample rate reporting mechanism is a bit of a mystery.  It\r
746   // seems that it can either return individual rates or a range of\r
747   // rates.  I assume that if the min / max range values are the same,\r
748   // then that represents a single supported rate and if the min / max\r
749   // range values are different, the device supports an arbitrary\r
750   // range of values (though there might be multiple ranges, so we'll\r
751   // use the most conservative range).\r
752   Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
753   bool haveValueRange = false;\r
754   info.sampleRates.clear();\r
755   for ( UInt32 i=0; i<nRanges; i++ ) {\r
756     if ( rangeList[i].mMinimum == rangeList[i].mMaximum )\r
757       info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );\r
758     else {\r
759       haveValueRange = true;\r
760       if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
761       if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
762     }\r
763   }\r
764 \r
765   if ( haveValueRange ) {\r
766     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
767       if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )\r
768         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
769     }\r
770   }\r
771 \r
772   // Sort and remove any redundant values\r
773   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
774   info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
775 \r
776   if ( info.sampleRates.size() == 0 ) {\r
777     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
778     errorText_ = errorStream_.str();\r
779     error( RtAudioError::WARNING );\r
780     return info;\r
781   }\r
782 \r
783   // CoreAudio always uses 32-bit floating point data for PCM streams.\r
784   // Thus, any other "physical" formats supported by the device are of\r
785   // no interest to the client.\r
786   info.nativeFormats = RTAUDIO_FLOAT32;\r
787 \r
788   if ( info.outputChannels > 0 )\r
789     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
790   if ( info.inputChannels > 0 )\r
791     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
792 \r
793   info.probed = true;\r
794   return info;\r
795 }\r
796 \r
797 static OSStatus callbackHandler( AudioDeviceID inDevice,\r
798                                  const AudioTimeStamp* /*inNow*/,\r
799                                  const AudioBufferList* inInputData,\r
800                                  const AudioTimeStamp* /*inInputTime*/,\r
801                                  AudioBufferList* outOutputData,\r
802                                  const AudioTimeStamp* /*inOutputTime*/,\r
803                                  void* infoPointer )\r
804 {\r
805   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
806 \r
807   RtApiCore *object = (RtApiCore *) info->object;\r
808   if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )\r
809     return kAudioHardwareUnspecifiedError;\r
810   else\r
811     return kAudioHardwareNoError;\r
812 }\r
813 \r
814 static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
815                               UInt32 nAddresses,\r
816                               const AudioObjectPropertyAddress properties[],\r
817                               void* handlePointer )\r
818 {\r
819   CoreHandle *handle = (CoreHandle *) handlePointer;\r
820   for ( UInt32 i=0; i<nAddresses; i++ ) {\r
821     if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {\r
822       if ( properties[i].mScope == kAudioDevicePropertyScopeInput )\r
823         handle->xrun[1] = true;\r
824       else\r
825         handle->xrun[0] = true;\r
826     }\r
827   }\r
828 \r
829   return kAudioHardwareNoError;\r
830 }\r
831 \r
832 static OSStatus rateListener( AudioObjectID inDevice,\r
833                               UInt32 /*nAddresses*/,\r
834                               const AudioObjectPropertyAddress /*properties*/[],\r
835                               void* ratePointer )\r
836 {\r
837   Float64 *rate = (Float64 *) ratePointer;\r
838   UInt32 dataSize = sizeof( Float64 );\r
839   AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
840                                           kAudioObjectPropertyScopeGlobal,\r
841                                           kAudioObjectPropertyElementMaster };\r
842   AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
843   return kAudioHardwareNoError;\r
844 }\r
845 \r
846 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
847                                    unsigned int firstChannel, unsigned int sampleRate,\r
848                                    RtAudioFormat format, unsigned int *bufferSize,\r
849                                    RtAudio::StreamOptions *options )\r
850 {\r
851   // Get device ID\r
852   unsigned int nDevices = getDeviceCount();\r
853   if ( nDevices == 0 ) {\r
854     // This should not happen because a check is made before this function is called.\r
855     errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";\r
856     return FAILURE;\r
857   }\r
858 \r
859   if ( device >= nDevices ) {\r
860     // This should not happen because a check is made before this function is called.\r
861     errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";\r
862     return FAILURE;\r
863   }\r
864 \r
865   AudioDeviceID deviceList[ nDevices ];\r
866   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
867   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
868                                           kAudioObjectPropertyScopeGlobal,\r
869                                           kAudioObjectPropertyElementMaster };\r
870   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
871                                                 0, NULL, &dataSize, (void *) &deviceList );\r
872   if ( result != noErr ) {\r
873     errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";\r
874     return FAILURE;\r
875   }\r
876 \r
877   AudioDeviceID id = deviceList[ device ];\r
878 \r
879   // Setup for stream mode.\r
880   bool isInput = false;\r
881   if ( mode == INPUT ) {\r
882     isInput = true;\r
883     property.mScope = kAudioDevicePropertyScopeInput;\r
884   }\r
885   else\r
886     property.mScope = kAudioDevicePropertyScopeOutput;\r
887 \r
888   // Get the stream "configuration".\r
889   AudioBufferList       *bufferList = nil;\r
890   dataSize = 0;\r
891   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
892   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
893   if ( result != noErr || dataSize == 0 ) {\r
894     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";\r
895     errorText_ = errorStream_.str();\r
896     return FAILURE;\r
897   }\r
898 \r
899   // Allocate the AudioBufferList.\r
900   bufferList = (AudioBufferList *) malloc( dataSize );\r
901   if ( bufferList == NULL ) {\r
902     errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";\r
903     return FAILURE;\r
904   }\r
905 \r
906   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
907   if (result != noErr || dataSize == 0) {\r
908     free( bufferList );\r
909     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
910     errorText_ = errorStream_.str();\r
911     return FAILURE;\r
912   }\r
913 \r
914   // Search for one or more streams that contain the desired number of\r
915   // channels. CoreAudio devices can have an arbitrary number of\r
916   // streams and each stream can have an arbitrary number of channels.\r
917   // For each stream, a single buffer of interleaved samples is\r
918   // provided.  RtAudio prefers the use of one stream of interleaved\r
919   // data or multiple consecutive single-channel streams.  However, we\r
920   // now support multiple consecutive multi-channel streams of\r
921   // interleaved data as well.\r
922   UInt32 iStream, offsetCounter = firstChannel;\r
923   UInt32 nStreams = bufferList->mNumberBuffers;\r
924   bool monoMode = false;\r
925   bool foundStream = false;\r
926 \r
927   // First check that the device supports the requested number of\r
928   // channels.\r
929   UInt32 deviceChannels = 0;\r
930   for ( iStream=0; iStream<nStreams; iStream++ )\r
931     deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;\r
932 \r
933   if ( deviceChannels < ( channels + firstChannel ) ) {\r
934     free( bufferList );\r
935     errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";\r
936     errorText_ = errorStream_.str();\r
937     return FAILURE;\r
938   }\r
939 \r
940   // Look for a single stream meeting our needs.\r
941   UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;\r
942   for ( iStream=0; iStream<nStreams; iStream++ ) {\r
943     streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
944     if ( streamChannels >= channels + offsetCounter ) {\r
945       firstStream = iStream;\r
946       channelOffset = offsetCounter;\r
947       foundStream = true;\r
948       break;\r
949     }\r
950     if ( streamChannels > offsetCounter ) break;\r
951     offsetCounter -= streamChannels;\r
952   }\r
953 \r
954   // If we didn't find a single stream above, then we should be able\r
955   // to meet the channel specification with multiple streams.\r
956   if ( foundStream == false ) {\r
957     monoMode = true;\r
958     offsetCounter = firstChannel;\r
959     for ( iStream=0; iStream<nStreams; iStream++ ) {\r
960       streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
961       if ( streamChannels > offsetCounter ) break;\r
962       offsetCounter -= streamChannels;\r
963     }\r
964 \r
965     firstStream = iStream;\r
966     channelOffset = offsetCounter;\r
967     Int32 channelCounter = channels + offsetCounter - streamChannels;\r
968 \r
969     if ( streamChannels > 1 ) monoMode = false;\r
970     while ( channelCounter > 0 ) {\r
971       streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;\r
972       if ( streamChannels > 1 ) monoMode = false;\r
973       channelCounter -= streamChannels;\r
974       streamCount++;\r
975     }\r
976   }\r
977 \r
978   free( bufferList );\r
979 \r
980   // Determine the buffer size.\r
981   AudioValueRange       bufferRange;\r
982   dataSize = sizeof( AudioValueRange );\r
983   property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\r
984   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );\r
985 \r
986   if ( result != noErr ) {\r
987     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";\r
988     errorText_ = errorStream_.str();\r
989     return FAILURE;\r
990   }\r
991 \r
992   if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
993   else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;\r
994   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
995 \r
996   // Set the buffer size.  For multiple streams, I'm assuming we only\r
997   // need to make this setting for the master channel.\r
998   UInt32 theSize = (UInt32) *bufferSize;\r
999   dataSize = sizeof( UInt32 );\r
1000   property.mSelector = kAudioDevicePropertyBufferFrameSize;\r
1001   result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );\r
1002 \r
1003   if ( result != noErr ) {\r
1004     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";\r
1005     errorText_ = errorStream_.str();\r
1006     return FAILURE;\r
1007   }\r
1008 \r
1009   // If attempting to setup a duplex stream, the bufferSize parameter\r
1010   // MUST be the same in both directions!\r
1011   *bufferSize = theSize;\r
1012   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
1013     errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";\r
1014     errorText_ = errorStream_.str();\r
1015     return FAILURE;\r
1016   }\r
1017 \r
1018   stream_.bufferSize = *bufferSize;\r
1019   stream_.nBuffers = 1;\r
1020 \r
1021   // Try to set "hog" mode ... it's not clear to me this is working.\r
1022   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
1023     pid_t hog_pid;\r
1024     dataSize = sizeof( hog_pid );\r
1025     property.mSelector = kAudioDevicePropertyHogMode;\r
1026     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );\r
1027     if ( result != noErr ) {\r
1028       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";\r
1029       errorText_ = errorStream_.str();\r
1030       return FAILURE;\r
1031     }\r
1032 \r
1033     if ( hog_pid != getpid() ) {\r
1034       hog_pid = getpid();\r
1035       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );\r
1036       if ( result != noErr ) {\r
1037         errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";\r
1038         errorText_ = errorStream_.str();\r
1039         return FAILURE;\r
1040       }\r
1041     }\r
1042   }\r
1043 \r
1044   // Check and if necessary, change the sample rate for the device.\r
1045   Float64 nominalRate;\r
1046   dataSize = sizeof( Float64 );\r
1047   property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
1048   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
1049   if ( result != noErr ) {\r
1050     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
1051     errorText_ = errorStream_.str();\r
1052     return FAILURE;\r
1053   }\r
1054 \r
1055   // Only change the sample rate if off by more than 1 Hz.\r
1056   if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
1057 \r
1058     // Set a property listener for the sample rate change\r
1059     Float64 reportedRate = 0.0;\r
1060     AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
1061     result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1062     if ( result != noErr ) {\r
1063       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
1064       errorText_ = errorStream_.str();\r
1065       return FAILURE;\r
1066     }\r
1067 \r
1068     nominalRate = (Float64) sampleRate;\r
1069     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
1070     if ( result != noErr ) {\r
1071       AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1072       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
1073       errorText_ = errorStream_.str();\r
1074       return FAILURE;\r
1075     }\r
1076 \r
1077     // Now wait until the reported nominal rate is what we just set.\r
1078     UInt32 microCounter = 0;\r
1079     while ( reportedRate != nominalRate ) {\r
1080       microCounter += 5000;\r
1081       if ( microCounter > 5000000 ) break;\r
1082       usleep( 5000 );\r
1083     }\r
1084 \r
1085     // Remove the property listener.\r
1086     AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1087 \r
1088     if ( microCounter > 5000000 ) {\r
1089       errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
1090       errorText_ = errorStream_.str();\r
1091       return FAILURE;\r
1092     }\r
1093   }\r
1094 \r
1095   // Now set the stream format for all streams.  Also, check the\r
1096   // physical format of the device and change that if necessary.\r
1097   AudioStreamBasicDescription   description;\r
1098   dataSize = sizeof( AudioStreamBasicDescription );\r
1099   property.mSelector = kAudioStreamPropertyVirtualFormat;\r
1100   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
1101   if ( result != noErr ) {\r
1102     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
1103     errorText_ = errorStream_.str();\r
1104     return FAILURE;\r
1105   }\r
1106 \r
1107   // Set the sample rate and data format id.  However, only make the\r
1108   // change if the sample rate is not within 1.0 of the desired\r
1109   // rate and the format is not linear pcm.\r
1110   bool updateFormat = false;\r
1111   if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
1112     description.mSampleRate = (Float64) sampleRate;\r
1113     updateFormat = true;\r
1114   }\r
1115 \r
1116   if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
1117     description.mFormatID = kAudioFormatLinearPCM;\r
1118     updateFormat = true;\r
1119   }\r
1120 \r
1121   if ( updateFormat ) {\r
1122     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
1123     if ( result != noErr ) {\r
1124       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
1125       errorText_ = errorStream_.str();\r
1126       return FAILURE;\r
1127     }\r
1128   }\r
1129 \r
1130   // Now check the physical format.\r
1131   property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
1132   result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );\r
1133   if ( result != noErr ) {\r
1134     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
1135     errorText_ = errorStream_.str();\r
1136     return FAILURE;\r
1137   }\r
1138 \r
1139   //std::cout << "Current physical stream format:" << std::endl;\r
1140   //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
1141   //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1142   //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
1143   //std::cout << "   sample rate = " << description.mSampleRate << std::endl;\r
1144 \r
1145   if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
1146     description.mFormatID = kAudioFormatLinearPCM;\r
1147     //description.mSampleRate = (Float64) sampleRate;\r
1148     AudioStreamBasicDescription testDescription = description;\r
1149     UInt32 formatFlags;\r
1150 \r
1151     // We'll try higher bit rates first and then work our way down.\r
1152     std::vector< std::pair<UInt32, UInt32>  > physicalFormats;\r
1153     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;\r
1154     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1155     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1156     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1157     physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed\r
1158     formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
1159     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
1160     formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
1161     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
1162     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1163     physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
1164     physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
1165 \r
1166     bool setPhysicalFormat = false;\r
1167     for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
1168       testDescription = description;\r
1169       testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
1170       testDescription.mFormatFlags = physicalFormats[i].second;\r
1171       if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
1172         testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;\r
1173       else\r
1174         testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
1175       testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
1176       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
1177       if ( result == noErr ) {\r
1178         setPhysicalFormat = true;\r
1179         //std::cout << "Updated physical stream format:" << std::endl;\r
1180         //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
1181         //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1182         //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
1183         //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;\r
1184         break;\r
1185       }\r
1186     }\r
1187 \r
1188     if ( !setPhysicalFormat ) {\r
1189       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
1190       errorText_ = errorStream_.str();\r
1191       return FAILURE;\r
1192     }\r
1193   } // done setting virtual/physical formats.\r
1194 \r
1195   // Get the stream / device latency.\r
1196   UInt32 latency;\r
1197   dataSize = sizeof( UInt32 );\r
1198   property.mSelector = kAudioDevicePropertyLatency;\r
1199   if ( AudioObjectHasProperty( id, &property ) == true ) {\r
1200     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );\r
1201     if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;\r
1202     else {\r
1203       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";\r
1204       errorText_ = errorStream_.str();\r
1205       error( RtAudioError::WARNING );\r
1206     }\r
1207   }\r
1208 \r
1209   // Byte-swapping: According to AudioHardware.h, the stream data will\r
1210   // always be presented in native-endian format, so we should never\r
1211   // need to byte swap.\r
1212   stream_.doByteSwap[mode] = false;\r
1213 \r
1214   // From the CoreAudio documentation, PCM data must be supplied as\r
1215   // 32-bit floats.\r
1216   stream_.userFormat = format;\r
1217   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
1218 \r
1219   if ( streamCount == 1 )\r
1220     stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;\r
1221   else // multiple streams\r
1222     stream_.nDeviceChannels[mode] = channels;\r
1223   stream_.nUserChannels[mode] = channels;\r
1224   stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream\r
1225   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
1226   else stream_.userInterleaved = true;\r
1227   stream_.deviceInterleaved[mode] = true;\r
1228   if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;\r
1229 \r
1230   // Set flags for buffer conversion.\r
1231   stream_.doConvertBuffer[mode] = false;\r
1232   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
1233     stream_.doConvertBuffer[mode] = true;\r
1234   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
1235     stream_.doConvertBuffer[mode] = true;\r
1236   if ( streamCount == 1 ) {\r
1237     if ( stream_.nUserChannels[mode] > 1 &&\r
1238          stream_.userInterleaved != stream_.deviceInterleaved[mode] )\r
1239       stream_.doConvertBuffer[mode] = true;\r
1240   }\r
1241   else if ( monoMode && stream_.userInterleaved )\r
1242     stream_.doConvertBuffer[mode] = true;\r
1243 \r
1244   // Allocate our CoreHandle structure for the stream.\r
1245   CoreHandle *handle = 0;\r
1246   if ( stream_.apiHandle == 0 ) {\r
1247     try {\r
1248       handle = new CoreHandle;\r
1249     }\r
1250     catch ( std::bad_alloc& ) {\r
1251       errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";\r
1252       goto error;\r
1253     }\r
1254 \r
1255     if ( pthread_cond_init( &handle->condition, NULL ) ) {\r
1256       errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";\r
1257       goto error;\r
1258     }\r
1259     stream_.apiHandle = (void *) handle;\r
1260   }\r
1261   else\r
1262     handle = (CoreHandle *) stream_.apiHandle;\r
1263   handle->iStream[mode] = firstStream;\r
1264   handle->nStreams[mode] = streamCount;\r
1265   handle->id[mode] = id;\r
1266 \r
1267   // Allocate necessary internal buffers.\r
1268   unsigned long bufferBytes;\r
1269   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
1270   //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
1271   stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
1272   memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
1273   if ( stream_.userBuffer[mode] == NULL ) {\r
1274     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
1275     goto error;\r
1276   }\r
1277 \r
1278   // If possible, we will make use of the CoreAudio stream buffers as\r
1279   // "device buffers".  However, we can't do this if using multiple\r
1280   // streams.\r
1281   if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {\r
1282 \r
1283     bool makeBuffer = true;\r
1284     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
1285     if ( mode == INPUT ) {\r
1286       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
1287         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
1288         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
1289       }\r
1290     }\r
1291 \r
1292     if ( makeBuffer ) {\r
1293       bufferBytes *= *bufferSize;\r
1294       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
1295       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
1296       if ( stream_.deviceBuffer == NULL ) {\r
1297         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";\r
1298         goto error;\r
1299       }\r
1300     }\r
1301   }\r
1302 \r
1303   stream_.sampleRate = sampleRate;\r
1304   stream_.device[mode] = device;\r
1305   stream_.state = STREAM_STOPPED;\r
1306   stream_.callbackInfo.object = (void *) this;\r
1307 \r
1308   // Setup the buffer conversion information structure.\r
1309   if ( stream_.doConvertBuffer[mode] ) {\r
1310     if ( streamCount > 1 ) setConvertInfo( mode, 0 );\r
1311     else setConvertInfo( mode, channelOffset );\r
1312   }\r
1313 \r
1314   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )\r
1315     // Only one callback procedure per device.\r
1316     stream_.mode = DUPLEX;\r
1317   else {\r
1318 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1319     result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );\r
1320 #else\r
1321     // deprecated in favor of AudioDeviceCreateIOProcID()\r
1322     result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );\r
1323 #endif\r
1324     if ( result != noErr ) {\r
1325       errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";\r
1326       errorText_ = errorStream_.str();\r
1327       goto error;\r
1328     }\r
1329     if ( stream_.mode == OUTPUT && mode == INPUT )\r
1330       stream_.mode = DUPLEX;\r
1331     else\r
1332       stream_.mode = mode;\r
1333   }\r
1334 \r
1335   // Setup the device property listener for over/underload.\r
1336   property.mSelector = kAudioDeviceProcessorOverload;\r
1337   property.mScope = kAudioObjectPropertyScopeGlobal;\r
1338   result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
1339 \r
1340   return SUCCESS;\r
1341 \r
1342  error:\r
1343   if ( handle ) {\r
1344     pthread_cond_destroy( &handle->condition );\r
1345     delete handle;\r
1346     stream_.apiHandle = 0;\r
1347   }\r
1348 \r
1349   for ( int i=0; i<2; i++ ) {\r
1350     if ( stream_.userBuffer[i] ) {\r
1351       free( stream_.userBuffer[i] );\r
1352       stream_.userBuffer[i] = 0;\r
1353     }\r
1354   }\r
1355 \r
1356   if ( stream_.deviceBuffer ) {\r
1357     free( stream_.deviceBuffer );\r
1358     stream_.deviceBuffer = 0;\r
1359   }\r
1360 \r
1361   stream_.state = STREAM_CLOSED;\r
1362   return FAILURE;\r
1363 }\r
1364 \r
1365 void RtApiCore :: closeStream( void )\r
1366 {\r
1367   if ( stream_.state == STREAM_CLOSED ) {\r
1368     errorText_ = "RtApiCore::closeStream(): no open stream to close!";\r
1369     error( RtAudioError::WARNING );\r
1370     return;\r
1371   }\r
1372 \r
1373   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1374   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1375     if ( stream_.state == STREAM_RUNNING )\r
1376       AudioDeviceStop( handle->id[0], callbackHandler );\r
1377 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1378     AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );\r
1379 #else\r
1380     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1381     AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );\r
1382 #endif\r
1383   }\r
1384 \r
1385   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1386     if ( stream_.state == STREAM_RUNNING )\r
1387       AudioDeviceStop( handle->id[1], callbackHandler );\r
1388 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1389     AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );\r
1390 #else\r
1391     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1392     AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );\r
1393 #endif\r
1394   }\r
1395 \r
1396   for ( int i=0; i<2; i++ ) {\r
1397     if ( stream_.userBuffer[i] ) {\r
1398       free( stream_.userBuffer[i] );\r
1399       stream_.userBuffer[i] = 0;\r
1400     }\r
1401   }\r
1402 \r
1403   if ( stream_.deviceBuffer ) {\r
1404     free( stream_.deviceBuffer );\r
1405     stream_.deviceBuffer = 0;\r
1406   }\r
1407 \r
1408   // Destroy pthread condition variable.\r
1409   pthread_cond_destroy( &handle->condition );\r
1410   delete handle;\r
1411   stream_.apiHandle = 0;\r
1412 \r
1413   stream_.mode = UNINITIALIZED;\r
1414   stream_.state = STREAM_CLOSED;\r
1415 }\r
1416 \r
1417 void RtApiCore :: startStream( void )\r
1418 {\r
1419   verifyStream();\r
1420   if ( stream_.state == STREAM_RUNNING ) {\r
1421     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
1422     error( RtAudioError::WARNING );\r
1423     return;\r
1424   }\r
1425 \r
1426   OSStatus result = noErr;\r
1427   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1428   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1429 \r
1430     result = AudioDeviceStart( handle->id[0], callbackHandler );\r
1431     if ( result != noErr ) {\r
1432       errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";\r
1433       errorText_ = errorStream_.str();\r
1434       goto unlock;\r
1435     }\r
1436   }\r
1437 \r
1438   if ( stream_.mode == INPUT ||\r
1439        ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1440 \r
1441     result = AudioDeviceStart( handle->id[1], callbackHandler );\r
1442     if ( result != noErr ) {\r
1443       errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";\r
1444       errorText_ = errorStream_.str();\r
1445       goto unlock;\r
1446     }\r
1447   }\r
1448 \r
1449   handle->drainCounter = 0;\r
1450   handle->internalDrain = false;\r
1451   stream_.state = STREAM_RUNNING;\r
1452 \r
1453  unlock:\r
1454   if ( result == noErr ) return;\r
1455   error( RtAudioError::SYSTEM_ERROR );\r
1456 }\r
1457 \r
1458 void RtApiCore :: stopStream( void )\r
1459 {\r
1460   verifyStream();\r
1461   if ( stream_.state == STREAM_STOPPED ) {\r
1462     errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";\r
1463     error( RtAudioError::WARNING );\r
1464     return;\r
1465   }\r
1466 \r
1467   OSStatus result = noErr;\r
1468   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1469   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1470 \r
1471     if ( handle->drainCounter == 0 ) {\r
1472       handle->drainCounter = 2;\r
1473       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
1474     }\r
1475 \r
1476     result = AudioDeviceStop( handle->id[0], callbackHandler );\r
1477     if ( result != noErr ) {\r
1478       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
1479       errorText_ = errorStream_.str();\r
1480       goto unlock;\r
1481     }\r
1482   }\r
1483 \r
1484   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1485 \r
1486     result = AudioDeviceStop( handle->id[1], callbackHandler );\r
1487     if ( result != noErr ) {\r
1488       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
1489       errorText_ = errorStream_.str();\r
1490       goto unlock;\r
1491     }\r
1492   }\r
1493 \r
1494   stream_.state = STREAM_STOPPED;\r
1495 \r
1496  unlock:\r
1497   if ( result == noErr ) return;\r
1498   error( RtAudioError::SYSTEM_ERROR );\r
1499 }\r
1500 \r
1501 void RtApiCore :: abortStream( void )\r
1502 {\r
1503   verifyStream();\r
1504   if ( stream_.state == STREAM_STOPPED ) {\r
1505     errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";\r
1506     error( RtAudioError::WARNING );\r
1507     return;\r
1508   }\r
1509 \r
1510   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1511   handle->drainCounter = 2;\r
1512 \r
1513   stopStream();\r
1514 }\r
1515 \r
1516 // This function will be called by a spawned thread when the user\r
1517 // callback function signals that the stream should be stopped or\r
1518 // aborted.  It is better to handle it this way because the\r
1519 // callbackEvent() function probably should return before the AudioDeviceStop()\r
1520 // function is called.\r
1521 static void *coreStopStream( void *ptr )\r
1522 {\r
1523   CallbackInfo *info = (CallbackInfo *) ptr;\r
1524   RtApiCore *object = (RtApiCore *) info->object;\r
1525 \r
1526   object->stopStream();\r
1527   pthread_exit( NULL );\r
1528 }\r
1529 \r
1530 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,\r
1531                                  const AudioBufferList *inBufferList,\r
1532                                  const AudioBufferList *outBufferList )\r
1533 {\r
1534   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
1535   if ( stream_.state == STREAM_CLOSED ) {\r
1536     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
1537     error( RtAudioError::WARNING );\r
1538     return FAILURE;\r
1539   }\r
1540 \r
1541   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
1542   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1543 \r
1544   // Check if we were draining the stream and signal is finished.\r
1545   if ( handle->drainCounter > 3 ) {\r
1546     ThreadHandle threadId;\r
1547 \r
1548     stream_.state = STREAM_STOPPING;\r
1549     if ( handle->internalDrain == true )\r
1550       pthread_create( &threadId, NULL, coreStopStream, info );\r
1551     else // external call to stopStream()\r
1552       pthread_cond_signal( &handle->condition );\r
1553     return SUCCESS;\r
1554   }\r
1555 \r
1556   AudioDeviceID outputDevice = handle->id[0];\r
1557 \r
1558   // Invoke user callback to get fresh output data UNLESS we are\r
1559   // draining stream or duplex mode AND the input/output devices are\r
1560   // different AND this function is called for the input device.\r
1561   if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {\r
1562     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
1563     double streamTime = getStreamTime();\r
1564     RtAudioStreamStatus status = 0;\r
1565     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
1566       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
1567       handle->xrun[0] = false;\r
1568     }\r
1569     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
1570       status |= RTAUDIO_INPUT_OVERFLOW;\r
1571       handle->xrun[1] = false;\r
1572     }\r
1573 \r
1574     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
1575                                   stream_.bufferSize, streamTime, status, info->userData );\r
1576     if ( cbReturnValue == 2 ) {\r
1577       stream_.state = STREAM_STOPPING;\r
1578       handle->drainCounter = 2;\r
1579       abortStream();\r
1580       return SUCCESS;\r
1581     }\r
1582     else if ( cbReturnValue == 1 ) {\r
1583       handle->drainCounter = 1;\r
1584       handle->internalDrain = true;\r
1585     }\r
1586   }\r
1587 \r
1588   if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {\r
1589 \r
1590     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
1591 \r
1592       if ( handle->nStreams[0] == 1 ) {\r
1593         memset( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1594                 0,\r
1595                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1596       }\r
1597       else { // fill multiple streams with zeros\r
1598         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1599           memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1600                   0,\r
1601                   outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );\r
1602         }\r
1603       }\r
1604     }\r
1605     else if ( handle->nStreams[0] == 1 ) {\r
1606       if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer\r
1607         convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,\r
1608                        stream_.userBuffer[0], stream_.convertInfo[0] );\r
1609       }\r
1610       else { // copy from user buffer\r
1611         memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1612                 stream_.userBuffer[0],\r
1613                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1614       }\r
1615     }\r
1616     else { // fill multiple streams\r
1617       Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];\r
1618       if ( stream_.doConvertBuffer[0] ) {\r
1619         convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
1620         inBuffer = (Float32 *) stream_.deviceBuffer;\r
1621       }\r
1622 \r
1623       if ( stream_.deviceInterleaved[0] == false ) { // mono mode\r
1624         UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;\r
1625         for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
1626           memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1627                   (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );\r
1628         }\r
1629       }\r
1630       else { // fill multiple multi-channel streams with interleaved data\r
1631         UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;\r
1632         Float32 *out, *in;\r
1633 \r
1634         bool inInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1635         UInt32 inChannels = stream_.nUserChannels[0];\r
1636         if ( stream_.doConvertBuffer[0] ) {\r
1637           inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1638           inChannels = stream_.nDeviceChannels[0];\r
1639         }\r
1640 \r
1641         if ( inInterleaved ) inOffset = 1;\r
1642         else inOffset = stream_.bufferSize;\r
1643 \r
1644         channelsLeft = inChannels;\r
1645         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1646           in = inBuffer;\r
1647           out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;\r
1648           streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;\r
1649 \r
1650           outJump = 0;\r
1651           // Account for possible channel offset in first stream\r
1652           if ( i == 0 && stream_.channelOffset[0] > 0 ) {\r
1653             streamChannels -= stream_.channelOffset[0];\r
1654             outJump = stream_.channelOffset[0];\r
1655             out += outJump;\r
1656           }\r
1657 \r
1658           // Account for possible unfilled channels at end of the last stream\r
1659           if ( streamChannels > channelsLeft ) {\r
1660             outJump = streamChannels - channelsLeft;\r
1661             streamChannels = channelsLeft;\r
1662           }\r
1663 \r
1664           // Determine input buffer offsets and skips\r
1665           if ( inInterleaved ) {\r
1666             inJump = inChannels;\r
1667             in += inChannels - channelsLeft;\r
1668           }\r
1669           else {\r
1670             inJump = 1;\r
1671             in += (inChannels - channelsLeft) * inOffset;\r
1672           }\r
1673 \r
1674           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1675             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1676               *out++ = in[j*inOffset];\r
1677             }\r
1678             out += outJump;\r
1679             in += inJump;\r
1680           }\r
1681           channelsLeft -= streamChannels;\r
1682         }\r
1683       }\r
1684     }\r
1685 \r
1686     if ( handle->drainCounter ) {\r
1687       handle->drainCounter++;\r
1688       goto unlock;\r
1689     }\r
1690   }\r
1691 \r
1692   AudioDeviceID inputDevice;\r
1693   inputDevice = handle->id[1];\r
1694   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {\r
1695 \r
1696     if ( handle->nStreams[1] == 1 ) {\r
1697       if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer\r
1698         convertBuffer( stream_.userBuffer[1],\r
1699                        (char *) inBufferList->mBuffers[handle->iStream[1]].mData,\r
1700                        stream_.convertInfo[1] );\r
1701       }\r
1702       else { // copy to user buffer\r
1703         memcpy( stream_.userBuffer[1],\r
1704                 inBufferList->mBuffers[handle->iStream[1]].mData,\r
1705                 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );\r
1706       }\r
1707     }\r
1708     else { // read from multiple streams\r
1709       Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];\r
1710       if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;\r
1711 \r
1712       if ( stream_.deviceInterleaved[1] == false ) { // mono mode\r
1713         UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;\r
1714         for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
1715           memcpy( (void *)&outBuffer[i*stream_.bufferSize],\r
1716                   inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );\r
1717         }\r
1718       }\r
1719       else { // read from multiple multi-channel streams\r
1720         UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;\r
1721         Float32 *out, *in;\r
1722 \r
1723         bool outInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1724         UInt32 outChannels = stream_.nUserChannels[1];\r
1725         if ( stream_.doConvertBuffer[1] ) {\r
1726           outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1727           outChannels = stream_.nDeviceChannels[1];\r
1728         }\r
1729 \r
1730         if ( outInterleaved ) outOffset = 1;\r
1731         else outOffset = stream_.bufferSize;\r
1732 \r
1733         channelsLeft = outChannels;\r
1734         for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {\r
1735           out = outBuffer;\r
1736           in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;\r
1737           streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;\r
1738 \r
1739           inJump = 0;\r
1740           // Account for possible channel offset in first stream\r
1741           if ( i == 0 && stream_.channelOffset[1] > 0 ) {\r
1742             streamChannels -= stream_.channelOffset[1];\r
1743             inJump = stream_.channelOffset[1];\r
1744             in += inJump;\r
1745           }\r
1746 \r
1747           // Account for possible unread channels at end of the last stream\r
1748           if ( streamChannels > channelsLeft ) {\r
1749             inJump = streamChannels - channelsLeft;\r
1750             streamChannels = channelsLeft;\r
1751           }\r
1752 \r
1753           // Determine output buffer offsets and skips\r
1754           if ( outInterleaved ) {\r
1755             outJump = outChannels;\r
1756             out += outChannels - channelsLeft;\r
1757           }\r
1758           else {\r
1759             outJump = 1;\r
1760             out += (outChannels - channelsLeft) * outOffset;\r
1761           }\r
1762 \r
1763           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1764             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1765               out[j*outOffset] = *in++;\r
1766             }\r
1767             out += outJump;\r
1768             in += inJump;\r
1769           }\r
1770           channelsLeft -= streamChannels;\r
1771         }\r
1772       }\r
1773       \r
1774       if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
1775         convertBuffer( stream_.userBuffer[1],\r
1776                        stream_.deviceBuffer,\r
1777                        stream_.convertInfo[1] );\r
1778       }\r
1779     }\r
1780   }\r
1781 \r
1782  unlock:\r
1783   //MUTEX_UNLOCK( &stream_.mutex );\r
1784 \r
1785   RtApi::tickStreamTime();\r
1786   return SUCCESS;\r
1787 }\r
1788 \r
1789 const char* RtApiCore :: getErrorCode( OSStatus code )\r
1790 {\r
1791   switch( code ) {\r
1792 \r
1793   case kAudioHardwareNotRunningError:\r
1794     return "kAudioHardwareNotRunningError";\r
1795 \r
1796   case kAudioHardwareUnspecifiedError:\r
1797     return "kAudioHardwareUnspecifiedError";\r
1798 \r
1799   case kAudioHardwareUnknownPropertyError:\r
1800     return "kAudioHardwareUnknownPropertyError";\r
1801 \r
1802   case kAudioHardwareBadPropertySizeError:\r
1803     return "kAudioHardwareBadPropertySizeError";\r
1804 \r
1805   case kAudioHardwareIllegalOperationError:\r
1806     return "kAudioHardwareIllegalOperationError";\r
1807 \r
1808   case kAudioHardwareBadObjectError:\r
1809     return "kAudioHardwareBadObjectError";\r
1810 \r
1811   case kAudioHardwareBadDeviceError:\r
1812     return "kAudioHardwareBadDeviceError";\r
1813 \r
1814   case kAudioHardwareBadStreamError:\r
1815     return "kAudioHardwareBadStreamError";\r
1816 \r
1817   case kAudioHardwareUnsupportedOperationError:\r
1818     return "kAudioHardwareUnsupportedOperationError";\r
1819 \r
1820   case kAudioDeviceUnsupportedFormatError:\r
1821     return "kAudioDeviceUnsupportedFormatError";\r
1822 \r
1823   case kAudioDevicePermissionsError:\r
1824     return "kAudioDevicePermissionsError";\r
1825 \r
1826   default:\r
1827     return "CoreAudio unknown error";\r
1828   }\r
1829 }\r
1830 \r
1831   //******************** End of __MACOSX_CORE__ *********************//\r
1832 #endif\r
1833 \r
1834 #if defined(__UNIX_JACK__)\r
1835 \r
1836 // JACK is a low-latency audio server, originally written for the\r
1837 // GNU/Linux operating system and now also ported to OS-X. It can\r
1838 // connect a number of different applications to an audio device, as\r
1839 // well as allowing them to share audio between themselves.\r
1840 //\r
1841 // When using JACK with RtAudio, "devices" refer to JACK clients that\r
1842 // have ports connected to the server.  The JACK server is typically\r
1843 // started in a terminal as follows:\r
1844 //\r
1845 // .jackd -d alsa -d hw:0\r
1846 //\r
1847 // or through an interface program such as qjackctl.  Many of the\r
1848 // parameters normally set for a stream are fixed by the JACK server\r
1849 // and can be specified when the JACK server is started.  In\r
1850 // particular,\r
1851 //\r
1852 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4\r
1853 //\r
1854 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample\r
1855 // frames, and number of buffers = 4.  Once the server is running, it\r
1856 // is not possible to override these values.  If the values are not\r
1857 // specified in the command-line, the JACK server uses default values.\r
1858 //\r
1859 // The JACK server does not have to be running when an instance of\r
1860 // RtApiJack is created, though the function getDeviceCount() will\r
1861 // report 0 devices found until JACK has been started.  When no\r
1862 // devices are available (i.e., the JACK server is not running), a\r
1863 // stream cannot be opened.\r
1864 \r
1865 #include <jack/jack.h>\r
1866 #include <unistd.h>\r
1867 #include <cstdio>\r
1868 \r
1869 // A structure to hold various information related to the Jack API\r
1870 // implementation.\r
1871 struct JackHandle {\r
1872   jack_client_t *client;\r
1873   jack_port_t **ports[2];\r
1874   std::string deviceName[2];\r
1875   bool xrun[2];\r
1876   pthread_cond_t condition;\r
1877   int drainCounter;       // Tracks callback counts when draining\r
1878   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
1879 \r
1880   JackHandle()\r
1881     :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
1882 };\r
1883 \r
1884 static void jackSilentError( const char * ) {};\r
1885 \r
1886 RtApiJack :: RtApiJack()\r
1887 {\r
1888   // Nothing to do here.\r
1889 #if !defined(__RTAUDIO_DEBUG__)\r
1890   // Turn off Jack's internal error reporting.\r
1891   jack_set_error_function( &jackSilentError );\r
1892 #endif\r
1893 }\r
1894 \r
1895 RtApiJack :: ~RtApiJack()\r
1896 {\r
1897   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
1898 }\r
1899 \r
1900 unsigned int RtApiJack :: getDeviceCount( void )\r
1901 {\r
1902   // See if we can become a jack client.\r
1903   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
1904   jack_status_t *status = NULL;\r
1905   jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );\r
1906   if ( client == 0 ) return 0;\r
1907 \r
1908   const char **ports;\r
1909   std::string port, previousPort;\r
1910   unsigned int nChannels = 0, nDevices = 0;\r
1911   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1912   if ( ports ) {\r
1913     // Parse the port names up to the first colon (:).\r
1914     size_t iColon = 0;\r
1915     do {\r
1916       port = (char *) ports[ nChannels ];\r
1917       iColon = port.find(":");\r
1918       if ( iColon != std::string::npos ) {\r
1919         port = port.substr( 0, iColon + 1 );\r
1920         if ( port != previousPort ) {\r
1921           nDevices++;\r
1922           previousPort = port;\r
1923         }\r
1924       }\r
1925     } while ( ports[++nChannels] );\r
1926     free( ports );\r
1927   }\r
1928 \r
1929   jack_client_close( client );\r
1930   return nDevices;\r
1931 }\r
1932 \r
1933 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )\r
1934 {\r
1935   RtAudio::DeviceInfo info;\r
1936   info.probed = false;\r
1937 \r
1938   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption\r
1939   jack_status_t *status = NULL;\r
1940   jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );\r
1941   if ( client == 0 ) {\r
1942     errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";\r
1943     error( RtAudioError::WARNING );\r
1944     return info;\r
1945   }\r
1946 \r
1947   const char **ports;\r
1948   std::string port, previousPort;\r
1949   unsigned int nPorts = 0, nDevices = 0;\r
1950   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1951   if ( ports ) {\r
1952     // Parse the port names up to the first colon (:).\r
1953     size_t iColon = 0;\r
1954     do {\r
1955       port = (char *) ports[ nPorts ];\r
1956       iColon = port.find(":");\r
1957       if ( iColon != std::string::npos ) {\r
1958         port = port.substr( 0, iColon );\r
1959         if ( port != previousPort ) {\r
1960           if ( nDevices == device ) info.name = port;\r
1961           nDevices++;\r
1962           previousPort = port;\r
1963         }\r
1964       }\r
1965     } while ( ports[++nPorts] );\r
1966     free( ports );\r
1967   }\r
1968 \r
1969   if ( device >= nDevices ) {\r
1970     jack_client_close( client );\r
1971     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
1972     error( RtAudioError::INVALID_USE );\r
1973     return info;\r
1974   }\r
1975 \r
1976   // Get the current jack server sample rate.\r
1977   info.sampleRates.clear();\r
1978   info.sampleRates.push_back( jack_get_sample_rate( client ) );\r
1979 \r
1980   // Count the available ports containing the client name as device\r
1981   // channels.  Jack "input ports" equal RtAudio output channels.\r
1982   unsigned int nChannels = 0;\r
1983   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );\r
1984   if ( ports ) {\r
1985     while ( ports[ nChannels ] ) nChannels++;\r
1986     free( ports );\r
1987     info.outputChannels = nChannels;\r
1988   }\r
1989 \r
1990   // Jack "output ports" equal RtAudio input channels.\r
1991   nChannels = 0;\r
1992   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );\r
1993   if ( ports ) {\r
1994     while ( ports[ nChannels ] ) nChannels++;\r
1995     free( ports );\r
1996     info.inputChannels = nChannels;\r
1997   }\r
1998 \r
1999   if ( info.outputChannels == 0 && info.inputChannels == 0 ) {\r
2000     jack_client_close(client);\r
2001     errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";\r
2002     error( RtAudioError::WARNING );\r
2003     return info;\r
2004   }\r
2005 \r
2006   // If device opens for both playback and capture, we determine the channels.\r
2007   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2008     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2009 \r
2010   // Jack always uses 32-bit floats.\r
2011   info.nativeFormats = RTAUDIO_FLOAT32;\r
2012 \r
2013   // Jack doesn't provide default devices so we'll use the first available one.\r
2014   if ( device == 0 && info.outputChannels > 0 )\r
2015     info.isDefaultOutput = true;\r
2016   if ( device == 0 && info.inputChannels > 0 )\r
2017     info.isDefaultInput = true;\r
2018 \r
2019   jack_client_close(client);\r
2020   info.probed = true;\r
2021   return info;\r
2022 }\r
2023 \r
2024 static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
2025 {\r
2026   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2027 \r
2028   RtApiJack *object = (RtApiJack *) info->object;\r
2029   if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;\r
2030 \r
2031   return 0;\r
2032 }\r
2033 \r
2034 // This function will be called by a spawned thread when the Jack\r
2035 // server signals that it is shutting down.  It is necessary to handle\r
2036 // it this way because the jackShutdown() function must return before\r
2037 // the jack_deactivate() function (in closeStream()) will return.\r
2038 static void *jackCloseStream( void *ptr )\r
2039 {\r
2040   CallbackInfo *info = (CallbackInfo *) ptr;\r
2041   RtApiJack *object = (RtApiJack *) info->object;\r
2042 \r
2043   object->closeStream();\r
2044 \r
2045   pthread_exit( NULL );\r
2046 }\r
2047 static void jackShutdown( void *infoPointer )\r
2048 {\r
2049   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2050   RtApiJack *object = (RtApiJack *) info->object;\r
2051 \r
2052   // Check current stream state.  If stopped, then we'll assume this\r
2053   // was called as a result of a call to RtApiJack::stopStream (the\r
2054   // deactivation of a client handle causes this function to be called).\r
2055   // If not, we'll assume the Jack server is shutting down or some\r
2056   // other problem occurred and we should close the stream.\r
2057   if ( object->isStreamRunning() == false ) return;\r
2058 \r
2059   ThreadHandle threadId;\r
2060   pthread_create( &threadId, NULL, jackCloseStream, info );\r
2061   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
2062 }\r
2063 \r
2064 static int jackXrun( void *infoPointer )\r
2065 {\r
2066   JackHandle *handle = (JackHandle *) infoPointer;\r
2067 \r
2068   if ( handle->ports[0] ) handle->xrun[0] = true;\r
2069   if ( handle->ports[1] ) handle->xrun[1] = true;\r
2070 \r
2071   return 0;\r
2072 }\r
2073 \r
2074 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2075                                    unsigned int firstChannel, unsigned int sampleRate,\r
2076                                    RtAudioFormat format, unsigned int *bufferSize,\r
2077                                    RtAudio::StreamOptions *options )\r
2078 {\r
2079   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2080 \r
2081   // Look for jack server and try to become a client (only do once per stream).\r
2082   jack_client_t *client = 0;\r
2083   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {\r
2084     jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
2085     jack_status_t *status = NULL;\r
2086     if ( options && !options->streamName.empty() )\r
2087       client = jack_client_open( options->streamName.c_str(), jackoptions, status );\r
2088     else\r
2089       client = jack_client_open( "RtApiJack", jackoptions, status );\r
2090     if ( client == 0 ) {\r
2091       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";\r
2092       error( RtAudioError::WARNING );\r
2093       return FAILURE;\r
2094     }\r
2095   }\r
2096   else {\r
2097     // The handle must have been created on an earlier pass.\r
2098     client = handle->client;\r
2099   }\r
2100 \r
2101   const char **ports;\r
2102   std::string port, previousPort, deviceName;\r
2103   unsigned int nPorts = 0, nDevices = 0;\r
2104   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2105   if ( ports ) {\r
2106     // Parse the port names up to the first colon (:).\r
2107     size_t iColon = 0;\r
2108     do {\r
2109       port = (char *) ports[ nPorts ];\r
2110       iColon = port.find(":");\r
2111       if ( iColon != std::string::npos ) {\r
2112         port = port.substr( 0, iColon );\r
2113         if ( port != previousPort ) {\r
2114           if ( nDevices == device ) deviceName = port;\r
2115           nDevices++;\r
2116           previousPort = port;\r
2117         }\r
2118       }\r
2119     } while ( ports[++nPorts] );\r
2120     free( ports );\r
2121   }\r
2122 \r
2123   if ( device >= nDevices ) {\r
2124     errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";\r
2125     return FAILURE;\r
2126   }\r
2127 \r
2128   // Count the available ports containing the client name as device\r
2129   // channels.  Jack "input ports" equal RtAudio output channels.\r
2130   unsigned int nChannels = 0;\r
2131   unsigned long flag = JackPortIsInput;\r
2132   if ( mode == INPUT ) flag = JackPortIsOutput;\r
2133   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2134   if ( ports ) {\r
2135     while ( ports[ nChannels ] ) nChannels++;\r
2136     free( ports );\r
2137   }\r
2138 \r
2139   // Compare the jack ports for specified client to the requested number of channels.\r
2140   if ( nChannels < (channels + firstChannel) ) {\r
2141     errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";\r
2142     errorText_ = errorStream_.str();\r
2143     return FAILURE;\r
2144   }\r
2145 \r
2146   // Check the jack server sample rate.\r
2147   unsigned int jackRate = jack_get_sample_rate( client );\r
2148   if ( sampleRate != jackRate ) {\r
2149     jack_client_close( client );\r
2150     errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";\r
2151     errorText_ = errorStream_.str();\r
2152     return FAILURE;\r
2153   }\r
2154   stream_.sampleRate = jackRate;\r
2155 \r
2156   // Get the latency of the JACK port.\r
2157   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2158   if ( ports[ firstChannel ] ) {\r
2159     // Added by Ge Wang\r
2160     jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
2161     // the range (usually the min and max are equal)\r
2162     jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
2163     // get the latency range\r
2164     jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
2165     // be optimistic, use the min!\r
2166     stream_.latency[mode] = latrange.min;\r
2167     //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
2168   }\r
2169   free( ports );\r
2170 \r
2171   // The jack server always uses 32-bit floating-point data.\r
2172   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2173   stream_.userFormat = format;\r
2174 \r
2175   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
2176   else stream_.userInterleaved = true;\r
2177 \r
2178   // Jack always uses non-interleaved buffers.\r
2179   stream_.deviceInterleaved[mode] = false;\r
2180 \r
2181   // Jack always provides host byte-ordered data.\r
2182   stream_.doByteSwap[mode] = false;\r
2183 \r
2184   // Get the buffer size.  The buffer size and number of buffers\r
2185   // (periods) is set when the jack server is started.\r
2186   stream_.bufferSize = (int) jack_get_buffer_size( client );\r
2187   *bufferSize = stream_.bufferSize;\r
2188 \r
2189   stream_.nDeviceChannels[mode] = channels;\r
2190   stream_.nUserChannels[mode] = channels;\r
2191 \r
2192   // Set flags for buffer conversion.\r
2193   stream_.doConvertBuffer[mode] = false;\r
2194   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
2195     stream_.doConvertBuffer[mode] = true;\r
2196   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
2197        stream_.nUserChannels[mode] > 1 )\r
2198     stream_.doConvertBuffer[mode] = true;\r
2199 \r
2200   // Allocate our JackHandle structure for the stream.\r
2201   if ( handle == 0 ) {\r
2202     try {\r
2203       handle = new JackHandle;\r
2204     }\r
2205     catch ( std::bad_alloc& ) {\r
2206       errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";\r
2207       goto error;\r
2208     }\r
2209 \r
2210     if ( pthread_cond_init(&handle->condition, NULL) ) {\r
2211       errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";\r
2212       goto error;\r
2213     }\r
2214     stream_.apiHandle = (void *) handle;\r
2215     handle->client = client;\r
2216   }\r
2217   handle->deviceName[mode] = deviceName;\r
2218 \r
2219   // Allocate necessary internal buffers.\r
2220   unsigned long bufferBytes;\r
2221   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
2222   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
2223   if ( stream_.userBuffer[mode] == NULL ) {\r
2224     errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";\r
2225     goto error;\r
2226   }\r
2227 \r
2228   if ( stream_.doConvertBuffer[mode] ) {\r
2229 \r
2230     bool makeBuffer = true;\r
2231     if ( mode == OUTPUT )\r
2232       bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
2233     else { // mode == INPUT\r
2234       bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );\r
2235       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
2236         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);\r
2237         if ( bufferBytes < bytesOut ) makeBuffer = false;\r
2238       }\r
2239     }\r
2240 \r
2241     if ( makeBuffer ) {\r
2242       bufferBytes *= *bufferSize;\r
2243       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
2244       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
2245       if ( stream_.deviceBuffer == NULL ) {\r
2246         errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";\r
2247         goto error;\r
2248       }\r
2249     }\r
2250   }\r
2251 \r
2252   // Allocate memory for the Jack ports (channels) identifiers.\r
2253   handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );\r
2254   if ( handle->ports[mode] == NULL )  {\r
2255     errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";\r
2256     goto error;\r
2257   }\r
2258 \r
2259   stream_.device[mode] = device;\r
2260   stream_.channelOffset[mode] = firstChannel;\r
2261   stream_.state = STREAM_STOPPED;\r
2262   stream_.callbackInfo.object = (void *) this;\r
2263 \r
2264   if ( stream_.mode == OUTPUT && mode == INPUT )\r
2265     // We had already set up the stream for output.\r
2266     stream_.mode = DUPLEX;\r
2267   else {\r
2268     stream_.mode = mode;\r
2269     jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );\r
2270     jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );\r
2271     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );\r
2272   }\r
2273 \r
2274   // Register our ports.\r
2275   char label[64];\r
2276   if ( mode == OUTPUT ) {\r
2277     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2278       snprintf( label, 64, "outport %d", i );\r
2279       handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,\r
2280                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );\r
2281     }\r
2282   }\r
2283   else {\r
2284     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2285       snprintf( label, 64, "inport %d", i );\r
2286       handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,\r
2287                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );\r
2288     }\r
2289   }\r
2290 \r
2291   // Setup the buffer conversion information structure.  We don't use\r
2292   // buffers to do channel offsets, so we override that parameter\r
2293   // here.\r
2294   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
2295 \r
2296   return SUCCESS;\r
2297 \r
2298  error:\r
2299   if ( handle ) {\r
2300     pthread_cond_destroy( &handle->condition );\r
2301     jack_client_close( handle->client );\r
2302 \r
2303     if ( handle->ports[0] ) free( handle->ports[0] );\r
2304     if ( handle->ports[1] ) free( handle->ports[1] );\r
2305 \r
2306     delete handle;\r
2307     stream_.apiHandle = 0;\r
2308   }\r
2309 \r
2310   for ( int i=0; i<2; i++ ) {\r
2311     if ( stream_.userBuffer[i] ) {\r
2312       free( stream_.userBuffer[i] );\r
2313       stream_.userBuffer[i] = 0;\r
2314     }\r
2315   }\r
2316 \r
2317   if ( stream_.deviceBuffer ) {\r
2318     free( stream_.deviceBuffer );\r
2319     stream_.deviceBuffer = 0;\r
2320   }\r
2321 \r
2322   return FAILURE;\r
2323 }\r
2324 \r
2325 void RtApiJack :: closeStream( void )\r
2326 {\r
2327   if ( stream_.state == STREAM_CLOSED ) {\r
2328     errorText_ = "RtApiJack::closeStream(): no open stream to close!";\r
2329     error( RtAudioError::WARNING );\r
2330     return;\r
2331   }\r
2332 \r
2333   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2334   if ( handle ) {\r
2335 \r
2336     if ( stream_.state == STREAM_RUNNING )\r
2337       jack_deactivate( handle->client );\r
2338 \r
2339     jack_client_close( handle->client );\r
2340   }\r
2341 \r
2342   if ( handle ) {\r
2343     if ( handle->ports[0] ) free( handle->ports[0] );\r
2344     if ( handle->ports[1] ) free( handle->ports[1] );\r
2345     pthread_cond_destroy( &handle->condition );\r
2346     delete handle;\r
2347     stream_.apiHandle = 0;\r
2348   }\r
2349 \r
2350   for ( int i=0; i<2; i++ ) {\r
2351     if ( stream_.userBuffer[i] ) {\r
2352       free( stream_.userBuffer[i] );\r
2353       stream_.userBuffer[i] = 0;\r
2354     }\r
2355   }\r
2356 \r
2357   if ( stream_.deviceBuffer ) {\r
2358     free( stream_.deviceBuffer );\r
2359     stream_.deviceBuffer = 0;\r
2360   }\r
2361 \r
2362   stream_.mode = UNINITIALIZED;\r
2363   stream_.state = STREAM_CLOSED;\r
2364 }\r
2365 \r
2366 void RtApiJack :: startStream( void )\r
2367 {\r
2368   verifyStream();\r
2369   if ( stream_.state == STREAM_RUNNING ) {\r
2370     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
2371     error( RtAudioError::WARNING );\r
2372     return;\r
2373   }\r
2374 \r
2375   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2376   int result = jack_activate( handle->client );\r
2377   if ( result ) {\r
2378     errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";\r
2379     goto unlock;\r
2380   }\r
2381 \r
2382   const char **ports;\r
2383 \r
2384   // Get the list of available ports.\r
2385   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2386     result = 1;\r
2387     ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);\r
2388     if ( ports == NULL) {\r
2389       errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";\r
2390       goto unlock;\r
2391     }\r
2392 \r
2393     // Now make the port connections.  Since RtAudio wasn't designed to\r
2394     // allow the user to select particular channels of a device, we'll\r
2395     // just open the first "nChannels" ports with offset.\r
2396     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2397       result = 1;\r
2398       if ( ports[ stream_.channelOffset[0] + i ] )\r
2399         result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );\r
2400       if ( result ) {\r
2401         free( ports );\r
2402         errorText_ = "RtApiJack::startStream(): error connecting output ports!";\r
2403         goto unlock;\r
2404       }\r
2405     }\r
2406     free(ports);\r
2407   }\r
2408 \r
2409   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2410     result = 1;\r
2411     ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );\r
2412     if ( ports == NULL) {\r
2413       errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";\r
2414       goto unlock;\r
2415     }\r
2416 \r
2417     // Now make the port connections.  See note above.\r
2418     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2419       result = 1;\r
2420       if ( ports[ stream_.channelOffset[1] + i ] )\r
2421         result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );\r
2422       if ( result ) {\r
2423         free( ports );\r
2424         errorText_ = "RtApiJack::startStream(): error connecting input ports!";\r
2425         goto unlock;\r
2426       }\r
2427     }\r
2428     free(ports);\r
2429   }\r
2430 \r
2431   handle->drainCounter = 0;\r
2432   handle->internalDrain = false;\r
2433   stream_.state = STREAM_RUNNING;\r
2434 \r
2435  unlock:\r
2436   if ( result == 0 ) return;\r
2437   error( RtAudioError::SYSTEM_ERROR );\r
2438 }\r
2439 \r
2440 void RtApiJack :: stopStream( void )\r
2441 {\r
2442   verifyStream();\r
2443   if ( stream_.state == STREAM_STOPPED ) {\r
2444     errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";\r
2445     error( RtAudioError::WARNING );\r
2446     return;\r
2447   }\r
2448 \r
2449   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2450   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2451 \r
2452     if ( handle->drainCounter == 0 ) {\r
2453       handle->drainCounter = 2;\r
2454       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
2455     }\r
2456   }\r
2457 \r
2458   jack_deactivate( handle->client );\r
2459   stream_.state = STREAM_STOPPED;\r
2460 }\r
2461 \r
2462 void RtApiJack :: abortStream( void )\r
2463 {\r
2464   verifyStream();\r
2465   if ( stream_.state == STREAM_STOPPED ) {\r
2466     errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";\r
2467     error( RtAudioError::WARNING );\r
2468     return;\r
2469   }\r
2470 \r
2471   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2472   handle->drainCounter = 2;\r
2473 \r
2474   stopStream();\r
2475 }\r
2476 \r
2477 // This function will be called by a spawned thread when the user\r
2478 // callback function signals that the stream should be stopped or\r
2479 // aborted.  It is necessary to handle it this way because the\r
2480 // callbackEvent() function must return before the jack_deactivate()\r
2481 // function will return.\r
2482 static void *jackStopStream( void *ptr )\r
2483 {\r
2484   CallbackInfo *info = (CallbackInfo *) ptr;\r
2485   RtApiJack *object = (RtApiJack *) info->object;\r
2486 \r
2487   object->stopStream();\r
2488   pthread_exit( NULL );\r
2489 }\r
2490 \r
2491 bool RtApiJack :: callbackEvent( unsigned long nframes )\r
2492 {\r
2493   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
2494   if ( stream_.state == STREAM_CLOSED ) {\r
2495     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
2496     error( RtAudioError::WARNING );\r
2497     return FAILURE;\r
2498   }\r
2499   if ( stream_.bufferSize != nframes ) {\r
2500     errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";\r
2501     error( RtAudioError::WARNING );\r
2502     return FAILURE;\r
2503   }\r
2504 \r
2505   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
2506   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2507 \r
2508   // Check if we were draining the stream and signal is finished.\r
2509   if ( handle->drainCounter > 3 ) {\r
2510     ThreadHandle threadId;\r
2511 \r
2512     stream_.state = STREAM_STOPPING;\r
2513     if ( handle->internalDrain == true )\r
2514       pthread_create( &threadId, NULL, jackStopStream, info );\r
2515     else\r
2516       pthread_cond_signal( &handle->condition );\r
2517     return SUCCESS;\r
2518   }\r
2519 \r
2520   // Invoke user callback first, to get fresh output data.\r
2521   if ( handle->drainCounter == 0 ) {\r
2522     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
2523     double streamTime = getStreamTime();\r
2524     RtAudioStreamStatus status = 0;\r
2525     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
2526       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
2527       handle->xrun[0] = false;\r
2528     }\r
2529     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
2530       status |= RTAUDIO_INPUT_OVERFLOW;\r
2531       handle->xrun[1] = false;\r
2532     }\r
2533     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
2534                                   stream_.bufferSize, streamTime, status, info->userData );\r
2535     if ( cbReturnValue == 2 ) {\r
2536       stream_.state = STREAM_STOPPING;\r
2537       handle->drainCounter = 2;\r
2538       ThreadHandle id;\r
2539       pthread_create( &id, NULL, jackStopStream, info );\r
2540       return SUCCESS;\r
2541     }\r
2542     else if ( cbReturnValue == 1 ) {\r
2543       handle->drainCounter = 1;\r
2544       handle->internalDrain = true;\r
2545     }\r
2546   }\r
2547 \r
2548   jack_default_audio_sample_t *jackbuffer;\r
2549   unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );\r
2550   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2551 \r
2552     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
2553 \r
2554       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2555         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2556         memset( jackbuffer, 0, bufferBytes );\r
2557       }\r
2558 \r
2559     }\r
2560     else if ( stream_.doConvertBuffer[0] ) {\r
2561 \r
2562       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
2563 \r
2564       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2565         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2566         memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );\r
2567       }\r
2568     }\r
2569     else { // no buffer conversion\r
2570       for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2571         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2572         memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
2573       }\r
2574     }\r
2575 \r
2576     if ( handle->drainCounter ) {\r
2577       handle->drainCounter++;\r
2578       goto unlock;\r
2579     }\r
2580   }\r
2581 \r
2582   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2583 \r
2584     if ( stream_.doConvertBuffer[1] ) {\r
2585       for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {\r
2586         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2587         memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );\r
2588       }\r
2589       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
2590     }\r
2591     else { // no buffer conversion\r
2592       for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2593         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2594         memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );\r
2595       }\r
2596     }\r
2597   }\r
2598 \r
2599  unlock:\r
2600   RtApi::tickStreamTime();\r
2601   return SUCCESS;\r
2602 }\r
2603   //******************** End of __UNIX_JACK__ *********************//\r
2604 #endif\r
2605 \r
2606 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows\r
2607 \r
2608 // The ASIO API is designed around a callback scheme, so this\r
2609 // implementation is similar to that used for OS-X CoreAudio and Linux\r
2610 // Jack.  The primary constraint with ASIO is that it only allows\r
2611 // access to a single driver at a time.  Thus, it is not possible to\r
2612 // have more than one simultaneous RtAudio stream.\r
2613 //\r
2614 // This implementation also requires a number of external ASIO files\r
2615 // and a few global variables.  The ASIO callback scheme does not\r
2616 // allow for the passing of user data, so we must create a global\r
2617 // pointer to our callbackInfo structure.\r
2618 //\r
2619 // On unix systems, we make use of a pthread condition variable.\r
2620 // Since there is no equivalent in Windows, I hacked something based\r
2621 // on information found in\r
2622 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r
2623 \r
2624 #include "asiosys.h"\r
2625 #include "asio.h"\r
2626 #include "iasiothiscallresolver.h"\r
2627 #include "asiodrivers.h"\r
2628 #include <cmath>\r
2629 \r
2630 static AsioDrivers drivers;\r
2631 static ASIOCallbacks asioCallbacks;\r
2632 static ASIODriverInfo driverInfo;\r
2633 static CallbackInfo *asioCallbackInfo;\r
2634 static bool asioXRun;\r
2635 \r
2636 struct AsioHandle {\r
2637   int drainCounter;       // Tracks callback counts when draining\r
2638   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
2639   ASIOBufferInfo *bufferInfos;\r
2640   HANDLE condition;\r
2641 \r
2642   AsioHandle()\r
2643     :drainCounter(0), internalDrain(false), bufferInfos(0) {}\r
2644 };\r
2645 \r
2646 // Function declarations (definitions at end of section)\r
2647 static const char* getAsioErrorString( ASIOError result );\r
2648 static void sampleRateChanged( ASIOSampleRate sRate );\r
2649 static long asioMessages( long selector, long value, void* message, double* opt );\r
2650 \r
2651 RtApiAsio :: RtApiAsio()\r
2652 {\r
2653   // ASIO cannot run on a multi-threaded appartment. You can call\r
2654   // CoInitialize beforehand, but it must be for appartment threading\r
2655   // (in which case, CoInitilialize will return S_FALSE here).\r
2656   coInitialized_ = false;\r
2657   HRESULT hr = CoInitialize( NULL ); \r
2658   if ( FAILED(hr) ) {\r
2659     errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
2660     error( RtAudioError::WARNING );\r
2661   }\r
2662   coInitialized_ = true;\r
2663 \r
2664   drivers.removeCurrentDriver();\r
2665   driverInfo.asioVersion = 2;\r
2666 \r
2667   // See note in DirectSound implementation about GetDesktopWindow().\r
2668   driverInfo.sysRef = GetForegroundWindow();\r
2669 }\r
2670 \r
2671 RtApiAsio :: ~RtApiAsio()\r
2672 {\r
2673   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
2674   if ( coInitialized_ ) CoUninitialize();\r
2675 }\r
2676 \r
2677 unsigned int RtApiAsio :: getDeviceCount( void )\r
2678 {\r
2679   return (unsigned int) drivers.asioGetNumDev();\r
2680 }\r
2681 \r
2682 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )\r
2683 {\r
2684   RtAudio::DeviceInfo info;\r
2685   info.probed = false;\r
2686 \r
2687   // Get device ID\r
2688   unsigned int nDevices = getDeviceCount();\r
2689   if ( nDevices == 0 ) {\r
2690     errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
2691     error( RtAudioError::INVALID_USE );\r
2692     return info;\r
2693   }\r
2694 \r
2695   if ( device >= nDevices ) {\r
2696     errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
2697     error( RtAudioError::INVALID_USE );\r
2698     return info;\r
2699   }\r
2700 \r
2701   // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.\r
2702   if ( stream_.state != STREAM_CLOSED ) {\r
2703     if ( device >= devices_.size() ) {\r
2704       errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";\r
2705       error( RtAudioError::WARNING );\r
2706       return info;\r
2707     }\r
2708     return devices_[ device ];\r
2709   }\r
2710 \r
2711   char driverName[32];\r
2712   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2713   if ( result != ASE_OK ) {\r
2714     errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2715     errorText_ = errorStream_.str();\r
2716     error( RtAudioError::WARNING );\r
2717     return info;\r
2718   }\r
2719 \r
2720   info.name = driverName;\r
2721 \r
2722   if ( !drivers.loadDriver( driverName ) ) {\r
2723     errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";\r
2724     errorText_ = errorStream_.str();\r
2725     error( RtAudioError::WARNING );\r
2726     return info;\r
2727   }\r
2728 \r
2729   result = ASIOInit( &driverInfo );\r
2730   if ( result != ASE_OK ) {\r
2731     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2732     errorText_ = errorStream_.str();\r
2733     error( RtAudioError::WARNING );\r
2734     return info;\r
2735   }\r
2736 \r
2737   // Determine the device channel information.\r
2738   long inputChannels, outputChannels;\r
2739   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2740   if ( result != ASE_OK ) {\r
2741     drivers.removeCurrentDriver();\r
2742     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2743     errorText_ = errorStream_.str();\r
2744     error( RtAudioError::WARNING );\r
2745     return info;\r
2746   }\r
2747 \r
2748   info.outputChannels = outputChannels;\r
2749   info.inputChannels = inputChannels;\r
2750   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2751     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2752 \r
2753   // Determine the supported sample rates.\r
2754   info.sampleRates.clear();\r
2755   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
2756     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
2757     if ( result == ASE_OK )\r
2758       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
2759   }\r
2760 \r
2761   // Determine supported data types ... just check first channel and assume rest are the same.\r
2762   ASIOChannelInfo channelInfo;\r
2763   channelInfo.channel = 0;\r
2764   channelInfo.isInput = true;\r
2765   if ( info.inputChannels <= 0 ) channelInfo.isInput = false;\r
2766   result = ASIOGetChannelInfo( &channelInfo );\r
2767   if ( result != ASE_OK ) {\r
2768     drivers.removeCurrentDriver();\r
2769     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";\r
2770     errorText_ = errorStream_.str();\r
2771     error( RtAudioError::WARNING );\r
2772     return info;\r
2773   }\r
2774 \r
2775   info.nativeFormats = 0;\r
2776   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )\r
2777     info.nativeFormats |= RTAUDIO_SINT16;\r
2778   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )\r
2779     info.nativeFormats |= RTAUDIO_SINT32;\r
2780   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )\r
2781     info.nativeFormats |= RTAUDIO_FLOAT32;\r
2782   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
2783     info.nativeFormats |= RTAUDIO_FLOAT64;\r
2784   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
2785     info.nativeFormats |= RTAUDIO_SINT24;\r
2786 \r
2787   if ( info.outputChannels > 0 )\r
2788     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
2789   if ( info.inputChannels > 0 )\r
2790     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
2791 \r
2792   info.probed = true;\r
2793   drivers.removeCurrentDriver();\r
2794   return info;\r
2795 }\r
2796 \r
2797 static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
2798 {\r
2799   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
2800   object->callbackEvent( index );\r
2801 }\r
2802 \r
2803 void RtApiAsio :: saveDeviceInfo( void )\r
2804 {\r
2805   devices_.clear();\r
2806 \r
2807   unsigned int nDevices = getDeviceCount();\r
2808   devices_.resize( nDevices );\r
2809   for ( unsigned int i=0; i<nDevices; i++ )\r
2810     devices_[i] = getDeviceInfo( i );\r
2811 }\r
2812 \r
2813 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2814                                    unsigned int firstChannel, unsigned int sampleRate,\r
2815                                    RtAudioFormat format, unsigned int *bufferSize,\r
2816                                    RtAudio::StreamOptions *options )\r
2817 {\r
2818   // For ASIO, a duplex stream MUST use the same driver.\r
2819   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {\r
2820     errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
2821     return FAILURE;\r
2822   }\r
2823 \r
2824   char driverName[32];\r
2825   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2826   if ( result != ASE_OK ) {\r
2827     errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2828     errorText_ = errorStream_.str();\r
2829     return FAILURE;\r
2830   }\r
2831 \r
2832   // Only load the driver once for duplex stream.\r
2833   if ( mode != INPUT || stream_.mode != OUTPUT ) {\r
2834     // The getDeviceInfo() function will not work when a stream is open\r
2835     // because ASIO does not allow multiple devices to run at the same\r
2836     // time.  Thus, we'll probe the system before opening a stream and\r
2837     // save the results for use by getDeviceInfo().\r
2838     this->saveDeviceInfo();\r
2839 \r
2840     if ( !drivers.loadDriver( driverName ) ) {\r
2841       errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
2842       errorText_ = errorStream_.str();\r
2843       return FAILURE;\r
2844     }\r
2845 \r
2846     result = ASIOInit( &driverInfo );\r
2847     if ( result != ASE_OK ) {\r
2848       errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2849       errorText_ = errorStream_.str();\r
2850       return FAILURE;\r
2851     }\r
2852   }\r
2853 \r
2854   // Check the device channel count.\r
2855   long inputChannels, outputChannels;\r
2856   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2857   if ( result != ASE_OK ) {\r
2858     drivers.removeCurrentDriver();\r
2859     errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2860     errorText_ = errorStream_.str();\r
2861     return FAILURE;\r
2862   }\r
2863 \r
2864   if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
2865        ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
2866     drivers.removeCurrentDriver();\r
2867     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
2868     errorText_ = errorStream_.str();\r
2869     return FAILURE;\r
2870   }\r
2871   stream_.nDeviceChannels[mode] = channels;\r
2872   stream_.nUserChannels[mode] = channels;\r
2873   stream_.channelOffset[mode] = firstChannel;\r
2874 \r
2875   // Verify the sample rate is supported.\r
2876   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
2877   if ( result != ASE_OK ) {\r
2878     drivers.removeCurrentDriver();\r
2879     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
2880     errorText_ = errorStream_.str();\r
2881     return FAILURE;\r
2882   }\r
2883 \r
2884   // Get the current sample rate\r
2885   ASIOSampleRate currentRate;\r
2886   result = ASIOGetSampleRate( &currentRate );\r
2887   if ( result != ASE_OK ) {\r
2888     drivers.removeCurrentDriver();\r
2889     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
2890     errorText_ = errorStream_.str();\r
2891     return FAILURE;\r
2892   }\r
2893 \r
2894   // Set the sample rate only if necessary\r
2895   if ( currentRate != sampleRate ) {\r
2896     result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
2897     if ( result != ASE_OK ) {\r
2898       drivers.removeCurrentDriver();\r
2899       errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
2900       errorText_ = errorStream_.str();\r
2901       return FAILURE;\r
2902     }\r
2903   }\r
2904 \r
2905   // Determine the driver data type.\r
2906   ASIOChannelInfo channelInfo;\r
2907   channelInfo.channel = 0;\r
2908   if ( mode == OUTPUT ) channelInfo.isInput = false;\r
2909   else channelInfo.isInput = true;\r
2910   result = ASIOGetChannelInfo( &channelInfo );\r
2911   if ( result != ASE_OK ) {\r
2912     drivers.removeCurrentDriver();\r
2913     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
2914     errorText_ = errorStream_.str();\r
2915     return FAILURE;\r
2916   }\r
2917 \r
2918   // Assuming WINDOWS host is always little-endian.\r
2919   stream_.doByteSwap[mode] = false;\r
2920   stream_.userFormat = format;\r
2921   stream_.deviceFormat[mode] = 0;\r
2922   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {\r
2923     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
2924     if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;\r
2925   }\r
2926   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {\r
2927     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
2928     if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;\r
2929   }\r
2930   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {\r
2931     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2932     if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;\r
2933   }\r
2934   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {\r
2935     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
2936     if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
2937   }\r
2938   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
2939     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
2940     if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
2941   }\r
2942 \r
2943   if ( stream_.deviceFormat[mode] == 0 ) {\r
2944     drivers.removeCurrentDriver();\r
2945     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
2946     errorText_ = errorStream_.str();\r
2947     return FAILURE;\r
2948   }\r
2949 \r
2950   // Set the buffer size.  For a duplex stream, this will end up\r
2951   // setting the buffer size based on the input constraints, which\r
2952   // should be ok.\r
2953   long minSize, maxSize, preferSize, granularity;\r
2954   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
2955   if ( result != ASE_OK ) {\r
2956     drivers.removeCurrentDriver();\r
2957     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
2958     errorText_ = errorStream_.str();\r
2959     return FAILURE;\r
2960   }\r
2961 \r
2962   if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
2963   else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
2964   else if ( granularity == -1 ) {\r
2965     // Make sure bufferSize is a power of two.\r
2966     int log2_of_min_size = 0;\r
2967     int log2_of_max_size = 0;\r
2968 \r
2969     for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
2970       if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
2971       if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
2972     }\r
2973 \r
2974     long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
2975     int min_delta_num = log2_of_min_size;\r
2976 \r
2977     for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
2978       long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
2979       if (current_delta < min_delta) {\r
2980         min_delta = current_delta;\r
2981         min_delta_num = i;\r
2982       }\r
2983     }\r
2984 \r
2985     *bufferSize = ( (unsigned int)1 << min_delta_num );\r
2986     if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
2987     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
2988   }\r
2989   else if ( granularity != 0 ) {\r
2990     // Set to an even multiple of granularity, rounding up.\r
2991     *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
2992   }\r
2993 \r
2994   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {\r
2995     drivers.removeCurrentDriver();\r
2996     errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
2997     return FAILURE;\r
2998   }\r
2999 \r
3000   stream_.bufferSize = *bufferSize;\r
3001   stream_.nBuffers = 2;\r
3002 \r
3003   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
3004   else stream_.userInterleaved = true;\r
3005 \r
3006   // ASIO always uses non-interleaved buffers.\r
3007   stream_.deviceInterleaved[mode] = false;\r
3008 \r
3009   // Allocate, if necessary, our AsioHandle structure for the stream.\r
3010   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3011   if ( handle == 0 ) {\r
3012     try {\r
3013       handle = new AsioHandle;\r
3014     }\r
3015     catch ( std::bad_alloc& ) {\r
3016       //if ( handle == NULL ) {    \r
3017       drivers.removeCurrentDriver();\r
3018       errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
3019       return FAILURE;\r
3020     }\r
3021     handle->bufferInfos = 0;\r
3022 \r
3023     // Create a manual-reset event.\r
3024     handle->condition = CreateEvent( NULL,   // no security\r
3025                                      TRUE,   // manual-reset\r
3026                                      FALSE,  // non-signaled initially\r
3027                                      NULL ); // unnamed\r
3028     stream_.apiHandle = (void *) handle;\r
3029   }\r
3030 \r
3031   // Create the ASIO internal buffers.  Since RtAudio sets up input\r
3032   // and output separately, we'll have to dispose of previously\r
3033   // created output buffers for a duplex stream.\r
3034   long inputLatency, outputLatency;\r
3035   if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
3036     ASIODisposeBuffers();\r
3037     if ( handle->bufferInfos ) free( handle->bufferInfos );\r
3038   }\r
3039 \r
3040   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
3041   bool buffersAllocated = false;\r
3042   unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3043   handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
3044   if ( handle->bufferInfos == NULL ) {\r
3045     errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
3046     errorText_ = errorStream_.str();\r
3047     goto error;\r
3048   }\r
3049 \r
3050   ASIOBufferInfo *infos;\r
3051   infos = handle->bufferInfos;\r
3052   for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {\r
3053     infos->isInput = ASIOFalse;\r
3054     infos->channelNum = i + stream_.channelOffset[0];\r
3055     infos->buffers[0] = infos->buffers[1] = 0;\r
3056   }\r
3057   for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {\r
3058     infos->isInput = ASIOTrue;\r
3059     infos->channelNum = i + stream_.channelOffset[1];\r
3060     infos->buffers[0] = infos->buffers[1] = 0;\r
3061   }\r
3062 \r
3063   // Set up the ASIO callback structure and create the ASIO data buffers.\r
3064   asioCallbacks.bufferSwitch = &bufferSwitch;\r
3065   asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
3066   asioCallbacks.asioMessage = &asioMessages;\r
3067   asioCallbacks.bufferSwitchTimeInfo = NULL;\r
3068   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3069   if ( result != ASE_OK ) {\r
3070     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
3071     errorText_ = errorStream_.str();\r
3072     goto error;\r
3073   }\r
3074   buffersAllocated = true;\r
3075 \r
3076   // Set flags for buffer conversion.\r
3077   stream_.doConvertBuffer[mode] = false;\r
3078   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
3079     stream_.doConvertBuffer[mode] = true;\r
3080   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
3081        stream_.nUserChannels[mode] > 1 )\r
3082     stream_.doConvertBuffer[mode] = true;\r
3083 \r
3084   // Allocate necessary internal buffers\r
3085   unsigned long bufferBytes;\r
3086   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
3087   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
3088   if ( stream_.userBuffer[mode] == NULL ) {\r
3089     errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";\r
3090     goto error;\r
3091   }\r
3092 \r
3093   if ( stream_.doConvertBuffer[mode] ) {\r
3094 \r
3095     bool makeBuffer = true;\r
3096     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
3097     if ( mode == INPUT ) {\r
3098       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
3099         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
3100         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
3101       }\r
3102     }\r
3103 \r
3104     if ( makeBuffer ) {\r
3105       bufferBytes *= *bufferSize;\r
3106       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
3107       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
3108       if ( stream_.deviceBuffer == NULL ) {\r
3109         errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";\r
3110         goto error;\r
3111       }\r
3112     }\r
3113   }\r
3114 \r
3115   stream_.sampleRate = sampleRate;\r
3116   stream_.device[mode] = device;\r
3117   stream_.state = STREAM_STOPPED;\r
3118   asioCallbackInfo = &stream_.callbackInfo;\r
3119   stream_.callbackInfo.object = (void *) this;\r
3120   if ( stream_.mode == OUTPUT && mode == INPUT )\r
3121     // We had already set up an output stream.\r
3122     stream_.mode = DUPLEX;\r
3123   else\r
3124     stream_.mode = mode;\r
3125 \r
3126   // Determine device latencies\r
3127   result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
3128   if ( result != ASE_OK ) {\r
3129     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
3130     errorText_ = errorStream_.str();\r
3131     error( RtAudioError::WARNING); // warn but don't fail\r
3132   }\r
3133   else {\r
3134     stream_.latency[0] = outputLatency;\r
3135     stream_.latency[1] = inputLatency;\r
3136   }\r
3137 \r
3138   // Setup the buffer conversion information structure.  We don't use\r
3139   // buffers to do channel offsets, so we override that parameter\r
3140   // here.\r
3141   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
3142 \r
3143   return SUCCESS;\r
3144 \r
3145  error:\r
3146   if ( buffersAllocated )\r
3147     ASIODisposeBuffers();\r
3148   drivers.removeCurrentDriver();\r
3149 \r
3150   if ( handle ) {\r
3151     CloseHandle( handle->condition );\r
3152     if ( handle->bufferInfos )\r
3153       free( handle->bufferInfos );\r
3154     delete handle;\r
3155     stream_.apiHandle = 0;\r
3156   }\r
3157 \r
3158   for ( int i=0; i<2; i++ ) {\r
3159     if ( stream_.userBuffer[i] ) {\r
3160       free( stream_.userBuffer[i] );\r
3161       stream_.userBuffer[i] = 0;\r
3162     }\r
3163   }\r
3164 \r
3165   if ( stream_.deviceBuffer ) {\r
3166     free( stream_.deviceBuffer );\r
3167     stream_.deviceBuffer = 0;\r
3168   }\r
3169 \r
3170   return FAILURE;\r
3171 }\r
3172 \r
3173 void RtApiAsio :: closeStream()\r
3174 {\r
3175   if ( stream_.state == STREAM_CLOSED ) {\r
3176     errorText_ = "RtApiAsio::closeStream(): no open stream to close!";\r
3177     error( RtAudioError::WARNING );\r
3178     return;\r
3179   }\r
3180 \r
3181   if ( stream_.state == STREAM_RUNNING ) {\r
3182     stream_.state = STREAM_STOPPED;\r
3183     ASIOStop();\r
3184   }\r
3185   ASIODisposeBuffers();\r
3186   drivers.removeCurrentDriver();\r
3187 \r
3188   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3189   if ( handle ) {\r
3190     CloseHandle( handle->condition );\r
3191     if ( handle->bufferInfos )\r
3192       free( handle->bufferInfos );\r
3193     delete handle;\r
3194     stream_.apiHandle = 0;\r
3195   }\r
3196 \r
3197   for ( int i=0; i<2; i++ ) {\r
3198     if ( stream_.userBuffer[i] ) {\r
3199       free( stream_.userBuffer[i] );\r
3200       stream_.userBuffer[i] = 0;\r
3201     }\r
3202   }\r
3203 \r
3204   if ( stream_.deviceBuffer ) {\r
3205     free( stream_.deviceBuffer );\r
3206     stream_.deviceBuffer = 0;\r
3207   }\r
3208 \r
3209   stream_.mode = UNINITIALIZED;\r
3210   stream_.state = STREAM_CLOSED;\r
3211 }\r
3212 \r
3213 bool stopThreadCalled = false;\r
3214 \r
3215 void RtApiAsio :: startStream()\r
3216 {\r
3217   verifyStream();\r
3218   if ( stream_.state == STREAM_RUNNING ) {\r
3219     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
3220     error( RtAudioError::WARNING );\r
3221     return;\r
3222   }\r
3223 \r
3224   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3225   ASIOError result = ASIOStart();\r
3226   if ( result != ASE_OK ) {\r
3227     errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";\r
3228     errorText_ = errorStream_.str();\r
3229     goto unlock;\r
3230   }\r
3231 \r
3232   handle->drainCounter = 0;\r
3233   handle->internalDrain = false;\r
3234   ResetEvent( handle->condition );\r
3235   stream_.state = STREAM_RUNNING;\r
3236   asioXRun = false;\r
3237 \r
3238  unlock:\r
3239   stopThreadCalled = false;\r
3240 \r
3241   if ( result == ASE_OK ) return;\r
3242   error( RtAudioError::SYSTEM_ERROR );\r
3243 }\r
3244 \r
3245 void RtApiAsio :: stopStream()\r
3246 {\r
3247   verifyStream();\r
3248   if ( stream_.state == STREAM_STOPPED ) {\r
3249     errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";\r
3250     error( RtAudioError::WARNING );\r
3251     return;\r
3252   }\r
3253 \r
3254   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3255   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3256     if ( handle->drainCounter == 0 ) {\r
3257       handle->drainCounter = 2;\r
3258       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
3259     }\r
3260   }\r
3261 \r
3262   stream_.state = STREAM_STOPPED;\r
3263 \r
3264   ASIOError result = ASIOStop();\r
3265   if ( result != ASE_OK ) {\r
3266     errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";\r
3267     errorText_ = errorStream_.str();\r
3268   }\r
3269 \r
3270   if ( result == ASE_OK ) return;\r
3271   error( RtAudioError::SYSTEM_ERROR );\r
3272 }\r
3273 \r
3274 void RtApiAsio :: abortStream()\r
3275 {\r
3276   verifyStream();\r
3277   if ( stream_.state == STREAM_STOPPED ) {\r
3278     errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";\r
3279     error( RtAudioError::WARNING );\r
3280     return;\r
3281   }\r
3282 \r
3283   // The following lines were commented-out because some behavior was\r
3284   // noted where the device buffers need to be zeroed to avoid\r
3285   // continuing sound, even when the device buffers are completely\r
3286   // disposed.  So now, calling abort is the same as calling stop.\r
3287   // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3288   // handle->drainCounter = 2;\r
3289   stopStream();\r
3290 }\r
3291 \r
3292 // This function will be called by a spawned thread when the user\r
3293 // callback function signals that the stream should be stopped or\r
3294 // aborted.  It is necessary to handle it this way because the\r
3295 // callbackEvent() function must return before the ASIOStop()\r
3296 // function will return.\r
3297 static unsigned __stdcall asioStopStream( void *ptr )\r
3298 {\r
3299   CallbackInfo *info = (CallbackInfo *) ptr;\r
3300   RtApiAsio *object = (RtApiAsio *) info->object;\r
3301 \r
3302   object->stopStream();\r
3303   _endthreadex( 0 );\r
3304   return 0;\r
3305 }\r
3306 \r
3307 bool RtApiAsio :: callbackEvent( long bufferIndex )\r
3308 {\r
3309   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
3310   if ( stream_.state == STREAM_CLOSED ) {\r
3311     errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
3312     error( RtAudioError::WARNING );\r
3313     return FAILURE;\r
3314   }\r
3315 \r
3316   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
3317   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3318 \r
3319   // Check if we were draining the stream and signal if finished.\r
3320   if ( handle->drainCounter > 3 ) {\r
3321 \r
3322     stream_.state = STREAM_STOPPING;\r
3323     if ( handle->internalDrain == false )\r
3324       SetEvent( handle->condition );\r
3325     else { // spawn a thread to stop the stream\r
3326       unsigned threadId;\r
3327       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3328                                                     &stream_.callbackInfo, 0, &threadId );\r
3329     }\r
3330     return SUCCESS;\r
3331   }\r
3332 \r
3333   // Invoke user callback to get fresh output data UNLESS we are\r
3334   // draining stream.\r
3335   if ( handle->drainCounter == 0 ) {\r
3336     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
3337     double streamTime = getStreamTime();\r
3338     RtAudioStreamStatus status = 0;\r
3339     if ( stream_.mode != INPUT && asioXRun == true ) {\r
3340       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
3341       asioXRun = false;\r
3342     }\r
3343     if ( stream_.mode != OUTPUT && asioXRun == true ) {\r
3344       status |= RTAUDIO_INPUT_OVERFLOW;\r
3345       asioXRun = false;\r
3346     }\r
3347     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
3348                                      stream_.bufferSize, streamTime, status, info->userData );\r
3349     if ( cbReturnValue == 2 ) {\r
3350       stream_.state = STREAM_STOPPING;\r
3351       handle->drainCounter = 2;\r
3352       unsigned threadId;\r
3353       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3354                                                     &stream_.callbackInfo, 0, &threadId );\r
3355       return SUCCESS;\r
3356     }\r
3357     else if ( cbReturnValue == 1 ) {\r
3358       handle->drainCounter = 1;\r
3359       handle->internalDrain = true;\r
3360     }\r
3361   }\r
3362 \r
3363   unsigned int nChannels, bufferBytes, i, j;\r
3364   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3365   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3366 \r
3367     bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );\r
3368 \r
3369     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
3370 \r
3371       for ( i=0, j=0; i<nChannels; i++ ) {\r
3372         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3373           memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );\r
3374       }\r
3375 \r
3376     }\r
3377     else if ( stream_.doConvertBuffer[0] ) {\r
3378 \r
3379       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
3380       if ( stream_.doByteSwap[0] )\r
3381         byteSwapBuffer( stream_.deviceBuffer,\r
3382                         stream_.bufferSize * stream_.nDeviceChannels[0],\r
3383                         stream_.deviceFormat[0] );\r
3384 \r
3385       for ( i=0, j=0; i<nChannels; i++ ) {\r
3386         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3387           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3388                   &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );\r
3389       }\r
3390 \r
3391     }\r
3392     else {\r
3393 \r
3394       if ( stream_.doByteSwap[0] )\r
3395         byteSwapBuffer( stream_.userBuffer[0],\r
3396                         stream_.bufferSize * stream_.nUserChannels[0],\r
3397                         stream_.userFormat );\r
3398 \r
3399       for ( i=0, j=0; i<nChannels; i++ ) {\r
3400         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3401           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3402                   &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );\r
3403       }\r
3404 \r
3405     }\r
3406 \r
3407     if ( handle->drainCounter ) {\r
3408       handle->drainCounter++;\r
3409       goto unlock;\r
3410     }\r
3411   }\r
3412 \r
3413   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
3414 \r
3415     bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);\r
3416 \r
3417     if (stream_.doConvertBuffer[1]) {\r
3418 \r
3419       // Always interleave ASIO input data.\r
3420       for ( i=0, j=0; i<nChannels; i++ ) {\r
3421         if ( handle->bufferInfos[i].isInput == ASIOTrue )\r
3422           memcpy( &stream_.deviceBuffer[j++*bufferBytes],\r
3423                   handle->bufferInfos[i].buffers[bufferIndex],\r
3424                   bufferBytes );\r
3425       }\r
3426 \r
3427       if ( stream_.doByteSwap[1] )\r
3428         byteSwapBuffer( stream_.deviceBuffer,\r
3429                         stream_.bufferSize * stream_.nDeviceChannels[1],\r
3430                         stream_.deviceFormat[1] );\r
3431       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
3432 \r
3433     }\r
3434     else {\r
3435       for ( i=0, j=0; i<nChannels; i++ ) {\r
3436         if ( handle->bufferInfos[i].isInput == ASIOTrue ) {\r
3437           memcpy( &stream_.userBuffer[1][bufferBytes*j++],\r
3438                   handle->bufferInfos[i].buffers[bufferIndex],\r
3439                   bufferBytes );\r
3440         }\r
3441       }\r
3442 \r
3443       if ( stream_.doByteSwap[1] )\r
3444         byteSwapBuffer( stream_.userBuffer[1],\r
3445                         stream_.bufferSize * stream_.nUserChannels[1],\r
3446                         stream_.userFormat );\r
3447     }\r
3448   }\r
3449 \r
3450  unlock:\r
3451   // The following call was suggested by Malte Clasen.  While the API\r
3452   // documentation indicates it should not be required, some device\r
3453   // drivers apparently do not function correctly without it.\r
3454   ASIOOutputReady();\r
3455 \r
3456   RtApi::tickStreamTime();\r
3457   return SUCCESS;\r
3458 }\r
3459 \r
3460 static void sampleRateChanged( ASIOSampleRate sRate )\r
3461 {\r
3462   // The ASIO documentation says that this usually only happens during\r
3463   // external sync.  Audio processing is not stopped by the driver,\r
3464   // actual sample rate might not have even changed, maybe only the\r
3465   // sample rate status of an AES/EBU or S/PDIF digital input at the\r
3466   // audio device.\r
3467 \r
3468   RtApi *object = (RtApi *) asioCallbackInfo->object;\r
3469   try {\r
3470     object->stopStream();\r
3471   }\r
3472   catch ( RtAudioError &exception ) {\r
3473     std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;\r
3474     return;\r
3475   }\r
3476 \r
3477   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
3478 }\r
3479 \r
3480 static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
3481 {\r
3482   long ret = 0;\r
3483 \r
3484   switch( selector ) {\r
3485   case kAsioSelectorSupported:\r
3486     if ( value == kAsioResetRequest\r
3487          || value == kAsioEngineVersion\r
3488          || value == kAsioResyncRequest\r
3489          || value == kAsioLatenciesChanged\r
3490          // The following three were added for ASIO 2.0, you don't\r
3491          // necessarily have to support them.\r
3492          || value == kAsioSupportsTimeInfo\r
3493          || value == kAsioSupportsTimeCode\r
3494          || value == kAsioSupportsInputMonitor)\r
3495       ret = 1L;\r
3496     break;\r
3497   case kAsioResetRequest:\r
3498     // Defer the task and perform the reset of the driver during the\r
3499     // next "safe" situation.  You cannot reset the driver right now,\r
3500     // as this code is called from the driver.  Reset the driver is\r
3501     // done by completely destruct is. I.e. ASIOStop(),\r
3502     // ASIODisposeBuffers(), Destruction Afterwards you initialize the\r
3503     // driver again.\r
3504     std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;\r
3505     ret = 1L;\r
3506     break;\r
3507   case kAsioResyncRequest:\r
3508     // This informs the application that the driver encountered some\r
3509     // non-fatal data loss.  It is used for synchronization purposes\r
3510     // of different media.  Added mainly to work around the Win16Mutex\r
3511     // problems in Windows 95/98 with the Windows Multimedia system,\r
3512     // which could lose data because the Mutex was held too long by\r
3513     // another thread.  However a driver can issue it in other\r
3514     // situations, too.\r
3515     // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;\r
3516     asioXRun = true;\r
3517     ret = 1L;\r
3518     break;\r
3519   case kAsioLatenciesChanged:\r
3520     // This will inform the host application that the drivers were\r
3521     // latencies changed.  Beware, it this does not mean that the\r
3522     // buffer sizes have changed!  You might need to update internal\r
3523     // delay data.\r
3524     std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;\r
3525     ret = 1L;\r
3526     break;\r
3527   case kAsioEngineVersion:\r
3528     // Return the supported ASIO version of the host application.  If\r
3529     // a host application does not implement this selector, ASIO 1.0\r
3530     // is assumed by the driver.\r
3531     ret = 2L;\r
3532     break;\r
3533   case kAsioSupportsTimeInfo:\r
3534     // Informs the driver whether the\r
3535     // asioCallbacks.bufferSwitchTimeInfo() callback is supported.\r
3536     // For compatibility with ASIO 1.0 drivers the host application\r
3537     // should always support the "old" bufferSwitch method, too.\r
3538     ret = 0;\r
3539     break;\r
3540   case kAsioSupportsTimeCode:\r
3541     // Informs the driver whether application is interested in time\r
3542     // code info.  If an application does not need to know about time\r
3543     // code, the driver has less work to do.\r
3544     ret = 0;\r
3545     break;\r
3546   }\r
3547   return ret;\r
3548 }\r
3549 \r
3550 static const char* getAsioErrorString( ASIOError result )\r
3551 {\r
3552   struct Messages \r
3553   {\r
3554     ASIOError value;\r
3555     const char*message;\r
3556   };\r
3557 \r
3558   static const Messages m[] = \r
3559     {\r
3560       {   ASE_NotPresent,    "Hardware input or output is not present or available." },\r
3561       {   ASE_HWMalfunction,  "Hardware is malfunctioning." },\r
3562       {   ASE_InvalidParameter, "Invalid input parameter." },\r
3563       {   ASE_InvalidMode,      "Invalid mode." },\r
3564       {   ASE_SPNotAdvancing,     "Sample position not advancing." },\r
3565       {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },\r
3566       {   ASE_NoMemory,           "Not enough memory to complete the request." }\r
3567     };\r
3568 \r
3569   for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )\r
3570     if ( m[i].value == result ) return m[i].message;\r
3571 \r
3572   return "Unknown error.";\r
3573 }\r
3574 \r
3575 //******************** End of __WINDOWS_ASIO__ *********************//\r
3576 #endif\r
3577 \r
3578 \r
3579 #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
3580 \r
3581 // Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
3582 // - Introduces support for the Windows WASAPI API\r
3583 // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
3584 // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
3585 // - Includes automatic internal conversion of sample rate, buffer size and channel count\r
3586 \r
3587 #ifndef INITGUID\r
3588   #define INITGUID\r
3589 #endif\r
3590 #include <audioclient.h>\r
3591 #include <avrt.h>\r
3592 #include <mmdeviceapi.h>\r
3593 #include <functiondiscoverykeys_devpkey.h>\r
3594 \r
3595 //=============================================================================\r
3596 \r
3597 #define SAFE_RELEASE( objectPtr )\\r
3598 if ( objectPtr )\\r
3599 {\\r
3600   objectPtr->Release();\\r
3601   objectPtr = NULL;\\r
3602 }\r
3603 \r
3604 typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
3605 \r
3606 //-----------------------------------------------------------------------------\r
3607 \r
3608 // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
3609 // Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
3610 // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
3611 // provide intermediate storage for read / write synchronization.\r
3612 class WasapiBuffer\r
3613 {\r
3614 public:\r
3615   WasapiBuffer()\r
3616     : buffer_( NULL ),\r
3617       bufferSize_( 0 ),\r
3618       inIndex_( 0 ),\r
3619       outIndex_( 0 ) {}\r
3620 \r
3621   ~WasapiBuffer() {\r
3622     delete buffer_;\r
3623   }\r
3624 \r
3625   // sets the length of the internal ring buffer\r
3626   void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
3627     delete buffer_;\r
3628 \r
3629     buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
3630 \r
3631     bufferSize_ = bufferSize;\r
3632     inIndex_ = 0;\r
3633     outIndex_ = 0;\r
3634   }\r
3635 \r
3636   // attempt to push a buffer into the ring buffer at the current "in" index\r
3637   bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3638   {\r
3639     if ( !buffer ||                 // incoming buffer is NULL\r
3640          bufferSize == 0 ||         // incoming buffer has no data\r
3641          bufferSize > bufferSize_ ) // incoming buffer too large\r
3642     {\r
3643       return false;\r
3644     }\r
3645 \r
3646     unsigned int relOutIndex = outIndex_;\r
3647     unsigned int inIndexEnd = inIndex_ + bufferSize;\r
3648     if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
3649       relOutIndex += bufferSize_;\r
3650     }\r
3651 \r
3652     // "in" index can end on the "out" index but cannot begin at it\r
3653     if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
3654       return false; // not enough space between "in" index and "out" index\r
3655     }\r
3656 \r
3657     // copy buffer from external to internal\r
3658     int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
3659     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3660     int fromInSize = bufferSize - fromZeroSize;\r
3661 \r
3662     switch( format )\r
3663       {\r
3664       case RTAUDIO_SINT8:\r
3665         memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
3666         memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
3667         break;\r
3668       case RTAUDIO_SINT16:\r
3669         memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
3670         memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
3671         break;\r
3672       case RTAUDIO_SINT24:\r
3673         memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
3674         memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
3675         break;\r
3676       case RTAUDIO_SINT32:\r
3677         memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
3678         memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
3679         break;\r
3680       case RTAUDIO_FLOAT32:\r
3681         memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
3682         memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
3683         break;\r
3684       case RTAUDIO_FLOAT64:\r
3685         memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
3686         memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
3687         break;\r
3688     }\r
3689 \r
3690     // update "in" index\r
3691     inIndex_ += bufferSize;\r
3692     inIndex_ %= bufferSize_;\r
3693 \r
3694     return true;\r
3695   }\r
3696 \r
3697   // attempt to pull a buffer from the ring buffer from the current "out" index\r
3698   bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3699   {\r
3700     if ( !buffer ||                 // incoming buffer is NULL\r
3701          bufferSize == 0 ||         // incoming buffer has no data\r
3702          bufferSize > bufferSize_ ) // incoming buffer too large\r
3703     {\r
3704       return false;\r
3705     }\r
3706 \r
3707     unsigned int relInIndex = inIndex_;\r
3708     unsigned int outIndexEnd = outIndex_ + bufferSize;\r
3709     if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
3710       relInIndex += bufferSize_;\r
3711     }\r
3712 \r
3713     // "out" index can begin at and end on the "in" index\r
3714     if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
3715       return false; // not enough space between "out" index and "in" index\r
3716     }\r
3717 \r
3718     // copy buffer from internal to external\r
3719     int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
3720     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3721     int fromOutSize = bufferSize - fromZeroSize;\r
3722 \r
3723     switch( format )\r
3724     {\r
3725       case RTAUDIO_SINT8:\r
3726         memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
3727         memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
3728         break;\r
3729       case RTAUDIO_SINT16:\r
3730         memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
3731         memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
3732         break;\r
3733       case RTAUDIO_SINT24:\r
3734         memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
3735         memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
3736         break;\r
3737       case RTAUDIO_SINT32:\r
3738         memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
3739         memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
3740         break;\r
3741       case RTAUDIO_FLOAT32:\r
3742         memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
3743         memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
3744         break;\r
3745       case RTAUDIO_FLOAT64:\r
3746         memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
3747         memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
3748         break;\r
3749     }\r
3750 \r
3751     // update "out" index\r
3752     outIndex_ += bufferSize;\r
3753     outIndex_ %= bufferSize_;\r
3754 \r
3755     return true;\r
3756   }\r
3757 \r
3758 private:\r
3759   char* buffer_;\r
3760   unsigned int bufferSize_;\r
3761   unsigned int inIndex_;\r
3762   unsigned int outIndex_;\r
3763 };\r
3764 \r
3765 //-----------------------------------------------------------------------------\r
3766 \r
3767 // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate and\r
3768 // channel counts between HW and the user. The convertBufferWasapi function is used to perform\r
3769 // these conversions between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
3770 // This sample rate converter favors speed over quality, and works best with conversions between\r
3771 // one rate and its multiple. RtApiWasapi will not populate a device's sample rate list with rates\r
3772 // that may cause artifacts via this conversion.\r
3773 void convertBufferWasapi( char* outBuffer,\r
3774                           const char* inBuffer,\r
3775                           const unsigned int& inChannelCount,\r
3776                           const unsigned int& outChannelCount,\r
3777                           const unsigned int& inSampleRate,\r
3778                           const unsigned int& outSampleRate,\r
3779                           const unsigned int& inSampleCount,\r
3780                           unsigned int& outSampleCount,\r
3781                           const RtAudioFormat& format )\r
3782 {\r
3783   // calculate the new outSampleCount and relative sampleStep\r
3784   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
3785   float sampleStep = 1.0f / sampleRatio;\r
3786   float inSampleFraction = 0.0f;\r
3787   unsigned int commonChannelCount = std::min( inChannelCount, outChannelCount );\r
3788 \r
3789   outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio );\r
3790 \r
3791   // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3792   for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3793   {\r
3794     unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3795 \r
3796     switch ( format )\r
3797     {\r
3798       case RTAUDIO_SINT8:\r
3799         memcpy( &( ( char* ) outBuffer )[ outSample * outChannelCount ], &( ( char* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( char ) );\r
3800         break;\r
3801       case RTAUDIO_SINT16:\r
3802         memcpy( &( ( short* ) outBuffer )[ outSample * outChannelCount ], &( ( short* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( short ) );\r
3803         break;\r
3804       case RTAUDIO_SINT24:\r
3805         memcpy( &( ( S24* ) outBuffer )[ outSample * outChannelCount ], &( ( S24* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( S24 ) );\r
3806         break;\r
3807       case RTAUDIO_SINT32:\r
3808         memcpy( &( ( int* ) outBuffer )[ outSample * outChannelCount ], &( ( int* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( int ) );\r
3809         break;\r
3810       case RTAUDIO_FLOAT32:\r
3811         memcpy( &( ( float* ) outBuffer )[ outSample * outChannelCount ], &( ( float* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( float ) );\r
3812         break;\r
3813       case RTAUDIO_FLOAT64:\r
3814         memcpy( &( ( double* ) outBuffer )[ outSample * outChannelCount ], &( ( double* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( double ) );\r
3815         break;\r
3816     }\r
3817 \r
3818     // jump to next in sample\r
3819     inSampleFraction += sampleStep;\r
3820   }\r
3821 }\r
3822 \r
3823 //-----------------------------------------------------------------------------\r
3824 \r
3825 // A structure to hold various information related to the WASAPI implementation.\r
3826 struct WasapiHandle\r
3827 {\r
3828   IAudioClient* captureAudioClient;\r
3829   IAudioClient* renderAudioClient;\r
3830   IAudioCaptureClient* captureClient;\r
3831   IAudioRenderClient* renderClient;\r
3832   HANDLE captureEvent;\r
3833   HANDLE renderEvent;\r
3834 \r
3835   WasapiHandle()\r
3836   : captureAudioClient( NULL ),\r
3837     renderAudioClient( NULL ),\r
3838     captureClient( NULL ),\r
3839     renderClient( NULL ),\r
3840     captureEvent( NULL ),\r
3841     renderEvent( NULL ) {}\r
3842 };\r
3843 \r
3844 //=============================================================================\r
3845 \r
3846 RtApiWasapi::RtApiWasapi()\r
3847   : coInitialized_( false ), deviceEnumerator_( NULL )\r
3848 {\r
3849   // WASAPI can run either apartment or multi-threaded\r
3850   HRESULT hr = CoInitialize( NULL );\r
3851 \r
3852   if ( !FAILED( hr ) )\r
3853     coInitialized_ = true;\r
3854 \r
3855   // Instantiate device enumerator\r
3856   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
3857                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
3858                          ( void** ) &deviceEnumerator_ );\r
3859 \r
3860   if ( FAILED( hr ) ) {\r
3861     errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
3862     error( RtAudioError::DRIVER_ERROR );\r
3863   }\r
3864 }\r
3865 \r
3866 //-----------------------------------------------------------------------------\r
3867 \r
3868 RtApiWasapi::~RtApiWasapi()\r
3869 {\r
3870   // if this object previously called CoInitialize()\r
3871   if ( coInitialized_ ) {\r
3872     CoUninitialize();\r
3873   }\r
3874 \r
3875   if ( stream_.state != STREAM_CLOSED ) {\r
3876     closeStream();\r
3877   }\r
3878 \r
3879   SAFE_RELEASE( deviceEnumerator_ );\r
3880 }\r
3881 \r
3882 //=============================================================================\r
3883 \r
3884 unsigned int RtApiWasapi::getDeviceCount( void )\r
3885 {\r
3886   unsigned int captureDeviceCount = 0;\r
3887   unsigned int renderDeviceCount = 0;\r
3888 \r
3889   IMMDeviceCollection* captureDevices = NULL;\r
3890   IMMDeviceCollection* renderDevices = NULL;\r
3891 \r
3892   // Count capture devices\r
3893   errorText_.clear();\r
3894   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3895   if ( FAILED( hr ) ) {\r
3896     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
3897     goto Exit;\r
3898   }\r
3899 \r
3900   hr = captureDevices->GetCount( &captureDeviceCount );\r
3901   if ( FAILED( hr ) ) {\r
3902     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
3903     goto Exit;\r
3904   }\r
3905 \r
3906   // Count render devices\r
3907   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3908   if ( FAILED( hr ) ) {\r
3909     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
3910     goto Exit;\r
3911   }\r
3912 \r
3913   hr = renderDevices->GetCount( &renderDeviceCount );\r
3914   if ( FAILED( hr ) ) {\r
3915     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
3916     goto Exit;\r
3917   }\r
3918 \r
3919 Exit:\r
3920   // release all references\r
3921   SAFE_RELEASE( captureDevices );\r
3922   SAFE_RELEASE( renderDevices );\r
3923 \r
3924   if ( errorText_.empty() )\r
3925     return captureDeviceCount + renderDeviceCount;\r
3926 \r
3927   error( RtAudioError::DRIVER_ERROR );\r
3928   return 0;\r
3929 }\r
3930 \r
3931 //-----------------------------------------------------------------------------\r
3932 \r
3933 RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
3934 {\r
3935   RtAudio::DeviceInfo info;\r
3936   unsigned int captureDeviceCount = 0;\r
3937   unsigned int renderDeviceCount = 0;\r
3938   std::wstring deviceName;\r
3939   std::string defaultDeviceName;\r
3940   bool isCaptureDevice = false;\r
3941 \r
3942   PROPVARIANT deviceNameProp;\r
3943   PROPVARIANT defaultDeviceNameProp;\r
3944 \r
3945   IMMDeviceCollection* captureDevices = NULL;\r
3946   IMMDeviceCollection* renderDevices = NULL;\r
3947   IMMDevice* devicePtr = NULL;\r
3948   IMMDevice* defaultDevicePtr = NULL;\r
3949   IAudioClient* audioClient = NULL;\r
3950   IPropertyStore* devicePropStore = NULL;\r
3951   IPropertyStore* defaultDevicePropStore = NULL;\r
3952 \r
3953   WAVEFORMATEX* deviceFormat = NULL;\r
3954   WAVEFORMATEX* closestMatchFormat = NULL;\r
3955 \r
3956   // probed\r
3957   info.probed = false;\r
3958 \r
3959   // Count capture devices\r
3960   errorText_.clear();\r
3961   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
3962   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3963   if ( FAILED( hr ) ) {\r
3964     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
3965     goto Exit;\r
3966   }\r
3967 \r
3968   hr = captureDevices->GetCount( &captureDeviceCount );\r
3969   if ( FAILED( hr ) ) {\r
3970     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
3971     goto Exit;\r
3972   }\r
3973 \r
3974   // Count render devices\r
3975   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3976   if ( FAILED( hr ) ) {\r
3977     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
3978     goto Exit;\r
3979   }\r
3980 \r
3981   hr = renderDevices->GetCount( &renderDeviceCount );\r
3982   if ( FAILED( hr ) ) {\r
3983     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
3984     goto Exit;\r
3985   }\r
3986 \r
3987   // validate device index\r
3988   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
3989     errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
3990     errorType = RtAudioError::INVALID_USE;\r
3991     goto Exit;\r
3992   }\r
3993 \r
3994   // determine whether index falls within capture or render devices\r
3995   if ( device >= renderDeviceCount ) {\r
3996     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
3997     if ( FAILED( hr ) ) {\r
3998       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
3999       goto Exit;\r
4000     }\r
4001     isCaptureDevice = true;\r
4002   }\r
4003   else {\r
4004     hr = renderDevices->Item( device, &devicePtr );\r
4005     if ( FAILED( hr ) ) {\r
4006       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
4007       goto Exit;\r
4008     }\r
4009     isCaptureDevice = false;\r
4010   }\r
4011 \r
4012   // get default device name\r
4013   if ( isCaptureDevice ) {\r
4014     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
4015     if ( FAILED( hr ) ) {\r
4016       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
4017       goto Exit;\r
4018     }\r
4019   }\r
4020   else {\r
4021     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
4022     if ( FAILED( hr ) ) {\r
4023       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
4024       goto Exit;\r
4025     }\r
4026   }\r
4027 \r
4028   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
4029   if ( FAILED( hr ) ) {\r
4030     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
4031     goto Exit;\r
4032   }\r
4033   PropVariantInit( &defaultDeviceNameProp );\r
4034 \r
4035   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
4036   if ( FAILED( hr ) ) {\r
4037     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
4038     goto Exit;\r
4039   }\r
4040 \r
4041   deviceName = defaultDeviceNameProp.pwszVal;\r
4042   defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );\r
4043 \r
4044   // name\r
4045   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
4046   if ( FAILED( hr ) ) {\r
4047     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
4048     goto Exit;\r
4049   }\r
4050 \r
4051   PropVariantInit( &deviceNameProp );\r
4052 \r
4053   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
4054   if ( FAILED( hr ) ) {\r
4055     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
4056     goto Exit;\r
4057   }\r
4058 \r
4059   deviceName = deviceNameProp.pwszVal;\r
4060   info.name = std::string( deviceName.begin(), deviceName.end() );\r
4061 \r
4062   // is default\r
4063   if ( isCaptureDevice ) {\r
4064     info.isDefaultInput = info.name == defaultDeviceName;\r
4065     info.isDefaultOutput = false;\r
4066   }\r
4067   else {\r
4068     info.isDefaultInput = false;\r
4069     info.isDefaultOutput = info.name == defaultDeviceName;\r
4070   }\r
4071 \r
4072   // channel count\r
4073   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
4074   if ( FAILED( hr ) ) {\r
4075     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
4076     goto Exit;\r
4077   }\r
4078 \r
4079   hr = audioClient->GetMixFormat( &deviceFormat );\r
4080   if ( FAILED( hr ) ) {\r
4081     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
4082     goto Exit;\r
4083   }\r
4084 \r
4085   if ( isCaptureDevice ) {\r
4086     info.inputChannels = deviceFormat->nChannels;\r
4087     info.outputChannels = 0;\r
4088     info.duplexChannels = 0;\r
4089   }\r
4090   else {\r
4091     info.inputChannels = 0;\r
4092     info.outputChannels = deviceFormat->nChannels;\r
4093     info.duplexChannels = 0;\r
4094   }\r
4095 \r
4096   // sample rates\r
4097   info.sampleRates.clear();\r
4098 \r
4099   // allow support for sample rates that are multiples of the base rate\r
4100   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
4101     if ( SAMPLE_RATES[i] < deviceFormat->nSamplesPerSec ) {\r
4102       if ( deviceFormat->nSamplesPerSec % SAMPLE_RATES[i] == 0 ) {\r
4103         info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4104       }\r
4105     }\r
4106     else {\r
4107       if ( SAMPLE_RATES[i] % deviceFormat->nSamplesPerSec == 0 ) {\r
4108         info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4109       }\r
4110     }\r
4111   }\r
4112 \r
4113   // native format\r
4114   info.nativeFormats = 0;\r
4115 \r
4116   if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
4117        ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4118          ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
4119   {\r
4120     if ( deviceFormat->wBitsPerSample == 32 ) {\r
4121       info.nativeFormats |= RTAUDIO_FLOAT32;\r
4122     }\r
4123     else if ( deviceFormat->wBitsPerSample == 64 ) {\r
4124       info.nativeFormats |= RTAUDIO_FLOAT64;\r
4125     }\r
4126   }\r
4127   else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
4128            ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4129              ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
4130   {\r
4131     if ( deviceFormat->wBitsPerSample == 8 ) {\r
4132       info.nativeFormats |= RTAUDIO_SINT8;\r
4133     }\r
4134     else if ( deviceFormat->wBitsPerSample == 16 ) {\r
4135       info.nativeFormats |= RTAUDIO_SINT16;\r
4136     }\r
4137     else if ( deviceFormat->wBitsPerSample == 24 ) {\r
4138       info.nativeFormats |= RTAUDIO_SINT24;\r
4139     }\r
4140     else if ( deviceFormat->wBitsPerSample == 32 ) {\r
4141       info.nativeFormats |= RTAUDIO_SINT32;\r
4142     }\r
4143   }\r
4144 \r
4145   // probed\r
4146   info.probed = true;\r
4147 \r
4148 Exit:\r
4149   // release all references\r
4150   PropVariantClear( &deviceNameProp );\r
4151   PropVariantClear( &defaultDeviceNameProp );\r
4152 \r
4153   SAFE_RELEASE( captureDevices );\r
4154   SAFE_RELEASE( renderDevices );\r
4155   SAFE_RELEASE( devicePtr );\r
4156   SAFE_RELEASE( defaultDevicePtr );\r
4157   SAFE_RELEASE( audioClient );\r
4158   SAFE_RELEASE( devicePropStore );\r
4159   SAFE_RELEASE( defaultDevicePropStore );\r
4160 \r
4161   CoTaskMemFree( deviceFormat );\r
4162   CoTaskMemFree( closestMatchFormat );\r
4163 \r
4164   if ( !errorText_.empty() )\r
4165     error( errorType );\r
4166   return info;\r
4167 }\r
4168 \r
4169 //-----------------------------------------------------------------------------\r
4170 \r
4171 unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
4172 {\r
4173   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4174     if ( getDeviceInfo( i ).isDefaultOutput ) {\r
4175       return i;\r
4176     }\r
4177   }\r
4178 \r
4179   return 0;\r
4180 }\r
4181 \r
4182 //-----------------------------------------------------------------------------\r
4183 \r
4184 unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
4185 {\r
4186   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4187     if ( getDeviceInfo( i ).isDefaultInput ) {\r
4188       return i;\r
4189     }\r
4190   }\r
4191 \r
4192   return 0;\r
4193 }\r
4194 \r
4195 //-----------------------------------------------------------------------------\r
4196 \r
4197 void RtApiWasapi::closeStream( void )\r
4198 {\r
4199   if ( stream_.state == STREAM_CLOSED ) {\r
4200     errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
4201     error( RtAudioError::WARNING );\r
4202     return;\r
4203   }\r
4204 \r
4205   if ( stream_.state != STREAM_STOPPED )\r
4206     stopStream();\r
4207 \r
4208   // clean up stream memory\r
4209   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
4210   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
4211 \r
4212   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
4213   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
4214 \r
4215   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
4216     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
4217 \r
4218   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
4219     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
4220 \r
4221   delete ( WasapiHandle* ) stream_.apiHandle;\r
4222   stream_.apiHandle = NULL;\r
4223 \r
4224   for ( int i = 0; i < 2; i++ ) {\r
4225     if ( stream_.userBuffer[i] ) {\r
4226       free( stream_.userBuffer[i] );\r
4227       stream_.userBuffer[i] = 0;\r
4228     }\r
4229   }\r
4230 \r
4231   if ( stream_.deviceBuffer ) {\r
4232     free( stream_.deviceBuffer );\r
4233     stream_.deviceBuffer = 0;\r
4234   }\r
4235 \r
4236   // update stream state\r
4237   stream_.state = STREAM_CLOSED;\r
4238 }\r
4239 \r
4240 //-----------------------------------------------------------------------------\r
4241 \r
4242 void RtApiWasapi::startStream( void )\r
4243 {\r
4244   verifyStream();\r
4245 \r
4246   if ( stream_.state == STREAM_RUNNING ) {\r
4247     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
4248     error( RtAudioError::WARNING );\r
4249     return;\r
4250   }\r
4251 \r
4252   // update stream state\r
4253   stream_.state = STREAM_RUNNING;\r
4254 \r
4255   // create WASAPI stream thread\r
4256   stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
4257 \r
4258   if ( !stream_.callbackInfo.thread ) {\r
4259     errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
4260     error( RtAudioError::THREAD_ERROR );\r
4261   }\r
4262   else {\r
4263     SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
4264     ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
4265   }\r
4266 }\r
4267 \r
4268 //-----------------------------------------------------------------------------\r
4269 \r
4270 void RtApiWasapi::stopStream( void )\r
4271 {\r
4272   verifyStream();\r
4273 \r
4274   if ( stream_.state == STREAM_STOPPED ) {\r
4275     errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
4276     error( RtAudioError::WARNING );\r
4277     return;\r
4278   }\r
4279 \r
4280   // inform stream thread by setting stream state to STREAM_STOPPING\r
4281   stream_.state = STREAM_STOPPING;\r
4282 \r
4283   // wait until stream thread is stopped\r
4284   while( stream_.state != STREAM_STOPPED ) {\r
4285     Sleep( 1 );\r
4286   }\r
4287 \r
4288   // Wait for the last buffer to play before stopping.\r
4289   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
4290 \r
4291   // stop capture client if applicable\r
4292   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4293     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4294     if ( FAILED( hr ) ) {\r
4295       errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
4296       error( RtAudioError::DRIVER_ERROR );\r
4297       return;\r
4298     }\r
4299   }\r
4300 \r
4301   // stop render client if applicable\r
4302   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4303     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4304     if ( FAILED( hr ) ) {\r
4305       errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
4306       error( RtAudioError::DRIVER_ERROR );\r
4307       return;\r
4308     }\r
4309   }\r
4310 \r
4311   // close thread handle\r
4312   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4313     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
4314     error( RtAudioError::THREAD_ERROR );\r
4315     return;\r
4316   }\r
4317 \r
4318   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4319 }\r
4320 \r
4321 //-----------------------------------------------------------------------------\r
4322 \r
4323 void RtApiWasapi::abortStream( void )\r
4324 {\r
4325   verifyStream();\r
4326 \r
4327   if ( stream_.state == STREAM_STOPPED ) {\r
4328     errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
4329     error( RtAudioError::WARNING );\r
4330     return;\r
4331   }\r
4332 \r
4333   // inform stream thread by setting stream state to STREAM_STOPPING\r
4334   stream_.state = STREAM_STOPPING;\r
4335 \r
4336   // wait until stream thread is stopped\r
4337   while ( stream_.state != STREAM_STOPPED ) {\r
4338     Sleep( 1 );\r
4339   }\r
4340 \r
4341   // stop capture client if applicable\r
4342   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4343     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4344     if ( FAILED( hr ) ) {\r
4345       errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
4346       error( RtAudioError::DRIVER_ERROR );\r
4347       return;\r
4348     }\r
4349   }\r
4350 \r
4351   // stop render client if applicable\r
4352   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4353     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4354     if ( FAILED( hr ) ) {\r
4355       errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
4356       error( RtAudioError::DRIVER_ERROR );\r
4357       return;\r
4358     }\r
4359   }\r
4360 \r
4361   // close thread handle\r
4362   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4363     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
4364     error( RtAudioError::THREAD_ERROR );\r
4365     return;\r
4366   }\r
4367 \r
4368   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4369 }\r
4370 \r
4371 //-----------------------------------------------------------------------------\r
4372 \r
4373 bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
4374                                    unsigned int firstChannel, unsigned int sampleRate,\r
4375                                    RtAudioFormat format, unsigned int* bufferSize,\r
4376                                    RtAudio::StreamOptions* options )\r
4377 {\r
4378   bool methodResult = FAILURE;\r
4379   unsigned int captureDeviceCount = 0;\r
4380   unsigned int renderDeviceCount = 0;\r
4381 \r
4382   IMMDeviceCollection* captureDevices = NULL;\r
4383   IMMDeviceCollection* renderDevices = NULL;\r
4384   IMMDevice* devicePtr = NULL;\r
4385   WAVEFORMATEX* deviceFormat = NULL;\r
4386   unsigned int bufferBytes;\r
4387   stream_.state = STREAM_STOPPED;\r
4388 \r
4389   // create API Handle if not already created\r
4390   if ( !stream_.apiHandle )\r
4391     stream_.apiHandle = ( void* ) new WasapiHandle();\r
4392 \r
4393   // Count capture devices\r
4394   errorText_.clear();\r
4395   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4396   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4397   if ( FAILED( hr ) ) {\r
4398     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
4399     goto Exit;\r
4400   }\r
4401 \r
4402   hr = captureDevices->GetCount( &captureDeviceCount );\r
4403   if ( FAILED( hr ) ) {\r
4404     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
4405     goto Exit;\r
4406   }\r
4407 \r
4408   // Count render devices\r
4409   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4410   if ( FAILED( hr ) ) {\r
4411     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
4412     goto Exit;\r
4413   }\r
4414 \r
4415   hr = renderDevices->GetCount( &renderDeviceCount );\r
4416   if ( FAILED( hr ) ) {\r
4417     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
4418     goto Exit;\r
4419   }\r
4420 \r
4421   // validate device index\r
4422   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4423     errorType = RtAudioError::INVALID_USE;\r
4424     errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
4425     goto Exit;\r
4426   }\r
4427 \r
4428   // determine whether index falls within capture or render devices\r
4429   if ( device >= renderDeviceCount ) {\r
4430     if ( mode != INPUT ) {\r
4431       errorType = RtAudioError::INVALID_USE;\r
4432       errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
4433       goto Exit;\r
4434     }\r
4435 \r
4436     // retrieve captureAudioClient from devicePtr\r
4437     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4438 \r
4439     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4440     if ( FAILED( hr ) ) {\r
4441       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
4442       goto Exit;\r
4443     }\r
4444 \r
4445     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4446                               NULL, ( void** ) &captureAudioClient );\r
4447     if ( FAILED( hr ) ) {\r
4448       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4449       goto Exit;\r
4450     }\r
4451 \r
4452     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
4453     if ( FAILED( hr ) ) {\r
4454       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4455       goto Exit;\r
4456     }\r
4457 \r
4458     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4459     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4460   }\r
4461   else {\r
4462     if ( mode != OUTPUT ) {\r
4463       errorType = RtAudioError::INVALID_USE;\r
4464       errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
4465       goto Exit;\r
4466     }\r
4467 \r
4468     // retrieve renderAudioClient from devicePtr\r
4469     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4470 \r
4471     hr = renderDevices->Item( device, &devicePtr );\r
4472     if ( FAILED( hr ) ) {\r
4473       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
4474       goto Exit;\r
4475     }\r
4476 \r
4477     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4478                               NULL, ( void** ) &renderAudioClient );\r
4479     if ( FAILED( hr ) ) {\r
4480       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4481       goto Exit;\r
4482     }\r
4483 \r
4484     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
4485     if ( FAILED( hr ) ) {\r
4486       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4487       goto Exit;\r
4488     }\r
4489 \r
4490     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4491     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4492   }\r
4493 \r
4494   // fill stream data\r
4495   if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
4496        ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
4497     stream_.mode = DUPLEX;\r
4498   }\r
4499   else {\r
4500     stream_.mode = mode;\r
4501   }\r
4502 \r
4503   stream_.device[mode] = device;\r
4504   stream_.doByteSwap[mode] = false;\r
4505   stream_.sampleRate = sampleRate;\r
4506   stream_.bufferSize = *bufferSize;\r
4507   stream_.nBuffers = 1;\r
4508   stream_.nUserChannels[mode] = channels;\r
4509   stream_.channelOffset[mode] = firstChannel;\r
4510   stream_.userFormat = format;\r
4511   stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
4512 \r
4513   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
4514     stream_.userInterleaved = false;\r
4515   else\r
4516     stream_.userInterleaved = true;\r
4517   stream_.deviceInterleaved[mode] = true;\r
4518 \r
4519   // Set flags for buffer conversion.\r
4520   stream_.doConvertBuffer[mode] = false;\r
4521   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
4522     stream_.doConvertBuffer[mode] = true;\r
4523   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
4524        stream_.nUserChannels[mode] > 1 )\r
4525     stream_.doConvertBuffer[mode] = true;\r
4526 \r
4527   if ( stream_.doConvertBuffer[mode] )\r
4528     setConvertInfo( mode, 0 );\r
4529 \r
4530   // Allocate necessary internal buffers\r
4531   bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
4532 \r
4533   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
4534   if ( !stream_.userBuffer[mode] ) {\r
4535     errorType = RtAudioError::MEMORY_ERROR;\r
4536     errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
4537     goto Exit;\r
4538   }\r
4539 \r
4540   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
4541     stream_.callbackInfo.priority = 15;\r
4542   else\r
4543     stream_.callbackInfo.priority = 0;\r
4544 \r
4545   ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
4546   ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
4547 \r
4548   methodResult = SUCCESS;\r
4549 \r
4550 Exit:\r
4551   //clean up\r
4552   SAFE_RELEASE( captureDevices );\r
4553   SAFE_RELEASE( renderDevices );\r
4554   SAFE_RELEASE( devicePtr );\r
4555   CoTaskMemFree( deviceFormat );\r
4556 \r
4557   // if method failed, close the stream\r
4558   if ( methodResult == FAILURE )\r
4559     closeStream();\r
4560 \r
4561   if ( !errorText_.empty() )\r
4562     error( errorType );\r
4563   return methodResult;\r
4564 }\r
4565 \r
4566 //=============================================================================\r
4567 \r
4568 DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
4569 {\r
4570   if ( wasapiPtr )\r
4571     ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
4572 \r
4573   return 0;\r
4574 }\r
4575 \r
4576 DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
4577 {\r
4578   if ( wasapiPtr )\r
4579     ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
4580 \r
4581   return 0;\r
4582 }\r
4583 \r
4584 DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
4585 {\r
4586   if ( wasapiPtr )\r
4587     ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
4588 \r
4589   return 0;\r
4590 }\r
4591 \r
4592 //-----------------------------------------------------------------------------\r
4593 \r
4594 void RtApiWasapi::wasapiThread()\r
4595 {\r
4596   // as this is a new thread, we must CoInitialize it\r
4597   CoInitialize( NULL );\r
4598 \r
4599   HRESULT hr;\r
4600 \r
4601   IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4602   IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4603   IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
4604   IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
4605   HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
4606   HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
4607 \r
4608   WAVEFORMATEX* captureFormat = NULL;\r
4609   WAVEFORMATEX* renderFormat = NULL;\r
4610   float captureSrRatio = 0.0f;\r
4611   float renderSrRatio = 0.0f;\r
4612   WasapiBuffer captureBuffer;\r
4613   WasapiBuffer renderBuffer;\r
4614 \r
4615   // declare local stream variables\r
4616   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
4617   BYTE* streamBuffer = NULL;\r
4618   unsigned long captureFlags = 0;\r
4619   unsigned int bufferFrameCount = 0;\r
4620   unsigned int numFramesPadding = 0;\r
4621   unsigned int convBufferSize = 0;\r
4622   bool callbackPushed = false;\r
4623   bool callbackPulled = false;\r
4624   bool callbackStopped = false;\r
4625   int callbackResult = 0;\r
4626 \r
4627   // convBuffer is used to store converted buffers between WASAPI and the user\r
4628   char* convBuffer = NULL;\r
4629   unsigned int deviceBufferSize = 0;\r
4630 \r
4631   errorText_.clear();\r
4632   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4633 \r
4634   // Attempt to assign "Pro Audio" characteristic to thread\r
4635   HMODULE AvrtDll = LoadLibrary( "AVRT.dll" );\r
4636   if ( AvrtDll ) {\r
4637     DWORD taskIndex = 0;\r
4638     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
4639     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
4640     FreeLibrary( AvrtDll );\r
4641   }\r
4642 \r
4643   // start capture stream if applicable\r
4644   if ( captureAudioClient ) {\r
4645     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
4646     if ( FAILED( hr ) ) {\r
4647       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4648       goto Exit;\r
4649     }\r
4650 \r
4651     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
4652 \r
4653     // initialize capture stream according to desire buffer size\r
4654     float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
4655     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
4656 \r
4657     if ( !captureClient ) {\r
4658       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4659                                            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4660                                            desiredBufferPeriod,\r
4661                                            desiredBufferPeriod,\r
4662                                            captureFormat,\r
4663                                            NULL );\r
4664       if ( FAILED( hr ) ) {\r
4665         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
4666         goto Exit;\r
4667       }\r
4668 \r
4669       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
4670                                            ( void** ) &captureClient );\r
4671       if ( FAILED( hr ) ) {\r
4672         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
4673         goto Exit;\r
4674       }\r
4675 \r
4676       // configure captureEvent to trigger on every available capture buffer\r
4677       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4678       if ( !captureEvent ) {\r
4679         errorType = RtAudioError::SYSTEM_ERROR;\r
4680         errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
4681         goto Exit;\r
4682       }\r
4683 \r
4684       hr = captureAudioClient->SetEventHandle( captureEvent );\r
4685       if ( FAILED( hr ) ) {\r
4686         errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
4687         goto Exit;\r
4688       }\r
4689 \r
4690       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
4691       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
4692     }\r
4693 \r
4694     unsigned int inBufferSize = 0;\r
4695     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
4696     if ( FAILED( hr ) ) {\r
4697       errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
4698       goto Exit;\r
4699     }\r
4700 \r
4701     // scale outBufferSize according to stream->user sample rate ratio\r
4702     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
4703     inBufferSize *= stream_.nDeviceChannels[INPUT];\r
4704 \r
4705     // set captureBuffer size\r
4706     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
4707 \r
4708     // reset the capture stream\r
4709     hr = captureAudioClient->Reset();\r
4710     if ( FAILED( hr ) ) {\r
4711       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
4712       goto Exit;\r
4713     }\r
4714 \r
4715     // start the capture stream\r
4716     hr = captureAudioClient->Start();\r
4717     if ( FAILED( hr ) ) {\r
4718       errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
4719       goto Exit;\r
4720     }\r
4721   }\r
4722 \r
4723   // start render stream if applicable\r
4724   if ( renderAudioClient ) {\r
4725     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
4726     if ( FAILED( hr ) ) {\r
4727       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4728       goto Exit;\r
4729     }\r
4730 \r
4731     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
4732 \r
4733     // initialize render stream according to desire buffer size\r
4734     float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
4735     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
4736 \r
4737     if ( !renderClient ) {\r
4738       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4739                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4740                                           desiredBufferPeriod,\r
4741                                           desiredBufferPeriod,\r
4742                                           renderFormat,\r
4743                                           NULL );\r
4744       if ( FAILED( hr ) ) {\r
4745         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
4746         goto Exit;\r
4747       }\r
4748 \r
4749       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
4750                                           ( void** ) &renderClient );\r
4751       if ( FAILED( hr ) ) {\r
4752         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
4753         goto Exit;\r
4754       }\r
4755 \r
4756       // configure renderEvent to trigger on every available render buffer\r
4757       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4758       if ( !renderEvent ) {\r
4759         errorType = RtAudioError::SYSTEM_ERROR;\r
4760         errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
4761         goto Exit;\r
4762       }\r
4763 \r
4764       hr = renderAudioClient->SetEventHandle( renderEvent );\r
4765       if ( FAILED( hr ) ) {\r
4766         errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
4767         goto Exit;\r
4768       }\r
4769 \r
4770       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
4771       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
4772     }\r
4773 \r
4774     unsigned int outBufferSize = 0;\r
4775     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
4776     if ( FAILED( hr ) ) {\r
4777       errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
4778       goto Exit;\r
4779     }\r
4780 \r
4781     // scale inBufferSize according to user->stream sample rate ratio\r
4782     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
4783     outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
4784 \r
4785     // set renderBuffer size\r
4786     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4787 \r
4788     // reset the render stream\r
4789     hr = renderAudioClient->Reset();\r
4790     if ( FAILED( hr ) ) {\r
4791       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
4792       goto Exit;\r
4793     }\r
4794 \r
4795     // start the render stream\r
4796     hr = renderAudioClient->Start();\r
4797     if ( FAILED( hr ) ) {\r
4798       errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
4799       goto Exit;\r
4800     }\r
4801   }\r
4802 \r
4803   if ( stream_.mode == INPUT ) {\r
4804     deviceBufferSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4805   }\r
4806   else if ( stream_.mode == OUTPUT ) {\r
4807     deviceBufferSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4808   }\r
4809   else if ( stream_.mode == DUPLEX ) {\r
4810     deviceBufferSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4811                             ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4812   }\r
4813 \r
4814   convBuffer = ( char* ) malloc( deviceBufferSize );\r
4815   stream_.deviceBuffer = ( char* ) malloc( deviceBufferSize );\r
4816   if ( !convBuffer || !stream_.deviceBuffer ) {\r
4817     errorType = RtAudioError::MEMORY_ERROR;\r
4818     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
4819     goto Exit;\r
4820   }\r
4821 \r
4822   // stream process loop\r
4823   while ( stream_.state != STREAM_STOPPING ) {\r
4824     if ( !callbackPulled ) {\r
4825       // Callback Input\r
4826       // ==============\r
4827       // 1. Pull callback buffer from inputBuffer\r
4828       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
4829       //                          Convert callback buffer to user format\r
4830 \r
4831       if ( captureAudioClient ) {\r
4832         // Pull callback buffer from inputBuffer\r
4833         callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
4834                                                    ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
4835                                                    stream_.deviceFormat[INPUT] );\r
4836 \r
4837         if ( callbackPulled ) {\r
4838           // Convert callback buffer to user sample rate and channel count\r
4839           convertBufferWasapi( stream_.deviceBuffer,\r
4840                                convBuffer,\r
4841                                stream_.nDeviceChannels[INPUT],\r
4842                                stream_.nUserChannels[INPUT],\r
4843                                captureFormat->nSamplesPerSec,\r
4844                                stream_.sampleRate,\r
4845                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
4846                                convBufferSize,\r
4847                                stream_.deviceFormat[INPUT] );\r
4848 \r
4849           if ( stream_.doConvertBuffer[INPUT] ) {\r
4850             // Convert callback buffer to user format\r
4851             convertBuffer( stream_.userBuffer[INPUT],\r
4852                            stream_.deviceBuffer,\r
4853                            stream_.convertInfo[INPUT] );\r
4854           }\r
4855           else {\r
4856             // no conversion, simple copy deviceBuffer to userBuffer\r
4857             memcpy( stream_.userBuffer[INPUT],\r
4858                     stream_.deviceBuffer,\r
4859                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
4860           }\r
4861         }\r
4862       }\r
4863       else {\r
4864         // if there is no capture stream, set callbackPulled flag\r
4865         callbackPulled = true;\r
4866       }\r
4867 \r
4868       // Execute Callback\r
4869       // ================\r
4870       // 1. Execute user callback method\r
4871       // 2. Handle return value from callback\r
4872 \r
4873       // if callback has not requested the stream to stop\r
4874       if ( callbackPulled && !callbackStopped ) {\r
4875         // Execute user callback method\r
4876         callbackResult = callback( stream_.userBuffer[OUTPUT],\r
4877                                    stream_.userBuffer[INPUT],\r
4878                                    stream_.bufferSize,\r
4879                                    getStreamTime(),\r
4880                                    captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
4881                                    stream_.callbackInfo.userData );\r
4882 \r
4883         // Handle return value from callback\r
4884         if ( callbackResult == 1 ) {\r
4885           // instantiate a thread to stop this thread\r
4886           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
4887           if ( !threadHandle ) {\r
4888             errorType = RtAudioError::THREAD_ERROR;\r
4889             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
4890             goto Exit;\r
4891           }\r
4892           else if ( !CloseHandle( threadHandle ) ) {\r
4893             errorType = RtAudioError::THREAD_ERROR;\r
4894             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
4895             goto Exit;\r
4896           }\r
4897 \r
4898           callbackStopped = true;\r
4899         }\r
4900         else if ( callbackResult == 2 ) {\r
4901           // instantiate a thread to stop this thread\r
4902           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
4903           if ( !threadHandle ) {\r
4904             errorType = RtAudioError::THREAD_ERROR;\r
4905             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
4906             goto Exit;\r
4907           }\r
4908           else if ( !CloseHandle( threadHandle ) ) {\r
4909             errorType = RtAudioError::THREAD_ERROR;\r
4910             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
4911             goto Exit;\r
4912           }\r
4913 \r
4914           callbackStopped = true;\r
4915         }\r
4916       }\r
4917     }\r
4918 \r
4919     // Callback Output\r
4920     // ===============\r
4921     // 1. Convert callback buffer to stream format\r
4922     // 2. Convert callback buffer to stream sample rate and channel count\r
4923     // 3. Push callback buffer into outputBuffer\r
4924 \r
4925     if ( renderAudioClient && callbackPulled ) {\r
4926       if ( stream_.doConvertBuffer[OUTPUT] ) {\r
4927         // Convert callback buffer to stream format\r
4928         convertBuffer( stream_.deviceBuffer,\r
4929                        stream_.userBuffer[OUTPUT],\r
4930                        stream_.convertInfo[OUTPUT] );\r
4931 \r
4932         // Convert callback buffer to stream sample rate and channel count\r
4933         convertBufferWasapi( convBuffer,\r
4934                              stream_.deviceBuffer,\r
4935                              stream_.nUserChannels[OUTPUT],\r
4936                              stream_.nDeviceChannels[OUTPUT],\r
4937                              stream_.sampleRate,\r
4938                              renderFormat->nSamplesPerSec,\r
4939                              stream_.bufferSize,\r
4940                              convBufferSize,\r
4941                              stream_.deviceFormat[OUTPUT] );\r
4942       }\r
4943       else {\r
4944         // Convert callback buffer to stream sample rate and channel count\r
4945         convertBufferWasapi( convBuffer,\r
4946                              stream_.userBuffer[OUTPUT],\r
4947                              stream_.nUserChannels[OUTPUT],\r
4948                              stream_.nDeviceChannels[OUTPUT],\r
4949                              stream_.sampleRate,\r
4950                              renderFormat->nSamplesPerSec,\r
4951                              stream_.bufferSize,\r
4952                              convBufferSize,\r
4953                              stream_.deviceFormat[OUTPUT] );\r
4954       }\r
4955 \r
4956       // Push callback buffer into outputBuffer\r
4957       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
4958                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
4959                                                 stream_.deviceFormat[OUTPUT] );\r
4960     }\r
4961 \r
4962     // Stream Capture\r
4963     // ==============\r
4964     // 1. Get capture buffer from stream\r
4965     // 2. Push capture buffer into inputBuffer\r
4966     // 3. If 2. was successful: Release capture buffer\r
4967 \r
4968     if ( captureAudioClient ) {\r
4969       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
4970       if ( !callbackPulled ) {\r
4971         WaitForSingleObject( captureEvent, INFINITE );\r
4972       }\r
4973 \r
4974       // Get capture buffer from stream\r
4975       hr = captureClient->GetBuffer( &streamBuffer,\r
4976                                      &bufferFrameCount,\r
4977                                      &captureFlags, NULL, NULL );\r
4978       if ( FAILED( hr ) ) {\r
4979         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
4980         goto Exit;\r
4981       }\r
4982 \r
4983       if ( bufferFrameCount != 0 ) {\r
4984         // Push capture buffer into inputBuffer\r
4985         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
4986                                       bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
4987                                       stream_.deviceFormat[INPUT] ) )\r
4988         {\r
4989           // Release capture buffer\r
4990           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
4991           if ( FAILED( hr ) ) {\r
4992             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
4993             goto Exit;\r
4994           }\r
4995         }\r
4996         else\r
4997         {\r
4998           // Inform WASAPI that capture was unsuccessful\r
4999           hr = captureClient->ReleaseBuffer( 0 );\r
5000           if ( FAILED( hr ) ) {\r
5001             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5002             goto Exit;\r
5003           }\r
5004         }\r
5005       }\r
5006       else\r
5007       {\r
5008         // Inform WASAPI that capture was unsuccessful\r
5009         hr = captureClient->ReleaseBuffer( 0 );\r
5010         if ( FAILED( hr ) ) {\r
5011           errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5012           goto Exit;\r
5013         }\r
5014       }\r
5015     }\r
5016 \r
5017     // Stream Render\r
5018     // =============\r
5019     // 1. Get render buffer from stream\r
5020     // 2. Pull next buffer from outputBuffer\r
5021     // 3. If 2. was successful: Fill render buffer with next buffer\r
5022     //                          Release render buffer\r
5023 \r
5024     if ( renderAudioClient ) {\r
5025       // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
5026       if ( callbackPulled && !callbackPushed ) {\r
5027         WaitForSingleObject( renderEvent, INFINITE );\r
5028       }\r
5029 \r
5030       // Get render buffer from stream\r
5031       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
5032       if ( FAILED( hr ) ) {\r
5033         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
5034         goto Exit;\r
5035       }\r
5036 \r
5037       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
5038       if ( FAILED( hr ) ) {\r
5039         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
5040         goto Exit;\r
5041       }\r
5042 \r
5043       bufferFrameCount -= numFramesPadding;\r
5044 \r
5045       if ( bufferFrameCount != 0 ) {\r
5046         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
5047         if ( FAILED( hr ) ) {\r
5048           errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
5049           goto Exit;\r
5050         }\r
5051 \r
5052         // Pull next buffer from outputBuffer\r
5053         // Fill render buffer with next buffer\r
5054         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
5055                                      bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
5056                                      stream_.deviceFormat[OUTPUT] ) )\r
5057         {\r
5058           // Release render buffer\r
5059           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
5060           if ( FAILED( hr ) ) {\r
5061             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5062             goto Exit;\r
5063           }\r
5064         }\r
5065         else\r
5066         {\r
5067           // Inform WASAPI that render was unsuccessful\r
5068           hr = renderClient->ReleaseBuffer( 0, 0 );\r
5069           if ( FAILED( hr ) ) {\r
5070             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5071             goto Exit;\r
5072           }\r
5073         }\r
5074       }\r
5075       else\r
5076       {\r
5077         // Inform WASAPI that render was unsuccessful\r
5078         hr = renderClient->ReleaseBuffer( 0, 0 );\r
5079         if ( FAILED( hr ) ) {\r
5080           errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5081           goto Exit;\r
5082         }\r
5083       }\r
5084     }\r
5085 \r
5086     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
5087     if ( callbackPushed ) {\r
5088       callbackPulled = false;\r
5089     }\r
5090 \r
5091     // tick stream time\r
5092     RtApi::tickStreamTime();\r
5093   }\r
5094 \r
5095 Exit:\r
5096   // clean up\r
5097   CoTaskMemFree( captureFormat );\r
5098   CoTaskMemFree( renderFormat );\r
5099 \r
5100   //delete convBuffer;\r
5101   free ( convBuffer );\r
5102 \r
5103   CoUninitialize();\r
5104 \r
5105   // update stream state\r
5106   stream_.state = STREAM_STOPPED;\r
5107 \r
5108   if ( errorText_.empty() )\r
5109     return;\r
5110   else\r
5111     error( errorType );\r
5112 }\r
5113 \r
5114 //******************** End of __WINDOWS_WASAPI__ *********************//\r
5115 #endif\r
5116 \r
5117 \r
5118 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
5119 \r
5120 // Modified by Robin Davies, October 2005\r
5121 // - Improvements to DirectX pointer chasing. \r
5122 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
5123 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
5124 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
5125 // Changed device query structure for RtAudio 4.0.7, January 2010\r
5126 \r
5127 #include <dsound.h>\r
5128 #include <assert.h>\r
5129 #include <algorithm>\r
5130 \r
5131 #if defined(__MINGW32__)\r
5132   // missing from latest mingw winapi\r
5133 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
5134 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
5135 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
5136 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
5137 #endif\r
5138 \r
5139 #define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
5140 \r
5141 #ifdef _MSC_VER // if Microsoft Visual C++\r
5142 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
5143 #endif\r
5144 \r
5145 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
5146 {\r
5147   if ( pointer > bufferSize ) pointer -= bufferSize;\r
5148   if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
5149   if ( pointer < earlierPointer ) pointer += bufferSize;\r
5150   return pointer >= earlierPointer && pointer < laterPointer;\r
5151 }\r
5152 \r
5153 // A structure to hold various information related to the DirectSound\r
5154 // API implementation.\r
5155 struct DsHandle {\r
5156   unsigned int drainCounter; // Tracks callback counts when draining\r
5157   bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
5158   void *id[2];\r
5159   void *buffer[2];\r
5160   bool xrun[2];\r
5161   UINT bufferPointer[2];  \r
5162   DWORD dsBufferSize[2];\r
5163   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
5164   HANDLE condition;\r
5165 \r
5166   DsHandle()\r
5167     :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }\r
5168 };\r
5169 \r
5170 // Declarations for utility functions, callbacks, and structures\r
5171 // specific to the DirectSound implementation.\r
5172 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
5173                                           LPCTSTR description,\r
5174                                           LPCTSTR module,\r
5175                                           LPVOID lpContext );\r
5176 \r
5177 static const char* getErrorString( int code );\r
5178 \r
5179 static unsigned __stdcall callbackHandler( void *ptr );\r
5180 \r
5181 struct DsDevice {\r
5182   LPGUID id[2];\r
5183   bool validId[2];\r
5184   bool found;\r
5185   std::string name;\r
5186 \r
5187   DsDevice()\r
5188   : found(false) { validId[0] = false; validId[1] = false; }\r
5189 };\r
5190 \r
5191 struct DsProbeData {\r
5192   bool isInput;\r
5193   std::vector<struct DsDevice>* dsDevices;\r
5194 };\r
5195 \r
5196 RtApiDs :: RtApiDs()\r
5197 {\r
5198   // Dsound will run both-threaded. If CoInitialize fails, then just\r
5199   // accept whatever the mainline chose for a threading model.\r
5200   coInitialized_ = false;\r
5201   HRESULT hr = CoInitialize( NULL );\r
5202   if ( !FAILED( hr ) ) coInitialized_ = true;\r
5203 }\r
5204 \r
5205 RtApiDs :: ~RtApiDs()\r
5206 {\r
5207   if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
5208   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
5209 }\r
5210 \r
5211 // The DirectSound default output is always the first device.\r
5212 unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
5213 {\r
5214   return 0;\r
5215 }\r
5216 \r
5217 // The DirectSound default input is always the first input device,\r
5218 // which is the first capture device enumerated.\r
5219 unsigned int RtApiDs :: getDefaultInputDevice( void )\r
5220 {\r
5221   return 0;\r
5222 }\r
5223 \r
5224 unsigned int RtApiDs :: getDeviceCount( void )\r
5225 {\r
5226   // Set query flag for previously found devices to false, so that we\r
5227   // can check for any devices that have disappeared.\r
5228   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5229     dsDevices[i].found = false;\r
5230 \r
5231   // Query DirectSound devices.\r
5232   struct DsProbeData probeInfo;\r
5233   probeInfo.isInput = false;\r
5234   probeInfo.dsDevices = &dsDevices;\r
5235   HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5236   if ( FAILED( result ) ) {\r
5237     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
5238     errorText_ = errorStream_.str();\r
5239     error( RtAudioError::WARNING );\r
5240   }\r
5241 \r
5242   // Query DirectSoundCapture devices.\r
5243   probeInfo.isInput = true;\r
5244   result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5245   if ( FAILED( result ) ) {\r
5246     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
5247     errorText_ = errorStream_.str();\r
5248     error( RtAudioError::WARNING );\r
5249   }\r
5250 \r
5251   // Clean out any devices that may have disappeared.\r
5252   std::vector< int > indices;\r
5253   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5254     if ( dsDevices[i].found == false ) indices.push_back( i );\r
5255   //unsigned int nErased = 0;\r
5256   for ( unsigned int i=0; i<indices.size(); i++ )\r
5257     dsDevices.erase( dsDevices.begin()+indices[i] );\r
5258   //dsDevices.erase( dsDevices.begin()-nErased++ );\r
5259 \r
5260   return static_cast<unsigned int>(dsDevices.size());\r
5261 }\r
5262 \r
5263 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
5264 {\r
5265   RtAudio::DeviceInfo info;\r
5266   info.probed = false;\r
5267 \r
5268   if ( dsDevices.size() == 0 ) {\r
5269     // Force a query of all devices\r
5270     getDeviceCount();\r
5271     if ( dsDevices.size() == 0 ) {\r
5272       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
5273       error( RtAudioError::INVALID_USE );\r
5274       return info;\r
5275     }\r
5276   }\r
5277 \r
5278   if ( device >= dsDevices.size() ) {\r
5279     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
5280     error( RtAudioError::INVALID_USE );\r
5281     return info;\r
5282   }\r
5283 \r
5284   HRESULT result;\r
5285   if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
5286 \r
5287   LPDIRECTSOUND output;\r
5288   DSCAPS outCaps;\r
5289   result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5290   if ( FAILED( result ) ) {\r
5291     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5292     errorText_ = errorStream_.str();\r
5293     error( RtAudioError::WARNING );\r
5294     goto probeInput;\r
5295   }\r
5296 \r
5297   outCaps.dwSize = sizeof( outCaps );\r
5298   result = output->GetCaps( &outCaps );\r
5299   if ( FAILED( result ) ) {\r
5300     output->Release();\r
5301     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
5302     errorText_ = errorStream_.str();\r
5303     error( RtAudioError::WARNING );\r
5304     goto probeInput;\r
5305   }\r
5306 \r
5307   // Get output channel information.\r
5308   info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
5309 \r
5310   // Get sample rate information.\r
5311   info.sampleRates.clear();\r
5312   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
5313     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
5314          SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )\r
5315       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
5316   }\r
5317 \r
5318   // Get format information.\r
5319   if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
5320   if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
5321 \r
5322   output->Release();\r
5323 \r
5324   if ( getDefaultOutputDevice() == device )\r
5325     info.isDefaultOutput = true;\r
5326 \r
5327   if ( dsDevices[ device ].validId[1] == false ) {\r
5328     info.name = dsDevices[ device ].name;\r
5329     info.probed = true;\r
5330     return info;\r
5331   }\r
5332 \r
5333  probeInput:\r
5334 \r
5335   LPDIRECTSOUNDCAPTURE input;\r
5336   result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5337   if ( FAILED( result ) ) {\r
5338     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5339     errorText_ = errorStream_.str();\r
5340     error( RtAudioError::WARNING );\r
5341     return info;\r
5342   }\r
5343 \r
5344   DSCCAPS inCaps;\r
5345   inCaps.dwSize = sizeof( inCaps );\r
5346   result = input->GetCaps( &inCaps );\r
5347   if ( FAILED( result ) ) {\r
5348     input->Release();\r
5349     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
5350     errorText_ = errorStream_.str();\r
5351     error( RtAudioError::WARNING );\r
5352     return info;\r
5353   }\r
5354 \r
5355   // Get input channel information.\r
5356   info.inputChannels = inCaps.dwChannels;\r
5357 \r
5358   // Get sample rate and format information.\r
5359   std::vector<unsigned int> rates;\r
5360   if ( inCaps.dwChannels >= 2 ) {\r
5361     if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5362     if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5363     if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5364     if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5365     if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5366     if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5367     if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5368     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5369 \r
5370     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5371       if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
5372       if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
5373       if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
5374       if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
5375     }\r
5376     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5377       if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
5378       if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
5379       if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
5380       if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
5381     }\r
5382   }\r
5383   else if ( inCaps.dwChannels == 1 ) {\r
5384     if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5385     if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5386     if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5387     if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5388     if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5389     if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5390     if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5391     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5392 \r
5393     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5394       if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
5395       if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
5396       if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
5397       if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
5398     }\r
5399     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5400       if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
5401       if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
5402       if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
5403       if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
5404     }\r
5405   }\r
5406   else info.inputChannels = 0; // technically, this would be an error\r
5407 \r
5408   input->Release();\r
5409 \r
5410   if ( info.inputChannels == 0 ) return info;\r
5411 \r
5412   // Copy the supported rates to the info structure but avoid duplication.\r
5413   bool found;\r
5414   for ( unsigned int i=0; i<rates.size(); i++ ) {\r
5415     found = false;\r
5416     for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
5417       if ( rates[i] == info.sampleRates[j] ) {\r
5418         found = true;\r
5419         break;\r
5420       }\r
5421     }\r
5422     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
5423   }\r
5424   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
5425 \r
5426   // If device opens for both playback and capture, we determine the channels.\r
5427   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
5428     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
5429 \r
5430   if ( device == 0 ) info.isDefaultInput = true;\r
5431 \r
5432   // Copy name and return.\r
5433   info.name = dsDevices[ device ].name;\r
5434   info.probed = true;\r
5435   return info;\r
5436 }\r
5437 \r
5438 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
5439                                  unsigned int firstChannel, unsigned int sampleRate,\r
5440                                  RtAudioFormat format, unsigned int *bufferSize,\r
5441                                  RtAudio::StreamOptions *options )\r
5442 {\r
5443   if ( channels + firstChannel > 2 ) {\r
5444     errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
5445     return FAILURE;\r
5446   }\r
5447 \r
5448   size_t nDevices = dsDevices.size();\r
5449   if ( nDevices == 0 ) {\r
5450     // This should not happen because a check is made before this function is called.\r
5451     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
5452     return FAILURE;\r
5453   }\r
5454 \r
5455   if ( device >= nDevices ) {\r
5456     // This should not happen because a check is made before this function is called.\r
5457     errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
5458     return FAILURE;\r
5459   }\r
5460 \r
5461   if ( mode == OUTPUT ) {\r
5462     if ( dsDevices[ device ].validId[0] == false ) {\r
5463       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
5464       errorText_ = errorStream_.str();\r
5465       return FAILURE;\r
5466     }\r
5467   }\r
5468   else { // mode == INPUT\r
5469     if ( dsDevices[ device ].validId[1] == false ) {\r
5470       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
5471       errorText_ = errorStream_.str();\r
5472       return FAILURE;\r
5473     }\r
5474   }\r
5475 \r
5476   // According to a note in PortAudio, using GetDesktopWindow()\r
5477   // instead of GetForegroundWindow() is supposed to avoid problems\r
5478   // that occur when the application's window is not the foreground\r
5479   // window.  Also, if the application window closes before the\r
5480   // DirectSound buffer, DirectSound can crash.  In the past, I had\r
5481   // problems when using GetDesktopWindow() but it seems fine now\r
5482   // (January 2010).  I'll leave it commented here.\r
5483   // HWND hWnd = GetForegroundWindow();\r
5484   HWND hWnd = GetDesktopWindow();\r
5485 \r
5486   // Check the numberOfBuffers parameter and limit the lowest value to\r
5487   // two.  This is a judgement call and a value of two is probably too\r
5488   // low for capture, but it should work for playback.\r
5489   int nBuffers = 0;\r
5490   if ( options ) nBuffers = options->numberOfBuffers;\r
5491   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
5492   if ( nBuffers < 2 ) nBuffers = 3;\r
5493 \r
5494   // Check the lower range of the user-specified buffer size and set\r
5495   // (arbitrarily) to a lower bound of 32.\r
5496   if ( *bufferSize < 32 ) *bufferSize = 32;\r
5497 \r
5498   // Create the wave format structure.  The data format setting will\r
5499   // be determined later.\r
5500   WAVEFORMATEX waveFormat;\r
5501   ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
5502   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
5503   waveFormat.nChannels = channels + firstChannel;\r
5504   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
5505 \r
5506   // Determine the device buffer size. By default, we'll use the value\r
5507   // defined above (32K), but we will grow it to make allowances for\r
5508   // very large software buffer sizes.\r
5509   DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
5510   DWORD dsPointerLeadTime = 0;\r
5511 \r
5512   void *ohandle = 0, *bhandle = 0;\r
5513   HRESULT result;\r
5514   if ( mode == OUTPUT ) {\r
5515 \r
5516     LPDIRECTSOUND output;\r
5517     result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5518     if ( FAILED( result ) ) {\r
5519       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5520       errorText_ = errorStream_.str();\r
5521       return FAILURE;\r
5522     }\r
5523 \r
5524     DSCAPS outCaps;\r
5525     outCaps.dwSize = sizeof( outCaps );\r
5526     result = output->GetCaps( &outCaps );\r
5527     if ( FAILED( result ) ) {\r
5528       output->Release();\r
5529       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
5530       errorText_ = errorStream_.str();\r
5531       return FAILURE;\r
5532     }\r
5533 \r
5534     // Check channel information.\r
5535     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
5536       errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
5537       errorText_ = errorStream_.str();\r
5538       return FAILURE;\r
5539     }\r
5540 \r
5541     // Check format information.  Use 16-bit format unless not\r
5542     // supported or user requests 8-bit.\r
5543     if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
5544          !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
5545       waveFormat.wBitsPerSample = 16;\r
5546       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5547     }\r
5548     else {\r
5549       waveFormat.wBitsPerSample = 8;\r
5550       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5551     }\r
5552     stream_.userFormat = format;\r
5553 \r
5554     // Update wave format structure and buffer information.\r
5555     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5556     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5557     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5558 \r
5559     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5560     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5561       dsBufferSize *= 2;\r
5562 \r
5563     // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
5564     // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
5565     // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
5566     result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
5567     if ( FAILED( result ) ) {\r
5568       output->Release();\r
5569       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
5570       errorText_ = errorStream_.str();\r
5571       return FAILURE;\r
5572     }\r
5573 \r
5574     // Even though we will write to the secondary buffer, we need to\r
5575     // access the primary buffer to set the correct output format\r
5576     // (since the default is 8-bit, 22 kHz!).  Setup the DS primary\r
5577     // buffer description.\r
5578     DSBUFFERDESC bufferDescription;\r
5579     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5580     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5581     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
5582 \r
5583     // Obtain the primary buffer\r
5584     LPDIRECTSOUNDBUFFER buffer;\r
5585     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5586     if ( FAILED( result ) ) {\r
5587       output->Release();\r
5588       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
5589       errorText_ = errorStream_.str();\r
5590       return FAILURE;\r
5591     }\r
5592 \r
5593     // Set the primary DS buffer sound format.\r
5594     result = buffer->SetFormat( &waveFormat );\r
5595     if ( FAILED( result ) ) {\r
5596       output->Release();\r
5597       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
5598       errorText_ = errorStream_.str();\r
5599       return FAILURE;\r
5600     }\r
5601 \r
5602     // Setup the secondary DS buffer description.\r
5603     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5604     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5605     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5606                                   DSBCAPS_GLOBALFOCUS |\r
5607                                   DSBCAPS_GETCURRENTPOSITION2 |\r
5608                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
5609     bufferDescription.dwBufferBytes = dsBufferSize;\r
5610     bufferDescription.lpwfxFormat = &waveFormat;\r
5611 \r
5612     // Try to create the secondary DS buffer.  If that doesn't work,\r
5613     // try to use software mixing.  Otherwise, there's a problem.\r
5614     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5615     if ( FAILED( result ) ) {\r
5616       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5617                                     DSBCAPS_GLOBALFOCUS |\r
5618                                     DSBCAPS_GETCURRENTPOSITION2 |\r
5619                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
5620       result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5621       if ( FAILED( result ) ) {\r
5622         output->Release();\r
5623         errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
5624         errorText_ = errorStream_.str();\r
5625         return FAILURE;\r
5626       }\r
5627     }\r
5628 \r
5629     // Get the buffer size ... might be different from what we specified.\r
5630     DSBCAPS dsbcaps;\r
5631     dsbcaps.dwSize = sizeof( DSBCAPS );\r
5632     result = buffer->GetCaps( &dsbcaps );\r
5633     if ( FAILED( result ) ) {\r
5634       output->Release();\r
5635       buffer->Release();\r
5636       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5637       errorText_ = errorStream_.str();\r
5638       return FAILURE;\r
5639     }\r
5640 \r
5641     dsBufferSize = dsbcaps.dwBufferBytes;\r
5642 \r
5643     // Lock the DS buffer\r
5644     LPVOID audioPtr;\r
5645     DWORD dataLen;\r
5646     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5647     if ( FAILED( result ) ) {\r
5648       output->Release();\r
5649       buffer->Release();\r
5650       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
5651       errorText_ = errorStream_.str();\r
5652       return FAILURE;\r
5653     }\r
5654 \r
5655     // Zero the DS buffer\r
5656     ZeroMemory( audioPtr, dataLen );\r
5657 \r
5658     // Unlock the DS buffer\r
5659     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5660     if ( FAILED( result ) ) {\r
5661       output->Release();\r
5662       buffer->Release();\r
5663       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
5664       errorText_ = errorStream_.str();\r
5665       return FAILURE;\r
5666     }\r
5667 \r
5668     ohandle = (void *) output;\r
5669     bhandle = (void *) buffer;\r
5670   }\r
5671 \r
5672   if ( mode == INPUT ) {\r
5673 \r
5674     LPDIRECTSOUNDCAPTURE input;\r
5675     result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5676     if ( FAILED( result ) ) {\r
5677       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5678       errorText_ = errorStream_.str();\r
5679       return FAILURE;\r
5680     }\r
5681 \r
5682     DSCCAPS inCaps;\r
5683     inCaps.dwSize = sizeof( inCaps );\r
5684     result = input->GetCaps( &inCaps );\r
5685     if ( FAILED( result ) ) {\r
5686       input->Release();\r
5687       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
5688       errorText_ = errorStream_.str();\r
5689       return FAILURE;\r
5690     }\r
5691 \r
5692     // Check channel information.\r
5693     if ( inCaps.dwChannels < channels + firstChannel ) {\r
5694       errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
5695       return FAILURE;\r
5696     }\r
5697 \r
5698     // Check format information.  Use 16-bit format unless user\r
5699     // requests 8-bit.\r
5700     DWORD deviceFormats;\r
5701     if ( channels + firstChannel == 2 ) {\r
5702       deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
5703       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5704         waveFormat.wBitsPerSample = 8;\r
5705         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5706       }\r
5707       else { // assume 16-bit is supported\r
5708         waveFormat.wBitsPerSample = 16;\r
5709         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5710       }\r
5711     }\r
5712     else { // channel == 1\r
5713       deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
5714       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5715         waveFormat.wBitsPerSample = 8;\r
5716         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5717       }\r
5718       else { // assume 16-bit is supported\r
5719         waveFormat.wBitsPerSample = 16;\r
5720         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5721       }\r
5722     }\r
5723     stream_.userFormat = format;\r
5724 \r
5725     // Update wave format structure and buffer information.\r
5726     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5727     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5728     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5729 \r
5730     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5731     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5732       dsBufferSize *= 2;\r
5733 \r
5734     // Setup the secondary DS buffer description.\r
5735     DSCBUFFERDESC bufferDescription;\r
5736     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
5737     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
5738     bufferDescription.dwFlags = 0;\r
5739     bufferDescription.dwReserved = 0;\r
5740     bufferDescription.dwBufferBytes = dsBufferSize;\r
5741     bufferDescription.lpwfxFormat = &waveFormat;\r
5742 \r
5743     // Create the capture buffer.\r
5744     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
5745     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
5746     if ( FAILED( result ) ) {\r
5747       input->Release();\r
5748       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
5749       errorText_ = errorStream_.str();\r
5750       return FAILURE;\r
5751     }\r
5752 \r
5753     // Get the buffer size ... might be different from what we specified.\r
5754     DSCBCAPS dscbcaps;\r
5755     dscbcaps.dwSize = sizeof( DSCBCAPS );\r
5756     result = buffer->GetCaps( &dscbcaps );\r
5757     if ( FAILED( result ) ) {\r
5758       input->Release();\r
5759       buffer->Release();\r
5760       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5761       errorText_ = errorStream_.str();\r
5762       return FAILURE;\r
5763     }\r
5764 \r
5765     dsBufferSize = dscbcaps.dwBufferBytes;\r
5766 \r
5767     // NOTE: We could have a problem here if this is a duplex stream\r
5768     // and the play and capture hardware buffer sizes are different\r
5769     // (I'm actually not sure if that is a problem or not).\r
5770     // Currently, we are not verifying that.\r
5771 \r
5772     // Lock the capture buffer\r
5773     LPVOID audioPtr;\r
5774     DWORD dataLen;\r
5775     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5776     if ( FAILED( result ) ) {\r
5777       input->Release();\r
5778       buffer->Release();\r
5779       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
5780       errorText_ = errorStream_.str();\r
5781       return FAILURE;\r
5782     }\r
5783 \r
5784     // Zero the buffer\r
5785     ZeroMemory( audioPtr, dataLen );\r
5786 \r
5787     // Unlock the buffer\r
5788     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5789     if ( FAILED( result ) ) {\r
5790       input->Release();\r
5791       buffer->Release();\r
5792       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
5793       errorText_ = errorStream_.str();\r
5794       return FAILURE;\r
5795     }\r
5796 \r
5797     ohandle = (void *) input;\r
5798     bhandle = (void *) buffer;\r
5799   }\r
5800 \r
5801   // Set various stream parameters\r
5802   DsHandle *handle = 0;\r
5803   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
5804   stream_.nUserChannels[mode] = channels;\r
5805   stream_.bufferSize = *bufferSize;\r
5806   stream_.channelOffset[mode] = firstChannel;\r
5807   stream_.deviceInterleaved[mode] = true;\r
5808   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
5809   else stream_.userInterleaved = true;\r
5810 \r
5811   // Set flag for buffer conversion\r
5812   stream_.doConvertBuffer[mode] = false;\r
5813   if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
5814     stream_.doConvertBuffer[mode] = true;\r
5815   if (stream_.userFormat != stream_.deviceFormat[mode])\r
5816     stream_.doConvertBuffer[mode] = true;\r
5817   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
5818        stream_.nUserChannels[mode] > 1 )\r
5819     stream_.doConvertBuffer[mode] = true;\r
5820 \r
5821   // Allocate necessary internal buffers\r
5822   long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
5823   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
5824   if ( stream_.userBuffer[mode] == NULL ) {\r
5825     errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
5826     goto error;\r
5827   }\r
5828 \r
5829   if ( stream_.doConvertBuffer[mode] ) {\r
5830 \r
5831     bool makeBuffer = true;\r
5832     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
5833     if ( mode == INPUT ) {\r
5834       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
5835         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
5836         if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
5837       }\r
5838     }\r
5839 \r
5840     if ( makeBuffer ) {\r
5841       bufferBytes *= *bufferSize;\r
5842       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
5843       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
5844       if ( stream_.deviceBuffer == NULL ) {\r
5845         errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
5846         goto error;\r
5847       }\r
5848     }\r
5849   }\r
5850 \r
5851   // Allocate our DsHandle structures for the stream.\r
5852   if ( stream_.apiHandle == 0 ) {\r
5853     try {\r
5854       handle = new DsHandle;\r
5855     }\r
5856     catch ( std::bad_alloc& ) {\r
5857       errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
5858       goto error;\r
5859     }\r
5860 \r
5861     // Create a manual-reset event.\r
5862     handle->condition = CreateEvent( NULL,   // no security\r
5863                                      TRUE,   // manual-reset\r
5864                                      FALSE,  // non-signaled initially\r
5865                                      NULL ); // unnamed\r
5866     stream_.apiHandle = (void *) handle;\r
5867   }\r
5868   else\r
5869     handle = (DsHandle *) stream_.apiHandle;\r
5870   handle->id[mode] = ohandle;\r
5871   handle->buffer[mode] = bhandle;\r
5872   handle->dsBufferSize[mode] = dsBufferSize;\r
5873   handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
5874 \r
5875   stream_.device[mode] = device;\r
5876   stream_.state = STREAM_STOPPED;\r
5877   if ( stream_.mode == OUTPUT && mode == INPUT )\r
5878     // We had already set up an output stream.\r
5879     stream_.mode = DUPLEX;\r
5880   else\r
5881     stream_.mode = mode;\r
5882   stream_.nBuffers = nBuffers;\r
5883   stream_.sampleRate = sampleRate;\r
5884 \r
5885   // Setup the buffer conversion information structure.\r
5886   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
5887 \r
5888   // Setup the callback thread.\r
5889   if ( stream_.callbackInfo.isRunning == false ) {\r
5890     unsigned threadId;\r
5891     stream_.callbackInfo.isRunning = true;\r
5892     stream_.callbackInfo.object = (void *) this;\r
5893     stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
5894                                                   &stream_.callbackInfo, 0, &threadId );\r
5895     if ( stream_.callbackInfo.thread == 0 ) {\r
5896       errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
5897       goto error;\r
5898     }\r
5899 \r
5900     // Boost DS thread priority\r
5901     SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
5902   }\r
5903   return SUCCESS;\r
5904 \r
5905  error:\r
5906   if ( handle ) {\r
5907     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5908       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5909       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5910       if ( buffer ) buffer->Release();\r
5911       object->Release();\r
5912     }\r
5913     if ( handle->buffer[1] ) {\r
5914       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5915       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5916       if ( buffer ) buffer->Release();\r
5917       object->Release();\r
5918     }\r
5919     CloseHandle( handle->condition );\r
5920     delete handle;\r
5921     stream_.apiHandle = 0;\r
5922   }\r
5923 \r
5924   for ( int i=0; i<2; i++ ) {\r
5925     if ( stream_.userBuffer[i] ) {\r
5926       free( stream_.userBuffer[i] );\r
5927       stream_.userBuffer[i] = 0;\r
5928     }\r
5929   }\r
5930 \r
5931   if ( stream_.deviceBuffer ) {\r
5932     free( stream_.deviceBuffer );\r
5933     stream_.deviceBuffer = 0;\r
5934   }\r
5935 \r
5936   stream_.state = STREAM_CLOSED;\r
5937   return FAILURE;\r
5938 }\r
5939 \r
5940 void RtApiDs :: closeStream()\r
5941 {\r
5942   if ( stream_.state == STREAM_CLOSED ) {\r
5943     errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
5944     error( RtAudioError::WARNING );\r
5945     return;\r
5946   }\r
5947 \r
5948   // Stop the callback thread.\r
5949   stream_.callbackInfo.isRunning = false;\r
5950   WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
5951   CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
5952 \r
5953   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
5954   if ( handle ) {\r
5955     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5956       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5957       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5958       if ( buffer ) {\r
5959         buffer->Stop();\r
5960         buffer->Release();\r
5961       }\r
5962       object->Release();\r
5963     }\r
5964     if ( handle->buffer[1] ) {\r
5965       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5966       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5967       if ( buffer ) {\r
5968         buffer->Stop();\r
5969         buffer->Release();\r
5970       }\r
5971       object->Release();\r
5972     }\r
5973     CloseHandle( handle->condition );\r
5974     delete handle;\r
5975     stream_.apiHandle = 0;\r
5976   }\r
5977 \r
5978   for ( int i=0; i<2; i++ ) {\r
5979     if ( stream_.userBuffer[i] ) {\r
5980       free( stream_.userBuffer[i] );\r
5981       stream_.userBuffer[i] = 0;\r
5982     }\r
5983   }\r
5984 \r
5985   if ( stream_.deviceBuffer ) {\r
5986     free( stream_.deviceBuffer );\r
5987     stream_.deviceBuffer = 0;\r
5988   }\r
5989 \r
5990   stream_.mode = UNINITIALIZED;\r
5991   stream_.state = STREAM_CLOSED;\r
5992 }\r
5993 \r
5994 void RtApiDs :: startStream()\r
5995 {\r
5996   verifyStream();\r
5997   if ( stream_.state == STREAM_RUNNING ) {\r
5998     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
5999     error( RtAudioError::WARNING );\r
6000     return;\r
6001   }\r
6002 \r
6003   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6004 \r
6005   // Increase scheduler frequency on lesser windows (a side-effect of\r
6006   // increasing timer accuracy).  On greater windows (Win2K or later),\r
6007   // this is already in effect.\r
6008   timeBeginPeriod( 1 ); \r
6009 \r
6010   buffersRolling = false;\r
6011   duplexPrerollBytes = 0;\r
6012 \r
6013   if ( stream_.mode == DUPLEX ) {\r
6014     // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
6015     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
6016   }\r
6017 \r
6018   HRESULT result = 0;\r
6019   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6020 \r
6021     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6022     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
6023     if ( FAILED( result ) ) {\r
6024       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
6025       errorText_ = errorStream_.str();\r
6026       goto unlock;\r
6027     }\r
6028   }\r
6029 \r
6030   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6031 \r
6032     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6033     result = buffer->Start( DSCBSTART_LOOPING );\r
6034     if ( FAILED( result ) ) {\r
6035       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
6036       errorText_ = errorStream_.str();\r
6037       goto unlock;\r
6038     }\r
6039   }\r
6040 \r
6041   handle->drainCounter = 0;\r
6042   handle->internalDrain = false;\r
6043   ResetEvent( handle->condition );\r
6044   stream_.state = STREAM_RUNNING;\r
6045 \r
6046  unlock:\r
6047   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6048 }\r
6049 \r
6050 void RtApiDs :: stopStream()\r
6051 {\r
6052   verifyStream();\r
6053   if ( stream_.state == STREAM_STOPPED ) {\r
6054     errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
6055     error( RtAudioError::WARNING );\r
6056     return;\r
6057   }\r
6058 \r
6059   HRESULT result = 0;\r
6060   LPVOID audioPtr;\r
6061   DWORD dataLen;\r
6062   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6063   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6064     if ( handle->drainCounter == 0 ) {\r
6065       handle->drainCounter = 2;\r
6066       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
6067     }\r
6068 \r
6069     stream_.state = STREAM_STOPPED;\r
6070 \r
6071     // Stop the buffer and clear memory\r
6072     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6073     result = buffer->Stop();\r
6074     if ( FAILED( result ) ) {\r
6075       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
6076       errorText_ = errorStream_.str();\r
6077       goto unlock;\r
6078     }\r
6079 \r
6080     // Lock the buffer and clear it so that if we start to play again,\r
6081     // we won't have old data playing.\r
6082     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6083     if ( FAILED( result ) ) {\r
6084       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
6085       errorText_ = errorStream_.str();\r
6086       goto unlock;\r
6087     }\r
6088 \r
6089     // Zero the DS buffer\r
6090     ZeroMemory( audioPtr, dataLen );\r
6091 \r
6092     // Unlock the DS buffer\r
6093     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6094     if ( FAILED( result ) ) {\r
6095       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
6096       errorText_ = errorStream_.str();\r
6097       goto unlock;\r
6098     }\r
6099 \r
6100     // If we start playing again, we must begin at beginning of buffer.\r
6101     handle->bufferPointer[0] = 0;\r
6102   }\r
6103 \r
6104   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6105     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6106     audioPtr = NULL;\r
6107     dataLen = 0;\r
6108 \r
6109     stream_.state = STREAM_STOPPED;\r
6110 \r
6111     result = buffer->Stop();\r
6112     if ( FAILED( result ) ) {\r
6113       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
6114       errorText_ = errorStream_.str();\r
6115       goto unlock;\r
6116     }\r
6117 \r
6118     // Lock the buffer and clear it so that if we start to play again,\r
6119     // we won't have old data playing.\r
6120     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6121     if ( FAILED( result ) ) {\r
6122       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
6123       errorText_ = errorStream_.str();\r
6124       goto unlock;\r
6125     }\r
6126 \r
6127     // Zero the DS buffer\r
6128     ZeroMemory( audioPtr, dataLen );\r
6129 \r
6130     // Unlock the DS buffer\r
6131     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6132     if ( FAILED( result ) ) {\r
6133       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
6134       errorText_ = errorStream_.str();\r
6135       goto unlock;\r
6136     }\r
6137 \r
6138     // If we start recording again, we must begin at beginning of buffer.\r
6139     handle->bufferPointer[1] = 0;\r
6140   }\r
6141 \r
6142  unlock:\r
6143   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
6144   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6145 }\r
6146 \r
6147 void RtApiDs :: abortStream()\r
6148 {\r
6149   verifyStream();\r
6150   if ( stream_.state == STREAM_STOPPED ) {\r
6151     errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
6152     error( RtAudioError::WARNING );\r
6153     return;\r
6154   }\r
6155 \r
6156   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6157   handle->drainCounter = 2;\r
6158 \r
6159   stopStream();\r
6160 }\r
6161 \r
6162 void RtApiDs :: callbackEvent()\r
6163 {\r
6164   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
6165     Sleep( 50 ); // sleep 50 milliseconds\r
6166     return;\r
6167   }\r
6168 \r
6169   if ( stream_.state == STREAM_CLOSED ) {\r
6170     errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
6171     error( RtAudioError::WARNING );\r
6172     return;\r
6173   }\r
6174 \r
6175   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
6176   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6177 \r
6178   // Check if we were draining the stream and signal is finished.\r
6179   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
6180 \r
6181     stream_.state = STREAM_STOPPING;\r
6182     if ( handle->internalDrain == false )\r
6183       SetEvent( handle->condition );\r
6184     else\r
6185       stopStream();\r
6186     return;\r
6187   }\r
6188 \r
6189   // Invoke user callback to get fresh output data UNLESS we are\r
6190   // draining stream.\r
6191   if ( handle->drainCounter == 0 ) {\r
6192     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
6193     double streamTime = getStreamTime();\r
6194     RtAudioStreamStatus status = 0;\r
6195     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
6196       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
6197       handle->xrun[0] = false;\r
6198     }\r
6199     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
6200       status |= RTAUDIO_INPUT_OVERFLOW;\r
6201       handle->xrun[1] = false;\r
6202     }\r
6203     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
6204                                   stream_.bufferSize, streamTime, status, info->userData );\r
6205     if ( cbReturnValue == 2 ) {\r
6206       stream_.state = STREAM_STOPPING;\r
6207       handle->drainCounter = 2;\r
6208       abortStream();\r
6209       return;\r
6210     }\r
6211     else if ( cbReturnValue == 1 ) {\r
6212       handle->drainCounter = 1;\r
6213       handle->internalDrain = true;\r
6214     }\r
6215   }\r
6216 \r
6217   HRESULT result;\r
6218   DWORD currentWritePointer, safeWritePointer;\r
6219   DWORD currentReadPointer, safeReadPointer;\r
6220   UINT nextWritePointer;\r
6221 \r
6222   LPVOID buffer1 = NULL;\r
6223   LPVOID buffer2 = NULL;\r
6224   DWORD bufferSize1 = 0;\r
6225   DWORD bufferSize2 = 0;\r
6226 \r
6227   char *buffer;\r
6228   long bufferBytes;\r
6229 \r
6230   if ( buffersRolling == false ) {\r
6231     if ( stream_.mode == DUPLEX ) {\r
6232       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6233 \r
6234       // It takes a while for the devices to get rolling. As a result,\r
6235       // there's no guarantee that the capture and write device pointers\r
6236       // will move in lockstep.  Wait here for both devices to start\r
6237       // rolling, and then set our buffer pointers accordingly.\r
6238       // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
6239       // bytes later than the write buffer.\r
6240 \r
6241       // Stub: a serious risk of having a pre-emptive scheduling round\r
6242       // take place between the two GetCurrentPosition calls... but I'm\r
6243       // really not sure how to solve the problem.  Temporarily boost to\r
6244       // Realtime priority, maybe; but I'm not sure what priority the\r
6245       // DirectSound service threads run at. We *should* be roughly\r
6246       // within a ms or so of correct.\r
6247 \r
6248       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6249       LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6250 \r
6251       DWORD startSafeWritePointer, startSafeReadPointer;\r
6252 \r
6253       result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
6254       if ( FAILED( result ) ) {\r
6255         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6256         errorText_ = errorStream_.str();\r
6257         error( RtAudioError::SYSTEM_ERROR );\r
6258         return;\r
6259       }\r
6260       result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
6261       if ( FAILED( result ) ) {\r
6262         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6263         errorText_ = errorStream_.str();\r
6264         error( RtAudioError::SYSTEM_ERROR );\r
6265         return;\r
6266       }\r
6267       while ( true ) {\r
6268         result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
6269         if ( FAILED( result ) ) {\r
6270           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6271           errorText_ = errorStream_.str();\r
6272           error( RtAudioError::SYSTEM_ERROR );\r
6273           return;\r
6274         }\r
6275         result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
6276         if ( FAILED( result ) ) {\r
6277           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6278           errorText_ = errorStream_.str();\r
6279           error( RtAudioError::SYSTEM_ERROR );\r
6280           return;\r
6281         }\r
6282         if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
6283         Sleep( 1 );\r
6284       }\r
6285 \r
6286       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6287 \r
6288       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6289       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6290       handle->bufferPointer[1] = safeReadPointer;\r
6291     }\r
6292     else if ( stream_.mode == OUTPUT ) {\r
6293 \r
6294       // Set the proper nextWritePosition after initial startup.\r
6295       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6296       result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6297       if ( FAILED( result ) ) {\r
6298         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6299         errorText_ = errorStream_.str();\r
6300         error( RtAudioError::SYSTEM_ERROR );\r
6301         return;\r
6302       }\r
6303       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6304       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6305     }\r
6306 \r
6307     buffersRolling = true;\r
6308   }\r
6309 \r
6310   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6311     \r
6312     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6313 \r
6314     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
6315       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6316       bufferBytes *= formatBytes( stream_.userFormat );\r
6317       memset( stream_.userBuffer[0], 0, bufferBytes );\r
6318     }\r
6319 \r
6320     // Setup parameters and do buffer conversion if necessary.\r
6321     if ( stream_.doConvertBuffer[0] ) {\r
6322       buffer = stream_.deviceBuffer;\r
6323       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
6324       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
6325       bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
6326     }\r
6327     else {\r
6328       buffer = stream_.userBuffer[0];\r
6329       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6330       bufferBytes *= formatBytes( stream_.userFormat );\r
6331     }\r
6332 \r
6333     // No byte swapping necessary in DirectSound implementation.\r
6334 \r
6335     // Ahhh ... windoze.  16-bit data is signed but 8-bit data is\r
6336     // unsigned.  So, we need to convert our signed 8-bit data here to\r
6337     // unsigned.\r
6338     if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
6339       for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
6340 \r
6341     DWORD dsBufferSize = handle->dsBufferSize[0];\r
6342     nextWritePointer = handle->bufferPointer[0];\r
6343 \r
6344     DWORD endWrite, leadPointer;\r
6345     while ( true ) {\r
6346       // Find out where the read and "safe write" pointers are.\r
6347       result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6348       if ( FAILED( result ) ) {\r
6349         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6350         errorText_ = errorStream_.str();\r
6351         error( RtAudioError::SYSTEM_ERROR );\r
6352         return;\r
6353       }\r
6354 \r
6355       // We will copy our output buffer into the region between\r
6356       // safeWritePointer and leadPointer.  If leadPointer is not\r
6357       // beyond the next endWrite position, wait until it is.\r
6358       leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
6359       //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
6360       if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
6361       if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
6362       endWrite = nextWritePointer + bufferBytes;\r
6363 \r
6364       // Check whether the entire write region is behind the play pointer.\r
6365       if ( leadPointer >= endWrite ) break;\r
6366 \r
6367       // If we are here, then we must wait until the leadPointer advances\r
6368       // beyond the end of our next write region. We use the\r
6369       // Sleep() function to suspend operation until that happens.\r
6370       double millis = ( endWrite - leadPointer ) * 1000.0;\r
6371       millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
6372       if ( millis < 1.0 ) millis = 1.0;\r
6373       Sleep( (DWORD) millis );\r
6374     }\r
6375 \r
6376     if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
6377          || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
6378       // We've strayed into the forbidden zone ... resync the read pointer.\r
6379       handle->xrun[0] = true;\r
6380       nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
6381       if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
6382       handle->bufferPointer[0] = nextWritePointer;\r
6383       endWrite = nextWritePointer + bufferBytes;\r
6384     }\r
6385 \r
6386     // Lock free space in the buffer\r
6387     result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
6388                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6389     if ( FAILED( result ) ) {\r
6390       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
6391       errorText_ = errorStream_.str();\r
6392       error( RtAudioError::SYSTEM_ERROR );\r
6393       return;\r
6394     }\r
6395 \r
6396     // Copy our buffer into the DS buffer\r
6397     CopyMemory( buffer1, buffer, bufferSize1 );\r
6398     if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
6399 \r
6400     // Update our buffer offset and unlock sound buffer\r
6401     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6402     if ( FAILED( result ) ) {\r
6403       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
6404       errorText_ = errorStream_.str();\r
6405       error( RtAudioError::SYSTEM_ERROR );\r
6406       return;\r
6407     }\r
6408     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6409     handle->bufferPointer[0] = nextWritePointer;\r
6410 \r
6411     if ( handle->drainCounter ) {\r
6412       handle->drainCounter++;\r
6413       goto unlock;\r
6414     }\r
6415   }\r
6416 \r
6417   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6418 \r
6419     // Setup parameters.\r
6420     if ( stream_.doConvertBuffer[1] ) {\r
6421       buffer = stream_.deviceBuffer;\r
6422       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
6423       bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
6424     }\r
6425     else {\r
6426       buffer = stream_.userBuffer[1];\r
6427       bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
6428       bufferBytes *= formatBytes( stream_.userFormat );\r
6429     }\r
6430 \r
6431     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6432     long nextReadPointer = handle->bufferPointer[1];\r
6433     DWORD dsBufferSize = handle->dsBufferSize[1];\r
6434 \r
6435     // Find out where the write and "safe read" pointers are.\r
6436     result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6437     if ( FAILED( result ) ) {\r
6438       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6439       errorText_ = errorStream_.str();\r
6440       error( RtAudioError::SYSTEM_ERROR );\r
6441       return;\r
6442     }\r
6443 \r
6444     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6445     DWORD endRead = nextReadPointer + bufferBytes;\r
6446 \r
6447     // Handling depends on whether we are INPUT or DUPLEX. \r
6448     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
6449     // then a wait here will drag the write pointers into the forbidden zone.\r
6450     // \r
6451     // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
6452     // it's in a safe position. This causes dropouts, but it seems to be the only \r
6453     // practical way to sync up the read and write pointers reliably, given the \r
6454     // the very complex relationship between phase and increment of the read and write \r
6455     // pointers.\r
6456     //\r
6457     // In order to minimize audible dropouts in DUPLEX mode, we will\r
6458     // provide a pre-roll period of 0.5 seconds in which we return\r
6459     // zeros from the read buffer while the pointers sync up.\r
6460 \r
6461     if ( stream_.mode == DUPLEX ) {\r
6462       if ( safeReadPointer < endRead ) {\r
6463         if ( duplexPrerollBytes <= 0 ) {\r
6464           // Pre-roll time over. Be more agressive.\r
6465           int adjustment = endRead-safeReadPointer;\r
6466 \r
6467           handle->xrun[1] = true;\r
6468           // Two cases:\r
6469           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
6470           //     and perform fine adjustments later.\r
6471           //   - small adjustments: back off by twice as much.\r
6472           if ( adjustment >= 2*bufferBytes )\r
6473             nextReadPointer = safeReadPointer-2*bufferBytes;\r
6474           else\r
6475             nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
6476 \r
6477           if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6478 \r
6479         }\r
6480         else {\r
6481           // In pre=roll time. Just do it.\r
6482           nextReadPointer = safeReadPointer - bufferBytes;\r
6483           while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6484         }\r
6485         endRead = nextReadPointer + bufferBytes;\r
6486       }\r
6487     }\r
6488     else { // mode == INPUT\r
6489       while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
6490         // See comments for playback.\r
6491         double millis = (endRead - safeReadPointer) * 1000.0;\r
6492         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
6493         if ( millis < 1.0 ) millis = 1.0;\r
6494         Sleep( (DWORD) millis );\r
6495 \r
6496         // Wake up and find out where we are now.\r
6497         result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6498         if ( FAILED( result ) ) {\r
6499           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6500           errorText_ = errorStream_.str();\r
6501           error( RtAudioError::SYSTEM_ERROR );\r
6502           return;\r
6503         }\r
6504       \r
6505         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6506       }\r
6507     }\r
6508 \r
6509     // Lock free space in the buffer\r
6510     result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
6511                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6512     if ( FAILED( result ) ) {\r
6513       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
6514       errorText_ = errorStream_.str();\r
6515       error( RtAudioError::SYSTEM_ERROR );\r
6516       return;\r
6517     }\r
6518 \r
6519     if ( duplexPrerollBytes <= 0 ) {\r
6520       // Copy our buffer into the DS buffer\r
6521       CopyMemory( buffer, buffer1, bufferSize1 );\r
6522       if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
6523     }\r
6524     else {\r
6525       memset( buffer, 0, bufferSize1 );\r
6526       if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
6527       duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
6528     }\r
6529 \r
6530     // Update our buffer offset and unlock sound buffer\r
6531     nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6532     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6533     if ( FAILED( result ) ) {\r
6534       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
6535       errorText_ = errorStream_.str();\r
6536       error( RtAudioError::SYSTEM_ERROR );\r
6537       return;\r
6538     }\r
6539     handle->bufferPointer[1] = nextReadPointer;\r
6540 \r
6541     // No byte swapping necessary in DirectSound implementation.\r
6542 \r
6543     // If necessary, convert 8-bit data from unsigned to signed.\r
6544     if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
6545       for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
6546 \r
6547     // Do buffer conversion if necessary.\r
6548     if ( stream_.doConvertBuffer[1] )\r
6549       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
6550   }\r
6551 \r
6552  unlock:\r
6553   RtApi::tickStreamTime();\r
6554 }\r
6555 \r
6556 // Definitions for utility functions and callbacks\r
6557 // specific to the DirectSound implementation.\r
6558 \r
6559 static unsigned __stdcall callbackHandler( void *ptr )\r
6560 {\r
6561   CallbackInfo *info = (CallbackInfo *) ptr;\r
6562   RtApiDs *object = (RtApiDs *) info->object;\r
6563   bool* isRunning = &info->isRunning;\r
6564 \r
6565   while ( *isRunning == true ) {\r
6566     object->callbackEvent();\r
6567   }\r
6568 \r
6569   _endthreadex( 0 );\r
6570   return 0;\r
6571 }\r
6572 \r
6573 #include "tchar.h"\r
6574 \r
6575 static std::string convertTChar( LPCTSTR name )\r
6576 {\r
6577 #if defined( UNICODE ) || defined( _UNICODE )\r
6578   int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);\r
6579   std::string s( length-1, '\0' );\r
6580   WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);\r
6581 #else\r
6582   std::string s( name );\r
6583 #endif\r
6584 \r
6585   return s;\r
6586 }\r
6587 \r
6588 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
6589                                           LPCTSTR description,\r
6590                                           LPCTSTR /*module*/,\r
6591                                           LPVOID lpContext )\r
6592 {\r
6593   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
6594   std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
6595 \r
6596   HRESULT hr;\r
6597   bool validDevice = false;\r
6598   if ( probeInfo.isInput == true ) {\r
6599     DSCCAPS caps;\r
6600     LPDIRECTSOUNDCAPTURE object;\r
6601 \r
6602     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
6603     if ( hr != DS_OK ) return TRUE;\r
6604 \r
6605     caps.dwSize = sizeof(caps);\r
6606     hr = object->GetCaps( &caps );\r
6607     if ( hr == DS_OK ) {\r
6608       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
6609         validDevice = true;\r
6610     }\r
6611     object->Release();\r
6612   }\r
6613   else {\r
6614     DSCAPS caps;\r
6615     LPDIRECTSOUND object;\r
6616     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
6617     if ( hr != DS_OK ) return TRUE;\r
6618 \r
6619     caps.dwSize = sizeof(caps);\r
6620     hr = object->GetCaps( &caps );\r
6621     if ( hr == DS_OK ) {\r
6622       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
6623         validDevice = true;\r
6624     }\r
6625     object->Release();\r
6626   }\r
6627 \r
6628   // If good device, then save its name and guid.\r
6629   std::string name = convertTChar( description );\r
6630   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
6631   if ( lpguid == NULL )\r
6632     name = "Default Device";\r
6633   if ( validDevice ) {\r
6634     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
6635       if ( dsDevices[i].name == name ) {\r
6636         dsDevices[i].found = true;\r
6637         if ( probeInfo.isInput ) {\r
6638           dsDevices[i].id[1] = lpguid;\r
6639           dsDevices[i].validId[1] = true;\r
6640         }\r
6641         else {\r
6642           dsDevices[i].id[0] = lpguid;\r
6643           dsDevices[i].validId[0] = true;\r
6644         }\r
6645         return TRUE;\r
6646       }\r
6647     }\r
6648 \r
6649     DsDevice device;\r
6650     device.name = name;\r
6651     device.found = true;\r
6652     if ( probeInfo.isInput ) {\r
6653       device.id[1] = lpguid;\r
6654       device.validId[1] = true;\r
6655     }\r
6656     else {\r
6657       device.id[0] = lpguid;\r
6658       device.validId[0] = true;\r
6659     }\r
6660     dsDevices.push_back( device );\r
6661   }\r
6662 \r
6663   return TRUE;\r
6664 }\r
6665 \r
6666 static const char* getErrorString( int code )\r
6667 {\r
6668   switch ( code ) {\r
6669 \r
6670   case DSERR_ALLOCATED:\r
6671     return "Already allocated";\r
6672 \r
6673   case DSERR_CONTROLUNAVAIL:\r
6674     return "Control unavailable";\r
6675 \r
6676   case DSERR_INVALIDPARAM:\r
6677     return "Invalid parameter";\r
6678 \r
6679   case DSERR_INVALIDCALL:\r
6680     return "Invalid call";\r
6681 \r
6682   case DSERR_GENERIC:\r
6683     return "Generic error";\r
6684 \r
6685   case DSERR_PRIOLEVELNEEDED:\r
6686     return "Priority level needed";\r
6687 \r
6688   case DSERR_OUTOFMEMORY:\r
6689     return "Out of memory";\r
6690 \r
6691   case DSERR_BADFORMAT:\r
6692     return "The sample rate or the channel format is not supported";\r
6693 \r
6694   case DSERR_UNSUPPORTED:\r
6695     return "Not supported";\r
6696 \r
6697   case DSERR_NODRIVER:\r
6698     return "No driver";\r
6699 \r
6700   case DSERR_ALREADYINITIALIZED:\r
6701     return "Already initialized";\r
6702 \r
6703   case DSERR_NOAGGREGATION:\r
6704     return "No aggregation";\r
6705 \r
6706   case DSERR_BUFFERLOST:\r
6707     return "Buffer lost";\r
6708 \r
6709   case DSERR_OTHERAPPHASPRIO:\r
6710     return "Another application already has priority";\r
6711 \r
6712   case DSERR_UNINITIALIZED:\r
6713     return "Uninitialized";\r
6714 \r
6715   default:\r
6716     return "DirectSound unknown error";\r
6717   }\r
6718 }\r
6719 //******************** End of __WINDOWS_DS__ *********************//\r
6720 #endif\r
6721 \r
6722 \r
6723 #if defined(__LINUX_ALSA__)\r
6724 \r
6725 #include <alsa/asoundlib.h>\r
6726 #include <unistd.h>\r
6727 \r
6728   // A structure to hold various information related to the ALSA API\r
6729   // implementation.\r
6730 struct AlsaHandle {\r
6731   snd_pcm_t *handles[2];\r
6732   bool synchronized;\r
6733   bool xrun[2];\r
6734   pthread_cond_t runnable_cv;\r
6735   bool runnable;\r
6736 \r
6737   AlsaHandle()\r
6738     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
6739 };\r
6740 \r
6741 static void *alsaCallbackHandler( void * ptr );\r
6742 \r
6743 RtApiAlsa :: RtApiAlsa()\r
6744 {\r
6745   // Nothing to do here.\r
6746 }\r
6747 \r
6748 RtApiAlsa :: ~RtApiAlsa()\r
6749 {\r
6750   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
6751 }\r
6752 \r
6753 unsigned int RtApiAlsa :: getDeviceCount( void )\r
6754 {\r
6755   unsigned nDevices = 0;\r
6756   int result, subdevice, card;\r
6757   char name[64];\r
6758   snd_ctl_t *handle;\r
6759 \r
6760   // Count cards and devices\r
6761   card = -1;\r
6762   snd_card_next( &card );\r
6763   while ( card >= 0 ) {\r
6764     sprintf( name, "hw:%d", card );\r
6765     result = snd_ctl_open( &handle, name, 0 );\r
6766     if ( result < 0 ) {\r
6767       errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6768       errorText_ = errorStream_.str();\r
6769       error( RtAudioError::WARNING );\r
6770       goto nextcard;\r
6771     }\r
6772     subdevice = -1;\r
6773     while( 1 ) {\r
6774       result = snd_ctl_pcm_next_device( handle, &subdevice );\r
6775       if ( result < 0 ) {\r
6776         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6777         errorText_ = errorStream_.str();\r
6778         error( RtAudioError::WARNING );\r
6779         break;\r
6780       }\r
6781       if ( subdevice < 0 )\r
6782         break;\r
6783       nDevices++;\r
6784     }\r
6785   nextcard:\r
6786     snd_ctl_close( handle );\r
6787     snd_card_next( &card );\r
6788   }\r
6789 \r
6790   result = snd_ctl_open( &handle, "default", 0 );\r
6791   if (result == 0) {\r
6792     nDevices++;\r
6793     snd_ctl_close( handle );\r
6794   }\r
6795 \r
6796   return nDevices;\r
6797 }\r
6798 \r
6799 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
6800 {\r
6801   RtAudio::DeviceInfo info;\r
6802   info.probed = false;\r
6803 \r
6804   unsigned nDevices = 0;\r
6805   int result, subdevice, card;\r
6806   char name[64];\r
6807   snd_ctl_t *chandle;\r
6808 \r
6809   // Count cards and devices\r
6810   card = -1;\r
6811   snd_card_next( &card );\r
6812   while ( card >= 0 ) {\r
6813     sprintf( name, "hw:%d", card );\r
6814     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6815     if ( result < 0 ) {\r
6816       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6817       errorText_ = errorStream_.str();\r
6818       error( RtAudioError::WARNING );\r
6819       goto nextcard;\r
6820     }\r
6821     subdevice = -1;\r
6822     while( 1 ) {\r
6823       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
6824       if ( result < 0 ) {\r
6825         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6826         errorText_ = errorStream_.str();\r
6827         error( RtAudioError::WARNING );\r
6828         break;\r
6829       }\r
6830       if ( subdevice < 0 ) break;\r
6831       if ( nDevices == device ) {\r
6832         sprintf( name, "hw:%d,%d", card, subdevice );\r
6833         goto foundDevice;\r
6834       }\r
6835       nDevices++;\r
6836     }\r
6837   nextcard:\r
6838     snd_ctl_close( chandle );\r
6839     snd_card_next( &card );\r
6840   }\r
6841 \r
6842   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
6843   if ( result == 0 ) {\r
6844     if ( nDevices == device ) {\r
6845       strcpy( name, "default" );\r
6846       goto foundDevice;\r
6847     }\r
6848     nDevices++;\r
6849   }\r
6850 \r
6851   if ( nDevices == 0 ) {\r
6852     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
6853     error( RtAudioError::INVALID_USE );\r
6854     return info;\r
6855   }\r
6856 \r
6857   if ( device >= nDevices ) {\r
6858     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
6859     error( RtAudioError::INVALID_USE );\r
6860     return info;\r
6861   }\r
6862 \r
6863  foundDevice:\r
6864 \r
6865   // If a stream is already open, we cannot probe the stream devices.\r
6866   // Thus, use the saved results.\r
6867   if ( stream_.state != STREAM_CLOSED &&\r
6868        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
6869     snd_ctl_close( chandle );\r
6870     if ( device >= devices_.size() ) {\r
6871       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
6872       error( RtAudioError::WARNING );\r
6873       return info;\r
6874     }\r
6875     return devices_[ device ];\r
6876   }\r
6877 \r
6878   int openMode = SND_PCM_ASYNC;\r
6879   snd_pcm_stream_t stream;\r
6880   snd_pcm_info_t *pcminfo;\r
6881   snd_pcm_info_alloca( &pcminfo );\r
6882   snd_pcm_t *phandle;\r
6883   snd_pcm_hw_params_t *params;\r
6884   snd_pcm_hw_params_alloca( &params );\r
6885 \r
6886   // First try for playback unless default device (which has subdev -1)\r
6887   stream = SND_PCM_STREAM_PLAYBACK;\r
6888   snd_pcm_info_set_stream( pcminfo, stream );\r
6889   if ( subdevice != -1 ) {\r
6890     snd_pcm_info_set_device( pcminfo, subdevice );\r
6891     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
6892 \r
6893     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6894     if ( result < 0 ) {\r
6895       // Device probably doesn't support playback.\r
6896       goto captureProbe;\r
6897     }\r
6898   }\r
6899 \r
6900   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
6901   if ( result < 0 ) {\r
6902     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6903     errorText_ = errorStream_.str();\r
6904     error( RtAudioError::WARNING );\r
6905     goto captureProbe;\r
6906   }\r
6907 \r
6908   // The device is open ... fill the parameter structure.\r
6909   result = snd_pcm_hw_params_any( phandle, params );\r
6910   if ( result < 0 ) {\r
6911     snd_pcm_close( phandle );\r
6912     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6913     errorText_ = errorStream_.str();\r
6914     error( RtAudioError::WARNING );\r
6915     goto captureProbe;\r
6916   }\r
6917 \r
6918   // Get output channel information.\r
6919   unsigned int value;\r
6920   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6921   if ( result < 0 ) {\r
6922     snd_pcm_close( phandle );\r
6923     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
6924     errorText_ = errorStream_.str();\r
6925     error( RtAudioError::WARNING );\r
6926     goto captureProbe;\r
6927   }\r
6928   info.outputChannels = value;\r
6929   snd_pcm_close( phandle );\r
6930 \r
6931  captureProbe:\r
6932   stream = SND_PCM_STREAM_CAPTURE;\r
6933   snd_pcm_info_set_stream( pcminfo, stream );\r
6934 \r
6935   // Now try for capture unless default device (with subdev = -1)\r
6936   if ( subdevice != -1 ) {\r
6937     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6938     snd_ctl_close( chandle );\r
6939     if ( result < 0 ) {\r
6940       // Device probably doesn't support capture.\r
6941       if ( info.outputChannels == 0 ) return info;\r
6942       goto probeParameters;\r
6943     }\r
6944   }\r
6945   else\r
6946     snd_ctl_close( chandle );\r
6947 \r
6948   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
6949   if ( result < 0 ) {\r
6950     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6951     errorText_ = errorStream_.str();\r
6952     error( RtAudioError::WARNING );\r
6953     if ( info.outputChannels == 0 ) return info;\r
6954     goto probeParameters;\r
6955   }\r
6956 \r
6957   // The device is open ... fill the parameter structure.\r
6958   result = snd_pcm_hw_params_any( phandle, params );\r
6959   if ( result < 0 ) {\r
6960     snd_pcm_close( phandle );\r
6961     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6962     errorText_ = errorStream_.str();\r
6963     error( RtAudioError::WARNING );\r
6964     if ( info.outputChannels == 0 ) return info;\r
6965     goto probeParameters;\r
6966   }\r
6967 \r
6968   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6969   if ( result < 0 ) {\r
6970     snd_pcm_close( phandle );\r
6971     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
6972     errorText_ = errorStream_.str();\r
6973     error( RtAudioError::WARNING );\r
6974     if ( info.outputChannels == 0 ) return info;\r
6975     goto probeParameters;\r
6976   }\r
6977   info.inputChannels = value;\r
6978   snd_pcm_close( phandle );\r
6979 \r
6980   // If device opens for both playback and capture, we determine the channels.\r
6981   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
6982     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
6983 \r
6984   // ALSA doesn't provide default devices so we'll use the first available one.\r
6985   if ( device == 0 && info.outputChannels > 0 )\r
6986     info.isDefaultOutput = true;\r
6987   if ( device == 0 && info.inputChannels > 0 )\r
6988     info.isDefaultInput = true;\r
6989 \r
6990  probeParameters:\r
6991   // At this point, we just need to figure out the supported data\r
6992   // formats and sample rates.  We'll proceed by opening the device in\r
6993   // the direction with the maximum number of channels, or playback if\r
6994   // they are equal.  This might limit our sample rate options, but so\r
6995   // be it.\r
6996 \r
6997   if ( info.outputChannels >= info.inputChannels )\r
6998     stream = SND_PCM_STREAM_PLAYBACK;\r
6999   else\r
7000     stream = SND_PCM_STREAM_CAPTURE;\r
7001   snd_pcm_info_set_stream( pcminfo, stream );\r
7002 \r
7003   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7004   if ( result < 0 ) {\r
7005     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7006     errorText_ = errorStream_.str();\r
7007     error( RtAudioError::WARNING );\r
7008     return info;\r
7009   }\r
7010 \r
7011   // The device is open ... fill the parameter structure.\r
7012   result = snd_pcm_hw_params_any( phandle, params );\r
7013   if ( result < 0 ) {\r
7014     snd_pcm_close( phandle );\r
7015     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7016     errorText_ = errorStream_.str();\r
7017     error( RtAudioError::WARNING );\r
7018     return info;\r
7019   }\r
7020 \r
7021   // Test our discrete set of sample rate values.\r
7022   info.sampleRates.clear();\r
7023   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7024     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )\r
7025       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7026   }\r
7027   if ( info.sampleRates.size() == 0 ) {\r
7028     snd_pcm_close( phandle );\r
7029     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7030     errorText_ = errorStream_.str();\r
7031     error( RtAudioError::WARNING );\r
7032     return info;\r
7033   }\r
7034 \r
7035   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7036   snd_pcm_format_t format;\r
7037   info.nativeFormats = 0;\r
7038   format = SND_PCM_FORMAT_S8;\r
7039   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7040     info.nativeFormats |= RTAUDIO_SINT8;\r
7041   format = SND_PCM_FORMAT_S16;\r
7042   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7043     info.nativeFormats |= RTAUDIO_SINT16;\r
7044   format = SND_PCM_FORMAT_S24;\r
7045   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7046     info.nativeFormats |= RTAUDIO_SINT24;\r
7047   format = SND_PCM_FORMAT_S32;\r
7048   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7049     info.nativeFormats |= RTAUDIO_SINT32;\r
7050   format = SND_PCM_FORMAT_FLOAT;\r
7051   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7052     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7053   format = SND_PCM_FORMAT_FLOAT64;\r
7054   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7055     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7056 \r
7057   // Check that we have at least one supported format\r
7058   if ( info.nativeFormats == 0 ) {\r
7059     snd_pcm_close( phandle );\r
7060     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7061     errorText_ = errorStream_.str();\r
7062     error( RtAudioError::WARNING );\r
7063     return info;\r
7064   }\r
7065 \r
7066   // Get the device name\r
7067   char *cardname;\r
7068   result = snd_card_get_name( card, &cardname );\r
7069   if ( result >= 0 ) {\r
7070     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7071     free( cardname );\r
7072   }\r
7073   info.name = name;\r
7074 \r
7075   // That's all ... close the device and return\r
7076   snd_pcm_close( phandle );\r
7077   info.probed = true;\r
7078   return info;\r
7079 }\r
7080 \r
7081 void RtApiAlsa :: saveDeviceInfo( void )\r
7082 {\r
7083   devices_.clear();\r
7084 \r
7085   unsigned int nDevices = getDeviceCount();\r
7086   devices_.resize( nDevices );\r
7087   for ( unsigned int i=0; i<nDevices; i++ )\r
7088     devices_[i] = getDeviceInfo( i );\r
7089 }\r
7090 \r
7091 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7092                                    unsigned int firstChannel, unsigned int sampleRate,\r
7093                                    RtAudioFormat format, unsigned int *bufferSize,\r
7094                                    RtAudio::StreamOptions *options )\r
7095 \r
7096 {\r
7097 #if defined(__RTAUDIO_DEBUG__)\r
7098   snd_output_t *out;\r
7099   snd_output_stdio_attach(&out, stderr, 0);\r
7100 #endif\r
7101 \r
7102   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7103 \r
7104   unsigned nDevices = 0;\r
7105   int result, subdevice, card;\r
7106   char name[64];\r
7107   snd_ctl_t *chandle;\r
7108 \r
7109   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7110     snprintf(name, sizeof(name), "%s", "default");\r
7111   else {\r
7112     // Count cards and devices\r
7113     card = -1;\r
7114     snd_card_next( &card );\r
7115     while ( card >= 0 ) {\r
7116       sprintf( name, "hw:%d", card );\r
7117       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7118       if ( result < 0 ) {\r
7119         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7120         errorText_ = errorStream_.str();\r
7121         return FAILURE;\r
7122       }\r
7123       subdevice = -1;\r
7124       while( 1 ) {\r
7125         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7126         if ( result < 0 ) break;\r
7127         if ( subdevice < 0 ) break;\r
7128         if ( nDevices == device ) {\r
7129           sprintf( name, "hw:%d,%d", card, subdevice );\r
7130           snd_ctl_close( chandle );\r
7131           goto foundDevice;\r
7132         }\r
7133         nDevices++;\r
7134       }\r
7135       snd_ctl_close( chandle );\r
7136       snd_card_next( &card );\r
7137     }\r
7138 \r
7139     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7140     if ( result == 0 ) {\r
7141       if ( nDevices == device ) {\r
7142         strcpy( name, "default" );\r
7143         goto foundDevice;\r
7144       }\r
7145       nDevices++;\r
7146     }\r
7147 \r
7148     if ( nDevices == 0 ) {\r
7149       // This should not happen because a check is made before this function is called.\r
7150       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7151       return FAILURE;\r
7152     }\r
7153 \r
7154     if ( device >= nDevices ) {\r
7155       // This should not happen because a check is made before this function is called.\r
7156       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7157       return FAILURE;\r
7158     }\r
7159   }\r
7160 \r
7161  foundDevice:\r
7162 \r
7163   // The getDeviceInfo() function will not work for a device that is\r
7164   // already open.  Thus, we'll probe the system before opening a\r
7165   // stream and save the results for use by getDeviceInfo().\r
7166   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7167     this->saveDeviceInfo();\r
7168 \r
7169   snd_pcm_stream_t stream;\r
7170   if ( mode == OUTPUT )\r
7171     stream = SND_PCM_STREAM_PLAYBACK;\r
7172   else\r
7173     stream = SND_PCM_STREAM_CAPTURE;\r
7174 \r
7175   snd_pcm_t *phandle;\r
7176   int openMode = SND_PCM_ASYNC;\r
7177   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7178   if ( result < 0 ) {\r
7179     if ( mode == OUTPUT )\r
7180       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7181     else\r
7182       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7183     errorText_ = errorStream_.str();\r
7184     return FAILURE;\r
7185   }\r
7186 \r
7187   // Fill the parameter structure.\r
7188   snd_pcm_hw_params_t *hw_params;\r
7189   snd_pcm_hw_params_alloca( &hw_params );\r
7190   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7191   if ( result < 0 ) {\r
7192     snd_pcm_close( phandle );\r
7193     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7194     errorText_ = errorStream_.str();\r
7195     return FAILURE;\r
7196   }\r
7197 \r
7198 #if defined(__RTAUDIO_DEBUG__)\r
7199   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7200   snd_pcm_hw_params_dump( hw_params, out );\r
7201 #endif\r
7202 \r
7203   // Set access ... check user preference.\r
7204   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7205     stream_.userInterleaved = false;\r
7206     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7207     if ( result < 0 ) {\r
7208       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7209       stream_.deviceInterleaved[mode] =  true;\r
7210     }\r
7211     else\r
7212       stream_.deviceInterleaved[mode] = false;\r
7213   }\r
7214   else {\r
7215     stream_.userInterleaved = true;\r
7216     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7217     if ( result < 0 ) {\r
7218       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7219       stream_.deviceInterleaved[mode] =  false;\r
7220     }\r
7221     else\r
7222       stream_.deviceInterleaved[mode] =  true;\r
7223   }\r
7224 \r
7225   if ( result < 0 ) {\r
7226     snd_pcm_close( phandle );\r
7227     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7228     errorText_ = errorStream_.str();\r
7229     return FAILURE;\r
7230   }\r
7231 \r
7232   // Determine how to set the device format.\r
7233   stream_.userFormat = format;\r
7234   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7235 \r
7236   if ( format == RTAUDIO_SINT8 )\r
7237     deviceFormat = SND_PCM_FORMAT_S8;\r
7238   else if ( format == RTAUDIO_SINT16 )\r
7239     deviceFormat = SND_PCM_FORMAT_S16;\r
7240   else if ( format == RTAUDIO_SINT24 )\r
7241     deviceFormat = SND_PCM_FORMAT_S24;\r
7242   else if ( format == RTAUDIO_SINT32 )\r
7243     deviceFormat = SND_PCM_FORMAT_S32;\r
7244   else if ( format == RTAUDIO_FLOAT32 )\r
7245     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7246   else if ( format == RTAUDIO_FLOAT64 )\r
7247     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7248 \r
7249   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7250     stream_.deviceFormat[mode] = format;\r
7251     goto setFormat;\r
7252   }\r
7253 \r
7254   // The user requested format is not natively supported by the device.\r
7255   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7256   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7257     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7258     goto setFormat;\r
7259   }\r
7260 \r
7261   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7262   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7263     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7264     goto setFormat;\r
7265   }\r
7266 \r
7267   deviceFormat = SND_PCM_FORMAT_S32;\r
7268   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7269     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7270     goto setFormat;\r
7271   }\r
7272 \r
7273   deviceFormat = SND_PCM_FORMAT_S24;\r
7274   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7275     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7276     goto setFormat;\r
7277   }\r
7278 \r
7279   deviceFormat = SND_PCM_FORMAT_S16;\r
7280   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7281     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7282     goto setFormat;\r
7283   }\r
7284 \r
7285   deviceFormat = SND_PCM_FORMAT_S8;\r
7286   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7287     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7288     goto setFormat;\r
7289   }\r
7290 \r
7291   // If we get here, no supported format was found.\r
7292   snd_pcm_close( phandle );\r
7293   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7294   errorText_ = errorStream_.str();\r
7295   return FAILURE;\r
7296 \r
7297  setFormat:\r
7298   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7299   if ( result < 0 ) {\r
7300     snd_pcm_close( phandle );\r
7301     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7302     errorText_ = errorStream_.str();\r
7303     return FAILURE;\r
7304   }\r
7305 \r
7306   // Determine whether byte-swaping is necessary.\r
7307   stream_.doByteSwap[mode] = false;\r
7308   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7309     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7310     if ( result == 0 )\r
7311       stream_.doByteSwap[mode] = true;\r
7312     else if (result < 0) {\r
7313       snd_pcm_close( phandle );\r
7314       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7315       errorText_ = errorStream_.str();\r
7316       return FAILURE;\r
7317     }\r
7318   }\r
7319 \r
7320   // Set the sample rate.\r
7321   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7322   if ( result < 0 ) {\r
7323     snd_pcm_close( phandle );\r
7324     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7325     errorText_ = errorStream_.str();\r
7326     return FAILURE;\r
7327   }\r
7328 \r
7329   // Determine the number of channels for this device.  We support a possible\r
7330   // minimum device channel number > than the value requested by the user.\r
7331   stream_.nUserChannels[mode] = channels;\r
7332   unsigned int value;\r
7333   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7334   unsigned int deviceChannels = value;\r
7335   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7336     snd_pcm_close( phandle );\r
7337     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7338     errorText_ = errorStream_.str();\r
7339     return FAILURE;\r
7340   }\r
7341 \r
7342   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7343   if ( result < 0 ) {\r
7344     snd_pcm_close( phandle );\r
7345     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7346     errorText_ = errorStream_.str();\r
7347     return FAILURE;\r
7348   }\r
7349   deviceChannels = value;\r
7350   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7351   stream_.nDeviceChannels[mode] = deviceChannels;\r
7352 \r
7353   // Set the device channels.\r
7354   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7355   if ( result < 0 ) {\r
7356     snd_pcm_close( phandle );\r
7357     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7358     errorText_ = errorStream_.str();\r
7359     return FAILURE;\r
7360   }\r
7361 \r
7362   // Set the buffer (or period) size.\r
7363   int dir = 0;\r
7364   snd_pcm_uframes_t periodSize = *bufferSize;\r
7365   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7366   if ( result < 0 ) {\r
7367     snd_pcm_close( phandle );\r
7368     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7369     errorText_ = errorStream_.str();\r
7370     return FAILURE;\r
7371   }\r
7372   *bufferSize = periodSize;\r
7373 \r
7374   // Set the buffer number, which in ALSA is referred to as the "period".\r
7375   unsigned int periods = 0;\r
7376   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7377   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7378   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7379   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7380   if ( result < 0 ) {\r
7381     snd_pcm_close( phandle );\r
7382     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7383     errorText_ = errorStream_.str();\r
7384     return FAILURE;\r
7385   }\r
7386 \r
7387   // If attempting to setup a duplex stream, the bufferSize parameter\r
7388   // MUST be the same in both directions!\r
7389   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7390     snd_pcm_close( phandle );\r
7391     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7392     errorText_ = errorStream_.str();\r
7393     return FAILURE;\r
7394   }\r
7395 \r
7396   stream_.bufferSize = *bufferSize;\r
7397 \r
7398   // Install the hardware configuration\r
7399   result = snd_pcm_hw_params( phandle, hw_params );\r
7400   if ( result < 0 ) {\r
7401     snd_pcm_close( phandle );\r
7402     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7403     errorText_ = errorStream_.str();\r
7404     return FAILURE;\r
7405   }\r
7406 \r
7407 #if defined(__RTAUDIO_DEBUG__)\r
7408   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7409   snd_pcm_hw_params_dump( hw_params, out );\r
7410 #endif\r
7411 \r
7412   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7413   snd_pcm_sw_params_t *sw_params = NULL;\r
7414   snd_pcm_sw_params_alloca( &sw_params );\r
7415   snd_pcm_sw_params_current( phandle, sw_params );\r
7416   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7417   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7418   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7419 \r
7420   // The following two settings were suggested by Theo Veenker\r
7421   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7422   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7423 \r
7424   // here are two options for a fix\r
7425   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7426   snd_pcm_uframes_t val;\r
7427   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7428   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7429 \r
7430   result = snd_pcm_sw_params( phandle, sw_params );\r
7431   if ( result < 0 ) {\r
7432     snd_pcm_close( phandle );\r
7433     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7434     errorText_ = errorStream_.str();\r
7435     return FAILURE;\r
7436   }\r
7437 \r
7438 #if defined(__RTAUDIO_DEBUG__)\r
7439   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7440   snd_pcm_sw_params_dump( sw_params, out );\r
7441 #endif\r
7442 \r
7443   // Set flags for buffer conversion\r
7444   stream_.doConvertBuffer[mode] = false;\r
7445   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7446     stream_.doConvertBuffer[mode] = true;\r
7447   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7448     stream_.doConvertBuffer[mode] = true;\r
7449   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7450        stream_.nUserChannels[mode] > 1 )\r
7451     stream_.doConvertBuffer[mode] = true;\r
7452 \r
7453   // Allocate the ApiHandle if necessary and then save.\r
7454   AlsaHandle *apiInfo = 0;\r
7455   if ( stream_.apiHandle == 0 ) {\r
7456     try {\r
7457       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7458     }\r
7459     catch ( std::bad_alloc& ) {\r
7460       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7461       goto error;\r
7462     }\r
7463 \r
7464     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7465       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7466       goto error;\r
7467     }\r
7468 \r
7469     stream_.apiHandle = (void *) apiInfo;\r
7470     apiInfo->handles[0] = 0;\r
7471     apiInfo->handles[1] = 0;\r
7472   }\r
7473   else {\r
7474     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7475   }\r
7476   apiInfo->handles[mode] = phandle;\r
7477   phandle = 0;\r
7478 \r
7479   // Allocate necessary internal buffers.\r
7480   unsigned long bufferBytes;\r
7481   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7482   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7483   if ( stream_.userBuffer[mode] == NULL ) {\r
7484     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7485     goto error;\r
7486   }\r
7487 \r
7488   if ( stream_.doConvertBuffer[mode] ) {\r
7489 \r
7490     bool makeBuffer = true;\r
7491     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7492     if ( mode == INPUT ) {\r
7493       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7494         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7495         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7496       }\r
7497     }\r
7498 \r
7499     if ( makeBuffer ) {\r
7500       bufferBytes *= *bufferSize;\r
7501       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7502       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7503       if ( stream_.deviceBuffer == NULL ) {\r
7504         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7505         goto error;\r
7506       }\r
7507     }\r
7508   }\r
7509 \r
7510   stream_.sampleRate = sampleRate;\r
7511   stream_.nBuffers = periods;\r
7512   stream_.device[mode] = device;\r
7513   stream_.state = STREAM_STOPPED;\r
7514 \r
7515   // Setup the buffer conversion information structure.\r
7516   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7517 \r
7518   // Setup thread if necessary.\r
7519   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7520     // We had already set up an output stream.\r
7521     stream_.mode = DUPLEX;\r
7522     // Link the streams if possible.\r
7523     apiInfo->synchronized = false;\r
7524     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7525       apiInfo->synchronized = true;\r
7526     else {\r
7527       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7528       error( RtAudioError::WARNING );\r
7529     }\r
7530   }\r
7531   else {\r
7532     stream_.mode = mode;\r
7533 \r
7534     // Setup callback thread.\r
7535     stream_.callbackInfo.object = (void *) this;\r
7536 \r
7537     // Set the thread attributes for joinable and realtime scheduling\r
7538     // priority (optional).  The higher priority will only take affect\r
7539     // if the program is run as root or suid. Note, under Linux\r
7540     // processes with CAP_SYS_NICE privilege, a user can change\r
7541     // scheduling policy and priority (thus need not be root). See\r
7542     // POSIX "capabilities".\r
7543     pthread_attr_t attr;\r
7544     pthread_attr_init( &attr );\r
7545     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7546 \r
7547 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7548     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7549       // We previously attempted to increase the audio callback priority\r
7550       // to SCHED_RR here via the attributes.  However, while no errors\r
7551       // were reported in doing so, it did not work.  So, now this is\r
7552       // done in the alsaCallbackHandler function.\r
7553       stream_.callbackInfo.doRealtime = true;\r
7554       int priority = options->priority;\r
7555       int min = sched_get_priority_min( SCHED_RR );\r
7556       int max = sched_get_priority_max( SCHED_RR );\r
7557       if ( priority < min ) priority = min;\r
7558       else if ( priority > max ) priority = max;\r
7559       stream_.callbackInfo.priority = priority;\r
7560     }\r
7561 #endif\r
7562 \r
7563     stream_.callbackInfo.isRunning = true;\r
7564     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7565     pthread_attr_destroy( &attr );\r
7566     if ( result ) {\r
7567       stream_.callbackInfo.isRunning = false;\r
7568       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7569       goto error;\r
7570     }\r
7571   }\r
7572 \r
7573   return SUCCESS;\r
7574 \r
7575  error:\r
7576   if ( apiInfo ) {\r
7577     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7578     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7579     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7580     delete apiInfo;\r
7581     stream_.apiHandle = 0;\r
7582   }\r
7583 \r
7584   if ( phandle) snd_pcm_close( phandle );\r
7585 \r
7586   for ( int i=0; i<2; i++ ) {\r
7587     if ( stream_.userBuffer[i] ) {\r
7588       free( stream_.userBuffer[i] );\r
7589       stream_.userBuffer[i] = 0;\r
7590     }\r
7591   }\r
7592 \r
7593   if ( stream_.deviceBuffer ) {\r
7594     free( stream_.deviceBuffer );\r
7595     stream_.deviceBuffer = 0;\r
7596   }\r
7597 \r
7598   stream_.state = STREAM_CLOSED;\r
7599   return FAILURE;\r
7600 }\r
7601 \r
7602 void RtApiAlsa :: closeStream()\r
7603 {\r
7604   if ( stream_.state == STREAM_CLOSED ) {\r
7605     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7606     error( RtAudioError::WARNING );\r
7607     return;\r
7608   }\r
7609 \r
7610   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7611   stream_.callbackInfo.isRunning = false;\r
7612   MUTEX_LOCK( &stream_.mutex );\r
7613   if ( stream_.state == STREAM_STOPPED ) {\r
7614     apiInfo->runnable = true;\r
7615     pthread_cond_signal( &apiInfo->runnable_cv );\r
7616   }\r
7617   MUTEX_UNLOCK( &stream_.mutex );\r
7618   pthread_join( stream_.callbackInfo.thread, NULL );\r
7619 \r
7620   if ( stream_.state == STREAM_RUNNING ) {\r
7621     stream_.state = STREAM_STOPPED;\r
7622     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7623       snd_pcm_drop( apiInfo->handles[0] );\r
7624     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7625       snd_pcm_drop( apiInfo->handles[1] );\r
7626   }\r
7627 \r
7628   if ( apiInfo ) {\r
7629     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7630     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7631     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7632     delete apiInfo;\r
7633     stream_.apiHandle = 0;\r
7634   }\r
7635 \r
7636   for ( int i=0; i<2; i++ ) {\r
7637     if ( stream_.userBuffer[i] ) {\r
7638       free( stream_.userBuffer[i] );\r
7639       stream_.userBuffer[i] = 0;\r
7640     }\r
7641   }\r
7642 \r
7643   if ( stream_.deviceBuffer ) {\r
7644     free( stream_.deviceBuffer );\r
7645     stream_.deviceBuffer = 0;\r
7646   }\r
7647 \r
7648   stream_.mode = UNINITIALIZED;\r
7649   stream_.state = STREAM_CLOSED;\r
7650 }\r
7651 \r
7652 void RtApiAlsa :: startStream()\r
7653 {\r
7654   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7655 \r
7656   verifyStream();\r
7657   if ( stream_.state == STREAM_RUNNING ) {\r
7658     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7659     error( RtAudioError::WARNING );\r
7660     return;\r
7661   }\r
7662 \r
7663   MUTEX_LOCK( &stream_.mutex );\r
7664 \r
7665   int result = 0;\r
7666   snd_pcm_state_t state;\r
7667   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7668   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7669   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7670     state = snd_pcm_state( handle[0] );\r
7671     if ( state != SND_PCM_STATE_PREPARED ) {\r
7672       result = snd_pcm_prepare( handle[0] );\r
7673       if ( result < 0 ) {\r
7674         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7675         errorText_ = errorStream_.str();\r
7676         goto unlock;\r
7677       }\r
7678     }\r
7679   }\r
7680 \r
7681   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7682     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7683     state = snd_pcm_state( handle[1] );\r
7684     if ( state != SND_PCM_STATE_PREPARED ) {\r
7685       result = snd_pcm_prepare( handle[1] );\r
7686       if ( result < 0 ) {\r
7687         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7688         errorText_ = errorStream_.str();\r
7689         goto unlock;\r
7690       }\r
7691     }\r
7692   }\r
7693 \r
7694   stream_.state = STREAM_RUNNING;\r
7695 \r
7696  unlock:\r
7697   apiInfo->runnable = true;\r
7698   pthread_cond_signal( &apiInfo->runnable_cv );\r
7699   MUTEX_UNLOCK( &stream_.mutex );\r
7700 \r
7701   if ( result >= 0 ) return;\r
7702   error( RtAudioError::SYSTEM_ERROR );\r
7703 }\r
7704 \r
7705 void RtApiAlsa :: stopStream()\r
7706 {\r
7707   verifyStream();\r
7708   if ( stream_.state == STREAM_STOPPED ) {\r
7709     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7710     error( RtAudioError::WARNING );\r
7711     return;\r
7712   }\r
7713 \r
7714   stream_.state = STREAM_STOPPED;\r
7715   MUTEX_LOCK( &stream_.mutex );\r
7716 \r
7717   int result = 0;\r
7718   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7719   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7720   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7721     if ( apiInfo->synchronized ) \r
7722       result = snd_pcm_drop( handle[0] );\r
7723     else\r
7724       result = snd_pcm_drain( handle[0] );\r
7725     if ( result < 0 ) {\r
7726       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7727       errorText_ = errorStream_.str();\r
7728       goto unlock;\r
7729     }\r
7730   }\r
7731 \r
7732   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7733     result = snd_pcm_drop( handle[1] );\r
7734     if ( result < 0 ) {\r
7735       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7736       errorText_ = errorStream_.str();\r
7737       goto unlock;\r
7738     }\r
7739   }\r
7740 \r
7741  unlock:\r
7742   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7743   MUTEX_UNLOCK( &stream_.mutex );\r
7744 \r
7745   if ( result >= 0 ) return;\r
7746   error( RtAudioError::SYSTEM_ERROR );\r
7747 }\r
7748 \r
7749 void RtApiAlsa :: abortStream()\r
7750 {\r
7751   verifyStream();\r
7752   if ( stream_.state == STREAM_STOPPED ) {\r
7753     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7754     error( RtAudioError::WARNING );\r
7755     return;\r
7756   }\r
7757 \r
7758   stream_.state = STREAM_STOPPED;\r
7759   MUTEX_LOCK( &stream_.mutex );\r
7760 \r
7761   int result = 0;\r
7762   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7763   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7764   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7765     result = snd_pcm_drop( handle[0] );\r
7766     if ( result < 0 ) {\r
7767       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7768       errorText_ = errorStream_.str();\r
7769       goto unlock;\r
7770     }\r
7771   }\r
7772 \r
7773   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7774     result = snd_pcm_drop( handle[1] );\r
7775     if ( result < 0 ) {\r
7776       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7777       errorText_ = errorStream_.str();\r
7778       goto unlock;\r
7779     }\r
7780   }\r
7781 \r
7782  unlock:\r
7783   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7784   MUTEX_UNLOCK( &stream_.mutex );\r
7785 \r
7786   if ( result >= 0 ) return;\r
7787   error( RtAudioError::SYSTEM_ERROR );\r
7788 }\r
7789 \r
7790 void RtApiAlsa :: callbackEvent()\r
7791 {\r
7792   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7793   if ( stream_.state == STREAM_STOPPED ) {\r
7794     MUTEX_LOCK( &stream_.mutex );\r
7795     while ( !apiInfo->runnable )\r
7796       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7797 \r
7798     if ( stream_.state != STREAM_RUNNING ) {\r
7799       MUTEX_UNLOCK( &stream_.mutex );\r
7800       return;\r
7801     }\r
7802     MUTEX_UNLOCK( &stream_.mutex );\r
7803   }\r
7804 \r
7805   if ( stream_.state == STREAM_CLOSED ) {\r
7806     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7807     error( RtAudioError::WARNING );\r
7808     return;\r
7809   }\r
7810 \r
7811   int doStopStream = 0;\r
7812   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7813   double streamTime = getStreamTime();\r
7814   RtAudioStreamStatus status = 0;\r
7815   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7816     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
7817     apiInfo->xrun[0] = false;\r
7818   }\r
7819   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
7820     status |= RTAUDIO_INPUT_OVERFLOW;\r
7821     apiInfo->xrun[1] = false;\r
7822   }\r
7823   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
7824                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
7825 \r
7826   if ( doStopStream == 2 ) {\r
7827     abortStream();\r
7828     return;\r
7829   }\r
7830 \r
7831   MUTEX_LOCK( &stream_.mutex );\r
7832 \r
7833   // The state might change while waiting on a mutex.\r
7834   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
7835 \r
7836   int result;\r
7837   char *buffer;\r
7838   int channels;\r
7839   snd_pcm_t **handle;\r
7840   snd_pcm_sframes_t frames;\r
7841   RtAudioFormat format;\r
7842   handle = (snd_pcm_t **) apiInfo->handles;\r
7843 \r
7844   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
7845 \r
7846     // Setup parameters.\r
7847     if ( stream_.doConvertBuffer[1] ) {\r
7848       buffer = stream_.deviceBuffer;\r
7849       channels = stream_.nDeviceChannels[1];\r
7850       format = stream_.deviceFormat[1];\r
7851     }\r
7852     else {\r
7853       buffer = stream_.userBuffer[1];\r
7854       channels = stream_.nUserChannels[1];\r
7855       format = stream_.userFormat;\r
7856     }\r
7857 \r
7858     // Read samples from device in interleaved/non-interleaved format.\r
7859     if ( stream_.deviceInterleaved[1] )\r
7860       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
7861     else {\r
7862       void *bufs[channels];\r
7863       size_t offset = stream_.bufferSize * formatBytes( format );\r
7864       for ( int i=0; i<channels; i++ )\r
7865         bufs[i] = (void *) (buffer + (i * offset));\r
7866       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
7867     }\r
7868 \r
7869     if ( result < (int) stream_.bufferSize ) {\r
7870       // Either an error or overrun occured.\r
7871       if ( result == -EPIPE ) {\r
7872         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
7873         if ( state == SND_PCM_STATE_XRUN ) {\r
7874           apiInfo->xrun[1] = true;\r
7875           result = snd_pcm_prepare( handle[1] );\r
7876           if ( result < 0 ) {\r
7877             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
7878             errorText_ = errorStream_.str();\r
7879           }\r
7880         }\r
7881         else {\r
7882           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7883           errorText_ = errorStream_.str();\r
7884         }\r
7885       }\r
7886       else {\r
7887         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
7888         errorText_ = errorStream_.str();\r
7889       }\r
7890       error( RtAudioError::WARNING );\r
7891       goto tryOutput;\r
7892     }\r
7893 \r
7894     // Do byte swapping if necessary.\r
7895     if ( stream_.doByteSwap[1] )\r
7896       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
7897 \r
7898     // Do buffer conversion if necessary.\r
7899     if ( stream_.doConvertBuffer[1] )\r
7900       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
7901 \r
7902     // Check stream latency\r
7903     result = snd_pcm_delay( handle[1], &frames );\r
7904     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
7905   }\r
7906 \r
7907  tryOutput:\r
7908 \r
7909   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7910 \r
7911     // Setup parameters and do buffer conversion if necessary.\r
7912     if ( stream_.doConvertBuffer[0] ) {\r
7913       buffer = stream_.deviceBuffer;\r
7914       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
7915       channels = stream_.nDeviceChannels[0];\r
7916       format = stream_.deviceFormat[0];\r
7917     }\r
7918     else {\r
7919       buffer = stream_.userBuffer[0];\r
7920       channels = stream_.nUserChannels[0];\r
7921       format = stream_.userFormat;\r
7922     }\r
7923 \r
7924     // Do byte swapping if necessary.\r
7925     if ( stream_.doByteSwap[0] )\r
7926       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
7927 \r
7928     // Write samples to device in interleaved/non-interleaved format.\r
7929     if ( stream_.deviceInterleaved[0] )\r
7930       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
7931     else {\r
7932       void *bufs[channels];\r
7933       size_t offset = stream_.bufferSize * formatBytes( format );\r
7934       for ( int i=0; i<channels; i++ )\r
7935         bufs[i] = (void *) (buffer + (i * offset));\r
7936       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
7937     }\r
7938 \r
7939     if ( result < (int) stream_.bufferSize ) {\r
7940       // Either an error or underrun occured.\r
7941       if ( result == -EPIPE ) {\r
7942         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
7943         if ( state == SND_PCM_STATE_XRUN ) {\r
7944           apiInfo->xrun[0] = true;\r
7945           result = snd_pcm_prepare( handle[0] );\r
7946           if ( result < 0 ) {\r
7947             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
7948             errorText_ = errorStream_.str();\r
7949           }\r
7950         }\r
7951         else {\r
7952           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7953           errorText_ = errorStream_.str();\r
7954         }\r
7955       }\r
7956       else {\r
7957         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
7958         errorText_ = errorStream_.str();\r
7959       }\r
7960       error( RtAudioError::WARNING );\r
7961       goto unlock;\r
7962     }\r
7963 \r
7964     // Check stream latency\r
7965     result = snd_pcm_delay( handle[0], &frames );\r
7966     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
7967   }\r
7968 \r
7969  unlock:\r
7970   MUTEX_UNLOCK( &stream_.mutex );\r
7971 \r
7972   RtApi::tickStreamTime();\r
7973   if ( doStopStream == 1 ) this->stopStream();\r
7974 }\r
7975 \r
7976 static void *alsaCallbackHandler( void *ptr )\r
7977 {\r
7978   CallbackInfo *info = (CallbackInfo *) ptr;\r
7979   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
7980   bool *isRunning = &info->isRunning;\r
7981 \r
7982 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7983   if ( &info->doRealtime ) {\r
7984     pthread_t tID = pthread_self();      // ID of this thread\r
7985     sched_param prio = { info->priority }; // scheduling priority of thread\r
7986     pthread_setschedparam( tID, SCHED_RR, &prio );\r
7987   }\r
7988 #endif\r
7989 \r
7990   while ( *isRunning == true ) {\r
7991     pthread_testcancel();\r
7992     object->callbackEvent();\r
7993   }\r
7994 \r
7995   pthread_exit( NULL );\r
7996 }\r
7997 \r
7998 //******************** End of __LINUX_ALSA__ *********************//\r
7999 #endif\r
8000 \r
8001 #if defined(__LINUX_PULSE__)\r
8002 \r
8003 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8004 // and Tristan Matthews.\r
8005 \r
8006 #include <pulse/error.h>\r
8007 #include <pulse/simple.h>\r
8008 #include <cstdio>\r
8009 \r
8010 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8011                                                       44100, 48000, 96000, 0};\r
8012 \r
8013 struct rtaudio_pa_format_mapping_t {\r
8014   RtAudioFormat rtaudio_format;\r
8015   pa_sample_format_t pa_format;\r
8016 };\r
8017 \r
8018 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8019   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8020   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8021   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8022   {0, PA_SAMPLE_INVALID}};\r
8023 \r
8024 struct PulseAudioHandle {\r
8025   pa_simple *s_play;\r
8026   pa_simple *s_rec;\r
8027   pthread_t thread;\r
8028   pthread_cond_t runnable_cv;\r
8029   bool runnable;\r
8030   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8031 };\r
8032 \r
8033 RtApiPulse::~RtApiPulse()\r
8034 {\r
8035   if ( stream_.state != STREAM_CLOSED )\r
8036     closeStream();\r
8037 }\r
8038 \r
8039 unsigned int RtApiPulse::getDeviceCount( void )\r
8040 {\r
8041   return 1;\r
8042 }\r
8043 \r
8044 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8045 {\r
8046   RtAudio::DeviceInfo info;\r
8047   info.probed = true;\r
8048   info.name = "PulseAudio";\r
8049   info.outputChannels = 2;\r
8050   info.inputChannels = 2;\r
8051   info.duplexChannels = 2;\r
8052   info.isDefaultOutput = true;\r
8053   info.isDefaultInput = true;\r
8054 \r
8055   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8056     info.sampleRates.push_back( *sr );\r
8057 \r
8058   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8059 \r
8060   return info;\r
8061 }\r
8062 \r
8063 static void *pulseaudio_callback( void * user )\r
8064 {\r
8065   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8066   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8067   volatile bool *isRunning = &cbi->isRunning;\r
8068 \r
8069   while ( *isRunning ) {\r
8070     pthread_testcancel();\r
8071     context->callbackEvent();\r
8072   }\r
8073 \r
8074   pthread_exit( NULL );\r
8075 }\r
8076 \r
8077 void RtApiPulse::closeStream( void )\r
8078 {\r
8079   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8080 \r
8081   stream_.callbackInfo.isRunning = false;\r
8082   if ( pah ) {\r
8083     MUTEX_LOCK( &stream_.mutex );\r
8084     if ( stream_.state == STREAM_STOPPED ) {\r
8085       pah->runnable = true;\r
8086       pthread_cond_signal( &pah->runnable_cv );\r
8087     }\r
8088     MUTEX_UNLOCK( &stream_.mutex );\r
8089 \r
8090     pthread_join( pah->thread, 0 );\r
8091     if ( pah->s_play ) {\r
8092       pa_simple_flush( pah->s_play, NULL );\r
8093       pa_simple_free( pah->s_play );\r
8094     }\r
8095     if ( pah->s_rec )\r
8096       pa_simple_free( pah->s_rec );\r
8097 \r
8098     pthread_cond_destroy( &pah->runnable_cv );\r
8099     delete pah;\r
8100     stream_.apiHandle = 0;\r
8101   }\r
8102 \r
8103   if ( stream_.userBuffer[0] ) {\r
8104     free( stream_.userBuffer[0] );\r
8105     stream_.userBuffer[0] = 0;\r
8106   }\r
8107   if ( stream_.userBuffer[1] ) {\r
8108     free( stream_.userBuffer[1] );\r
8109     stream_.userBuffer[1] = 0;\r
8110   }\r
8111 \r
8112   stream_.state = STREAM_CLOSED;\r
8113   stream_.mode = UNINITIALIZED;\r
8114 }\r
8115 \r
8116 void RtApiPulse::callbackEvent( void )\r
8117 {\r
8118   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8119 \r
8120   if ( stream_.state == STREAM_STOPPED ) {\r
8121     MUTEX_LOCK( &stream_.mutex );\r
8122     while ( !pah->runnable )\r
8123       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8124 \r
8125     if ( stream_.state != STREAM_RUNNING ) {\r
8126       MUTEX_UNLOCK( &stream_.mutex );\r
8127       return;\r
8128     }\r
8129     MUTEX_UNLOCK( &stream_.mutex );\r
8130   }\r
8131 \r
8132   if ( stream_.state == STREAM_CLOSED ) {\r
8133     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8134       "this shouldn't happen!";\r
8135     error( RtAudioError::WARNING );\r
8136     return;\r
8137   }\r
8138 \r
8139   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8140   double streamTime = getStreamTime();\r
8141   RtAudioStreamStatus status = 0;\r
8142   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8143                                stream_.bufferSize, streamTime, status,\r
8144                                stream_.callbackInfo.userData );\r
8145 \r
8146   if ( doStopStream == 2 ) {\r
8147     abortStream();\r
8148     return;\r
8149   }\r
8150 \r
8151   MUTEX_LOCK( &stream_.mutex );\r
8152   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8153   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8154 \r
8155   if ( stream_.state != STREAM_RUNNING )\r
8156     goto unlock;\r
8157 \r
8158   int pa_error;\r
8159   size_t bytes;\r
8160   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8161     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8162         convertBuffer( stream_.deviceBuffer,\r
8163                        stream_.userBuffer[OUTPUT],\r
8164                        stream_.convertInfo[OUTPUT] );\r
8165         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8166                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8167     } else\r
8168         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8169                 formatBytes( stream_.userFormat );\r
8170 \r
8171     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8172       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8173         pa_strerror( pa_error ) << ".";\r
8174       errorText_ = errorStream_.str();\r
8175       error( RtAudioError::WARNING );\r
8176     }\r
8177   }\r
8178 \r
8179   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8180     if ( stream_.doConvertBuffer[INPUT] )\r
8181       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8182         formatBytes( stream_.deviceFormat[INPUT] );\r
8183     else\r
8184       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8185         formatBytes( stream_.userFormat );\r
8186             \r
8187     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8188       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8189         pa_strerror( pa_error ) << ".";\r
8190       errorText_ = errorStream_.str();\r
8191       error( RtAudioError::WARNING );\r
8192     }\r
8193     if ( stream_.doConvertBuffer[INPUT] ) {\r
8194       convertBuffer( stream_.userBuffer[INPUT],\r
8195                      stream_.deviceBuffer,\r
8196                      stream_.convertInfo[INPUT] );\r
8197     }\r
8198   }\r
8199 \r
8200  unlock:\r
8201   MUTEX_UNLOCK( &stream_.mutex );\r
8202   RtApi::tickStreamTime();\r
8203 \r
8204   if ( doStopStream == 1 )\r
8205     stopStream();\r
8206 }\r
8207 \r
8208 void RtApiPulse::startStream( void )\r
8209 {\r
8210   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8211 \r
8212   if ( stream_.state == STREAM_CLOSED ) {\r
8213     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8214     error( RtAudioError::INVALID_USE );\r
8215     return;\r
8216   }\r
8217   if ( stream_.state == STREAM_RUNNING ) {\r
8218     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8219     error( RtAudioError::WARNING );\r
8220     return;\r
8221   }\r
8222 \r
8223   MUTEX_LOCK( &stream_.mutex );\r
8224 \r
8225   stream_.state = STREAM_RUNNING;\r
8226 \r
8227   pah->runnable = true;\r
8228   pthread_cond_signal( &pah->runnable_cv );\r
8229   MUTEX_UNLOCK( &stream_.mutex );\r
8230 }\r
8231 \r
8232 void RtApiPulse::stopStream( void )\r
8233 {\r
8234   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8235 \r
8236   if ( stream_.state == STREAM_CLOSED ) {\r
8237     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8238     error( RtAudioError::INVALID_USE );\r
8239     return;\r
8240   }\r
8241   if ( stream_.state == STREAM_STOPPED ) {\r
8242     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8243     error( RtAudioError::WARNING );\r
8244     return;\r
8245   }\r
8246 \r
8247   stream_.state = STREAM_STOPPED;\r
8248   MUTEX_LOCK( &stream_.mutex );\r
8249 \r
8250   if ( pah && pah->s_play ) {\r
8251     int pa_error;\r
8252     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8253       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8254         pa_strerror( pa_error ) << ".";\r
8255       errorText_ = errorStream_.str();\r
8256       MUTEX_UNLOCK( &stream_.mutex );\r
8257       error( RtAudioError::SYSTEM_ERROR );\r
8258       return;\r
8259     }\r
8260   }\r
8261 \r
8262   stream_.state = STREAM_STOPPED;\r
8263   MUTEX_UNLOCK( &stream_.mutex );\r
8264 }\r
8265 \r
8266 void RtApiPulse::abortStream( void )\r
8267 {\r
8268   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8269 \r
8270   if ( stream_.state == STREAM_CLOSED ) {\r
8271     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8272     error( RtAudioError::INVALID_USE );\r
8273     return;\r
8274   }\r
8275   if ( stream_.state == STREAM_STOPPED ) {\r
8276     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8277     error( RtAudioError::WARNING );\r
8278     return;\r
8279   }\r
8280 \r
8281   stream_.state = STREAM_STOPPED;\r
8282   MUTEX_LOCK( &stream_.mutex );\r
8283 \r
8284   if ( pah && pah->s_play ) {\r
8285     int pa_error;\r
8286     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8287       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8288         pa_strerror( pa_error ) << ".";\r
8289       errorText_ = errorStream_.str();\r
8290       MUTEX_UNLOCK( &stream_.mutex );\r
8291       error( RtAudioError::SYSTEM_ERROR );\r
8292       return;\r
8293     }\r
8294   }\r
8295 \r
8296   stream_.state = STREAM_STOPPED;\r
8297   MUTEX_UNLOCK( &stream_.mutex );\r
8298 }\r
8299 \r
8300 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8301                                   unsigned int channels, unsigned int firstChannel,\r
8302                                   unsigned int sampleRate, RtAudioFormat format,\r
8303                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8304 {\r
8305   PulseAudioHandle *pah = 0;\r
8306   unsigned long bufferBytes = 0;\r
8307   pa_sample_spec ss;\r
8308 \r
8309   if ( device != 0 ) return false;\r
8310   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8311   if ( channels != 1 && channels != 2 ) {\r
8312     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
8313     return false;\r
8314   }\r
8315   ss.channels = channels;\r
8316 \r
8317   if ( firstChannel != 0 ) return false;\r
8318 \r
8319   bool sr_found = false;\r
8320   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8321     if ( sampleRate == *sr ) {\r
8322       sr_found = true;\r
8323       stream_.sampleRate = sampleRate;\r
8324       ss.rate = sampleRate;\r
8325       break;\r
8326     }\r
8327   }\r
8328   if ( !sr_found ) {\r
8329     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8330     return false;\r
8331   }\r
8332 \r
8333   bool sf_found = 0;\r
8334   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8335         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8336     if ( format == sf->rtaudio_format ) {\r
8337       sf_found = true;\r
8338       stream_.userFormat = sf->rtaudio_format;\r
8339       stream_.deviceFormat[mode] = stream_.userFormat;\r
8340       ss.format = sf->pa_format;\r
8341       break;\r
8342     }\r
8343   }\r
8344   if ( !sf_found ) { // Use internal data format conversion.\r
8345     stream_.userFormat = format;\r
8346     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8347     ss.format = PA_SAMPLE_FLOAT32LE;\r
8348   }\r
8349 \r
8350   // Set other stream parameters.\r
8351   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8352   else stream_.userInterleaved = true;\r
8353   stream_.deviceInterleaved[mode] = true;\r
8354   stream_.nBuffers = 1;\r
8355   stream_.doByteSwap[mode] = false;\r
8356   stream_.nUserChannels[mode] = channels;\r
8357   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8358   stream_.channelOffset[mode] = 0;\r
8359   std::string streamName = "RtAudio";\r
8360 \r
8361   // Set flags for buffer conversion.\r
8362   stream_.doConvertBuffer[mode] = false;\r
8363   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8364     stream_.doConvertBuffer[mode] = true;\r
8365   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8366     stream_.doConvertBuffer[mode] = true;\r
8367 \r
8368   // Allocate necessary internal buffers.\r
8369   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8370   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8371   if ( stream_.userBuffer[mode] == NULL ) {\r
8372     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8373     goto error;\r
8374   }\r
8375   stream_.bufferSize = *bufferSize;\r
8376 \r
8377   if ( stream_.doConvertBuffer[mode] ) {\r
8378 \r
8379     bool makeBuffer = true;\r
8380     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8381     if ( mode == INPUT ) {\r
8382       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8383         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8384         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8385       }\r
8386     }\r
8387 \r
8388     if ( makeBuffer ) {\r
8389       bufferBytes *= *bufferSize;\r
8390       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8391       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8392       if ( stream_.deviceBuffer == NULL ) {\r
8393         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8394         goto error;\r
8395       }\r
8396     }\r
8397   }\r
8398 \r
8399   stream_.device[mode] = device;\r
8400 \r
8401   // Setup the buffer conversion information structure.\r
8402   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8403 \r
8404   if ( !stream_.apiHandle ) {\r
8405     PulseAudioHandle *pah = new PulseAudioHandle;\r
8406     if ( !pah ) {\r
8407       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8408       goto error;\r
8409     }\r
8410 \r
8411     stream_.apiHandle = pah;\r
8412     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8413       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8414       goto error;\r
8415     }\r
8416   }\r
8417   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8418 \r
8419   int error;\r
8420   if ( !options->streamName.empty() ) streamName = options->streamName;\r
8421   switch ( mode ) {\r
8422   case INPUT:\r
8423     pa_buffer_attr buffer_attr;\r
8424     buffer_attr.fragsize = bufferBytes;\r
8425     buffer_attr.maxlength = -1;\r
8426 \r
8427     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8428     if ( !pah->s_rec ) {\r
8429       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8430       goto error;\r
8431     }\r
8432     break;\r
8433   case OUTPUT:\r
8434     pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
8435     if ( !pah->s_play ) {\r
8436       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8437       goto error;\r
8438     }\r
8439     break;\r
8440   default:\r
8441     goto error;\r
8442   }\r
8443 \r
8444   if ( stream_.mode == UNINITIALIZED )\r
8445     stream_.mode = mode;\r
8446   else if ( stream_.mode == mode )\r
8447     goto error;\r
8448   else\r
8449     stream_.mode = DUPLEX;\r
8450 \r
8451   if ( !stream_.callbackInfo.isRunning ) {\r
8452     stream_.callbackInfo.object = this;\r
8453     stream_.callbackInfo.isRunning = true;\r
8454     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8455       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8456       goto error;\r
8457     }\r
8458   }\r
8459 \r
8460   stream_.state = STREAM_STOPPED;\r
8461   return true;\r
8462  \r
8463  error:\r
8464   if ( pah && stream_.callbackInfo.isRunning ) {\r
8465     pthread_cond_destroy( &pah->runnable_cv );\r
8466     delete pah;\r
8467     stream_.apiHandle = 0;\r
8468   }\r
8469 \r
8470   for ( int i=0; i<2; i++ ) {\r
8471     if ( stream_.userBuffer[i] ) {\r
8472       free( stream_.userBuffer[i] );\r
8473       stream_.userBuffer[i] = 0;\r
8474     }\r
8475   }\r
8476 \r
8477   if ( stream_.deviceBuffer ) {\r
8478     free( stream_.deviceBuffer );\r
8479     stream_.deviceBuffer = 0;\r
8480   }\r
8481 \r
8482   return FAILURE;\r
8483 }\r
8484 \r
8485 //******************** End of __LINUX_PULSE__ *********************//\r
8486 #endif\r
8487 \r
8488 #if defined(__LINUX_OSS__)\r
8489 \r
8490 #include <unistd.h>\r
8491 #include <sys/ioctl.h>\r
8492 #include <unistd.h>\r
8493 #include <fcntl.h>\r
8494 #include <sys/soundcard.h>\r
8495 #include <errno.h>\r
8496 #include <math.h>\r
8497 \r
8498 static void *ossCallbackHandler(void * ptr);\r
8499 \r
8500 // A structure to hold various information related to the OSS API\r
8501 // implementation.\r
8502 struct OssHandle {\r
8503   int id[2];    // device ids\r
8504   bool xrun[2];\r
8505   bool triggered;\r
8506   pthread_cond_t runnable;\r
8507 \r
8508   OssHandle()\r
8509     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8510 };\r
8511 \r
8512 RtApiOss :: RtApiOss()\r
8513 {\r
8514   // Nothing to do here.\r
8515 }\r
8516 \r
8517 RtApiOss :: ~RtApiOss()\r
8518 {\r
8519   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8520 }\r
8521 \r
8522 unsigned int RtApiOss :: getDeviceCount( void )\r
8523 {\r
8524   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8525   if ( mixerfd == -1 ) {\r
8526     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8527     error( RtAudioError::WARNING );\r
8528     return 0;\r
8529   }\r
8530 \r
8531   oss_sysinfo sysinfo;\r
8532   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8533     close( mixerfd );\r
8534     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8535     error( RtAudioError::WARNING );\r
8536     return 0;\r
8537   }\r
8538 \r
8539   close( mixerfd );\r
8540   return sysinfo.numaudios;\r
8541 }\r
8542 \r
8543 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8544 {\r
8545   RtAudio::DeviceInfo info;\r
8546   info.probed = false;\r
8547 \r
8548   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8549   if ( mixerfd == -1 ) {\r
8550     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8551     error( RtAudioError::WARNING );\r
8552     return info;\r
8553   }\r
8554 \r
8555   oss_sysinfo sysinfo;\r
8556   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8557   if ( result == -1 ) {\r
8558     close( mixerfd );\r
8559     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8560     error( RtAudioError::WARNING );\r
8561     return info;\r
8562   }\r
8563 \r
8564   unsigned nDevices = sysinfo.numaudios;\r
8565   if ( nDevices == 0 ) {\r
8566     close( mixerfd );\r
8567     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8568     error( RtAudioError::INVALID_USE );\r
8569     return info;\r
8570   }\r
8571 \r
8572   if ( device >= nDevices ) {\r
8573     close( mixerfd );\r
8574     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8575     error( RtAudioError::INVALID_USE );\r
8576     return info;\r
8577   }\r
8578 \r
8579   oss_audioinfo ainfo;\r
8580   ainfo.dev = device;\r
8581   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8582   close( mixerfd );\r
8583   if ( result == -1 ) {\r
8584     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8585     errorText_ = errorStream_.str();\r
8586     error( RtAudioError::WARNING );\r
8587     return info;\r
8588   }\r
8589 \r
8590   // Probe channels\r
8591   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8592   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8593   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8594     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8595       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8596   }\r
8597 \r
8598   // Probe data formats ... do for input\r
8599   unsigned long mask = ainfo.iformats;\r
8600   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8601     info.nativeFormats |= RTAUDIO_SINT16;\r
8602   if ( mask & AFMT_S8 )\r
8603     info.nativeFormats |= RTAUDIO_SINT8;\r
8604   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8605     info.nativeFormats |= RTAUDIO_SINT32;\r
8606   if ( mask & AFMT_FLOAT )\r
8607     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8608   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8609     info.nativeFormats |= RTAUDIO_SINT24;\r
8610 \r
8611   // Check that we have at least one supported format\r
8612   if ( info.nativeFormats == 0 ) {\r
8613     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8614     errorText_ = errorStream_.str();\r
8615     error( RtAudioError::WARNING );\r
8616     return info;\r
8617   }\r
8618 \r
8619   // Probe the supported sample rates.\r
8620   info.sampleRates.clear();\r
8621   if ( ainfo.nrates ) {\r
8622     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8623       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8624         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8625           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8626           break;\r
8627         }\r
8628       }\r
8629     }\r
8630   }\r
8631   else {\r
8632     // Check min and max rate values;\r
8633     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8634       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )\r
8635         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8636     }\r
8637   }\r
8638 \r
8639   if ( info.sampleRates.size() == 0 ) {\r
8640     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8641     errorText_ = errorStream_.str();\r
8642     error( RtAudioError::WARNING );\r
8643   }\r
8644   else {\r
8645     info.probed = true;\r
8646     info.name = ainfo.name;\r
8647   }\r
8648 \r
8649   return info;\r
8650 }\r
8651 \r
8652 \r
8653 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8654                                   unsigned int firstChannel, unsigned int sampleRate,\r
8655                                   RtAudioFormat format, unsigned int *bufferSize,\r
8656                                   RtAudio::StreamOptions *options )\r
8657 {\r
8658   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8659   if ( mixerfd == -1 ) {\r
8660     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8661     return FAILURE;\r
8662   }\r
8663 \r
8664   oss_sysinfo sysinfo;\r
8665   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8666   if ( result == -1 ) {\r
8667     close( mixerfd );\r
8668     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8669     return FAILURE;\r
8670   }\r
8671 \r
8672   unsigned nDevices = sysinfo.numaudios;\r
8673   if ( nDevices == 0 ) {\r
8674     // This should not happen because a check is made before this function is called.\r
8675     close( mixerfd );\r
8676     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8677     return FAILURE;\r
8678   }\r
8679 \r
8680   if ( device >= nDevices ) {\r
8681     // This should not happen because a check is made before this function is called.\r
8682     close( mixerfd );\r
8683     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8684     return FAILURE;\r
8685   }\r
8686 \r
8687   oss_audioinfo ainfo;\r
8688   ainfo.dev = device;\r
8689   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8690   close( mixerfd );\r
8691   if ( result == -1 ) {\r
8692     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8693     errorText_ = errorStream_.str();\r
8694     return FAILURE;\r
8695   }\r
8696 \r
8697   // Check if device supports input or output\r
8698   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8699        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8700     if ( mode == OUTPUT )\r
8701       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8702     else\r
8703       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8704     errorText_ = errorStream_.str();\r
8705     return FAILURE;\r
8706   }\r
8707 \r
8708   int flags = 0;\r
8709   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8710   if ( mode == OUTPUT )\r
8711     flags |= O_WRONLY;\r
8712   else { // mode == INPUT\r
8713     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8714       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8715       close( handle->id[0] );\r
8716       handle->id[0] = 0;\r
8717       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8718         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8719         errorText_ = errorStream_.str();\r
8720         return FAILURE;\r
8721       }\r
8722       // Check that the number previously set channels is the same.\r
8723       if ( stream_.nUserChannels[0] != channels ) {\r
8724         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8725         errorText_ = errorStream_.str();\r
8726         return FAILURE;\r
8727       }\r
8728       flags |= O_RDWR;\r
8729     }\r
8730     else\r
8731       flags |= O_RDONLY;\r
8732   }\r
8733 \r
8734   // Set exclusive access if specified.\r
8735   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8736 \r
8737   // Try to open the device.\r
8738   int fd;\r
8739   fd = open( ainfo.devnode, flags, 0 );\r
8740   if ( fd == -1 ) {\r
8741     if ( errno == EBUSY )\r
8742       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8743     else\r
8744       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8745     errorText_ = errorStream_.str();\r
8746     return FAILURE;\r
8747   }\r
8748 \r
8749   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8750   /*\r
8751     if ( flags | O_RDWR ) {\r
8752     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
8753     if ( result == -1) {\r
8754     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
8755     errorText_ = errorStream_.str();\r
8756     return FAILURE;\r
8757     }\r
8758     }\r
8759   */\r
8760 \r
8761   // Check the device channel support.\r
8762   stream_.nUserChannels[mode] = channels;\r
8763   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
8764     close( fd );\r
8765     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
8766     errorText_ = errorStream_.str();\r
8767     return FAILURE;\r
8768   }\r
8769 \r
8770   // Set the number of channels.\r
8771   int deviceChannels = channels + firstChannel;\r
8772   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
8773   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
8774     close( fd );\r
8775     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
8776     errorText_ = errorStream_.str();\r
8777     return FAILURE;\r
8778   }\r
8779   stream_.nDeviceChannels[mode] = deviceChannels;\r
8780 \r
8781   // Get the data format mask\r
8782   int mask;\r
8783   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
8784   if ( result == -1 ) {\r
8785     close( fd );\r
8786     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
8787     errorText_ = errorStream_.str();\r
8788     return FAILURE;\r
8789   }\r
8790 \r
8791   // Determine how to set the device format.\r
8792   stream_.userFormat = format;\r
8793   int deviceFormat = -1;\r
8794   stream_.doByteSwap[mode] = false;\r
8795   if ( format == RTAUDIO_SINT8 ) {\r
8796     if ( mask & AFMT_S8 ) {\r
8797       deviceFormat = AFMT_S8;\r
8798       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8799     }\r
8800   }\r
8801   else if ( format == RTAUDIO_SINT16 ) {\r
8802     if ( mask & AFMT_S16_NE ) {\r
8803       deviceFormat = AFMT_S16_NE;\r
8804       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8805     }\r
8806     else if ( mask & AFMT_S16_OE ) {\r
8807       deviceFormat = AFMT_S16_OE;\r
8808       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8809       stream_.doByteSwap[mode] = true;\r
8810     }\r
8811   }\r
8812   else if ( format == RTAUDIO_SINT24 ) {\r
8813     if ( mask & AFMT_S24_NE ) {\r
8814       deviceFormat = AFMT_S24_NE;\r
8815       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8816     }\r
8817     else if ( mask & AFMT_S24_OE ) {\r
8818       deviceFormat = AFMT_S24_OE;\r
8819       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8820       stream_.doByteSwap[mode] = true;\r
8821     }\r
8822   }\r
8823   else if ( format == RTAUDIO_SINT32 ) {\r
8824     if ( mask & AFMT_S32_NE ) {\r
8825       deviceFormat = AFMT_S32_NE;\r
8826       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8827     }\r
8828     else if ( mask & AFMT_S32_OE ) {\r
8829       deviceFormat = AFMT_S32_OE;\r
8830       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8831       stream_.doByteSwap[mode] = true;\r
8832     }\r
8833   }\r
8834 \r
8835   if ( deviceFormat == -1 ) {\r
8836     // The user requested format is not natively supported by the device.\r
8837     if ( mask & AFMT_S16_NE ) {\r
8838       deviceFormat = AFMT_S16_NE;\r
8839       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8840     }\r
8841     else if ( mask & AFMT_S32_NE ) {\r
8842       deviceFormat = AFMT_S32_NE;\r
8843       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8844     }\r
8845     else if ( mask & AFMT_S24_NE ) {\r
8846       deviceFormat = AFMT_S24_NE;\r
8847       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8848     }\r
8849     else if ( mask & AFMT_S16_OE ) {\r
8850       deviceFormat = AFMT_S16_OE;\r
8851       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8852       stream_.doByteSwap[mode] = true;\r
8853     }\r
8854     else if ( mask & AFMT_S32_OE ) {\r
8855       deviceFormat = AFMT_S32_OE;\r
8856       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8857       stream_.doByteSwap[mode] = true;\r
8858     }\r
8859     else if ( mask & AFMT_S24_OE ) {\r
8860       deviceFormat = AFMT_S24_OE;\r
8861       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8862       stream_.doByteSwap[mode] = true;\r
8863     }\r
8864     else if ( mask & AFMT_S8) {\r
8865       deviceFormat = AFMT_S8;\r
8866       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8867     }\r
8868   }\r
8869 \r
8870   if ( stream_.deviceFormat[mode] == 0 ) {\r
8871     // This really shouldn't happen ...\r
8872     close( fd );\r
8873     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8874     errorText_ = errorStream_.str();\r
8875     return FAILURE;\r
8876   }\r
8877 \r
8878   // Set the data format.\r
8879   int temp = deviceFormat;\r
8880   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
8881   if ( result == -1 || deviceFormat != temp ) {\r
8882     close( fd );\r
8883     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
8884     errorText_ = errorStream_.str();\r
8885     return FAILURE;\r
8886   }\r
8887 \r
8888   // Attempt to set the buffer size.  According to OSS, the minimum\r
8889   // number of buffers is two.  The supposed minimum buffer size is 16\r
8890   // bytes, so that will be our lower bound.  The argument to this\r
8891   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
8892   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
8893   // We'll check the actual value used near the end of the setup\r
8894   // procedure.\r
8895   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
8896   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
8897   int buffers = 0;\r
8898   if ( options ) buffers = options->numberOfBuffers;\r
8899   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
8900   if ( buffers < 2 ) buffers = 3;\r
8901   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
8902   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
8903   if ( result == -1 ) {\r
8904     close( fd );\r
8905     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
8906     errorText_ = errorStream_.str();\r
8907     return FAILURE;\r
8908   }\r
8909   stream_.nBuffers = buffers;\r
8910 \r
8911   // Save buffer size (in sample frames).\r
8912   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
8913   stream_.bufferSize = *bufferSize;\r
8914 \r
8915   // Set the sample rate.\r
8916   int srate = sampleRate;\r
8917   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
8918   if ( result == -1 ) {\r
8919     close( fd );\r
8920     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
8921     errorText_ = errorStream_.str();\r
8922     return FAILURE;\r
8923   }\r
8924 \r
8925   // Verify the sample rate setup worked.\r
8926   if ( abs( srate - sampleRate ) > 100 ) {\r
8927     close( fd );\r
8928     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
8929     errorText_ = errorStream_.str();\r
8930     return FAILURE;\r
8931   }\r
8932   stream_.sampleRate = sampleRate;\r
8933 \r
8934   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8935     // We're doing duplex setup here.\r
8936     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
8937     stream_.nDeviceChannels[0] = deviceChannels;\r
8938   }\r
8939 \r
8940   // Set interleaving parameters.\r
8941   stream_.userInterleaved = true;\r
8942   stream_.deviceInterleaved[mode] =  true;\r
8943   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
8944     stream_.userInterleaved = false;\r
8945 \r
8946   // Set flags for buffer conversion\r
8947   stream_.doConvertBuffer[mode] = false;\r
8948   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8949     stream_.doConvertBuffer[mode] = true;\r
8950   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8951     stream_.doConvertBuffer[mode] = true;\r
8952   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
8953        stream_.nUserChannels[mode] > 1 )\r
8954     stream_.doConvertBuffer[mode] = true;\r
8955 \r
8956   // Allocate the stream handles if necessary and then save.\r
8957   if ( stream_.apiHandle == 0 ) {\r
8958     try {\r
8959       handle = new OssHandle;\r
8960     }\r
8961     catch ( std::bad_alloc& ) {\r
8962       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
8963       goto error;\r
8964     }\r
8965 \r
8966     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
8967       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
8968       goto error;\r
8969     }\r
8970 \r
8971     stream_.apiHandle = (void *) handle;\r
8972   }\r
8973   else {\r
8974     handle = (OssHandle *) stream_.apiHandle;\r
8975   }\r
8976   handle->id[mode] = fd;\r
8977 \r
8978   // Allocate necessary internal buffers.\r
8979   unsigned long bufferBytes;\r
8980   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8981   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8982   if ( stream_.userBuffer[mode] == NULL ) {\r
8983     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
8984     goto error;\r
8985   }\r
8986 \r
8987   if ( stream_.doConvertBuffer[mode] ) {\r
8988 \r
8989     bool makeBuffer = true;\r
8990     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8991     if ( mode == INPUT ) {\r
8992       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8993         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8994         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8995       }\r
8996     }\r
8997 \r
8998     if ( makeBuffer ) {\r
8999       bufferBytes *= *bufferSize;\r
9000       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9001       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9002       if ( stream_.deviceBuffer == NULL ) {\r
9003         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9004         goto error;\r
9005       }\r
9006     }\r
9007   }\r
9008 \r
9009   stream_.device[mode] = device;\r
9010   stream_.state = STREAM_STOPPED;\r
9011 \r
9012   // Setup the buffer conversion information structure.\r
9013   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9014 \r
9015   // Setup thread if necessary.\r
9016   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9017     // We had already set up an output stream.\r
9018     stream_.mode = DUPLEX;\r
9019     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9020   }\r
9021   else {\r
9022     stream_.mode = mode;\r
9023 \r
9024     // Setup callback thread.\r
9025     stream_.callbackInfo.object = (void *) this;\r
9026 \r
9027     // Set the thread attributes for joinable and realtime scheduling\r
9028     // priority.  The higher priority will only take affect if the\r
9029     // program is run as root or suid.\r
9030     pthread_attr_t attr;\r
9031     pthread_attr_init( &attr );\r
9032     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9033 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9034     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9035       struct sched_param param;\r
9036       int priority = options->priority;\r
9037       int min = sched_get_priority_min( SCHED_RR );\r
9038       int max = sched_get_priority_max( SCHED_RR );\r
9039       if ( priority < min ) priority = min;\r
9040       else if ( priority > max ) priority = max;\r
9041       param.sched_priority = priority;\r
9042       pthread_attr_setschedparam( &attr, &param );\r
9043       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9044     }\r
9045     else\r
9046       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9047 #else\r
9048     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9049 #endif\r
9050 \r
9051     stream_.callbackInfo.isRunning = true;\r
9052     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9053     pthread_attr_destroy( &attr );\r
9054     if ( result ) {\r
9055       stream_.callbackInfo.isRunning = false;\r
9056       errorText_ = "RtApiOss::error creating callback thread!";\r
9057       goto error;\r
9058     }\r
9059   }\r
9060 \r
9061   return SUCCESS;\r
9062 \r
9063  error:\r
9064   if ( handle ) {\r
9065     pthread_cond_destroy( &handle->runnable );\r
9066     if ( handle->id[0] ) close( handle->id[0] );\r
9067     if ( handle->id[1] ) close( handle->id[1] );\r
9068     delete handle;\r
9069     stream_.apiHandle = 0;\r
9070   }\r
9071 \r
9072   for ( int i=0; i<2; i++ ) {\r
9073     if ( stream_.userBuffer[i] ) {\r
9074       free( stream_.userBuffer[i] );\r
9075       stream_.userBuffer[i] = 0;\r
9076     }\r
9077   }\r
9078 \r
9079   if ( stream_.deviceBuffer ) {\r
9080     free( stream_.deviceBuffer );\r
9081     stream_.deviceBuffer = 0;\r
9082   }\r
9083 \r
9084   return FAILURE;\r
9085 }\r
9086 \r
9087 void RtApiOss :: closeStream()\r
9088 {\r
9089   if ( stream_.state == STREAM_CLOSED ) {\r
9090     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9091     error( RtAudioError::WARNING );\r
9092     return;\r
9093   }\r
9094 \r
9095   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9096   stream_.callbackInfo.isRunning = false;\r
9097   MUTEX_LOCK( &stream_.mutex );\r
9098   if ( stream_.state == STREAM_STOPPED )\r
9099     pthread_cond_signal( &handle->runnable );\r
9100   MUTEX_UNLOCK( &stream_.mutex );\r
9101   pthread_join( stream_.callbackInfo.thread, NULL );\r
9102 \r
9103   if ( stream_.state == STREAM_RUNNING ) {\r
9104     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9105       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9106     else\r
9107       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9108     stream_.state = STREAM_STOPPED;\r
9109   }\r
9110 \r
9111   if ( handle ) {\r
9112     pthread_cond_destroy( &handle->runnable );\r
9113     if ( handle->id[0] ) close( handle->id[0] );\r
9114     if ( handle->id[1] ) close( handle->id[1] );\r
9115     delete handle;\r
9116     stream_.apiHandle = 0;\r
9117   }\r
9118 \r
9119   for ( int i=0; i<2; i++ ) {\r
9120     if ( stream_.userBuffer[i] ) {\r
9121       free( stream_.userBuffer[i] );\r
9122       stream_.userBuffer[i] = 0;\r
9123     }\r
9124   }\r
9125 \r
9126   if ( stream_.deviceBuffer ) {\r
9127     free( stream_.deviceBuffer );\r
9128     stream_.deviceBuffer = 0;\r
9129   }\r
9130 \r
9131   stream_.mode = UNINITIALIZED;\r
9132   stream_.state = STREAM_CLOSED;\r
9133 }\r
9134 \r
9135 void RtApiOss :: startStream()\r
9136 {\r
9137   verifyStream();\r
9138   if ( stream_.state == STREAM_RUNNING ) {\r
9139     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9140     error( RtAudioError::WARNING );\r
9141     return;\r
9142   }\r
9143 \r
9144   MUTEX_LOCK( &stream_.mutex );\r
9145 \r
9146   stream_.state = STREAM_RUNNING;\r
9147 \r
9148   // No need to do anything else here ... OSS automatically starts\r
9149   // when fed samples.\r
9150 \r
9151   MUTEX_UNLOCK( &stream_.mutex );\r
9152 \r
9153   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9154   pthread_cond_signal( &handle->runnable );\r
9155 }\r
9156 \r
9157 void RtApiOss :: stopStream()\r
9158 {\r
9159   verifyStream();\r
9160   if ( stream_.state == STREAM_STOPPED ) {\r
9161     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9162     error( RtAudioError::WARNING );\r
9163     return;\r
9164   }\r
9165 \r
9166   MUTEX_LOCK( &stream_.mutex );\r
9167 \r
9168   // The state might change while waiting on a mutex.\r
9169   if ( stream_.state == STREAM_STOPPED ) {\r
9170     MUTEX_UNLOCK( &stream_.mutex );\r
9171     return;\r
9172   }\r
9173 \r
9174   int result = 0;\r
9175   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9176   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9177 \r
9178     // Flush the output with zeros a few times.\r
9179     char *buffer;\r
9180     int samples;\r
9181     RtAudioFormat format;\r
9182 \r
9183     if ( stream_.doConvertBuffer[0] ) {\r
9184       buffer = stream_.deviceBuffer;\r
9185       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9186       format = stream_.deviceFormat[0];\r
9187     }\r
9188     else {\r
9189       buffer = stream_.userBuffer[0];\r
9190       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9191       format = stream_.userFormat;\r
9192     }\r
9193 \r
9194     memset( buffer, 0, samples * formatBytes(format) );\r
9195     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9196       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9197       if ( result == -1 ) {\r
9198         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9199         error( RtAudioError::WARNING );\r
9200       }\r
9201     }\r
9202 \r
9203     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9204     if ( result == -1 ) {\r
9205       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9206       errorText_ = errorStream_.str();\r
9207       goto unlock;\r
9208     }\r
9209     handle->triggered = false;\r
9210   }\r
9211 \r
9212   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9213     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9214     if ( result == -1 ) {\r
9215       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9216       errorText_ = errorStream_.str();\r
9217       goto unlock;\r
9218     }\r
9219   }\r
9220 \r
9221  unlock:\r
9222   stream_.state = STREAM_STOPPED;\r
9223   MUTEX_UNLOCK( &stream_.mutex );\r
9224 \r
9225   if ( result != -1 ) return;\r
9226   error( RtAudioError::SYSTEM_ERROR );\r
9227 }\r
9228 \r
9229 void RtApiOss :: abortStream()\r
9230 {\r
9231   verifyStream();\r
9232   if ( stream_.state == STREAM_STOPPED ) {\r
9233     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9234     error( RtAudioError::WARNING );\r
9235     return;\r
9236   }\r
9237 \r
9238   MUTEX_LOCK( &stream_.mutex );\r
9239 \r
9240   // The state might change while waiting on a mutex.\r
9241   if ( stream_.state == STREAM_STOPPED ) {\r
9242     MUTEX_UNLOCK( &stream_.mutex );\r
9243     return;\r
9244   }\r
9245 \r
9246   int result = 0;\r
9247   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9248   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9249     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9250     if ( result == -1 ) {\r
9251       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9252       errorText_ = errorStream_.str();\r
9253       goto unlock;\r
9254     }\r
9255     handle->triggered = false;\r
9256   }\r
9257 \r
9258   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9259     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9260     if ( result == -1 ) {\r
9261       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9262       errorText_ = errorStream_.str();\r
9263       goto unlock;\r
9264     }\r
9265   }\r
9266 \r
9267  unlock:\r
9268   stream_.state = STREAM_STOPPED;\r
9269   MUTEX_UNLOCK( &stream_.mutex );\r
9270 \r
9271   if ( result != -1 ) return;\r
9272   error( RtAudioError::SYSTEM_ERROR );\r
9273 }\r
9274 \r
9275 void RtApiOss :: callbackEvent()\r
9276 {\r
9277   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9278   if ( stream_.state == STREAM_STOPPED ) {\r
9279     MUTEX_LOCK( &stream_.mutex );\r
9280     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9281     if ( stream_.state != STREAM_RUNNING ) {\r
9282       MUTEX_UNLOCK( &stream_.mutex );\r
9283       return;\r
9284     }\r
9285     MUTEX_UNLOCK( &stream_.mutex );\r
9286   }\r
9287 \r
9288   if ( stream_.state == STREAM_CLOSED ) {\r
9289     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9290     error( RtAudioError::WARNING );\r
9291     return;\r
9292   }\r
9293 \r
9294   // Invoke user callback to get fresh output data.\r
9295   int doStopStream = 0;\r
9296   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9297   double streamTime = getStreamTime();\r
9298   RtAudioStreamStatus status = 0;\r
9299   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9300     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9301     handle->xrun[0] = false;\r
9302   }\r
9303   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9304     status |= RTAUDIO_INPUT_OVERFLOW;\r
9305     handle->xrun[1] = false;\r
9306   }\r
9307   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9308                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9309   if ( doStopStream == 2 ) {\r
9310     this->abortStream();\r
9311     return;\r
9312   }\r
9313 \r
9314   MUTEX_LOCK( &stream_.mutex );\r
9315 \r
9316   // The state might change while waiting on a mutex.\r
9317   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9318 \r
9319   int result;\r
9320   char *buffer;\r
9321   int samples;\r
9322   RtAudioFormat format;\r
9323 \r
9324   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9325 \r
9326     // Setup parameters and do buffer conversion if necessary.\r
9327     if ( stream_.doConvertBuffer[0] ) {\r
9328       buffer = stream_.deviceBuffer;\r
9329       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9330       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9331       format = stream_.deviceFormat[0];\r
9332     }\r
9333     else {\r
9334       buffer = stream_.userBuffer[0];\r
9335       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9336       format = stream_.userFormat;\r
9337     }\r
9338 \r
9339     // Do byte swapping if necessary.\r
9340     if ( stream_.doByteSwap[0] )\r
9341       byteSwapBuffer( buffer, samples, format );\r
9342 \r
9343     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9344       int trig = 0;\r
9345       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9346       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9347       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9348       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9349       handle->triggered = true;\r
9350     }\r
9351     else\r
9352       // Write samples to device.\r
9353       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9354 \r
9355     if ( result == -1 ) {\r
9356       // We'll assume this is an underrun, though there isn't a\r
9357       // specific means for determining that.\r
9358       handle->xrun[0] = true;\r
9359       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9360       error( RtAudioError::WARNING );\r
9361       // Continue on to input section.\r
9362     }\r
9363   }\r
9364 \r
9365   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9366 \r
9367     // Setup parameters.\r
9368     if ( stream_.doConvertBuffer[1] ) {\r
9369       buffer = stream_.deviceBuffer;\r
9370       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9371       format = stream_.deviceFormat[1];\r
9372     }\r
9373     else {\r
9374       buffer = stream_.userBuffer[1];\r
9375       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9376       format = stream_.userFormat;\r
9377     }\r
9378 \r
9379     // Read samples from device.\r
9380     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9381 \r
9382     if ( result == -1 ) {\r
9383       // We'll assume this is an overrun, though there isn't a\r
9384       // specific means for determining that.\r
9385       handle->xrun[1] = true;\r
9386       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9387       error( RtAudioError::WARNING );\r
9388       goto unlock;\r
9389     }\r
9390 \r
9391     // Do byte swapping if necessary.\r
9392     if ( stream_.doByteSwap[1] )\r
9393       byteSwapBuffer( buffer, samples, format );\r
9394 \r
9395     // Do buffer conversion if necessary.\r
9396     if ( stream_.doConvertBuffer[1] )\r
9397       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9398   }\r
9399 \r
9400  unlock:\r
9401   MUTEX_UNLOCK( &stream_.mutex );\r
9402 \r
9403   RtApi::tickStreamTime();\r
9404   if ( doStopStream == 1 ) this->stopStream();\r
9405 }\r
9406 \r
9407 static void *ossCallbackHandler( void *ptr )\r
9408 {\r
9409   CallbackInfo *info = (CallbackInfo *) ptr;\r
9410   RtApiOss *object = (RtApiOss *) info->object;\r
9411   bool *isRunning = &info->isRunning;\r
9412 \r
9413   while ( *isRunning == true ) {\r
9414     pthread_testcancel();\r
9415     object->callbackEvent();\r
9416   }\r
9417 \r
9418   pthread_exit( NULL );\r
9419 }\r
9420 \r
9421 //******************** End of __LINUX_OSS__ *********************//\r
9422 #endif\r
9423 \r
9424 \r
9425 // *************************************************** //\r
9426 //\r
9427 // Protected common (OS-independent) RtAudio methods.\r
9428 //\r
9429 // *************************************************** //\r
9430 \r
9431 // This method can be modified to control the behavior of error\r
9432 // message printing.\r
9433 void RtApi :: error( RtAudioError::Type type )\r
9434 {\r
9435   errorStream_.str(""); // clear the ostringstream\r
9436 \r
9437   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9438   if ( errorCallback ) {\r
9439     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9440 \r
9441     if ( firstErrorOccurred_ )\r
9442       return;\r
9443 \r
9444     firstErrorOccurred_ = true;\r
9445     const std::string errorMessage = errorText_;\r
9446 \r
9447     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9448       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9449       abortStream();\r
9450     }\r
9451 \r
9452     errorCallback( type, errorMessage );\r
9453     firstErrorOccurred_ = false;\r
9454     return;\r
9455   }\r
9456 \r
9457   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9458     std::cerr << '\n' << errorText_ << "\n\n";\r
9459   else if ( type != RtAudioError::WARNING )\r
9460     throw( RtAudioError( errorText_, type ) );\r
9461 }\r
9462 \r
9463 void RtApi :: verifyStream()\r
9464 {\r
9465   if ( stream_.state == STREAM_CLOSED ) {\r
9466     errorText_ = "RtApi:: a stream is not open!";\r
9467     error( RtAudioError::INVALID_USE );\r
9468   }\r
9469 }\r
9470 \r
9471 void RtApi :: clearStreamInfo()\r
9472 {\r
9473   stream_.mode = UNINITIALIZED;\r
9474   stream_.state = STREAM_CLOSED;\r
9475   stream_.sampleRate = 0;\r
9476   stream_.bufferSize = 0;\r
9477   stream_.nBuffers = 0;\r
9478   stream_.userFormat = 0;\r
9479   stream_.userInterleaved = true;\r
9480   stream_.streamTime = 0.0;\r
9481   stream_.apiHandle = 0;\r
9482   stream_.deviceBuffer = 0;\r
9483   stream_.callbackInfo.callback = 0;\r
9484   stream_.callbackInfo.userData = 0;\r
9485   stream_.callbackInfo.isRunning = false;\r
9486   stream_.callbackInfo.errorCallback = 0;\r
9487   for ( int i=0; i<2; i++ ) {\r
9488     stream_.device[i] = 11111;\r
9489     stream_.doConvertBuffer[i] = false;\r
9490     stream_.deviceInterleaved[i] = true;\r
9491     stream_.doByteSwap[i] = false;\r
9492     stream_.nUserChannels[i] = 0;\r
9493     stream_.nDeviceChannels[i] = 0;\r
9494     stream_.channelOffset[i] = 0;\r
9495     stream_.deviceFormat[i] = 0;\r
9496     stream_.latency[i] = 0;\r
9497     stream_.userBuffer[i] = 0;\r
9498     stream_.convertInfo[i].channels = 0;\r
9499     stream_.convertInfo[i].inJump = 0;\r
9500     stream_.convertInfo[i].outJump = 0;\r
9501     stream_.convertInfo[i].inFormat = 0;\r
9502     stream_.convertInfo[i].outFormat = 0;\r
9503     stream_.convertInfo[i].inOffset.clear();\r
9504     stream_.convertInfo[i].outOffset.clear();\r
9505   }\r
9506 }\r
9507 \r
9508 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9509 {\r
9510   if ( format == RTAUDIO_SINT16 )\r
9511     return 2;\r
9512   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9513     return 4;\r
9514   else if ( format == RTAUDIO_FLOAT64 )\r
9515     return 8;\r
9516   else if ( format == RTAUDIO_SINT24 )\r
9517     return 3;\r
9518   else if ( format == RTAUDIO_SINT8 )\r
9519     return 1;\r
9520 \r
9521   errorText_ = "RtApi::formatBytes: undefined format.";\r
9522   error( RtAudioError::WARNING );\r
9523 \r
9524   return 0;\r
9525 }\r
9526 \r
9527 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9528 {\r
9529   if ( mode == INPUT ) { // convert device to user buffer\r
9530     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9531     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9532     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9533     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9534   }\r
9535   else { // convert user to device buffer\r
9536     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9537     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9538     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9539     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9540   }\r
9541 \r
9542   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9543     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9544   else\r
9545     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9546 \r
9547   // Set up the interleave/deinterleave offsets.\r
9548   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9549     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9550          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9551       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9552         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9553         stream_.convertInfo[mode].outOffset.push_back( k );\r
9554         stream_.convertInfo[mode].inJump = 1;\r
9555       }\r
9556     }\r
9557     else {\r
9558       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9559         stream_.convertInfo[mode].inOffset.push_back( k );\r
9560         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9561         stream_.convertInfo[mode].outJump = 1;\r
9562       }\r
9563     }\r
9564   }\r
9565   else { // no (de)interleaving\r
9566     if ( stream_.userInterleaved ) {\r
9567       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9568         stream_.convertInfo[mode].inOffset.push_back( k );\r
9569         stream_.convertInfo[mode].outOffset.push_back( k );\r
9570       }\r
9571     }\r
9572     else {\r
9573       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9574         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9575         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9576         stream_.convertInfo[mode].inJump = 1;\r
9577         stream_.convertInfo[mode].outJump = 1;\r
9578       }\r
9579     }\r
9580   }\r
9581 \r
9582   // Add channel offset.\r
9583   if ( firstChannel > 0 ) {\r
9584     if ( stream_.deviceInterleaved[mode] ) {\r
9585       if ( mode == OUTPUT ) {\r
9586         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9587           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9588       }\r
9589       else {\r
9590         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9591           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9592       }\r
9593     }\r
9594     else {\r
9595       if ( mode == OUTPUT ) {\r
9596         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9597           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9598       }\r
9599       else {\r
9600         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9601           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9602       }\r
9603     }\r
9604   }\r
9605 }\r
9606 \r
9607 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9608 {\r
9609   // This function does format conversion, input/output channel compensation, and\r
9610   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9611   // the lower three bytes of a 32-bit integer.\r
9612 \r
9613   // Clear our device buffer when in/out duplex device channels are different\r
9614   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9615        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9616     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9617 \r
9618   int j;\r
9619   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9620     Float64 scale;\r
9621     Float64 *out = (Float64 *)outBuffer;\r
9622 \r
9623     if (info.inFormat == RTAUDIO_SINT8) {\r
9624       signed char *in = (signed char *)inBuffer;\r
9625       scale = 1.0 / 127.5;\r
9626       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9627         for (j=0; j<info.channels; j++) {\r
9628           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9629           out[info.outOffset[j]] += 0.5;\r
9630           out[info.outOffset[j]] *= scale;\r
9631         }\r
9632         in += info.inJump;\r
9633         out += info.outJump;\r
9634       }\r
9635     }\r
9636     else if (info.inFormat == RTAUDIO_SINT16) {\r
9637       Int16 *in = (Int16 *)inBuffer;\r
9638       scale = 1.0 / 32767.5;\r
9639       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9640         for (j=0; j<info.channels; j++) {\r
9641           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9642           out[info.outOffset[j]] += 0.5;\r
9643           out[info.outOffset[j]] *= scale;\r
9644         }\r
9645         in += info.inJump;\r
9646         out += info.outJump;\r
9647       }\r
9648     }\r
9649     else if (info.inFormat == RTAUDIO_SINT24) {\r
9650       Int24 *in = (Int24 *)inBuffer;\r
9651       scale = 1.0 / 8388607.5;\r
9652       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9653         for (j=0; j<info.channels; j++) {\r
9654           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9655           out[info.outOffset[j]] += 0.5;\r
9656           out[info.outOffset[j]] *= scale;\r
9657         }\r
9658         in += info.inJump;\r
9659         out += info.outJump;\r
9660       }\r
9661     }\r
9662     else if (info.inFormat == RTAUDIO_SINT32) {\r
9663       Int32 *in = (Int32 *)inBuffer;\r
9664       scale = 1.0 / 2147483647.5;\r
9665       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9666         for (j=0; j<info.channels; j++) {\r
9667           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9668           out[info.outOffset[j]] += 0.5;\r
9669           out[info.outOffset[j]] *= scale;\r
9670         }\r
9671         in += info.inJump;\r
9672         out += info.outJump;\r
9673       }\r
9674     }\r
9675     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9676       Float32 *in = (Float32 *)inBuffer;\r
9677       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9678         for (j=0; j<info.channels; j++) {\r
9679           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9680         }\r
9681         in += info.inJump;\r
9682         out += info.outJump;\r
9683       }\r
9684     }\r
9685     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9686       // Channel compensation and/or (de)interleaving only.\r
9687       Float64 *in = (Float64 *)inBuffer;\r
9688       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9689         for (j=0; j<info.channels; j++) {\r
9690           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9691         }\r
9692         in += info.inJump;\r
9693         out += info.outJump;\r
9694       }\r
9695     }\r
9696   }\r
9697   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9698     Float32 scale;\r
9699     Float32 *out = (Float32 *)outBuffer;\r
9700 \r
9701     if (info.inFormat == RTAUDIO_SINT8) {\r
9702       signed char *in = (signed char *)inBuffer;\r
9703       scale = (Float32) ( 1.0 / 127.5 );\r
9704       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9705         for (j=0; j<info.channels; j++) {\r
9706           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9707           out[info.outOffset[j]] += 0.5;\r
9708           out[info.outOffset[j]] *= scale;\r
9709         }\r
9710         in += info.inJump;\r
9711         out += info.outJump;\r
9712       }\r
9713     }\r
9714     else if (info.inFormat == RTAUDIO_SINT16) {\r
9715       Int16 *in = (Int16 *)inBuffer;\r
9716       scale = (Float32) ( 1.0 / 32767.5 );\r
9717       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9718         for (j=0; j<info.channels; j++) {\r
9719           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9720           out[info.outOffset[j]] += 0.5;\r
9721           out[info.outOffset[j]] *= scale;\r
9722         }\r
9723         in += info.inJump;\r
9724         out += info.outJump;\r
9725       }\r
9726     }\r
9727     else if (info.inFormat == RTAUDIO_SINT24) {\r
9728       Int24 *in = (Int24 *)inBuffer;\r
9729       scale = (Float32) ( 1.0 / 8388607.5 );\r
9730       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9731         for (j=0; j<info.channels; j++) {\r
9732           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9733           out[info.outOffset[j]] += 0.5;\r
9734           out[info.outOffset[j]] *= scale;\r
9735         }\r
9736         in += info.inJump;\r
9737         out += info.outJump;\r
9738       }\r
9739     }\r
9740     else if (info.inFormat == RTAUDIO_SINT32) {\r
9741       Int32 *in = (Int32 *)inBuffer;\r
9742       scale = (Float32) ( 1.0 / 2147483647.5 );\r
9743       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9744         for (j=0; j<info.channels; j++) {\r
9745           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9746           out[info.outOffset[j]] += 0.5;\r
9747           out[info.outOffset[j]] *= scale;\r
9748         }\r
9749         in += info.inJump;\r
9750         out += info.outJump;\r
9751       }\r
9752     }\r
9753     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9754       // Channel compensation and/or (de)interleaving only.\r
9755       Float32 *in = (Float32 *)inBuffer;\r
9756       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9757         for (j=0; j<info.channels; j++) {\r
9758           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9759         }\r
9760         in += info.inJump;\r
9761         out += info.outJump;\r
9762       }\r
9763     }\r
9764     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9765       Float64 *in = (Float64 *)inBuffer;\r
9766       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9767         for (j=0; j<info.channels; j++) {\r
9768           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9769         }\r
9770         in += info.inJump;\r
9771         out += info.outJump;\r
9772       }\r
9773     }\r
9774   }\r
9775   else if (info.outFormat == RTAUDIO_SINT32) {\r
9776     Int32 *out = (Int32 *)outBuffer;\r
9777     if (info.inFormat == RTAUDIO_SINT8) {\r
9778       signed char *in = (signed char *)inBuffer;\r
9779       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9780         for (j=0; j<info.channels; j++) {\r
9781           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9782           out[info.outOffset[j]] <<= 24;\r
9783         }\r
9784         in += info.inJump;\r
9785         out += info.outJump;\r
9786       }\r
9787     }\r
9788     else if (info.inFormat == RTAUDIO_SINT16) {\r
9789       Int16 *in = (Int16 *)inBuffer;\r
9790       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9791         for (j=0; j<info.channels; j++) {\r
9792           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9793           out[info.outOffset[j]] <<= 16;\r
9794         }\r
9795         in += info.inJump;\r
9796         out += info.outJump;\r
9797       }\r
9798     }\r
9799     else if (info.inFormat == RTAUDIO_SINT24) {\r
9800       Int24 *in = (Int24 *)inBuffer;\r
9801       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9802         for (j=0; j<info.channels; j++) {\r
9803           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
9804           out[info.outOffset[j]] <<= 8;\r
9805         }\r
9806         in += info.inJump;\r
9807         out += info.outJump;\r
9808       }\r
9809     }\r
9810     else if (info.inFormat == RTAUDIO_SINT32) {\r
9811       // Channel compensation and/or (de)interleaving only.\r
9812       Int32 *in = (Int32 *)inBuffer;\r
9813       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9814         for (j=0; j<info.channels; j++) {\r
9815           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9816         }\r
9817         in += info.inJump;\r
9818         out += info.outJump;\r
9819       }\r
9820     }\r
9821     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9822       Float32 *in = (Float32 *)inBuffer;\r
9823       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9824         for (j=0; j<info.channels; j++) {\r
9825           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9826         }\r
9827         in += info.inJump;\r
9828         out += info.outJump;\r
9829       }\r
9830     }\r
9831     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9832       Float64 *in = (Float64 *)inBuffer;\r
9833       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9834         for (j=0; j<info.channels; j++) {\r
9835           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9836         }\r
9837         in += info.inJump;\r
9838         out += info.outJump;\r
9839       }\r
9840     }\r
9841   }\r
9842   else if (info.outFormat == RTAUDIO_SINT24) {\r
9843     Int24 *out = (Int24 *)outBuffer;\r
9844     if (info.inFormat == RTAUDIO_SINT8) {\r
9845       signed char *in = (signed char *)inBuffer;\r
9846       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9847         for (j=0; j<info.channels; j++) {\r
9848           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
9849           //out[info.outOffset[j]] <<= 16;\r
9850         }\r
9851         in += info.inJump;\r
9852         out += info.outJump;\r
9853       }\r
9854     }\r
9855     else if (info.inFormat == RTAUDIO_SINT16) {\r
9856       Int16 *in = (Int16 *)inBuffer;\r
9857       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9858         for (j=0; j<info.channels; j++) {\r
9859           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
9860           //out[info.outOffset[j]] <<= 8;\r
9861         }\r
9862         in += info.inJump;\r
9863         out += info.outJump;\r
9864       }\r
9865     }\r
9866     else if (info.inFormat == RTAUDIO_SINT24) {\r
9867       // Channel compensation and/or (de)interleaving only.\r
9868       Int24 *in = (Int24 *)inBuffer;\r
9869       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9870         for (j=0; j<info.channels; j++) {\r
9871           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9872         }\r
9873         in += info.inJump;\r
9874         out += info.outJump;\r
9875       }\r
9876     }\r
9877     else if (info.inFormat == RTAUDIO_SINT32) {\r
9878       Int32 *in = (Int32 *)inBuffer;\r
9879       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9880         for (j=0; j<info.channels; j++) {\r
9881           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
9882           //out[info.outOffset[j]] >>= 8;\r
9883         }\r
9884         in += info.inJump;\r
9885         out += info.outJump;\r
9886       }\r
9887     }\r
9888     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9889       Float32 *in = (Float32 *)inBuffer;\r
9890       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9891         for (j=0; j<info.channels; j++) {\r
9892           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9893         }\r
9894         in += info.inJump;\r
9895         out += info.outJump;\r
9896       }\r
9897     }\r
9898     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9899       Float64 *in = (Float64 *)inBuffer;\r
9900       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9901         for (j=0; j<info.channels; j++) {\r
9902           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9903         }\r
9904         in += info.inJump;\r
9905         out += info.outJump;\r
9906       }\r
9907     }\r
9908   }\r
9909   else if (info.outFormat == RTAUDIO_SINT16) {\r
9910     Int16 *out = (Int16 *)outBuffer;\r
9911     if (info.inFormat == RTAUDIO_SINT8) {\r
9912       signed char *in = (signed char *)inBuffer;\r
9913       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9914         for (j=0; j<info.channels; j++) {\r
9915           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
9916           out[info.outOffset[j]] <<= 8;\r
9917         }\r
9918         in += info.inJump;\r
9919         out += info.outJump;\r
9920       }\r
9921     }\r
9922     else if (info.inFormat == RTAUDIO_SINT16) {\r
9923       // Channel compensation and/or (de)interleaving only.\r
9924       Int16 *in = (Int16 *)inBuffer;\r
9925       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9926         for (j=0; j<info.channels; j++) {\r
9927           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9928         }\r
9929         in += info.inJump;\r
9930         out += info.outJump;\r
9931       }\r
9932     }\r
9933     else if (info.inFormat == RTAUDIO_SINT24) {\r
9934       Int24 *in = (Int24 *)inBuffer;\r
9935       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9936         for (j=0; j<info.channels; j++) {\r
9937           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
9938         }\r
9939         in += info.inJump;\r
9940         out += info.outJump;\r
9941       }\r
9942     }\r
9943     else if (info.inFormat == RTAUDIO_SINT32) {\r
9944       Int32 *in = (Int32 *)inBuffer;\r
9945       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9946         for (j=0; j<info.channels; j++) {\r
9947           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
9948         }\r
9949         in += info.inJump;\r
9950         out += info.outJump;\r
9951       }\r
9952     }\r
9953     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9954       Float32 *in = (Float32 *)inBuffer;\r
9955       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9956         for (j=0; j<info.channels; j++) {\r
9957           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9958         }\r
9959         in += info.inJump;\r
9960         out += info.outJump;\r
9961       }\r
9962     }\r
9963     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9964       Float64 *in = (Float64 *)inBuffer;\r
9965       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9966         for (j=0; j<info.channels; j++) {\r
9967           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9968         }\r
9969         in += info.inJump;\r
9970         out += info.outJump;\r
9971       }\r
9972     }\r
9973   }\r
9974   else if (info.outFormat == RTAUDIO_SINT8) {\r
9975     signed char *out = (signed char *)outBuffer;\r
9976     if (info.inFormat == RTAUDIO_SINT8) {\r
9977       // Channel compensation and/or (de)interleaving only.\r
9978       signed char *in = (signed char *)inBuffer;\r
9979       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9980         for (j=0; j<info.channels; j++) {\r
9981           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9982         }\r
9983         in += info.inJump;\r
9984         out += info.outJump;\r
9985       }\r
9986     }\r
9987     if (info.inFormat == RTAUDIO_SINT16) {\r
9988       Int16 *in = (Int16 *)inBuffer;\r
9989       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9990         for (j=0; j<info.channels; j++) {\r
9991           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
9992         }\r
9993         in += info.inJump;\r
9994         out += info.outJump;\r
9995       }\r
9996     }\r
9997     else if (info.inFormat == RTAUDIO_SINT24) {\r
9998       Int24 *in = (Int24 *)inBuffer;\r
9999       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10000         for (j=0; j<info.channels; j++) {\r
10001           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10002         }\r
10003         in += info.inJump;\r
10004         out += info.outJump;\r
10005       }\r
10006     }\r
10007     else if (info.inFormat == RTAUDIO_SINT32) {\r
10008       Int32 *in = (Int32 *)inBuffer;\r
10009       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10010         for (j=0; j<info.channels; j++) {\r
10011           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10012         }\r
10013         in += info.inJump;\r
10014         out += info.outJump;\r
10015       }\r
10016     }\r
10017     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10018       Float32 *in = (Float32 *)inBuffer;\r
10019       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10020         for (j=0; j<info.channels; j++) {\r
10021           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10022         }\r
10023         in += info.inJump;\r
10024         out += info.outJump;\r
10025       }\r
10026     }\r
10027     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10028       Float64 *in = (Float64 *)inBuffer;\r
10029       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10030         for (j=0; j<info.channels; j++) {\r
10031           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10032         }\r
10033         in += info.inJump;\r
10034         out += info.outJump;\r
10035       }\r
10036     }\r
10037   }\r
10038 }\r
10039 \r
10040 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10041 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10042 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10043 \r
10044 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10045 {\r
10046   register char val;\r
10047   register char *ptr;\r
10048 \r
10049   ptr = buffer;\r
10050   if ( format == RTAUDIO_SINT16 ) {\r
10051     for ( unsigned int i=0; i<samples; i++ ) {\r
10052       // Swap 1st and 2nd bytes.\r
10053       val = *(ptr);\r
10054       *(ptr) = *(ptr+1);\r
10055       *(ptr+1) = val;\r
10056 \r
10057       // Increment 2 bytes.\r
10058       ptr += 2;\r
10059     }\r
10060   }\r
10061   else if ( format == RTAUDIO_SINT32 ||\r
10062             format == RTAUDIO_FLOAT32 ) {\r
10063     for ( unsigned int i=0; i<samples; i++ ) {\r
10064       // Swap 1st and 4th bytes.\r
10065       val = *(ptr);\r
10066       *(ptr) = *(ptr+3);\r
10067       *(ptr+3) = val;\r
10068 \r
10069       // Swap 2nd and 3rd bytes.\r
10070       ptr += 1;\r
10071       val = *(ptr);\r
10072       *(ptr) = *(ptr+1);\r
10073       *(ptr+1) = val;\r
10074 \r
10075       // Increment 3 more bytes.\r
10076       ptr += 3;\r
10077     }\r
10078   }\r
10079   else if ( format == RTAUDIO_SINT24 ) {\r
10080     for ( unsigned int i=0; i<samples; i++ ) {\r
10081       // Swap 1st and 3rd bytes.\r
10082       val = *(ptr);\r
10083       *(ptr) = *(ptr+2);\r
10084       *(ptr+2) = val;\r
10085 \r
10086       // Increment 2 more bytes.\r
10087       ptr += 2;\r
10088     }\r
10089   }\r
10090   else if ( format == RTAUDIO_FLOAT64 ) {\r
10091     for ( unsigned int i=0; i<samples; i++ ) {\r
10092       // Swap 1st and 8th bytes\r
10093       val = *(ptr);\r
10094       *(ptr) = *(ptr+7);\r
10095       *(ptr+7) = val;\r
10096 \r
10097       // Swap 2nd and 7th bytes\r
10098       ptr += 1;\r
10099       val = *(ptr);\r
10100       *(ptr) = *(ptr+5);\r
10101       *(ptr+5) = val;\r
10102 \r
10103       // Swap 3rd and 6th bytes\r
10104       ptr += 1;\r
10105       val = *(ptr);\r
10106       *(ptr) = *(ptr+3);\r
10107       *(ptr+3) = val;\r
10108 \r
10109       // Swap 4th and 5th bytes\r
10110       ptr += 1;\r
10111       val = *(ptr);\r
10112       *(ptr) = *(ptr+1);\r
10113       *(ptr+1) = val;\r
10114 \r
10115       // Increment 5 more bytes.\r
10116       ptr += 5;\r
10117     }\r
10118   }\r
10119 }\r
10120 \r
10121   // Indentation settings for Vim and Emacs\r
10122   //\r
10123   // Local Variables:\r
10124   // c-basic-offset: 2\r
10125   // indent-tabs-mode: nil\r
10126   // End:\r
10127   //\r
10128   // vim: et sts=2 sw=2\r
10129 \r