alsa: avoid uninitialized read
[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.1\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 void RtApi :: setStreamTime( double time )\r
408 {\r
409   verifyStream();\r
410 \r
411   if ( time >= 0.0 )\r
412     stream_.streamTime = time;\r
413 }\r
414 \r
415 unsigned int RtApi :: getStreamSampleRate( void )\r
416 {\r
417  verifyStream();\r
418 \r
419  return stream_.sampleRate;\r
420 }\r
421 \r
422 \r
423 // *************************************************** //\r
424 //\r
425 // OS/API-specific methods.\r
426 //\r
427 // *************************************************** //\r
428 \r
429 #if defined(__MACOSX_CORE__)\r
430 \r
431 // The OS X CoreAudio API is designed to use a separate callback\r
432 // procedure for each of its audio devices.  A single RtAudio duplex\r
433 // stream using two different devices is supported here, though it\r
434 // cannot be guaranteed to always behave correctly because we cannot\r
435 // synchronize these two callbacks.\r
436 //\r
437 // A property listener is installed for over/underrun information.\r
438 // However, no functionality is currently provided to allow property\r
439 // listeners to trigger user handlers because it is unclear what could\r
440 // be done if a critical stream parameter (buffer size, sample rate,\r
441 // device disconnect) notification arrived.  The listeners entail\r
442 // quite a bit of extra code and most likely, a user program wouldn't\r
443 // be prepared for the result anyway.  However, we do provide a flag\r
444 // to the client callback function to inform of an over/underrun.\r
445 \r
446 // A structure to hold various information related to the CoreAudio API\r
447 // implementation.\r
448 struct CoreHandle {\r
449   AudioDeviceID id[2];    // device ids\r
450 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
451   AudioDeviceIOProcID procId[2];\r
452 #endif\r
453   UInt32 iStream[2];      // device stream index (or first if using multiple)\r
454   UInt32 nStreams[2];     // number of streams to use\r
455   bool xrun[2];\r
456   char *deviceBuffer;\r
457   pthread_cond_t condition;\r
458   int drainCounter;       // Tracks callback counts when draining\r
459   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
460 \r
461   CoreHandle()\r
462     :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
463 };\r
464 \r
465 RtApiCore:: RtApiCore()\r
466 {\r
467 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
468   // This is a largely undocumented but absolutely necessary\r
469   // requirement starting with OS-X 10.6.  If not called, queries and\r
470   // updates to various audio device properties are not handled\r
471   // correctly.\r
472   CFRunLoopRef theRunLoop = NULL;\r
473   AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
474                                           kAudioObjectPropertyScopeGlobal,\r
475                                           kAudioObjectPropertyElementMaster };\r
476   OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
477   if ( result != noErr ) {\r
478     errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
479     error( RtAudioError::WARNING );\r
480   }\r
481 #endif\r
482 }\r
483 \r
484 RtApiCore :: ~RtApiCore()\r
485 {\r
486   // The subclass destructor gets called before the base class\r
487   // destructor, so close an existing stream before deallocating\r
488   // apiDeviceId memory.\r
489   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
490 }\r
491 \r
492 unsigned int RtApiCore :: getDeviceCount( void )\r
493 {\r
494   // Find out how many audio devices there are, if any.\r
495   UInt32 dataSize;\r
496   AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
497   OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );\r
498   if ( result != noErr ) {\r
499     errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";\r
500     error( RtAudioError::WARNING );\r
501     return 0;\r
502   }\r
503 \r
504   return dataSize / sizeof( AudioDeviceID );\r
505 }\r
506 \r
507 unsigned int RtApiCore :: getDefaultInputDevice( void )\r
508 {\r
509   unsigned int nDevices = getDeviceCount();\r
510   if ( nDevices <= 1 ) return 0;\r
511 \r
512   AudioDeviceID id;\r
513   UInt32 dataSize = sizeof( AudioDeviceID );\r
514   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
515   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
516   if ( result != noErr ) {\r
517     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";\r
518     error( RtAudioError::WARNING );\r
519     return 0;\r
520   }\r
521 \r
522   dataSize *= nDevices;\r
523   AudioDeviceID deviceList[ nDevices ];\r
524   property.mSelector = kAudioHardwarePropertyDevices;\r
525   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
526   if ( result != noErr ) {\r
527     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";\r
528     error( RtAudioError::WARNING );\r
529     return 0;\r
530   }\r
531 \r
532   for ( unsigned int i=0; i<nDevices; i++ )\r
533     if ( id == deviceList[i] ) return i;\r
534 \r
535   errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";\r
536   error( RtAudioError::WARNING );\r
537   return 0;\r
538 }\r
539 \r
540 unsigned int RtApiCore :: getDefaultOutputDevice( void )\r
541 {\r
542   unsigned int nDevices = getDeviceCount();\r
543   if ( nDevices <= 1 ) return 0;\r
544 \r
545   AudioDeviceID id;\r
546   UInt32 dataSize = sizeof( AudioDeviceID );\r
547   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
548   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
549   if ( result != noErr ) {\r
550     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";\r
551     error( RtAudioError::WARNING );\r
552     return 0;\r
553   }\r
554 \r
555   dataSize = sizeof( AudioDeviceID ) * nDevices;\r
556   AudioDeviceID deviceList[ nDevices ];\r
557   property.mSelector = kAudioHardwarePropertyDevices;\r
558   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
559   if ( result != noErr ) {\r
560     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";\r
561     error( RtAudioError::WARNING );\r
562     return 0;\r
563   }\r
564 \r
565   for ( unsigned int i=0; i<nDevices; i++ )\r
566     if ( id == deviceList[i] ) return i;\r
567 \r
568   errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";\r
569   error( RtAudioError::WARNING );\r
570   return 0;\r
571 }\r
572 \r
573 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )\r
574 {\r
575   RtAudio::DeviceInfo info;\r
576   info.probed = false;\r
577 \r
578   // Get device ID\r
579   unsigned int nDevices = getDeviceCount();\r
580   if ( nDevices == 0 ) {\r
581     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
582     error( RtAudioError::INVALID_USE );\r
583     return info;\r
584   }\r
585 \r
586   if ( device >= nDevices ) {\r
587     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
588     error( RtAudioError::INVALID_USE );\r
589     return info;\r
590   }\r
591 \r
592   AudioDeviceID deviceList[ nDevices ];\r
593   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
594   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
595                                           kAudioObjectPropertyScopeGlobal,\r
596                                           kAudioObjectPropertyElementMaster };\r
597   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
598                                                 0, NULL, &dataSize, (void *) &deviceList );\r
599   if ( result != noErr ) {\r
600     errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";\r
601     error( RtAudioError::WARNING );\r
602     return info;\r
603   }\r
604 \r
605   AudioDeviceID id = deviceList[ device ];\r
606 \r
607   // Get the device name.\r
608   info.name.erase();\r
609   CFStringRef cfname;\r
610   dataSize = sizeof( CFStringRef );\r
611   property.mSelector = kAudioObjectPropertyManufacturer;\r
612   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
613   if ( result != noErr ) {\r
614     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";\r
615     errorText_ = errorStream_.str();\r
616     error( RtAudioError::WARNING );\r
617     return info;\r
618   }\r
619 \r
620   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
621   int length = CFStringGetLength(cfname);\r
622   char *mname = (char *)malloc(length * 3 + 1);\r
623 #if defined( UNICODE ) || defined( _UNICODE )\r
624   CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
625 #else\r
626   CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
627 #endif\r
628   info.name.append( (const char *)mname, strlen(mname) );\r
629   info.name.append( ": " );\r
630   CFRelease( cfname );\r
631   free(mname);\r
632 \r
633   property.mSelector = kAudioObjectPropertyName;\r
634   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
635   if ( result != noErr ) {\r
636     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";\r
637     errorText_ = errorStream_.str();\r
638     error( RtAudioError::WARNING );\r
639     return info;\r
640   }\r
641 \r
642   //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
643   length = CFStringGetLength(cfname);\r
644   char *name = (char *)malloc(length * 3 + 1);\r
645 #if defined( UNICODE ) || defined( _UNICODE )\r
646   CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
647 #else\r
648   CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
649 #endif\r
650   info.name.append( (const char *)name, strlen(name) );\r
651   CFRelease( cfname );\r
652   free(name);\r
653 \r
654   // Get the output stream "configuration".\r
655   AudioBufferList       *bufferList = nil;\r
656   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
657   property.mScope = kAudioDevicePropertyScopeOutput;\r
658   //  property.mElement = kAudioObjectPropertyElementWildcard;\r
659   dataSize = 0;\r
660   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
661   if ( result != noErr || dataSize == 0 ) {\r
662     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";\r
663     errorText_ = errorStream_.str();\r
664     error( RtAudioError::WARNING );\r
665     return info;\r
666   }\r
667 \r
668   // Allocate the AudioBufferList.\r
669   bufferList = (AudioBufferList *) malloc( dataSize );\r
670   if ( bufferList == NULL ) {\r
671     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";\r
672     error( RtAudioError::WARNING );\r
673     return info;\r
674   }\r
675 \r
676   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
677   if ( result != noErr || dataSize == 0 ) {\r
678     free( bufferList );\r
679     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";\r
680     errorText_ = errorStream_.str();\r
681     error( RtAudioError::WARNING );\r
682     return info;\r
683   }\r
684 \r
685   // Get output channel information.\r
686   unsigned int i, nStreams = bufferList->mNumberBuffers;\r
687   for ( i=0; i<nStreams; i++ )\r
688     info.outputChannels += bufferList->mBuffers[i].mNumberChannels;\r
689   free( bufferList );\r
690 \r
691   // Get the input stream "configuration".\r
692   property.mScope = kAudioDevicePropertyScopeInput;\r
693   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
694   if ( result != noErr || dataSize == 0 ) {\r
695     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";\r
696     errorText_ = errorStream_.str();\r
697     error( RtAudioError::WARNING );\r
698     return info;\r
699   }\r
700 \r
701   // Allocate the AudioBufferList.\r
702   bufferList = (AudioBufferList *) malloc( dataSize );\r
703   if ( bufferList == NULL ) {\r
704     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";\r
705     error( RtAudioError::WARNING );\r
706     return info;\r
707   }\r
708 \r
709   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
710   if (result != noErr || dataSize == 0) {\r
711     free( bufferList );\r
712     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";\r
713     errorText_ = errorStream_.str();\r
714     error( RtAudioError::WARNING );\r
715     return info;\r
716   }\r
717 \r
718   // Get input channel information.\r
719   nStreams = bufferList->mNumberBuffers;\r
720   for ( i=0; i<nStreams; i++ )\r
721     info.inputChannels += bufferList->mBuffers[i].mNumberChannels;\r
722   free( bufferList );\r
723 \r
724   // If device opens for both playback and capture, we determine the channels.\r
725   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
726     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
727 \r
728   // Probe the device sample rates.\r
729   bool isInput = false;\r
730   if ( info.outputChannels == 0 ) isInput = true;\r
731 \r
732   // Determine the supported sample rates.\r
733   property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\r
734   if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;\r
735   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
736   if ( result != kAudioHardwareNoError || dataSize == 0 ) {\r
737     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";\r
738     errorText_ = errorStream_.str();\r
739     error( RtAudioError::WARNING );\r
740     return info;\r
741   }\r
742 \r
743   UInt32 nRanges = dataSize / sizeof( AudioValueRange );\r
744   AudioValueRange rangeList[ nRanges ];\r
745   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );\r
746   if ( result != kAudioHardwareNoError ) {\r
747     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";\r
748     errorText_ = errorStream_.str();\r
749     error( RtAudioError::WARNING );\r
750     return info;\r
751   }\r
752 \r
753   // The sample rate reporting mechanism is a bit of a mystery.  It\r
754   // seems that it can either return individual rates or a range of\r
755   // rates.  I assume that if the min / max range values are the same,\r
756   // then that represents a single supported rate and if the min / max\r
757   // range values are different, the device supports an arbitrary\r
758   // range of values (though there might be multiple ranges, so we'll\r
759   // use the most conservative range).\r
760   Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
761   bool haveValueRange = false;\r
762   info.sampleRates.clear();\r
763   for ( UInt32 i=0; i<nRanges; i++ ) {\r
764     if ( rangeList[i].mMinimum == rangeList[i].mMaximum )\r
765       info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );\r
766     else {\r
767       haveValueRange = true;\r
768       if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
769       if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
770     }\r
771   }\r
772 \r
773   if ( haveValueRange ) {\r
774     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
775       if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )\r
776         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
777     }\r
778   }\r
779 \r
780   // Sort and remove any redundant values\r
781   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
782   info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
783 \r
784   if ( info.sampleRates.size() == 0 ) {\r
785     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
786     errorText_ = errorStream_.str();\r
787     error( RtAudioError::WARNING );\r
788     return info;\r
789   }\r
790 \r
791   // CoreAudio always uses 32-bit floating point data for PCM streams.\r
792   // Thus, any other "physical" formats supported by the device are of\r
793   // no interest to the client.\r
794   info.nativeFormats = RTAUDIO_FLOAT32;\r
795 \r
796   if ( info.outputChannels > 0 )\r
797     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
798   if ( info.inputChannels > 0 )\r
799     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
800 \r
801   info.probed = true;\r
802   return info;\r
803 }\r
804 \r
805 static OSStatus callbackHandler( AudioDeviceID inDevice,\r
806                                  const AudioTimeStamp* /*inNow*/,\r
807                                  const AudioBufferList* inInputData,\r
808                                  const AudioTimeStamp* /*inInputTime*/,\r
809                                  AudioBufferList* outOutputData,\r
810                                  const AudioTimeStamp* /*inOutputTime*/,\r
811                                  void* infoPointer )\r
812 {\r
813   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
814 \r
815   RtApiCore *object = (RtApiCore *) info->object;\r
816   if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )\r
817     return kAudioHardwareUnspecifiedError;\r
818   else\r
819     return kAudioHardwareNoError;\r
820 }\r
821 \r
822 static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
823                               UInt32 nAddresses,\r
824                               const AudioObjectPropertyAddress properties[],\r
825                               void* handlePointer )\r
826 {\r
827   CoreHandle *handle = (CoreHandle *) handlePointer;\r
828   for ( UInt32 i=0; i<nAddresses; i++ ) {\r
829     if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {\r
830       if ( properties[i].mScope == kAudioDevicePropertyScopeInput )\r
831         handle->xrun[1] = true;\r
832       else\r
833         handle->xrun[0] = true;\r
834     }\r
835   }\r
836 \r
837   return kAudioHardwareNoError;\r
838 }\r
839 \r
840 static OSStatus rateListener( AudioObjectID inDevice,\r
841                               UInt32 /*nAddresses*/,\r
842                               const AudioObjectPropertyAddress /*properties*/[],\r
843                               void* ratePointer )\r
844 {\r
845   Float64 *rate = (Float64 *) ratePointer;\r
846   UInt32 dataSize = sizeof( Float64 );\r
847   AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
848                                           kAudioObjectPropertyScopeGlobal,\r
849                                           kAudioObjectPropertyElementMaster };\r
850   AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
851   return kAudioHardwareNoError;\r
852 }\r
853 \r
854 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
855                                    unsigned int firstChannel, unsigned int sampleRate,\r
856                                    RtAudioFormat format, unsigned int *bufferSize,\r
857                                    RtAudio::StreamOptions *options )\r
858 {\r
859   // Get device ID\r
860   unsigned int nDevices = getDeviceCount();\r
861   if ( nDevices == 0 ) {\r
862     // This should not happen because a check is made before this function is called.\r
863     errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";\r
864     return FAILURE;\r
865   }\r
866 \r
867   if ( device >= nDevices ) {\r
868     // This should not happen because a check is made before this function is called.\r
869     errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";\r
870     return FAILURE;\r
871   }\r
872 \r
873   AudioDeviceID deviceList[ nDevices ];\r
874   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
875   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
876                                           kAudioObjectPropertyScopeGlobal,\r
877                                           kAudioObjectPropertyElementMaster };\r
878   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
879                                                 0, NULL, &dataSize, (void *) &deviceList );\r
880   if ( result != noErr ) {\r
881     errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";\r
882     return FAILURE;\r
883   }\r
884 \r
885   AudioDeviceID id = deviceList[ device ];\r
886 \r
887   // Setup for stream mode.\r
888   bool isInput = false;\r
889   if ( mode == INPUT ) {\r
890     isInput = true;\r
891     property.mScope = kAudioDevicePropertyScopeInput;\r
892   }\r
893   else\r
894     property.mScope = kAudioDevicePropertyScopeOutput;\r
895 \r
896   // Get the stream "configuration".\r
897   AudioBufferList       *bufferList = nil;\r
898   dataSize = 0;\r
899   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
900   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
901   if ( result != noErr || dataSize == 0 ) {\r
902     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";\r
903     errorText_ = errorStream_.str();\r
904     return FAILURE;\r
905   }\r
906 \r
907   // Allocate the AudioBufferList.\r
908   bufferList = (AudioBufferList *) malloc( dataSize );\r
909   if ( bufferList == NULL ) {\r
910     errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";\r
911     return FAILURE;\r
912   }\r
913 \r
914   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
915   if (result != noErr || dataSize == 0) {\r
916     free( bufferList );\r
917     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
918     errorText_ = errorStream_.str();\r
919     return FAILURE;\r
920   }\r
921 \r
922   // Search for one or more streams that contain the desired number of\r
923   // channels. CoreAudio devices can have an arbitrary number of\r
924   // streams and each stream can have an arbitrary number of channels.\r
925   // For each stream, a single buffer of interleaved samples is\r
926   // provided.  RtAudio prefers the use of one stream of interleaved\r
927   // data or multiple consecutive single-channel streams.  However, we\r
928   // now support multiple consecutive multi-channel streams of\r
929   // interleaved data as well.\r
930   UInt32 iStream, offsetCounter = firstChannel;\r
931   UInt32 nStreams = bufferList->mNumberBuffers;\r
932   bool monoMode = false;\r
933   bool foundStream = false;\r
934 \r
935   // First check that the device supports the requested number of\r
936   // channels.\r
937   UInt32 deviceChannels = 0;\r
938   for ( iStream=0; iStream<nStreams; iStream++ )\r
939     deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;\r
940 \r
941   if ( deviceChannels < ( channels + firstChannel ) ) {\r
942     free( bufferList );\r
943     errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";\r
944     errorText_ = errorStream_.str();\r
945     return FAILURE;\r
946   }\r
947 \r
948   // Look for a single stream meeting our needs.\r
949   UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;\r
950   for ( iStream=0; iStream<nStreams; iStream++ ) {\r
951     streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
952     if ( streamChannels >= channels + offsetCounter ) {\r
953       firstStream = iStream;\r
954       channelOffset = offsetCounter;\r
955       foundStream = true;\r
956       break;\r
957     }\r
958     if ( streamChannels > offsetCounter ) break;\r
959     offsetCounter -= streamChannels;\r
960   }\r
961 \r
962   // If we didn't find a single stream above, then we should be able\r
963   // to meet the channel specification with multiple streams.\r
964   if ( foundStream == false ) {\r
965     monoMode = true;\r
966     offsetCounter = firstChannel;\r
967     for ( iStream=0; iStream<nStreams; iStream++ ) {\r
968       streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
969       if ( streamChannels > offsetCounter ) break;\r
970       offsetCounter -= streamChannels;\r
971     }\r
972 \r
973     firstStream = iStream;\r
974     channelOffset = offsetCounter;\r
975     Int32 channelCounter = channels + offsetCounter - streamChannels;\r
976 \r
977     if ( streamChannels > 1 ) monoMode = false;\r
978     while ( channelCounter > 0 ) {\r
979       streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;\r
980       if ( streamChannels > 1 ) monoMode = false;\r
981       channelCounter -= streamChannels;\r
982       streamCount++;\r
983     }\r
984   }\r
985 \r
986   free( bufferList );\r
987 \r
988   // Determine the buffer size.\r
989   AudioValueRange       bufferRange;\r
990   dataSize = sizeof( AudioValueRange );\r
991   property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\r
992   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );\r
993 \r
994   if ( result != noErr ) {\r
995     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";\r
996     errorText_ = errorStream_.str();\r
997     return FAILURE;\r
998   }\r
999 \r
1000   if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
1001   else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;\r
1002   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
1003 \r
1004   // Set the buffer size.  For multiple streams, I'm assuming we only\r
1005   // need to make this setting for the master channel.\r
1006   UInt32 theSize = (UInt32) *bufferSize;\r
1007   dataSize = sizeof( UInt32 );\r
1008   property.mSelector = kAudioDevicePropertyBufferFrameSize;\r
1009   result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );\r
1010 \r
1011   if ( result != noErr ) {\r
1012     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";\r
1013     errorText_ = errorStream_.str();\r
1014     return FAILURE;\r
1015   }\r
1016 \r
1017   // If attempting to setup a duplex stream, the bufferSize parameter\r
1018   // MUST be the same in both directions!\r
1019   *bufferSize = theSize;\r
1020   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
1021     errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";\r
1022     errorText_ = errorStream_.str();\r
1023     return FAILURE;\r
1024   }\r
1025 \r
1026   stream_.bufferSize = *bufferSize;\r
1027   stream_.nBuffers = 1;\r
1028 \r
1029   // Try to set "hog" mode ... it's not clear to me this is working.\r
1030   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
1031     pid_t hog_pid;\r
1032     dataSize = sizeof( hog_pid );\r
1033     property.mSelector = kAudioDevicePropertyHogMode;\r
1034     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );\r
1035     if ( result != noErr ) {\r
1036       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";\r
1037       errorText_ = errorStream_.str();\r
1038       return FAILURE;\r
1039     }\r
1040 \r
1041     if ( hog_pid != getpid() ) {\r
1042       hog_pid = getpid();\r
1043       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );\r
1044       if ( result != noErr ) {\r
1045         errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";\r
1046         errorText_ = errorStream_.str();\r
1047         return FAILURE;\r
1048       }\r
1049     }\r
1050   }\r
1051 \r
1052   // Check and if necessary, change the sample rate for the device.\r
1053   Float64 nominalRate;\r
1054   dataSize = sizeof( Float64 );\r
1055   property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
1056   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
1057   if ( result != noErr ) {\r
1058     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
1059     errorText_ = errorStream_.str();\r
1060     return FAILURE;\r
1061   }\r
1062 \r
1063   // Only change the sample rate if off by more than 1 Hz.\r
1064   if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
1065 \r
1066     // Set a property listener for the sample rate change\r
1067     Float64 reportedRate = 0.0;\r
1068     AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
1069     result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1070     if ( result != noErr ) {\r
1071       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
1072       errorText_ = errorStream_.str();\r
1073       return FAILURE;\r
1074     }\r
1075 \r
1076     nominalRate = (Float64) sampleRate;\r
1077     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
1078     if ( result != noErr ) {\r
1079       AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1080       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
1081       errorText_ = errorStream_.str();\r
1082       return FAILURE;\r
1083     }\r
1084 \r
1085     // Now wait until the reported nominal rate is what we just set.\r
1086     UInt32 microCounter = 0;\r
1087     while ( reportedRate != nominalRate ) {\r
1088       microCounter += 5000;\r
1089       if ( microCounter > 5000000 ) break;\r
1090       usleep( 5000 );\r
1091     }\r
1092 \r
1093     // Remove the property listener.\r
1094     AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1095 \r
1096     if ( microCounter > 5000000 ) {\r
1097       errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
1098       errorText_ = errorStream_.str();\r
1099       return FAILURE;\r
1100     }\r
1101   }\r
1102 \r
1103   // Now set the stream format for all streams.  Also, check the\r
1104   // physical format of the device and change that if necessary.\r
1105   AudioStreamBasicDescription   description;\r
1106   dataSize = sizeof( AudioStreamBasicDescription );\r
1107   property.mSelector = kAudioStreamPropertyVirtualFormat;\r
1108   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
1109   if ( result != noErr ) {\r
1110     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
1111     errorText_ = errorStream_.str();\r
1112     return FAILURE;\r
1113   }\r
1114 \r
1115   // Set the sample rate and data format id.  However, only make the\r
1116   // change if the sample rate is not within 1.0 of the desired\r
1117   // rate and the format is not linear pcm.\r
1118   bool updateFormat = false;\r
1119   if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
1120     description.mSampleRate = (Float64) sampleRate;\r
1121     updateFormat = true;\r
1122   }\r
1123 \r
1124   if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
1125     description.mFormatID = kAudioFormatLinearPCM;\r
1126     updateFormat = true;\r
1127   }\r
1128 \r
1129   if ( updateFormat ) {\r
1130     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
1131     if ( result != noErr ) {\r
1132       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
1133       errorText_ = errorStream_.str();\r
1134       return FAILURE;\r
1135     }\r
1136   }\r
1137 \r
1138   // Now check the physical format.\r
1139   property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
1140   result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );\r
1141   if ( result != noErr ) {\r
1142     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
1143     errorText_ = errorStream_.str();\r
1144     return FAILURE;\r
1145   }\r
1146 \r
1147   //std::cout << "Current physical stream format:" << std::endl;\r
1148   //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
1149   //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1150   //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
1151   //std::cout << "   sample rate = " << description.mSampleRate << std::endl;\r
1152 \r
1153   if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
1154     description.mFormatID = kAudioFormatLinearPCM;\r
1155     //description.mSampleRate = (Float64) sampleRate;\r
1156     AudioStreamBasicDescription testDescription = description;\r
1157     UInt32 formatFlags;\r
1158 \r
1159     // We'll try higher bit rates first and then work our way down.\r
1160     std::vector< std::pair<UInt32, UInt32>  > physicalFormats;\r
1161     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;\r
1162     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1163     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1164     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1165     physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed\r
1166     formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
1167     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
1168     formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
1169     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
1170     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1171     physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
1172     physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
1173 \r
1174     bool setPhysicalFormat = false;\r
1175     for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
1176       testDescription = description;\r
1177       testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
1178       testDescription.mFormatFlags = physicalFormats[i].second;\r
1179       if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
1180         testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;\r
1181       else\r
1182         testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
1183       testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
1184       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
1185       if ( result == noErr ) {\r
1186         setPhysicalFormat = true;\r
1187         //std::cout << "Updated physical stream format:" << std::endl;\r
1188         //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
1189         //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1190         //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
1191         //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;\r
1192         break;\r
1193       }\r
1194     }\r
1195 \r
1196     if ( !setPhysicalFormat ) {\r
1197       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
1198       errorText_ = errorStream_.str();\r
1199       return FAILURE;\r
1200     }\r
1201   } // done setting virtual/physical formats.\r
1202 \r
1203   // Get the stream / device latency.\r
1204   UInt32 latency;\r
1205   dataSize = sizeof( UInt32 );\r
1206   property.mSelector = kAudioDevicePropertyLatency;\r
1207   if ( AudioObjectHasProperty( id, &property ) == true ) {\r
1208     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );\r
1209     if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;\r
1210     else {\r
1211       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";\r
1212       errorText_ = errorStream_.str();\r
1213       error( RtAudioError::WARNING );\r
1214     }\r
1215   }\r
1216 \r
1217   // Byte-swapping: According to AudioHardware.h, the stream data will\r
1218   // always be presented in native-endian format, so we should never\r
1219   // need to byte swap.\r
1220   stream_.doByteSwap[mode] = false;\r
1221 \r
1222   // From the CoreAudio documentation, PCM data must be supplied as\r
1223   // 32-bit floats.\r
1224   stream_.userFormat = format;\r
1225   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
1226 \r
1227   if ( streamCount == 1 )\r
1228     stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;\r
1229   else // multiple streams\r
1230     stream_.nDeviceChannels[mode] = channels;\r
1231   stream_.nUserChannels[mode] = channels;\r
1232   stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream\r
1233   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
1234   else stream_.userInterleaved = true;\r
1235   stream_.deviceInterleaved[mode] = true;\r
1236   if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;\r
1237 \r
1238   // Set flags for buffer conversion.\r
1239   stream_.doConvertBuffer[mode] = false;\r
1240   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
1241     stream_.doConvertBuffer[mode] = true;\r
1242   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
1243     stream_.doConvertBuffer[mode] = true;\r
1244   if ( streamCount == 1 ) {\r
1245     if ( stream_.nUserChannels[mode] > 1 &&\r
1246          stream_.userInterleaved != stream_.deviceInterleaved[mode] )\r
1247       stream_.doConvertBuffer[mode] = true;\r
1248   }\r
1249   else if ( monoMode && stream_.userInterleaved )\r
1250     stream_.doConvertBuffer[mode] = true;\r
1251 \r
1252   // Allocate our CoreHandle structure for the stream.\r
1253   CoreHandle *handle = 0;\r
1254   if ( stream_.apiHandle == 0 ) {\r
1255     try {\r
1256       handle = new CoreHandle;\r
1257     }\r
1258     catch ( std::bad_alloc& ) {\r
1259       errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";\r
1260       goto error;\r
1261     }\r
1262 \r
1263     if ( pthread_cond_init( &handle->condition, NULL ) ) {\r
1264       errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";\r
1265       goto error;\r
1266     }\r
1267     stream_.apiHandle = (void *) handle;\r
1268   }\r
1269   else\r
1270     handle = (CoreHandle *) stream_.apiHandle;\r
1271   handle->iStream[mode] = firstStream;\r
1272   handle->nStreams[mode] = streamCount;\r
1273   handle->id[mode] = id;\r
1274 \r
1275   // Allocate necessary internal buffers.\r
1276   unsigned long bufferBytes;\r
1277   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
1278   //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
1279   stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
1280   memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
1281   if ( stream_.userBuffer[mode] == NULL ) {\r
1282     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
1283     goto error;\r
1284   }\r
1285 \r
1286   // If possible, we will make use of the CoreAudio stream buffers as\r
1287   // "device buffers".  However, we can't do this if using multiple\r
1288   // streams.\r
1289   if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {\r
1290 \r
1291     bool makeBuffer = true;\r
1292     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
1293     if ( mode == INPUT ) {\r
1294       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
1295         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
1296         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
1297       }\r
1298     }\r
1299 \r
1300     if ( makeBuffer ) {\r
1301       bufferBytes *= *bufferSize;\r
1302       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
1303       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
1304       if ( stream_.deviceBuffer == NULL ) {\r
1305         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";\r
1306         goto error;\r
1307       }\r
1308     }\r
1309   }\r
1310 \r
1311   stream_.sampleRate = sampleRate;\r
1312   stream_.device[mode] = device;\r
1313   stream_.state = STREAM_STOPPED;\r
1314   stream_.callbackInfo.object = (void *) this;\r
1315 \r
1316   // Setup the buffer conversion information structure.\r
1317   if ( stream_.doConvertBuffer[mode] ) {\r
1318     if ( streamCount > 1 ) setConvertInfo( mode, 0 );\r
1319     else setConvertInfo( mode, channelOffset );\r
1320   }\r
1321 \r
1322   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )\r
1323     // Only one callback procedure per device.\r
1324     stream_.mode = DUPLEX;\r
1325   else {\r
1326 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1327     result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );\r
1328 #else\r
1329     // deprecated in favor of AudioDeviceCreateIOProcID()\r
1330     result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );\r
1331 #endif\r
1332     if ( result != noErr ) {\r
1333       errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";\r
1334       errorText_ = errorStream_.str();\r
1335       goto error;\r
1336     }\r
1337     if ( stream_.mode == OUTPUT && mode == INPUT )\r
1338       stream_.mode = DUPLEX;\r
1339     else\r
1340       stream_.mode = mode;\r
1341   }\r
1342 \r
1343   // Setup the device property listener for over/underload.\r
1344   property.mSelector = kAudioDeviceProcessorOverload;\r
1345   property.mScope = kAudioObjectPropertyScopeGlobal;\r
1346   result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
1347 \r
1348   return SUCCESS;\r
1349 \r
1350  error:\r
1351   if ( handle ) {\r
1352     pthread_cond_destroy( &handle->condition );\r
1353     delete handle;\r
1354     stream_.apiHandle = 0;\r
1355   }\r
1356 \r
1357   for ( int i=0; i<2; i++ ) {\r
1358     if ( stream_.userBuffer[i] ) {\r
1359       free( stream_.userBuffer[i] );\r
1360       stream_.userBuffer[i] = 0;\r
1361     }\r
1362   }\r
1363 \r
1364   if ( stream_.deviceBuffer ) {\r
1365     free( stream_.deviceBuffer );\r
1366     stream_.deviceBuffer = 0;\r
1367   }\r
1368 \r
1369   stream_.state = STREAM_CLOSED;\r
1370   return FAILURE;\r
1371 }\r
1372 \r
1373 void RtApiCore :: closeStream( void )\r
1374 {\r
1375   if ( stream_.state == STREAM_CLOSED ) {\r
1376     errorText_ = "RtApiCore::closeStream(): no open stream to close!";\r
1377     error( RtAudioError::WARNING );\r
1378     return;\r
1379   }\r
1380 \r
1381   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1382   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1383     if ( stream_.state == STREAM_RUNNING )\r
1384       AudioDeviceStop( handle->id[0], callbackHandler );\r
1385 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1386     AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );\r
1387 #else\r
1388     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1389     AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );\r
1390 #endif\r
1391   }\r
1392 \r
1393   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1394     if ( stream_.state == STREAM_RUNNING )\r
1395       AudioDeviceStop( handle->id[1], callbackHandler );\r
1396 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1397     AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );\r
1398 #else\r
1399     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1400     AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );\r
1401 #endif\r
1402   }\r
1403 \r
1404   for ( int i=0; i<2; i++ ) {\r
1405     if ( stream_.userBuffer[i] ) {\r
1406       free( stream_.userBuffer[i] );\r
1407       stream_.userBuffer[i] = 0;\r
1408     }\r
1409   }\r
1410 \r
1411   if ( stream_.deviceBuffer ) {\r
1412     free( stream_.deviceBuffer );\r
1413     stream_.deviceBuffer = 0;\r
1414   }\r
1415 \r
1416   // Destroy pthread condition variable.\r
1417   pthread_cond_destroy( &handle->condition );\r
1418   delete handle;\r
1419   stream_.apiHandle = 0;\r
1420 \r
1421   stream_.mode = UNINITIALIZED;\r
1422   stream_.state = STREAM_CLOSED;\r
1423 }\r
1424 \r
1425 void RtApiCore :: startStream( void )\r
1426 {\r
1427   verifyStream();\r
1428   if ( stream_.state == STREAM_RUNNING ) {\r
1429     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
1430     error( RtAudioError::WARNING );\r
1431     return;\r
1432   }\r
1433 \r
1434   OSStatus result = noErr;\r
1435   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1436   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1437 \r
1438     result = AudioDeviceStart( handle->id[0], callbackHandler );\r
1439     if ( result != noErr ) {\r
1440       errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";\r
1441       errorText_ = errorStream_.str();\r
1442       goto unlock;\r
1443     }\r
1444   }\r
1445 \r
1446   if ( stream_.mode == INPUT ||\r
1447        ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1448 \r
1449     result = AudioDeviceStart( handle->id[1], callbackHandler );\r
1450     if ( result != noErr ) {\r
1451       errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";\r
1452       errorText_ = errorStream_.str();\r
1453       goto unlock;\r
1454     }\r
1455   }\r
1456 \r
1457   handle->drainCounter = 0;\r
1458   handle->internalDrain = false;\r
1459   stream_.state = STREAM_RUNNING;\r
1460 \r
1461  unlock:\r
1462   if ( result == noErr ) return;\r
1463   error( RtAudioError::SYSTEM_ERROR );\r
1464 }\r
1465 \r
1466 void RtApiCore :: stopStream( void )\r
1467 {\r
1468   verifyStream();\r
1469   if ( stream_.state == STREAM_STOPPED ) {\r
1470     errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";\r
1471     error( RtAudioError::WARNING );\r
1472     return;\r
1473   }\r
1474 \r
1475   OSStatus result = noErr;\r
1476   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1477   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1478 \r
1479     if ( handle->drainCounter == 0 ) {\r
1480       handle->drainCounter = 2;\r
1481       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
1482     }\r
1483 \r
1484     result = AudioDeviceStop( handle->id[0], callbackHandler );\r
1485     if ( result != noErr ) {\r
1486       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
1487       errorText_ = errorStream_.str();\r
1488       goto unlock;\r
1489     }\r
1490   }\r
1491 \r
1492   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1493 \r
1494     result = AudioDeviceStop( handle->id[1], callbackHandler );\r
1495     if ( result != noErr ) {\r
1496       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
1497       errorText_ = errorStream_.str();\r
1498       goto unlock;\r
1499     }\r
1500   }\r
1501 \r
1502   stream_.state = STREAM_STOPPED;\r
1503 \r
1504  unlock:\r
1505   if ( result == noErr ) return;\r
1506   error( RtAudioError::SYSTEM_ERROR );\r
1507 }\r
1508 \r
1509 void RtApiCore :: abortStream( void )\r
1510 {\r
1511   verifyStream();\r
1512   if ( stream_.state == STREAM_STOPPED ) {\r
1513     errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";\r
1514     error( RtAudioError::WARNING );\r
1515     return;\r
1516   }\r
1517 \r
1518   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1519   handle->drainCounter = 2;\r
1520 \r
1521   stopStream();\r
1522 }\r
1523 \r
1524 // This function will be called by a spawned thread when the user\r
1525 // callback function signals that the stream should be stopped or\r
1526 // aborted.  It is better to handle it this way because the\r
1527 // callbackEvent() function probably should return before the AudioDeviceStop()\r
1528 // function is called.\r
1529 static void *coreStopStream( void *ptr )\r
1530 {\r
1531   CallbackInfo *info = (CallbackInfo *) ptr;\r
1532   RtApiCore *object = (RtApiCore *) info->object;\r
1533 \r
1534   object->stopStream();\r
1535   pthread_exit( NULL );\r
1536 }\r
1537 \r
1538 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,\r
1539                                  const AudioBufferList *inBufferList,\r
1540                                  const AudioBufferList *outBufferList )\r
1541 {\r
1542   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
1543   if ( stream_.state == STREAM_CLOSED ) {\r
1544     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
1545     error( RtAudioError::WARNING );\r
1546     return FAILURE;\r
1547   }\r
1548 \r
1549   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
1550   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1551 \r
1552   // Check if we were draining the stream and signal is finished.\r
1553   if ( handle->drainCounter > 3 ) {\r
1554     ThreadHandle threadId;\r
1555 \r
1556     stream_.state = STREAM_STOPPING;\r
1557     if ( handle->internalDrain == true )\r
1558       pthread_create( &threadId, NULL, coreStopStream, info );\r
1559     else // external call to stopStream()\r
1560       pthread_cond_signal( &handle->condition );\r
1561     return SUCCESS;\r
1562   }\r
1563 \r
1564   AudioDeviceID outputDevice = handle->id[0];\r
1565 \r
1566   // Invoke user callback to get fresh output data UNLESS we are\r
1567   // draining stream or duplex mode AND the input/output devices are\r
1568   // different AND this function is called for the input device.\r
1569   if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {\r
1570     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
1571     double streamTime = getStreamTime();\r
1572     RtAudioStreamStatus status = 0;\r
1573     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
1574       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
1575       handle->xrun[0] = false;\r
1576     }\r
1577     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
1578       status |= RTAUDIO_INPUT_OVERFLOW;\r
1579       handle->xrun[1] = false;\r
1580     }\r
1581 \r
1582     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
1583                                   stream_.bufferSize, streamTime, status, info->userData );\r
1584     if ( cbReturnValue == 2 ) {\r
1585       stream_.state = STREAM_STOPPING;\r
1586       handle->drainCounter = 2;\r
1587       abortStream();\r
1588       return SUCCESS;\r
1589     }\r
1590     else if ( cbReturnValue == 1 ) {\r
1591       handle->drainCounter = 1;\r
1592       handle->internalDrain = true;\r
1593     }\r
1594   }\r
1595 \r
1596   if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {\r
1597 \r
1598     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
1599 \r
1600       if ( handle->nStreams[0] == 1 ) {\r
1601         memset( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1602                 0,\r
1603                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1604       }\r
1605       else { // fill multiple streams with zeros\r
1606         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1607           memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1608                   0,\r
1609                   outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );\r
1610         }\r
1611       }\r
1612     }\r
1613     else if ( handle->nStreams[0] == 1 ) {\r
1614       if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer\r
1615         convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,\r
1616                        stream_.userBuffer[0], stream_.convertInfo[0] );\r
1617       }\r
1618       else { // copy from user buffer\r
1619         memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1620                 stream_.userBuffer[0],\r
1621                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1622       }\r
1623     }\r
1624     else { // fill multiple streams\r
1625       Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];\r
1626       if ( stream_.doConvertBuffer[0] ) {\r
1627         convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
1628         inBuffer = (Float32 *) stream_.deviceBuffer;\r
1629       }\r
1630 \r
1631       if ( stream_.deviceInterleaved[0] == false ) { // mono mode\r
1632         UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;\r
1633         for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
1634           memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1635                   (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );\r
1636         }\r
1637       }\r
1638       else { // fill multiple multi-channel streams with interleaved data\r
1639         UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;\r
1640         Float32 *out, *in;\r
1641 \r
1642         bool inInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1643         UInt32 inChannels = stream_.nUserChannels[0];\r
1644         if ( stream_.doConvertBuffer[0] ) {\r
1645           inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1646           inChannels = stream_.nDeviceChannels[0];\r
1647         }\r
1648 \r
1649         if ( inInterleaved ) inOffset = 1;\r
1650         else inOffset = stream_.bufferSize;\r
1651 \r
1652         channelsLeft = inChannels;\r
1653         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1654           in = inBuffer;\r
1655           out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;\r
1656           streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;\r
1657 \r
1658           outJump = 0;\r
1659           // Account for possible channel offset in first stream\r
1660           if ( i == 0 && stream_.channelOffset[0] > 0 ) {\r
1661             streamChannels -= stream_.channelOffset[0];\r
1662             outJump = stream_.channelOffset[0];\r
1663             out += outJump;\r
1664           }\r
1665 \r
1666           // Account for possible unfilled channels at end of the last stream\r
1667           if ( streamChannels > channelsLeft ) {\r
1668             outJump = streamChannels - channelsLeft;\r
1669             streamChannels = channelsLeft;\r
1670           }\r
1671 \r
1672           // Determine input buffer offsets and skips\r
1673           if ( inInterleaved ) {\r
1674             inJump = inChannels;\r
1675             in += inChannels - channelsLeft;\r
1676           }\r
1677           else {\r
1678             inJump = 1;\r
1679             in += (inChannels - channelsLeft) * inOffset;\r
1680           }\r
1681 \r
1682           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1683             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1684               *out++ = in[j*inOffset];\r
1685             }\r
1686             out += outJump;\r
1687             in += inJump;\r
1688           }\r
1689           channelsLeft -= streamChannels;\r
1690         }\r
1691       }\r
1692     }\r
1693   }\r
1694 \r
1695   // Don't bother draining input\r
1696   if ( handle->drainCounter ) {\r
1697     handle->drainCounter++;\r
1698     goto unlock;\r
1699   }\r
1700 \r
1701   AudioDeviceID inputDevice;\r
1702   inputDevice = handle->id[1];\r
1703   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {\r
1704 \r
1705     if ( handle->nStreams[1] == 1 ) {\r
1706       if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer\r
1707         convertBuffer( stream_.userBuffer[1],\r
1708                        (char *) inBufferList->mBuffers[handle->iStream[1]].mData,\r
1709                        stream_.convertInfo[1] );\r
1710       }\r
1711       else { // copy to user buffer\r
1712         memcpy( stream_.userBuffer[1],\r
1713                 inBufferList->mBuffers[handle->iStream[1]].mData,\r
1714                 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );\r
1715       }\r
1716     }\r
1717     else { // read from multiple streams\r
1718       Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];\r
1719       if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;\r
1720 \r
1721       if ( stream_.deviceInterleaved[1] == false ) { // mono mode\r
1722         UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;\r
1723         for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
1724           memcpy( (void *)&outBuffer[i*stream_.bufferSize],\r
1725                   inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );\r
1726         }\r
1727       }\r
1728       else { // read from multiple multi-channel streams\r
1729         UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;\r
1730         Float32 *out, *in;\r
1731 \r
1732         bool outInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1733         UInt32 outChannels = stream_.nUserChannels[1];\r
1734         if ( stream_.doConvertBuffer[1] ) {\r
1735           outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1736           outChannels = stream_.nDeviceChannels[1];\r
1737         }\r
1738 \r
1739         if ( outInterleaved ) outOffset = 1;\r
1740         else outOffset = stream_.bufferSize;\r
1741 \r
1742         channelsLeft = outChannels;\r
1743         for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {\r
1744           out = outBuffer;\r
1745           in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;\r
1746           streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;\r
1747 \r
1748           inJump = 0;\r
1749           // Account for possible channel offset in first stream\r
1750           if ( i == 0 && stream_.channelOffset[1] > 0 ) {\r
1751             streamChannels -= stream_.channelOffset[1];\r
1752             inJump = stream_.channelOffset[1];\r
1753             in += inJump;\r
1754           }\r
1755 \r
1756           // Account for possible unread channels at end of the last stream\r
1757           if ( streamChannels > channelsLeft ) {\r
1758             inJump = streamChannels - channelsLeft;\r
1759             streamChannels = channelsLeft;\r
1760           }\r
1761 \r
1762           // Determine output buffer offsets and skips\r
1763           if ( outInterleaved ) {\r
1764             outJump = outChannels;\r
1765             out += outChannels - channelsLeft;\r
1766           }\r
1767           else {\r
1768             outJump = 1;\r
1769             out += (outChannels - channelsLeft) * outOffset;\r
1770           }\r
1771 \r
1772           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1773             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1774               out[j*outOffset] = *in++;\r
1775             }\r
1776             out += outJump;\r
1777             in += inJump;\r
1778           }\r
1779           channelsLeft -= streamChannels;\r
1780         }\r
1781       }\r
1782       \r
1783       if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
1784         convertBuffer( stream_.userBuffer[1],\r
1785                        stream_.deviceBuffer,\r
1786                        stream_.convertInfo[1] );\r
1787       }\r
1788     }\r
1789   }\r
1790 \r
1791  unlock:\r
1792   //MUTEX_UNLOCK( &stream_.mutex );\r
1793 \r
1794   RtApi::tickStreamTime();\r
1795   return SUCCESS;\r
1796 }\r
1797 \r
1798 const char* RtApiCore :: getErrorCode( OSStatus code )\r
1799 {\r
1800   switch( code ) {\r
1801 \r
1802   case kAudioHardwareNotRunningError:\r
1803     return "kAudioHardwareNotRunningError";\r
1804 \r
1805   case kAudioHardwareUnspecifiedError:\r
1806     return "kAudioHardwareUnspecifiedError";\r
1807 \r
1808   case kAudioHardwareUnknownPropertyError:\r
1809     return "kAudioHardwareUnknownPropertyError";\r
1810 \r
1811   case kAudioHardwareBadPropertySizeError:\r
1812     return "kAudioHardwareBadPropertySizeError";\r
1813 \r
1814   case kAudioHardwareIllegalOperationError:\r
1815     return "kAudioHardwareIllegalOperationError";\r
1816 \r
1817   case kAudioHardwareBadObjectError:\r
1818     return "kAudioHardwareBadObjectError";\r
1819 \r
1820   case kAudioHardwareBadDeviceError:\r
1821     return "kAudioHardwareBadDeviceError";\r
1822 \r
1823   case kAudioHardwareBadStreamError:\r
1824     return "kAudioHardwareBadStreamError";\r
1825 \r
1826   case kAudioHardwareUnsupportedOperationError:\r
1827     return "kAudioHardwareUnsupportedOperationError";\r
1828 \r
1829   case kAudioDeviceUnsupportedFormatError:\r
1830     return "kAudioDeviceUnsupportedFormatError";\r
1831 \r
1832   case kAudioDevicePermissionsError:\r
1833     return "kAudioDevicePermissionsError";\r
1834 \r
1835   default:\r
1836     return "CoreAudio unknown error";\r
1837   }\r
1838 }\r
1839 \r
1840   //******************** End of __MACOSX_CORE__ *********************//\r
1841 #endif\r
1842 \r
1843 #if defined(__UNIX_JACK__)\r
1844 \r
1845 // JACK is a low-latency audio server, originally written for the\r
1846 // GNU/Linux operating system and now also ported to OS-X. It can\r
1847 // connect a number of different applications to an audio device, as\r
1848 // well as allowing them to share audio between themselves.\r
1849 //\r
1850 // When using JACK with RtAudio, "devices" refer to JACK clients that\r
1851 // have ports connected to the server.  The JACK server is typically\r
1852 // started in a terminal as follows:\r
1853 //\r
1854 // .jackd -d alsa -d hw:0\r
1855 //\r
1856 // or through an interface program such as qjackctl.  Many of the\r
1857 // parameters normally set for a stream are fixed by the JACK server\r
1858 // and can be specified when the JACK server is started.  In\r
1859 // particular,\r
1860 //\r
1861 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4\r
1862 //\r
1863 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample\r
1864 // frames, and number of buffers = 4.  Once the server is running, it\r
1865 // is not possible to override these values.  If the values are not\r
1866 // specified in the command-line, the JACK server uses default values.\r
1867 //\r
1868 // The JACK server does not have to be running when an instance of\r
1869 // RtApiJack is created, though the function getDeviceCount() will\r
1870 // report 0 devices found until JACK has been started.  When no\r
1871 // devices are available (i.e., the JACK server is not running), a\r
1872 // stream cannot be opened.\r
1873 \r
1874 #include <jack/jack.h>\r
1875 #include <unistd.h>\r
1876 #include <cstdio>\r
1877 \r
1878 // A structure to hold various information related to the Jack API\r
1879 // implementation.\r
1880 struct JackHandle {\r
1881   jack_client_t *client;\r
1882   jack_port_t **ports[2];\r
1883   std::string deviceName[2];\r
1884   bool xrun[2];\r
1885   pthread_cond_t condition;\r
1886   int drainCounter;       // Tracks callback counts when draining\r
1887   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
1888 \r
1889   JackHandle()\r
1890     :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
1891 };\r
1892 \r
1893 static void jackSilentError( const char * ) {};\r
1894 \r
1895 RtApiJack :: RtApiJack()\r
1896 {\r
1897   // Nothing to do here.\r
1898 #if !defined(__RTAUDIO_DEBUG__)\r
1899   // Turn off Jack's internal error reporting.\r
1900   jack_set_error_function( &jackSilentError );\r
1901 #endif\r
1902 }\r
1903 \r
1904 RtApiJack :: ~RtApiJack()\r
1905 {\r
1906   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
1907 }\r
1908 \r
1909 unsigned int RtApiJack :: getDeviceCount( void )\r
1910 {\r
1911   // See if we can become a jack client.\r
1912   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
1913   jack_status_t *status = NULL;\r
1914   jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );\r
1915   if ( client == 0 ) return 0;\r
1916 \r
1917   const char **ports;\r
1918   std::string port, previousPort;\r
1919   unsigned int nChannels = 0, nDevices = 0;\r
1920   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1921   if ( ports ) {\r
1922     // Parse the port names up to the first colon (:).\r
1923     size_t iColon = 0;\r
1924     do {\r
1925       port = (char *) ports[ nChannels ];\r
1926       iColon = port.find(":");\r
1927       if ( iColon != std::string::npos ) {\r
1928         port = port.substr( 0, iColon + 1 );\r
1929         if ( port != previousPort ) {\r
1930           nDevices++;\r
1931           previousPort = port;\r
1932         }\r
1933       }\r
1934     } while ( ports[++nChannels] );\r
1935     free( ports );\r
1936   }\r
1937 \r
1938   jack_client_close( client );\r
1939   return nDevices;\r
1940 }\r
1941 \r
1942 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )\r
1943 {\r
1944   RtAudio::DeviceInfo info;\r
1945   info.probed = false;\r
1946 \r
1947   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption\r
1948   jack_status_t *status = NULL;\r
1949   jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );\r
1950   if ( client == 0 ) {\r
1951     errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";\r
1952     error( RtAudioError::WARNING );\r
1953     return info;\r
1954   }\r
1955 \r
1956   const char **ports;\r
1957   std::string port, previousPort;\r
1958   unsigned int nPorts = 0, nDevices = 0;\r
1959   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1960   if ( ports ) {\r
1961     // Parse the port names up to the first colon (:).\r
1962     size_t iColon = 0;\r
1963     do {\r
1964       port = (char *) ports[ nPorts ];\r
1965       iColon = port.find(":");\r
1966       if ( iColon != std::string::npos ) {\r
1967         port = port.substr( 0, iColon );\r
1968         if ( port != previousPort ) {\r
1969           if ( nDevices == device ) info.name = port;\r
1970           nDevices++;\r
1971           previousPort = port;\r
1972         }\r
1973       }\r
1974     } while ( ports[++nPorts] );\r
1975     free( ports );\r
1976   }\r
1977 \r
1978   if ( device >= nDevices ) {\r
1979     jack_client_close( client );\r
1980     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
1981     error( RtAudioError::INVALID_USE );\r
1982     return info;\r
1983   }\r
1984 \r
1985   // Get the current jack server sample rate.\r
1986   info.sampleRates.clear();\r
1987   info.sampleRates.push_back( jack_get_sample_rate( client ) );\r
1988 \r
1989   // Count the available ports containing the client name as device\r
1990   // channels.  Jack "input ports" equal RtAudio output channels.\r
1991   unsigned int nChannels = 0;\r
1992   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );\r
1993   if ( ports ) {\r
1994     while ( ports[ nChannels ] ) nChannels++;\r
1995     free( ports );\r
1996     info.outputChannels = nChannels;\r
1997   }\r
1998 \r
1999   // Jack "output ports" equal RtAudio input channels.\r
2000   nChannels = 0;\r
2001   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );\r
2002   if ( ports ) {\r
2003     while ( ports[ nChannels ] ) nChannels++;\r
2004     free( ports );\r
2005     info.inputChannels = nChannels;\r
2006   }\r
2007 \r
2008   if ( info.outputChannels == 0 && info.inputChannels == 0 ) {\r
2009     jack_client_close(client);\r
2010     errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";\r
2011     error( RtAudioError::WARNING );\r
2012     return info;\r
2013   }\r
2014 \r
2015   // If device opens for both playback and capture, we determine the channels.\r
2016   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2017     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2018 \r
2019   // Jack always uses 32-bit floats.\r
2020   info.nativeFormats = RTAUDIO_FLOAT32;\r
2021 \r
2022   // Jack doesn't provide default devices so we'll use the first available one.\r
2023   if ( device == 0 && info.outputChannels > 0 )\r
2024     info.isDefaultOutput = true;\r
2025   if ( device == 0 && info.inputChannels > 0 )\r
2026     info.isDefaultInput = true;\r
2027 \r
2028   jack_client_close(client);\r
2029   info.probed = true;\r
2030   return info;\r
2031 }\r
2032 \r
2033 static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
2034 {\r
2035   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2036 \r
2037   RtApiJack *object = (RtApiJack *) info->object;\r
2038   if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;\r
2039 \r
2040   return 0;\r
2041 }\r
2042 \r
2043 // This function will be called by a spawned thread when the Jack\r
2044 // server signals that it is shutting down.  It is necessary to handle\r
2045 // it this way because the jackShutdown() function must return before\r
2046 // the jack_deactivate() function (in closeStream()) will return.\r
2047 static void *jackCloseStream( void *ptr )\r
2048 {\r
2049   CallbackInfo *info = (CallbackInfo *) ptr;\r
2050   RtApiJack *object = (RtApiJack *) info->object;\r
2051 \r
2052   object->closeStream();\r
2053 \r
2054   pthread_exit( NULL );\r
2055 }\r
2056 static void jackShutdown( void *infoPointer )\r
2057 {\r
2058   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2059   RtApiJack *object = (RtApiJack *) info->object;\r
2060 \r
2061   // Check current stream state.  If stopped, then we'll assume this\r
2062   // was called as a result of a call to RtApiJack::stopStream (the\r
2063   // deactivation of a client handle causes this function to be called).\r
2064   // If not, we'll assume the Jack server is shutting down or some\r
2065   // other problem occurred and we should close the stream.\r
2066   if ( object->isStreamRunning() == false ) return;\r
2067 \r
2068   ThreadHandle threadId;\r
2069   pthread_create( &threadId, NULL, jackCloseStream, info );\r
2070   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
2071 }\r
2072 \r
2073 static int jackXrun( void *infoPointer )\r
2074 {\r
2075   JackHandle *handle = (JackHandle *) infoPointer;\r
2076 \r
2077   if ( handle->ports[0] ) handle->xrun[0] = true;\r
2078   if ( handle->ports[1] ) handle->xrun[1] = true;\r
2079 \r
2080   return 0;\r
2081 }\r
2082 \r
2083 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2084                                    unsigned int firstChannel, unsigned int sampleRate,\r
2085                                    RtAudioFormat format, unsigned int *bufferSize,\r
2086                                    RtAudio::StreamOptions *options )\r
2087 {\r
2088   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2089 \r
2090   // Look for jack server and try to become a client (only do once per stream).\r
2091   jack_client_t *client = 0;\r
2092   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {\r
2093     jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
2094     jack_status_t *status = NULL;\r
2095     if ( options && !options->streamName.empty() )\r
2096       client = jack_client_open( options->streamName.c_str(), jackoptions, status );\r
2097     else\r
2098       client = jack_client_open( "RtApiJack", jackoptions, status );\r
2099     if ( client == 0 ) {\r
2100       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";\r
2101       error( RtAudioError::WARNING );\r
2102       return FAILURE;\r
2103     }\r
2104   }\r
2105   else {\r
2106     // The handle must have been created on an earlier pass.\r
2107     client = handle->client;\r
2108   }\r
2109 \r
2110   const char **ports;\r
2111   std::string port, previousPort, deviceName;\r
2112   unsigned int nPorts = 0, nDevices = 0;\r
2113   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2114   if ( ports ) {\r
2115     // Parse the port names up to the first colon (:).\r
2116     size_t iColon = 0;\r
2117     do {\r
2118       port = (char *) ports[ nPorts ];\r
2119       iColon = port.find(":");\r
2120       if ( iColon != std::string::npos ) {\r
2121         port = port.substr( 0, iColon );\r
2122         if ( port != previousPort ) {\r
2123           if ( nDevices == device ) deviceName = port;\r
2124           nDevices++;\r
2125           previousPort = port;\r
2126         }\r
2127       }\r
2128     } while ( ports[++nPorts] );\r
2129     free( ports );\r
2130   }\r
2131 \r
2132   if ( device >= nDevices ) {\r
2133     errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";\r
2134     return FAILURE;\r
2135   }\r
2136 \r
2137   // Count the available ports containing the client name as device\r
2138   // channels.  Jack "input ports" equal RtAudio output channels.\r
2139   unsigned int nChannels = 0;\r
2140   unsigned long flag = JackPortIsInput;\r
2141   if ( mode == INPUT ) flag = JackPortIsOutput;\r
2142   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2143   if ( ports ) {\r
2144     while ( ports[ nChannels ] ) nChannels++;\r
2145     free( ports );\r
2146   }\r
2147 \r
2148   // Compare the jack ports for specified client to the requested number of channels.\r
2149   if ( nChannels < (channels + firstChannel) ) {\r
2150     errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";\r
2151     errorText_ = errorStream_.str();\r
2152     return FAILURE;\r
2153   }\r
2154 \r
2155   // Check the jack server sample rate.\r
2156   unsigned int jackRate = jack_get_sample_rate( client );\r
2157   if ( sampleRate != jackRate ) {\r
2158     jack_client_close( client );\r
2159     errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";\r
2160     errorText_ = errorStream_.str();\r
2161     return FAILURE;\r
2162   }\r
2163   stream_.sampleRate = jackRate;\r
2164 \r
2165   // Get the latency of the JACK port.\r
2166   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2167   if ( ports[ firstChannel ] ) {\r
2168     // Added by Ge Wang\r
2169     jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
2170     // the range (usually the min and max are equal)\r
2171     jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
2172     // get the latency range\r
2173     jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
2174     // be optimistic, use the min!\r
2175     stream_.latency[mode] = latrange.min;\r
2176     //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
2177   }\r
2178   free( ports );\r
2179 \r
2180   // The jack server always uses 32-bit floating-point data.\r
2181   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2182   stream_.userFormat = format;\r
2183 \r
2184   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
2185   else stream_.userInterleaved = true;\r
2186 \r
2187   // Jack always uses non-interleaved buffers.\r
2188   stream_.deviceInterleaved[mode] = false;\r
2189 \r
2190   // Jack always provides host byte-ordered data.\r
2191   stream_.doByteSwap[mode] = false;\r
2192 \r
2193   // Get the buffer size.  The buffer size and number of buffers\r
2194   // (periods) is set when the jack server is started.\r
2195   stream_.bufferSize = (int) jack_get_buffer_size( client );\r
2196   *bufferSize = stream_.bufferSize;\r
2197 \r
2198   stream_.nDeviceChannels[mode] = channels;\r
2199   stream_.nUserChannels[mode] = channels;\r
2200 \r
2201   // Set flags for buffer conversion.\r
2202   stream_.doConvertBuffer[mode] = false;\r
2203   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
2204     stream_.doConvertBuffer[mode] = true;\r
2205   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
2206        stream_.nUserChannels[mode] > 1 )\r
2207     stream_.doConvertBuffer[mode] = true;\r
2208 \r
2209   // Allocate our JackHandle structure for the stream.\r
2210   if ( handle == 0 ) {\r
2211     try {\r
2212       handle = new JackHandle;\r
2213     }\r
2214     catch ( std::bad_alloc& ) {\r
2215       errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";\r
2216       goto error;\r
2217     }\r
2218 \r
2219     if ( pthread_cond_init(&handle->condition, NULL) ) {\r
2220       errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";\r
2221       goto error;\r
2222     }\r
2223     stream_.apiHandle = (void *) handle;\r
2224     handle->client = client;\r
2225   }\r
2226   handle->deviceName[mode] = deviceName;\r
2227 \r
2228   // Allocate necessary internal buffers.\r
2229   unsigned long bufferBytes;\r
2230   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
2231   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
2232   if ( stream_.userBuffer[mode] == NULL ) {\r
2233     errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";\r
2234     goto error;\r
2235   }\r
2236 \r
2237   if ( stream_.doConvertBuffer[mode] ) {\r
2238 \r
2239     bool makeBuffer = true;\r
2240     if ( mode == OUTPUT )\r
2241       bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
2242     else { // mode == INPUT\r
2243       bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );\r
2244       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
2245         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);\r
2246         if ( bufferBytes < bytesOut ) makeBuffer = false;\r
2247       }\r
2248     }\r
2249 \r
2250     if ( makeBuffer ) {\r
2251       bufferBytes *= *bufferSize;\r
2252       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
2253       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
2254       if ( stream_.deviceBuffer == NULL ) {\r
2255         errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";\r
2256         goto error;\r
2257       }\r
2258     }\r
2259   }\r
2260 \r
2261   // Allocate memory for the Jack ports (channels) identifiers.\r
2262   handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );\r
2263   if ( handle->ports[mode] == NULL )  {\r
2264     errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";\r
2265     goto error;\r
2266   }\r
2267 \r
2268   stream_.device[mode] = device;\r
2269   stream_.channelOffset[mode] = firstChannel;\r
2270   stream_.state = STREAM_STOPPED;\r
2271   stream_.callbackInfo.object = (void *) this;\r
2272 \r
2273   if ( stream_.mode == OUTPUT && mode == INPUT )\r
2274     // We had already set up the stream for output.\r
2275     stream_.mode = DUPLEX;\r
2276   else {\r
2277     stream_.mode = mode;\r
2278     jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );\r
2279     jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );\r
2280     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );\r
2281   }\r
2282 \r
2283   // Register our ports.\r
2284   char label[64];\r
2285   if ( mode == OUTPUT ) {\r
2286     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2287       snprintf( label, 64, "outport %d", i );\r
2288       handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,\r
2289                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );\r
2290     }\r
2291   }\r
2292   else {\r
2293     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2294       snprintf( label, 64, "inport %d", i );\r
2295       handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,\r
2296                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );\r
2297     }\r
2298   }\r
2299 \r
2300   // Setup the buffer conversion information structure.  We don't use\r
2301   // buffers to do channel offsets, so we override that parameter\r
2302   // here.\r
2303   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
2304 \r
2305   return SUCCESS;\r
2306 \r
2307  error:\r
2308   if ( handle ) {\r
2309     pthread_cond_destroy( &handle->condition );\r
2310     jack_client_close( handle->client );\r
2311 \r
2312     if ( handle->ports[0] ) free( handle->ports[0] );\r
2313     if ( handle->ports[1] ) free( handle->ports[1] );\r
2314 \r
2315     delete handle;\r
2316     stream_.apiHandle = 0;\r
2317   }\r
2318 \r
2319   for ( int i=0; i<2; i++ ) {\r
2320     if ( stream_.userBuffer[i] ) {\r
2321       free( stream_.userBuffer[i] );\r
2322       stream_.userBuffer[i] = 0;\r
2323     }\r
2324   }\r
2325 \r
2326   if ( stream_.deviceBuffer ) {\r
2327     free( stream_.deviceBuffer );\r
2328     stream_.deviceBuffer = 0;\r
2329   }\r
2330 \r
2331   return FAILURE;\r
2332 }\r
2333 \r
2334 void RtApiJack :: closeStream( void )\r
2335 {\r
2336   if ( stream_.state == STREAM_CLOSED ) {\r
2337     errorText_ = "RtApiJack::closeStream(): no open stream to close!";\r
2338     error( RtAudioError::WARNING );\r
2339     return;\r
2340   }\r
2341 \r
2342   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2343   if ( handle ) {\r
2344 \r
2345     if ( stream_.state == STREAM_RUNNING )\r
2346       jack_deactivate( handle->client );\r
2347 \r
2348     jack_client_close( handle->client );\r
2349   }\r
2350 \r
2351   if ( handle ) {\r
2352     if ( handle->ports[0] ) free( handle->ports[0] );\r
2353     if ( handle->ports[1] ) free( handle->ports[1] );\r
2354     pthread_cond_destroy( &handle->condition );\r
2355     delete handle;\r
2356     stream_.apiHandle = 0;\r
2357   }\r
2358 \r
2359   for ( int i=0; i<2; i++ ) {\r
2360     if ( stream_.userBuffer[i] ) {\r
2361       free( stream_.userBuffer[i] );\r
2362       stream_.userBuffer[i] = 0;\r
2363     }\r
2364   }\r
2365 \r
2366   if ( stream_.deviceBuffer ) {\r
2367     free( stream_.deviceBuffer );\r
2368     stream_.deviceBuffer = 0;\r
2369   }\r
2370 \r
2371   stream_.mode = UNINITIALIZED;\r
2372   stream_.state = STREAM_CLOSED;\r
2373 }\r
2374 \r
2375 void RtApiJack :: startStream( void )\r
2376 {\r
2377   verifyStream();\r
2378   if ( stream_.state == STREAM_RUNNING ) {\r
2379     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
2380     error( RtAudioError::WARNING );\r
2381     return;\r
2382   }\r
2383 \r
2384   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2385   int result = jack_activate( handle->client );\r
2386   if ( result ) {\r
2387     errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";\r
2388     goto unlock;\r
2389   }\r
2390 \r
2391   const char **ports;\r
2392 \r
2393   // Get the list of available ports.\r
2394   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2395     result = 1;\r
2396     ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);\r
2397     if ( ports == NULL) {\r
2398       errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";\r
2399       goto unlock;\r
2400     }\r
2401 \r
2402     // Now make the port connections.  Since RtAudio wasn't designed to\r
2403     // allow the user to select particular channels of a device, we'll\r
2404     // just open the first "nChannels" ports with offset.\r
2405     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2406       result = 1;\r
2407       if ( ports[ stream_.channelOffset[0] + i ] )\r
2408         result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );\r
2409       if ( result ) {\r
2410         free( ports );\r
2411         errorText_ = "RtApiJack::startStream(): error connecting output ports!";\r
2412         goto unlock;\r
2413       }\r
2414     }\r
2415     free(ports);\r
2416   }\r
2417 \r
2418   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2419     result = 1;\r
2420     ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );\r
2421     if ( ports == NULL) {\r
2422       errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";\r
2423       goto unlock;\r
2424     }\r
2425 \r
2426     // Now make the port connections.  See note above.\r
2427     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2428       result = 1;\r
2429       if ( ports[ stream_.channelOffset[1] + i ] )\r
2430         result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );\r
2431       if ( result ) {\r
2432         free( ports );\r
2433         errorText_ = "RtApiJack::startStream(): error connecting input ports!";\r
2434         goto unlock;\r
2435       }\r
2436     }\r
2437     free(ports);\r
2438   }\r
2439 \r
2440   handle->drainCounter = 0;\r
2441   handle->internalDrain = false;\r
2442   stream_.state = STREAM_RUNNING;\r
2443 \r
2444  unlock:\r
2445   if ( result == 0 ) return;\r
2446   error( RtAudioError::SYSTEM_ERROR );\r
2447 }\r
2448 \r
2449 void RtApiJack :: stopStream( void )\r
2450 {\r
2451   verifyStream();\r
2452   if ( stream_.state == STREAM_STOPPED ) {\r
2453     errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";\r
2454     error( RtAudioError::WARNING );\r
2455     return;\r
2456   }\r
2457 \r
2458   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2459   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2460 \r
2461     if ( handle->drainCounter == 0 ) {\r
2462       handle->drainCounter = 2;\r
2463       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
2464     }\r
2465   }\r
2466 \r
2467   jack_deactivate( handle->client );\r
2468   stream_.state = STREAM_STOPPED;\r
2469 }\r
2470 \r
2471 void RtApiJack :: abortStream( void )\r
2472 {\r
2473   verifyStream();\r
2474   if ( stream_.state == STREAM_STOPPED ) {\r
2475     errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";\r
2476     error( RtAudioError::WARNING );\r
2477     return;\r
2478   }\r
2479 \r
2480   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2481   handle->drainCounter = 2;\r
2482 \r
2483   stopStream();\r
2484 }\r
2485 \r
2486 // This function will be called by a spawned thread when the user\r
2487 // callback function signals that the stream should be stopped or\r
2488 // aborted.  It is necessary to handle it this way because the\r
2489 // callbackEvent() function must return before the jack_deactivate()\r
2490 // function will return.\r
2491 static void *jackStopStream( void *ptr )\r
2492 {\r
2493   CallbackInfo *info = (CallbackInfo *) ptr;\r
2494   RtApiJack *object = (RtApiJack *) info->object;\r
2495 \r
2496   object->stopStream();\r
2497   pthread_exit( NULL );\r
2498 }\r
2499 \r
2500 bool RtApiJack :: callbackEvent( unsigned long nframes )\r
2501 {\r
2502   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
2503   if ( stream_.state == STREAM_CLOSED ) {\r
2504     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
2505     error( RtAudioError::WARNING );\r
2506     return FAILURE;\r
2507   }\r
2508   if ( stream_.bufferSize != nframes ) {\r
2509     errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";\r
2510     error( RtAudioError::WARNING );\r
2511     return FAILURE;\r
2512   }\r
2513 \r
2514   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
2515   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2516 \r
2517   // Check if we were draining the stream and signal is finished.\r
2518   if ( handle->drainCounter > 3 ) {\r
2519     ThreadHandle threadId;\r
2520 \r
2521     stream_.state = STREAM_STOPPING;\r
2522     if ( handle->internalDrain == true )\r
2523       pthread_create( &threadId, NULL, jackStopStream, info );\r
2524     else\r
2525       pthread_cond_signal( &handle->condition );\r
2526     return SUCCESS;\r
2527   }\r
2528 \r
2529   // Invoke user callback first, to get fresh output data.\r
2530   if ( handle->drainCounter == 0 ) {\r
2531     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
2532     double streamTime = getStreamTime();\r
2533     RtAudioStreamStatus status = 0;\r
2534     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
2535       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
2536       handle->xrun[0] = false;\r
2537     }\r
2538     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
2539       status |= RTAUDIO_INPUT_OVERFLOW;\r
2540       handle->xrun[1] = false;\r
2541     }\r
2542     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
2543                                   stream_.bufferSize, streamTime, status, info->userData );\r
2544     if ( cbReturnValue == 2 ) {\r
2545       stream_.state = STREAM_STOPPING;\r
2546       handle->drainCounter = 2;\r
2547       ThreadHandle id;\r
2548       pthread_create( &id, NULL, jackStopStream, info );\r
2549       return SUCCESS;\r
2550     }\r
2551     else if ( cbReturnValue == 1 ) {\r
2552       handle->drainCounter = 1;\r
2553       handle->internalDrain = true;\r
2554     }\r
2555   }\r
2556 \r
2557   jack_default_audio_sample_t *jackbuffer;\r
2558   unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );\r
2559   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2560 \r
2561     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
2562 \r
2563       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2564         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2565         memset( jackbuffer, 0, bufferBytes );\r
2566       }\r
2567 \r
2568     }\r
2569     else if ( stream_.doConvertBuffer[0] ) {\r
2570 \r
2571       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
2572 \r
2573       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2574         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2575         memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );\r
2576       }\r
2577     }\r
2578     else { // no buffer conversion\r
2579       for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2580         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2581         memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
2582       }\r
2583     }\r
2584   }\r
2585 \r
2586   // Don't bother draining input\r
2587   if ( handle->drainCounter ) {\r
2588     handle->drainCounter++;\r
2589     goto unlock;\r
2590   }\r
2591 \r
2592   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2593 \r
2594     if ( stream_.doConvertBuffer[1] ) {\r
2595       for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {\r
2596         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2597         memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );\r
2598       }\r
2599       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
2600     }\r
2601     else { // no buffer conversion\r
2602       for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2603         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2604         memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );\r
2605       }\r
2606     }\r
2607   }\r
2608 \r
2609  unlock:\r
2610   RtApi::tickStreamTime();\r
2611   return SUCCESS;\r
2612 }\r
2613   //******************** End of __UNIX_JACK__ *********************//\r
2614 #endif\r
2615 \r
2616 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows\r
2617 \r
2618 // The ASIO API is designed around a callback scheme, so this\r
2619 // implementation is similar to that used for OS-X CoreAudio and Linux\r
2620 // Jack.  The primary constraint with ASIO is that it only allows\r
2621 // access to a single driver at a time.  Thus, it is not possible to\r
2622 // have more than one simultaneous RtAudio stream.\r
2623 //\r
2624 // This implementation also requires a number of external ASIO files\r
2625 // and a few global variables.  The ASIO callback scheme does not\r
2626 // allow for the passing of user data, so we must create a global\r
2627 // pointer to our callbackInfo structure.\r
2628 //\r
2629 // On unix systems, we make use of a pthread condition variable.\r
2630 // Since there is no equivalent in Windows, I hacked something based\r
2631 // on information found in\r
2632 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r
2633 \r
2634 #include "asiosys.h"\r
2635 #include "asio.h"\r
2636 #include "iasiothiscallresolver.h"\r
2637 #include "asiodrivers.h"\r
2638 #include <cmath>\r
2639 \r
2640 static AsioDrivers drivers;\r
2641 static ASIOCallbacks asioCallbacks;\r
2642 static ASIODriverInfo driverInfo;\r
2643 static CallbackInfo *asioCallbackInfo;\r
2644 static bool asioXRun;\r
2645 \r
2646 struct AsioHandle {\r
2647   int drainCounter;       // Tracks callback counts when draining\r
2648   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
2649   ASIOBufferInfo *bufferInfos;\r
2650   HANDLE condition;\r
2651 \r
2652   AsioHandle()\r
2653     :drainCounter(0), internalDrain(false), bufferInfos(0) {}\r
2654 };\r
2655 \r
2656 // Function declarations (definitions at end of section)\r
2657 static const char* getAsioErrorString( ASIOError result );\r
2658 static void sampleRateChanged( ASIOSampleRate sRate );\r
2659 static long asioMessages( long selector, long value, void* message, double* opt );\r
2660 \r
2661 RtApiAsio :: RtApiAsio()\r
2662 {\r
2663   // ASIO cannot run on a multi-threaded appartment. You can call\r
2664   // CoInitialize beforehand, but it must be for appartment threading\r
2665   // (in which case, CoInitilialize will return S_FALSE here).\r
2666   coInitialized_ = false;\r
2667   HRESULT hr = CoInitialize( NULL ); \r
2668   if ( FAILED(hr) ) {\r
2669     errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
2670     error( RtAudioError::WARNING );\r
2671   }\r
2672   coInitialized_ = true;\r
2673 \r
2674   drivers.removeCurrentDriver();\r
2675   driverInfo.asioVersion = 2;\r
2676 \r
2677   // See note in DirectSound implementation about GetDesktopWindow().\r
2678   driverInfo.sysRef = GetForegroundWindow();\r
2679 }\r
2680 \r
2681 RtApiAsio :: ~RtApiAsio()\r
2682 {\r
2683   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
2684   if ( coInitialized_ ) CoUninitialize();\r
2685 }\r
2686 \r
2687 unsigned int RtApiAsio :: getDeviceCount( void )\r
2688 {\r
2689   return (unsigned int) drivers.asioGetNumDev();\r
2690 }\r
2691 \r
2692 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )\r
2693 {\r
2694   RtAudio::DeviceInfo info;\r
2695   info.probed = false;\r
2696 \r
2697   // Get device ID\r
2698   unsigned int nDevices = getDeviceCount();\r
2699   if ( nDevices == 0 ) {\r
2700     errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
2701     error( RtAudioError::INVALID_USE );\r
2702     return info;\r
2703   }\r
2704 \r
2705   if ( device >= nDevices ) {\r
2706     errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
2707     error( RtAudioError::INVALID_USE );\r
2708     return info;\r
2709   }\r
2710 \r
2711   // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.\r
2712   if ( stream_.state != STREAM_CLOSED ) {\r
2713     if ( device >= devices_.size() ) {\r
2714       errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";\r
2715       error( RtAudioError::WARNING );\r
2716       return info;\r
2717     }\r
2718     return devices_[ device ];\r
2719   }\r
2720 \r
2721   char driverName[32];\r
2722   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2723   if ( result != ASE_OK ) {\r
2724     errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2725     errorText_ = errorStream_.str();\r
2726     error( RtAudioError::WARNING );\r
2727     return info;\r
2728   }\r
2729 \r
2730   info.name = driverName;\r
2731 \r
2732   if ( !drivers.loadDriver( driverName ) ) {\r
2733     errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";\r
2734     errorText_ = errorStream_.str();\r
2735     error( RtAudioError::WARNING );\r
2736     return info;\r
2737   }\r
2738 \r
2739   result = ASIOInit( &driverInfo );\r
2740   if ( result != ASE_OK ) {\r
2741     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2742     errorText_ = errorStream_.str();\r
2743     error( RtAudioError::WARNING );\r
2744     return info;\r
2745   }\r
2746 \r
2747   // Determine the device channel information.\r
2748   long inputChannels, outputChannels;\r
2749   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2750   if ( result != ASE_OK ) {\r
2751     drivers.removeCurrentDriver();\r
2752     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2753     errorText_ = errorStream_.str();\r
2754     error( RtAudioError::WARNING );\r
2755     return info;\r
2756   }\r
2757 \r
2758   info.outputChannels = outputChannels;\r
2759   info.inputChannels = inputChannels;\r
2760   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2761     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2762 \r
2763   // Determine the supported sample rates.\r
2764   info.sampleRates.clear();\r
2765   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
2766     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
2767     if ( result == ASE_OK )\r
2768       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
2769   }\r
2770 \r
2771   // Determine supported data types ... just check first channel and assume rest are the same.\r
2772   ASIOChannelInfo channelInfo;\r
2773   channelInfo.channel = 0;\r
2774   channelInfo.isInput = true;\r
2775   if ( info.inputChannels <= 0 ) channelInfo.isInput = false;\r
2776   result = ASIOGetChannelInfo( &channelInfo );\r
2777   if ( result != ASE_OK ) {\r
2778     drivers.removeCurrentDriver();\r
2779     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";\r
2780     errorText_ = errorStream_.str();\r
2781     error( RtAudioError::WARNING );\r
2782     return info;\r
2783   }\r
2784 \r
2785   info.nativeFormats = 0;\r
2786   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )\r
2787     info.nativeFormats |= RTAUDIO_SINT16;\r
2788   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )\r
2789     info.nativeFormats |= RTAUDIO_SINT32;\r
2790   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )\r
2791     info.nativeFormats |= RTAUDIO_FLOAT32;\r
2792   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
2793     info.nativeFormats |= RTAUDIO_FLOAT64;\r
2794   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
2795     info.nativeFormats |= RTAUDIO_SINT24;\r
2796 \r
2797   if ( info.outputChannels > 0 )\r
2798     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
2799   if ( info.inputChannels > 0 )\r
2800     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
2801 \r
2802   info.probed = true;\r
2803   drivers.removeCurrentDriver();\r
2804   return info;\r
2805 }\r
2806 \r
2807 static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
2808 {\r
2809   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
2810   object->callbackEvent( index );\r
2811 }\r
2812 \r
2813 void RtApiAsio :: saveDeviceInfo( void )\r
2814 {\r
2815   devices_.clear();\r
2816 \r
2817   unsigned int nDevices = getDeviceCount();\r
2818   devices_.resize( nDevices );\r
2819   for ( unsigned int i=0; i<nDevices; i++ )\r
2820     devices_[i] = getDeviceInfo( i );\r
2821 }\r
2822 \r
2823 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2824                                    unsigned int firstChannel, unsigned int sampleRate,\r
2825                                    RtAudioFormat format, unsigned int *bufferSize,\r
2826                                    RtAudio::StreamOptions *options )\r
2827 {\r
2828   // For ASIO, a duplex stream MUST use the same driver.\r
2829   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {\r
2830     errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
2831     return FAILURE;\r
2832   }\r
2833 \r
2834   char driverName[32];\r
2835   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2836   if ( result != ASE_OK ) {\r
2837     errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2838     errorText_ = errorStream_.str();\r
2839     return FAILURE;\r
2840   }\r
2841 \r
2842   // Only load the driver once for duplex stream.\r
2843   if ( mode != INPUT || stream_.mode != OUTPUT ) {\r
2844     // The getDeviceInfo() function will not work when a stream is open\r
2845     // because ASIO does not allow multiple devices to run at the same\r
2846     // time.  Thus, we'll probe the system before opening a stream and\r
2847     // save the results for use by getDeviceInfo().\r
2848     this->saveDeviceInfo();\r
2849 \r
2850     if ( !drivers.loadDriver( driverName ) ) {\r
2851       errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
2852       errorText_ = errorStream_.str();\r
2853       return FAILURE;\r
2854     }\r
2855 \r
2856     result = ASIOInit( &driverInfo );\r
2857     if ( result != ASE_OK ) {\r
2858       errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2859       errorText_ = errorStream_.str();\r
2860       return FAILURE;\r
2861     }\r
2862   }\r
2863 \r
2864   // Check the device channel count.\r
2865   long inputChannels, outputChannels;\r
2866   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2867   if ( result != ASE_OK ) {\r
2868     drivers.removeCurrentDriver();\r
2869     errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2870     errorText_ = errorStream_.str();\r
2871     return FAILURE;\r
2872   }\r
2873 \r
2874   if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
2875        ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
2876     drivers.removeCurrentDriver();\r
2877     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
2878     errorText_ = errorStream_.str();\r
2879     return FAILURE;\r
2880   }\r
2881   stream_.nDeviceChannels[mode] = channels;\r
2882   stream_.nUserChannels[mode] = channels;\r
2883   stream_.channelOffset[mode] = firstChannel;\r
2884 \r
2885   // Verify the sample rate is supported.\r
2886   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
2887   if ( result != ASE_OK ) {\r
2888     drivers.removeCurrentDriver();\r
2889     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
2890     errorText_ = errorStream_.str();\r
2891     return FAILURE;\r
2892   }\r
2893 \r
2894   // Get the current sample rate\r
2895   ASIOSampleRate currentRate;\r
2896   result = ASIOGetSampleRate( &currentRate );\r
2897   if ( result != ASE_OK ) {\r
2898     drivers.removeCurrentDriver();\r
2899     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
2900     errorText_ = errorStream_.str();\r
2901     return FAILURE;\r
2902   }\r
2903 \r
2904   // Set the sample rate only if necessary\r
2905   if ( currentRate != sampleRate ) {\r
2906     result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
2907     if ( result != ASE_OK ) {\r
2908       drivers.removeCurrentDriver();\r
2909       errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
2910       errorText_ = errorStream_.str();\r
2911       return FAILURE;\r
2912     }\r
2913   }\r
2914 \r
2915   // Determine the driver data type.\r
2916   ASIOChannelInfo channelInfo;\r
2917   channelInfo.channel = 0;\r
2918   if ( mode == OUTPUT ) channelInfo.isInput = false;\r
2919   else channelInfo.isInput = true;\r
2920   result = ASIOGetChannelInfo( &channelInfo );\r
2921   if ( result != ASE_OK ) {\r
2922     drivers.removeCurrentDriver();\r
2923     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
2924     errorText_ = errorStream_.str();\r
2925     return FAILURE;\r
2926   }\r
2927 \r
2928   // Assuming WINDOWS host is always little-endian.\r
2929   stream_.doByteSwap[mode] = false;\r
2930   stream_.userFormat = format;\r
2931   stream_.deviceFormat[mode] = 0;\r
2932   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {\r
2933     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
2934     if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;\r
2935   }\r
2936   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {\r
2937     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
2938     if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;\r
2939   }\r
2940   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {\r
2941     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2942     if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;\r
2943   }\r
2944   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {\r
2945     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
2946     if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
2947   }\r
2948   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
2949     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
2950     if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
2951   }\r
2952 \r
2953   if ( stream_.deviceFormat[mode] == 0 ) {\r
2954     drivers.removeCurrentDriver();\r
2955     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
2956     errorText_ = errorStream_.str();\r
2957     return FAILURE;\r
2958   }\r
2959 \r
2960   // Set the buffer size.  For a duplex stream, this will end up\r
2961   // setting the buffer size based on the input constraints, which\r
2962   // should be ok.\r
2963   long minSize, maxSize, preferSize, granularity;\r
2964   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
2965   if ( result != ASE_OK ) {\r
2966     drivers.removeCurrentDriver();\r
2967     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
2968     errorText_ = errorStream_.str();\r
2969     return FAILURE;\r
2970   }\r
2971 \r
2972   if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
2973   else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
2974   else if ( granularity == -1 ) {\r
2975     // Make sure bufferSize is a power of two.\r
2976     int log2_of_min_size = 0;\r
2977     int log2_of_max_size = 0;\r
2978 \r
2979     for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
2980       if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
2981       if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
2982     }\r
2983 \r
2984     long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
2985     int min_delta_num = log2_of_min_size;\r
2986 \r
2987     for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
2988       long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
2989       if (current_delta < min_delta) {\r
2990         min_delta = current_delta;\r
2991         min_delta_num = i;\r
2992       }\r
2993     }\r
2994 \r
2995     *bufferSize = ( (unsigned int)1 << min_delta_num );\r
2996     if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
2997     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
2998   }\r
2999   else if ( granularity != 0 ) {\r
3000     // Set to an even multiple of granularity, rounding up.\r
3001     *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
3002   }\r
3003 \r
3004   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {\r
3005     drivers.removeCurrentDriver();\r
3006     errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
3007     return FAILURE;\r
3008   }\r
3009 \r
3010   stream_.bufferSize = *bufferSize;\r
3011   stream_.nBuffers = 2;\r
3012 \r
3013   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
3014   else stream_.userInterleaved = true;\r
3015 \r
3016   // ASIO always uses non-interleaved buffers.\r
3017   stream_.deviceInterleaved[mode] = false;\r
3018 \r
3019   // Allocate, if necessary, our AsioHandle structure for the stream.\r
3020   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3021   if ( handle == 0 ) {\r
3022     try {\r
3023       handle = new AsioHandle;\r
3024     }\r
3025     catch ( std::bad_alloc& ) {\r
3026       //if ( handle == NULL ) {    \r
3027       drivers.removeCurrentDriver();\r
3028       errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
3029       return FAILURE;\r
3030     }\r
3031     handle->bufferInfos = 0;\r
3032 \r
3033     // Create a manual-reset event.\r
3034     handle->condition = CreateEvent( NULL,   // no security\r
3035                                      TRUE,   // manual-reset\r
3036                                      FALSE,  // non-signaled initially\r
3037                                      NULL ); // unnamed\r
3038     stream_.apiHandle = (void *) handle;\r
3039   }\r
3040 \r
3041   // Create the ASIO internal buffers.  Since RtAudio sets up input\r
3042   // and output separately, we'll have to dispose of previously\r
3043   // created output buffers for a duplex stream.\r
3044   long inputLatency, outputLatency;\r
3045   if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
3046     ASIODisposeBuffers();\r
3047     if ( handle->bufferInfos ) free( handle->bufferInfos );\r
3048   }\r
3049 \r
3050   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
3051   bool buffersAllocated = false;\r
3052   unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3053   handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
3054   if ( handle->bufferInfos == NULL ) {\r
3055     errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
3056     errorText_ = errorStream_.str();\r
3057     goto error;\r
3058   }\r
3059 \r
3060   ASIOBufferInfo *infos;\r
3061   infos = handle->bufferInfos;\r
3062   for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {\r
3063     infos->isInput = ASIOFalse;\r
3064     infos->channelNum = i + stream_.channelOffset[0];\r
3065     infos->buffers[0] = infos->buffers[1] = 0;\r
3066   }\r
3067   for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {\r
3068     infos->isInput = ASIOTrue;\r
3069     infos->channelNum = i + stream_.channelOffset[1];\r
3070     infos->buffers[0] = infos->buffers[1] = 0;\r
3071   }\r
3072 \r
3073   // Set up the ASIO callback structure and create the ASIO data buffers.\r
3074   asioCallbacks.bufferSwitch = &bufferSwitch;\r
3075   asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
3076   asioCallbacks.asioMessage = &asioMessages;\r
3077   asioCallbacks.bufferSwitchTimeInfo = NULL;\r
3078   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3079   if ( result != ASE_OK ) {\r
3080     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
3081     errorText_ = errorStream_.str();\r
3082     goto error;\r
3083   }\r
3084   buffersAllocated = true;\r
3085 \r
3086   // Set flags for buffer conversion.\r
3087   stream_.doConvertBuffer[mode] = false;\r
3088   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
3089     stream_.doConvertBuffer[mode] = true;\r
3090   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
3091        stream_.nUserChannels[mode] > 1 )\r
3092     stream_.doConvertBuffer[mode] = true;\r
3093 \r
3094   // Allocate necessary internal buffers\r
3095   unsigned long bufferBytes;\r
3096   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
3097   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
3098   if ( stream_.userBuffer[mode] == NULL ) {\r
3099     errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";\r
3100     goto error;\r
3101   }\r
3102 \r
3103   if ( stream_.doConvertBuffer[mode] ) {\r
3104 \r
3105     bool makeBuffer = true;\r
3106     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
3107     if ( mode == INPUT ) {\r
3108       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
3109         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
3110         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
3111       }\r
3112     }\r
3113 \r
3114     if ( makeBuffer ) {\r
3115       bufferBytes *= *bufferSize;\r
3116       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
3117       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
3118       if ( stream_.deviceBuffer == NULL ) {\r
3119         errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";\r
3120         goto error;\r
3121       }\r
3122     }\r
3123   }\r
3124 \r
3125   stream_.sampleRate = sampleRate;\r
3126   stream_.device[mode] = device;\r
3127   stream_.state = STREAM_STOPPED;\r
3128   asioCallbackInfo = &stream_.callbackInfo;\r
3129   stream_.callbackInfo.object = (void *) this;\r
3130   if ( stream_.mode == OUTPUT && mode == INPUT )\r
3131     // We had already set up an output stream.\r
3132     stream_.mode = DUPLEX;\r
3133   else\r
3134     stream_.mode = mode;\r
3135 \r
3136   // Determine device latencies\r
3137   result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
3138   if ( result != ASE_OK ) {\r
3139     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
3140     errorText_ = errorStream_.str();\r
3141     error( RtAudioError::WARNING); // warn but don't fail\r
3142   }\r
3143   else {\r
3144     stream_.latency[0] = outputLatency;\r
3145     stream_.latency[1] = inputLatency;\r
3146   }\r
3147 \r
3148   // Setup the buffer conversion information structure.  We don't use\r
3149   // buffers to do channel offsets, so we override that parameter\r
3150   // here.\r
3151   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
3152 \r
3153   return SUCCESS;\r
3154 \r
3155  error:\r
3156   if ( buffersAllocated )\r
3157     ASIODisposeBuffers();\r
3158   drivers.removeCurrentDriver();\r
3159 \r
3160   if ( handle ) {\r
3161     CloseHandle( handle->condition );\r
3162     if ( handle->bufferInfos )\r
3163       free( handle->bufferInfos );\r
3164     delete handle;\r
3165     stream_.apiHandle = 0;\r
3166   }\r
3167 \r
3168   for ( int i=0; i<2; i++ ) {\r
3169     if ( stream_.userBuffer[i] ) {\r
3170       free( stream_.userBuffer[i] );\r
3171       stream_.userBuffer[i] = 0;\r
3172     }\r
3173   }\r
3174 \r
3175   if ( stream_.deviceBuffer ) {\r
3176     free( stream_.deviceBuffer );\r
3177     stream_.deviceBuffer = 0;\r
3178   }\r
3179 \r
3180   return FAILURE;\r
3181 }\r
3182 \r
3183 void RtApiAsio :: closeStream()\r
3184 {\r
3185   if ( stream_.state == STREAM_CLOSED ) {\r
3186     errorText_ = "RtApiAsio::closeStream(): no open stream to close!";\r
3187     error( RtAudioError::WARNING );\r
3188     return;\r
3189   }\r
3190 \r
3191   if ( stream_.state == STREAM_RUNNING ) {\r
3192     stream_.state = STREAM_STOPPED;\r
3193     ASIOStop();\r
3194   }\r
3195   ASIODisposeBuffers();\r
3196   drivers.removeCurrentDriver();\r
3197 \r
3198   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3199   if ( handle ) {\r
3200     CloseHandle( handle->condition );\r
3201     if ( handle->bufferInfos )\r
3202       free( handle->bufferInfos );\r
3203     delete handle;\r
3204     stream_.apiHandle = 0;\r
3205   }\r
3206 \r
3207   for ( int i=0; i<2; i++ ) {\r
3208     if ( stream_.userBuffer[i] ) {\r
3209       free( stream_.userBuffer[i] );\r
3210       stream_.userBuffer[i] = 0;\r
3211     }\r
3212   }\r
3213 \r
3214   if ( stream_.deviceBuffer ) {\r
3215     free( stream_.deviceBuffer );\r
3216     stream_.deviceBuffer = 0;\r
3217   }\r
3218 \r
3219   stream_.mode = UNINITIALIZED;\r
3220   stream_.state = STREAM_CLOSED;\r
3221 }\r
3222 \r
3223 bool stopThreadCalled = false;\r
3224 \r
3225 void RtApiAsio :: startStream()\r
3226 {\r
3227   verifyStream();\r
3228   if ( stream_.state == STREAM_RUNNING ) {\r
3229     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
3230     error( RtAudioError::WARNING );\r
3231     return;\r
3232   }\r
3233 \r
3234   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3235   ASIOError result = ASIOStart();\r
3236   if ( result != ASE_OK ) {\r
3237     errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";\r
3238     errorText_ = errorStream_.str();\r
3239     goto unlock;\r
3240   }\r
3241 \r
3242   handle->drainCounter = 0;\r
3243   handle->internalDrain = false;\r
3244   ResetEvent( handle->condition );\r
3245   stream_.state = STREAM_RUNNING;\r
3246   asioXRun = false;\r
3247 \r
3248  unlock:\r
3249   stopThreadCalled = false;\r
3250 \r
3251   if ( result == ASE_OK ) return;\r
3252   error( RtAudioError::SYSTEM_ERROR );\r
3253 }\r
3254 \r
3255 void RtApiAsio :: stopStream()\r
3256 {\r
3257   verifyStream();\r
3258   if ( stream_.state == STREAM_STOPPED ) {\r
3259     errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";\r
3260     error( RtAudioError::WARNING );\r
3261     return;\r
3262   }\r
3263 \r
3264   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3265   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3266     if ( handle->drainCounter == 0 ) {\r
3267       handle->drainCounter = 2;\r
3268       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
3269     }\r
3270   }\r
3271 \r
3272   stream_.state = STREAM_STOPPED;\r
3273 \r
3274   ASIOError result = ASIOStop();\r
3275   if ( result != ASE_OK ) {\r
3276     errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";\r
3277     errorText_ = errorStream_.str();\r
3278   }\r
3279 \r
3280   if ( result == ASE_OK ) return;\r
3281   error( RtAudioError::SYSTEM_ERROR );\r
3282 }\r
3283 \r
3284 void RtApiAsio :: abortStream()\r
3285 {\r
3286   verifyStream();\r
3287   if ( stream_.state == STREAM_STOPPED ) {\r
3288     errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";\r
3289     error( RtAudioError::WARNING );\r
3290     return;\r
3291   }\r
3292 \r
3293   // The following lines were commented-out because some behavior was\r
3294   // noted where the device buffers need to be zeroed to avoid\r
3295   // continuing sound, even when the device buffers are completely\r
3296   // disposed.  So now, calling abort is the same as calling stop.\r
3297   // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3298   // handle->drainCounter = 2;\r
3299   stopStream();\r
3300 }\r
3301 \r
3302 // This function will be called by a spawned thread when the user\r
3303 // callback function signals that the stream should be stopped or\r
3304 // aborted.  It is necessary to handle it this way because the\r
3305 // callbackEvent() function must return before the ASIOStop()\r
3306 // function will return.\r
3307 static unsigned __stdcall asioStopStream( void *ptr )\r
3308 {\r
3309   CallbackInfo *info = (CallbackInfo *) ptr;\r
3310   RtApiAsio *object = (RtApiAsio *) info->object;\r
3311 \r
3312   object->stopStream();\r
3313   _endthreadex( 0 );\r
3314   return 0;\r
3315 }\r
3316 \r
3317 bool RtApiAsio :: callbackEvent( long bufferIndex )\r
3318 {\r
3319   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
3320   if ( stream_.state == STREAM_CLOSED ) {\r
3321     errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
3322     error( RtAudioError::WARNING );\r
3323     return FAILURE;\r
3324   }\r
3325 \r
3326   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
3327   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3328 \r
3329   // Check if we were draining the stream and signal if finished.\r
3330   if ( handle->drainCounter > 3 ) {\r
3331 \r
3332     stream_.state = STREAM_STOPPING;\r
3333     if ( handle->internalDrain == false )\r
3334       SetEvent( handle->condition );\r
3335     else { // spawn a thread to stop the stream\r
3336       unsigned threadId;\r
3337       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3338                                                     &stream_.callbackInfo, 0, &threadId );\r
3339     }\r
3340     return SUCCESS;\r
3341   }\r
3342 \r
3343   // Invoke user callback to get fresh output data UNLESS we are\r
3344   // draining stream.\r
3345   if ( handle->drainCounter == 0 ) {\r
3346     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
3347     double streamTime = getStreamTime();\r
3348     RtAudioStreamStatus status = 0;\r
3349     if ( stream_.mode != INPUT && asioXRun == true ) {\r
3350       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
3351       asioXRun = false;\r
3352     }\r
3353     if ( stream_.mode != OUTPUT && asioXRun == true ) {\r
3354       status |= RTAUDIO_INPUT_OVERFLOW;\r
3355       asioXRun = false;\r
3356     }\r
3357     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
3358                                      stream_.bufferSize, streamTime, status, info->userData );\r
3359     if ( cbReturnValue == 2 ) {\r
3360       stream_.state = STREAM_STOPPING;\r
3361       handle->drainCounter = 2;\r
3362       unsigned threadId;\r
3363       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3364                                                     &stream_.callbackInfo, 0, &threadId );\r
3365       return SUCCESS;\r
3366     }\r
3367     else if ( cbReturnValue == 1 ) {\r
3368       handle->drainCounter = 1;\r
3369       handle->internalDrain = true;\r
3370     }\r
3371   }\r
3372 \r
3373   unsigned int nChannels, bufferBytes, i, j;\r
3374   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3375   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3376 \r
3377     bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );\r
3378 \r
3379     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
3380 \r
3381       for ( i=0, j=0; i<nChannels; i++ ) {\r
3382         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3383           memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );\r
3384       }\r
3385 \r
3386     }\r
3387     else if ( stream_.doConvertBuffer[0] ) {\r
3388 \r
3389       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
3390       if ( stream_.doByteSwap[0] )\r
3391         byteSwapBuffer( stream_.deviceBuffer,\r
3392                         stream_.bufferSize * stream_.nDeviceChannels[0],\r
3393                         stream_.deviceFormat[0] );\r
3394 \r
3395       for ( i=0, j=0; i<nChannels; i++ ) {\r
3396         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3397           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3398                   &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );\r
3399       }\r
3400 \r
3401     }\r
3402     else {\r
3403 \r
3404       if ( stream_.doByteSwap[0] )\r
3405         byteSwapBuffer( stream_.userBuffer[0],\r
3406                         stream_.bufferSize * stream_.nUserChannels[0],\r
3407                         stream_.userFormat );\r
3408 \r
3409       for ( i=0, j=0; i<nChannels; i++ ) {\r
3410         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3411           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3412                   &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );\r
3413       }\r
3414 \r
3415     }\r
3416   }\r
3417 \r
3418   // Don't bother draining input\r
3419   if ( handle->drainCounter ) {\r
3420     handle->drainCounter++;\r
3421     goto unlock;\r
3422   }\r
3423 \r
3424   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
3425 \r
3426     bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);\r
3427 \r
3428     if (stream_.doConvertBuffer[1]) {\r
3429 \r
3430       // Always interleave ASIO input data.\r
3431       for ( i=0, j=0; i<nChannels; i++ ) {\r
3432         if ( handle->bufferInfos[i].isInput == ASIOTrue )\r
3433           memcpy( &stream_.deviceBuffer[j++*bufferBytes],\r
3434                   handle->bufferInfos[i].buffers[bufferIndex],\r
3435                   bufferBytes );\r
3436       }\r
3437 \r
3438       if ( stream_.doByteSwap[1] )\r
3439         byteSwapBuffer( stream_.deviceBuffer,\r
3440                         stream_.bufferSize * stream_.nDeviceChannels[1],\r
3441                         stream_.deviceFormat[1] );\r
3442       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
3443 \r
3444     }\r
3445     else {\r
3446       for ( i=0, j=0; i<nChannels; i++ ) {\r
3447         if ( handle->bufferInfos[i].isInput == ASIOTrue ) {\r
3448           memcpy( &stream_.userBuffer[1][bufferBytes*j++],\r
3449                   handle->bufferInfos[i].buffers[bufferIndex],\r
3450                   bufferBytes );\r
3451         }\r
3452       }\r
3453 \r
3454       if ( stream_.doByteSwap[1] )\r
3455         byteSwapBuffer( stream_.userBuffer[1],\r
3456                         stream_.bufferSize * stream_.nUserChannels[1],\r
3457                         stream_.userFormat );\r
3458     }\r
3459   }\r
3460 \r
3461  unlock:\r
3462   // The following call was suggested by Malte Clasen.  While the API\r
3463   // documentation indicates it should not be required, some device\r
3464   // drivers apparently do not function correctly without it.\r
3465   ASIOOutputReady();\r
3466 \r
3467   RtApi::tickStreamTime();\r
3468   return SUCCESS;\r
3469 }\r
3470 \r
3471 static void sampleRateChanged( ASIOSampleRate sRate )\r
3472 {\r
3473   // The ASIO documentation says that this usually only happens during\r
3474   // external sync.  Audio processing is not stopped by the driver,\r
3475   // actual sample rate might not have even changed, maybe only the\r
3476   // sample rate status of an AES/EBU or S/PDIF digital input at the\r
3477   // audio device.\r
3478 \r
3479   RtApi *object = (RtApi *) asioCallbackInfo->object;\r
3480   try {\r
3481     object->stopStream();\r
3482   }\r
3483   catch ( RtAudioError &exception ) {\r
3484     std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;\r
3485     return;\r
3486   }\r
3487 \r
3488   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
3489 }\r
3490 \r
3491 static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
3492 {\r
3493   long ret = 0;\r
3494 \r
3495   switch( selector ) {\r
3496   case kAsioSelectorSupported:\r
3497     if ( value == kAsioResetRequest\r
3498          || value == kAsioEngineVersion\r
3499          || value == kAsioResyncRequest\r
3500          || value == kAsioLatenciesChanged\r
3501          // The following three were added for ASIO 2.0, you don't\r
3502          // necessarily have to support them.\r
3503          || value == kAsioSupportsTimeInfo\r
3504          || value == kAsioSupportsTimeCode\r
3505          || value == kAsioSupportsInputMonitor)\r
3506       ret = 1L;\r
3507     break;\r
3508   case kAsioResetRequest:\r
3509     // Defer the task and perform the reset of the driver during the\r
3510     // next "safe" situation.  You cannot reset the driver right now,\r
3511     // as this code is called from the driver.  Reset the driver is\r
3512     // done by completely destruct is. I.e. ASIOStop(),\r
3513     // ASIODisposeBuffers(), Destruction Afterwards you initialize the\r
3514     // driver again.\r
3515     std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;\r
3516     ret = 1L;\r
3517     break;\r
3518   case kAsioResyncRequest:\r
3519     // This informs the application that the driver encountered some\r
3520     // non-fatal data loss.  It is used for synchronization purposes\r
3521     // of different media.  Added mainly to work around the Win16Mutex\r
3522     // problems in Windows 95/98 with the Windows Multimedia system,\r
3523     // which could lose data because the Mutex was held too long by\r
3524     // another thread.  However a driver can issue it in other\r
3525     // situations, too.\r
3526     // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;\r
3527     asioXRun = true;\r
3528     ret = 1L;\r
3529     break;\r
3530   case kAsioLatenciesChanged:\r
3531     // This will inform the host application that the drivers were\r
3532     // latencies changed.  Beware, it this does not mean that the\r
3533     // buffer sizes have changed!  You might need to update internal\r
3534     // delay data.\r
3535     std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;\r
3536     ret = 1L;\r
3537     break;\r
3538   case kAsioEngineVersion:\r
3539     // Return the supported ASIO version of the host application.  If\r
3540     // a host application does not implement this selector, ASIO 1.0\r
3541     // is assumed by the driver.\r
3542     ret = 2L;\r
3543     break;\r
3544   case kAsioSupportsTimeInfo:\r
3545     // Informs the driver whether the\r
3546     // asioCallbacks.bufferSwitchTimeInfo() callback is supported.\r
3547     // For compatibility with ASIO 1.0 drivers the host application\r
3548     // should always support the "old" bufferSwitch method, too.\r
3549     ret = 0;\r
3550     break;\r
3551   case kAsioSupportsTimeCode:\r
3552     // Informs the driver whether application is interested in time\r
3553     // code info.  If an application does not need to know about time\r
3554     // code, the driver has less work to do.\r
3555     ret = 0;\r
3556     break;\r
3557   }\r
3558   return ret;\r
3559 }\r
3560 \r
3561 static const char* getAsioErrorString( ASIOError result )\r
3562 {\r
3563   struct Messages \r
3564   {\r
3565     ASIOError value;\r
3566     const char*message;\r
3567   };\r
3568 \r
3569   static const Messages m[] = \r
3570     {\r
3571       {   ASE_NotPresent,    "Hardware input or output is not present or available." },\r
3572       {   ASE_HWMalfunction,  "Hardware is malfunctioning." },\r
3573       {   ASE_InvalidParameter, "Invalid input parameter." },\r
3574       {   ASE_InvalidMode,      "Invalid mode." },\r
3575       {   ASE_SPNotAdvancing,     "Sample position not advancing." },\r
3576       {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },\r
3577       {   ASE_NoMemory,           "Not enough memory to complete the request." }\r
3578     };\r
3579 \r
3580   for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )\r
3581     if ( m[i].value == result ) return m[i].message;\r
3582 \r
3583   return "Unknown error.";\r
3584 }\r
3585 \r
3586 //******************** End of __WINDOWS_ASIO__ *********************//\r
3587 #endif\r
3588 \r
3589 \r
3590 #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
3591 \r
3592 // Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
3593 // - Introduces support for the Windows WASAPI API\r
3594 // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
3595 // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
3596 // - Includes automatic internal conversion of sample rate and buffer size between hardware and the user\r
3597 \r
3598 #ifndef INITGUID\r
3599   #define INITGUID\r
3600 #endif\r
3601 #include <audioclient.h>\r
3602 #include <avrt.h>\r
3603 #include <mmdeviceapi.h>\r
3604 #include <functiondiscoverykeys_devpkey.h>\r
3605 \r
3606 //=============================================================================\r
3607 \r
3608 #define SAFE_RELEASE( objectPtr )\\r
3609 if ( objectPtr )\\r
3610 {\\r
3611   objectPtr->Release();\\r
3612   objectPtr = NULL;\\r
3613 }\r
3614 \r
3615 typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
3616 \r
3617 //-----------------------------------------------------------------------------\r
3618 \r
3619 // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
3620 // Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
3621 // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
3622 // provide intermediate storage for read / write synchronization.\r
3623 class WasapiBuffer\r
3624 {\r
3625 public:\r
3626   WasapiBuffer()\r
3627     : buffer_( NULL ),\r
3628       bufferSize_( 0 ),\r
3629       inIndex_( 0 ),\r
3630       outIndex_( 0 ) {}\r
3631 \r
3632   ~WasapiBuffer() {\r
3633     delete buffer_;\r
3634   }\r
3635 \r
3636   // sets the length of the internal ring buffer\r
3637   void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
3638     delete buffer_;\r
3639 \r
3640     buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
3641 \r
3642     bufferSize_ = bufferSize;\r
3643     inIndex_ = 0;\r
3644     outIndex_ = 0;\r
3645   }\r
3646 \r
3647   // attempt to push a buffer into the ring buffer at the current "in" index\r
3648   bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3649   {\r
3650     if ( !buffer ||                 // incoming buffer is NULL\r
3651          bufferSize == 0 ||         // incoming buffer has no data\r
3652          bufferSize > bufferSize_ ) // incoming buffer too large\r
3653     {\r
3654       return false;\r
3655     }\r
3656 \r
3657     unsigned int relOutIndex = outIndex_;\r
3658     unsigned int inIndexEnd = inIndex_ + bufferSize;\r
3659     if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
3660       relOutIndex += bufferSize_;\r
3661     }\r
3662 \r
3663     // "in" index can end on the "out" index but cannot begin at it\r
3664     if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
3665       return false; // not enough space between "in" index and "out" index\r
3666     }\r
3667 \r
3668     // copy buffer from external to internal\r
3669     int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
3670     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3671     int fromInSize = bufferSize - fromZeroSize;\r
3672 \r
3673     switch( format )\r
3674       {\r
3675       case RTAUDIO_SINT8:\r
3676         memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
3677         memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
3678         break;\r
3679       case RTAUDIO_SINT16:\r
3680         memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
3681         memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
3682         break;\r
3683       case RTAUDIO_SINT24:\r
3684         memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
3685         memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
3686         break;\r
3687       case RTAUDIO_SINT32:\r
3688         memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
3689         memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
3690         break;\r
3691       case RTAUDIO_FLOAT32:\r
3692         memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
3693         memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
3694         break;\r
3695       case RTAUDIO_FLOAT64:\r
3696         memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
3697         memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
3698         break;\r
3699     }\r
3700 \r
3701     // update "in" index\r
3702     inIndex_ += bufferSize;\r
3703     inIndex_ %= bufferSize_;\r
3704 \r
3705     return true;\r
3706   }\r
3707 \r
3708   // attempt to pull a buffer from the ring buffer from the current "out" index\r
3709   bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3710   {\r
3711     if ( !buffer ||                 // incoming buffer is NULL\r
3712          bufferSize == 0 ||         // incoming buffer has no data\r
3713          bufferSize > bufferSize_ ) // incoming buffer too large\r
3714     {\r
3715       return false;\r
3716     }\r
3717 \r
3718     unsigned int relInIndex = inIndex_;\r
3719     unsigned int outIndexEnd = outIndex_ + bufferSize;\r
3720     if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
3721       relInIndex += bufferSize_;\r
3722     }\r
3723 \r
3724     // "out" index can begin at and end on the "in" index\r
3725     if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
3726       return false; // not enough space between "out" index and "in" index\r
3727     }\r
3728 \r
3729     // copy buffer from internal to external\r
3730     int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
3731     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3732     int fromOutSize = bufferSize - fromZeroSize;\r
3733 \r
3734     switch( format )\r
3735     {\r
3736       case RTAUDIO_SINT8:\r
3737         memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
3738         memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
3739         break;\r
3740       case RTAUDIO_SINT16:\r
3741         memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
3742         memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
3743         break;\r
3744       case RTAUDIO_SINT24:\r
3745         memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
3746         memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
3747         break;\r
3748       case RTAUDIO_SINT32:\r
3749         memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
3750         memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
3751         break;\r
3752       case RTAUDIO_FLOAT32:\r
3753         memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
3754         memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
3755         break;\r
3756       case RTAUDIO_FLOAT64:\r
3757         memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
3758         memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
3759         break;\r
3760     }\r
3761 \r
3762     // update "out" index\r
3763     outIndex_ += bufferSize;\r
3764     outIndex_ %= bufferSize_;\r
3765 \r
3766     return true;\r
3767   }\r
3768 \r
3769 private:\r
3770   char* buffer_;\r
3771   unsigned int bufferSize_;\r
3772   unsigned int inIndex_;\r
3773   unsigned int outIndex_;\r
3774 };\r
3775 \r
3776 //-----------------------------------------------------------------------------\r
3777 \r
3778 // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate\r
3779 // between HW and the user. The convertBufferWasapi function is used to perform this conversion\r
3780 // between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
3781 // This sample rate converter favors speed over quality, and works best with conversions between\r
3782 // one rate and its multiple.\r
3783 void convertBufferWasapi( char* outBuffer,\r
3784                           const char* inBuffer,\r
3785                           const unsigned int& channelCount,\r
3786                           const unsigned int& inSampleRate,\r
3787                           const unsigned int& outSampleRate,\r
3788                           const unsigned int& inSampleCount,\r
3789                           unsigned int& outSampleCount,\r
3790                           const RtAudioFormat& format )\r
3791 {\r
3792   // calculate the new outSampleCount and relative sampleStep\r
3793   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
3794   float sampleStep = 1.0f / sampleRatio;\r
3795   float inSampleFraction = 0.0f;\r
3796 \r
3797   outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio );\r
3798 \r
3799   // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3800   for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3801   {\r
3802     unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3803 \r
3804     switch ( format )\r
3805     {\r
3806       case RTAUDIO_SINT8:\r
3807         memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
3808         break;\r
3809       case RTAUDIO_SINT16:\r
3810         memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
3811         break;\r
3812       case RTAUDIO_SINT24:\r
3813         memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
3814         break;\r
3815       case RTAUDIO_SINT32:\r
3816         memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
3817         break;\r
3818       case RTAUDIO_FLOAT32:\r
3819         memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
3820         break;\r
3821       case RTAUDIO_FLOAT64:\r
3822         memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
3823         break;\r
3824     }\r
3825 \r
3826     // jump to next in sample\r
3827     inSampleFraction += sampleStep;\r
3828   }\r
3829 }\r
3830 \r
3831 //-----------------------------------------------------------------------------\r
3832 \r
3833 // A structure to hold various information related to the WASAPI implementation.\r
3834 struct WasapiHandle\r
3835 {\r
3836   IAudioClient* captureAudioClient;\r
3837   IAudioClient* renderAudioClient;\r
3838   IAudioCaptureClient* captureClient;\r
3839   IAudioRenderClient* renderClient;\r
3840   HANDLE captureEvent;\r
3841   HANDLE renderEvent;\r
3842 \r
3843   WasapiHandle()\r
3844   : captureAudioClient( NULL ),\r
3845     renderAudioClient( NULL ),\r
3846     captureClient( NULL ),\r
3847     renderClient( NULL ),\r
3848     captureEvent( NULL ),\r
3849     renderEvent( NULL ) {}\r
3850 };\r
3851 \r
3852 //=============================================================================\r
3853 \r
3854 RtApiWasapi::RtApiWasapi()\r
3855   : coInitialized_( false ), deviceEnumerator_( NULL )\r
3856 {\r
3857   // WASAPI can run either apartment or multi-threaded\r
3858   HRESULT hr = CoInitialize( NULL );\r
3859   if ( !FAILED( hr ) )\r
3860     coInitialized_ = true;\r
3861 \r
3862   // Instantiate device enumerator\r
3863   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
3864                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
3865                          ( void** ) &deviceEnumerator_ );\r
3866 \r
3867   if ( FAILED( hr ) ) {\r
3868     errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
3869     error( RtAudioError::DRIVER_ERROR );\r
3870   }\r
3871 }\r
3872 \r
3873 //-----------------------------------------------------------------------------\r
3874 \r
3875 RtApiWasapi::~RtApiWasapi()\r
3876 {\r
3877   if ( stream_.state != STREAM_CLOSED )\r
3878     closeStream();\r
3879 \r
3880   SAFE_RELEASE( deviceEnumerator_ );\r
3881 \r
3882   // If this object previously called CoInitialize()\r
3883   if ( coInitialized_ )\r
3884     CoUninitialize();\r
3885 }\r
3886 \r
3887 //=============================================================================\r
3888 \r
3889 unsigned int RtApiWasapi::getDeviceCount( void )\r
3890 {\r
3891   unsigned int captureDeviceCount = 0;\r
3892   unsigned int renderDeviceCount = 0;\r
3893 \r
3894   IMMDeviceCollection* captureDevices = NULL;\r
3895   IMMDeviceCollection* renderDevices = NULL;\r
3896 \r
3897   // Count capture devices\r
3898   errorText_.clear();\r
3899   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3900   if ( FAILED( hr ) ) {\r
3901     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
3902     goto Exit;\r
3903   }\r
3904 \r
3905   hr = captureDevices->GetCount( &captureDeviceCount );\r
3906   if ( FAILED( hr ) ) {\r
3907     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
3908     goto Exit;\r
3909   }\r
3910 \r
3911   // Count render devices\r
3912   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3913   if ( FAILED( hr ) ) {\r
3914     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
3915     goto Exit;\r
3916   }\r
3917 \r
3918   hr = renderDevices->GetCount( &renderDeviceCount );\r
3919   if ( FAILED( hr ) ) {\r
3920     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
3921     goto Exit;\r
3922   }\r
3923 \r
3924 Exit:\r
3925   // release all references\r
3926   SAFE_RELEASE( captureDevices );\r
3927   SAFE_RELEASE( renderDevices );\r
3928 \r
3929   if ( errorText_.empty() )\r
3930     return captureDeviceCount + renderDeviceCount;\r
3931 \r
3932   error( RtAudioError::DRIVER_ERROR );\r
3933   return 0;\r
3934 }\r
3935 \r
3936 //-----------------------------------------------------------------------------\r
3937 \r
3938 RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
3939 {\r
3940   RtAudio::DeviceInfo info;\r
3941   unsigned int captureDeviceCount = 0;\r
3942   unsigned int renderDeviceCount = 0;\r
3943   std::wstring deviceName;\r
3944   std::string defaultDeviceName;\r
3945   bool isCaptureDevice = false;\r
3946 \r
3947   PROPVARIANT deviceNameProp;\r
3948   PROPVARIANT defaultDeviceNameProp;\r
3949 \r
3950   IMMDeviceCollection* captureDevices = NULL;\r
3951   IMMDeviceCollection* renderDevices = NULL;\r
3952   IMMDevice* devicePtr = NULL;\r
3953   IMMDevice* defaultDevicePtr = NULL;\r
3954   IAudioClient* audioClient = NULL;\r
3955   IPropertyStore* devicePropStore = NULL;\r
3956   IPropertyStore* defaultDevicePropStore = NULL;\r
3957 \r
3958   WAVEFORMATEX* deviceFormat = NULL;\r
3959   WAVEFORMATEX* closestMatchFormat = NULL;\r
3960 \r
3961   // probed\r
3962   info.probed = false;\r
3963 \r
3964   // Count capture devices\r
3965   errorText_.clear();\r
3966   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
3967   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3968   if ( FAILED( hr ) ) {\r
3969     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
3970     goto Exit;\r
3971   }\r
3972 \r
3973   hr = captureDevices->GetCount( &captureDeviceCount );\r
3974   if ( FAILED( hr ) ) {\r
3975     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
3976     goto Exit;\r
3977   }\r
3978 \r
3979   // Count render devices\r
3980   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3981   if ( FAILED( hr ) ) {\r
3982     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
3983     goto Exit;\r
3984   }\r
3985 \r
3986   hr = renderDevices->GetCount( &renderDeviceCount );\r
3987   if ( FAILED( hr ) ) {\r
3988     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
3989     goto Exit;\r
3990   }\r
3991 \r
3992   // validate device index\r
3993   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
3994     errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
3995     errorType = RtAudioError::INVALID_USE;\r
3996     goto Exit;\r
3997   }\r
3998 \r
3999   // determine whether index falls within capture or render devices\r
4000   if ( device >= renderDeviceCount ) {\r
4001     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4002     if ( FAILED( hr ) ) {\r
4003       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
4004       goto Exit;\r
4005     }\r
4006     isCaptureDevice = true;\r
4007   }\r
4008   else {\r
4009     hr = renderDevices->Item( device, &devicePtr );\r
4010     if ( FAILED( hr ) ) {\r
4011       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
4012       goto Exit;\r
4013     }\r
4014     isCaptureDevice = false;\r
4015   }\r
4016 \r
4017   // get default device name\r
4018   if ( isCaptureDevice ) {\r
4019     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
4020     if ( FAILED( hr ) ) {\r
4021       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
4022       goto Exit;\r
4023     }\r
4024   }\r
4025   else {\r
4026     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
4027     if ( FAILED( hr ) ) {\r
4028       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
4029       goto Exit;\r
4030     }\r
4031   }\r
4032 \r
4033   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
4034   if ( FAILED( hr ) ) {\r
4035     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
4036     goto Exit;\r
4037   }\r
4038   PropVariantInit( &defaultDeviceNameProp );\r
4039 \r
4040   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
4041   if ( FAILED( hr ) ) {\r
4042     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
4043     goto Exit;\r
4044   }\r
4045 \r
4046   deviceName = defaultDeviceNameProp.pwszVal;\r
4047   defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );\r
4048 \r
4049   // name\r
4050   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
4051   if ( FAILED( hr ) ) {\r
4052     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
4053     goto Exit;\r
4054   }\r
4055 \r
4056   PropVariantInit( &deviceNameProp );\r
4057 \r
4058   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
4059   if ( FAILED( hr ) ) {\r
4060     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
4061     goto Exit;\r
4062   }\r
4063 \r
4064   deviceName = deviceNameProp.pwszVal;\r
4065   info.name = std::string( deviceName.begin(), deviceName.end() );\r
4066 \r
4067   // is default\r
4068   if ( isCaptureDevice ) {\r
4069     info.isDefaultInput = info.name == defaultDeviceName;\r
4070     info.isDefaultOutput = false;\r
4071   }\r
4072   else {\r
4073     info.isDefaultInput = false;\r
4074     info.isDefaultOutput = info.name == defaultDeviceName;\r
4075   }\r
4076 \r
4077   // channel count\r
4078   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
4079   if ( FAILED( hr ) ) {\r
4080     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
4081     goto Exit;\r
4082   }\r
4083 \r
4084   hr = audioClient->GetMixFormat( &deviceFormat );\r
4085   if ( FAILED( hr ) ) {\r
4086     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
4087     goto Exit;\r
4088   }\r
4089 \r
4090   if ( isCaptureDevice ) {\r
4091     info.inputChannels = deviceFormat->nChannels;\r
4092     info.outputChannels = 0;\r
4093     info.duplexChannels = 0;\r
4094   }\r
4095   else {\r
4096     info.inputChannels = 0;\r
4097     info.outputChannels = deviceFormat->nChannels;\r
4098     info.duplexChannels = 0;\r
4099   }\r
4100 \r
4101   // sample rates\r
4102   info.sampleRates.clear();\r
4103 \r
4104   // allow support for all sample rates as we have a built-in sample rate converter\r
4105   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
4106     info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4107   }\r
4108 \r
4109   // native format\r
4110   info.nativeFormats = 0;\r
4111 \r
4112   if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
4113        ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4114          ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
4115   {\r
4116     if ( deviceFormat->wBitsPerSample == 32 ) {\r
4117       info.nativeFormats |= RTAUDIO_FLOAT32;\r
4118     }\r
4119     else if ( deviceFormat->wBitsPerSample == 64 ) {\r
4120       info.nativeFormats |= RTAUDIO_FLOAT64;\r
4121     }\r
4122   }\r
4123   else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
4124            ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4125              ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
4126   {\r
4127     if ( deviceFormat->wBitsPerSample == 8 ) {\r
4128       info.nativeFormats |= RTAUDIO_SINT8;\r
4129     }\r
4130     else if ( deviceFormat->wBitsPerSample == 16 ) {\r
4131       info.nativeFormats |= RTAUDIO_SINT16;\r
4132     }\r
4133     else if ( deviceFormat->wBitsPerSample == 24 ) {\r
4134       info.nativeFormats |= RTAUDIO_SINT24;\r
4135     }\r
4136     else if ( deviceFormat->wBitsPerSample == 32 ) {\r
4137       info.nativeFormats |= RTAUDIO_SINT32;\r
4138     }\r
4139   }\r
4140 \r
4141   // probed\r
4142   info.probed = true;\r
4143 \r
4144 Exit:\r
4145   // release all references\r
4146   PropVariantClear( &deviceNameProp );\r
4147   PropVariantClear( &defaultDeviceNameProp );\r
4148 \r
4149   SAFE_RELEASE( captureDevices );\r
4150   SAFE_RELEASE( renderDevices );\r
4151   SAFE_RELEASE( devicePtr );\r
4152   SAFE_RELEASE( defaultDevicePtr );\r
4153   SAFE_RELEASE( audioClient );\r
4154   SAFE_RELEASE( devicePropStore );\r
4155   SAFE_RELEASE( defaultDevicePropStore );\r
4156 \r
4157   CoTaskMemFree( deviceFormat );\r
4158   CoTaskMemFree( closestMatchFormat );\r
4159 \r
4160   if ( !errorText_.empty() )\r
4161     error( errorType );\r
4162   return info;\r
4163 }\r
4164 \r
4165 //-----------------------------------------------------------------------------\r
4166 \r
4167 unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
4168 {\r
4169   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4170     if ( getDeviceInfo( i ).isDefaultOutput ) {\r
4171       return i;\r
4172     }\r
4173   }\r
4174 \r
4175   return 0;\r
4176 }\r
4177 \r
4178 //-----------------------------------------------------------------------------\r
4179 \r
4180 unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
4181 {\r
4182   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4183     if ( getDeviceInfo( i ).isDefaultInput ) {\r
4184       return i;\r
4185     }\r
4186   }\r
4187 \r
4188   return 0;\r
4189 }\r
4190 \r
4191 //-----------------------------------------------------------------------------\r
4192 \r
4193 void RtApiWasapi::closeStream( void )\r
4194 {\r
4195   if ( stream_.state == STREAM_CLOSED ) {\r
4196     errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
4197     error( RtAudioError::WARNING );\r
4198     return;\r
4199   }\r
4200 \r
4201   if ( stream_.state != STREAM_STOPPED )\r
4202     stopStream();\r
4203 \r
4204   // clean up stream memory\r
4205   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
4206   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
4207 \r
4208   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
4209   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
4210 \r
4211   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
4212     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
4213 \r
4214   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
4215     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
4216 \r
4217   delete ( WasapiHandle* ) stream_.apiHandle;\r
4218   stream_.apiHandle = NULL;\r
4219 \r
4220   for ( int i = 0; i < 2; i++ ) {\r
4221     if ( stream_.userBuffer[i] ) {\r
4222       free( stream_.userBuffer[i] );\r
4223       stream_.userBuffer[i] = 0;\r
4224     }\r
4225   }\r
4226 \r
4227   if ( stream_.deviceBuffer ) {\r
4228     free( stream_.deviceBuffer );\r
4229     stream_.deviceBuffer = 0;\r
4230   }\r
4231 \r
4232   // update stream state\r
4233   stream_.state = STREAM_CLOSED;\r
4234 }\r
4235 \r
4236 //-----------------------------------------------------------------------------\r
4237 \r
4238 void RtApiWasapi::startStream( void )\r
4239 {\r
4240   verifyStream();\r
4241 \r
4242   if ( stream_.state == STREAM_RUNNING ) {\r
4243     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
4244     error( RtAudioError::WARNING );\r
4245     return;\r
4246   }\r
4247 \r
4248   // update stream state\r
4249   stream_.state = STREAM_RUNNING;\r
4250 \r
4251   // create WASAPI stream thread\r
4252   stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
4253 \r
4254   if ( !stream_.callbackInfo.thread ) {\r
4255     errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
4256     error( RtAudioError::THREAD_ERROR );\r
4257   }\r
4258   else {\r
4259     SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
4260     ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
4261   }\r
4262 }\r
4263 \r
4264 //-----------------------------------------------------------------------------\r
4265 \r
4266 void RtApiWasapi::stopStream( void )\r
4267 {\r
4268   verifyStream();\r
4269 \r
4270   if ( stream_.state == STREAM_STOPPED ) {\r
4271     errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
4272     error( RtAudioError::WARNING );\r
4273     return;\r
4274   }\r
4275 \r
4276   // inform stream thread by setting stream state to STREAM_STOPPING\r
4277   stream_.state = STREAM_STOPPING;\r
4278 \r
4279   // wait until stream thread is stopped\r
4280   while( stream_.state != STREAM_STOPPED ) {\r
4281     Sleep( 1 );\r
4282   }\r
4283 \r
4284   // Wait for the last buffer to play before stopping.\r
4285   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
4286 \r
4287   // stop capture client if applicable\r
4288   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4289     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4290     if ( FAILED( hr ) ) {\r
4291       errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
4292       error( RtAudioError::DRIVER_ERROR );\r
4293       return;\r
4294     }\r
4295   }\r
4296 \r
4297   // stop render client if applicable\r
4298   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4299     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4300     if ( FAILED( hr ) ) {\r
4301       errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
4302       error( RtAudioError::DRIVER_ERROR );\r
4303       return;\r
4304     }\r
4305   }\r
4306 \r
4307   // close thread handle\r
4308   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4309     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
4310     error( RtAudioError::THREAD_ERROR );\r
4311     return;\r
4312   }\r
4313 \r
4314   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4315 }\r
4316 \r
4317 //-----------------------------------------------------------------------------\r
4318 \r
4319 void RtApiWasapi::abortStream( void )\r
4320 {\r
4321   verifyStream();\r
4322 \r
4323   if ( stream_.state == STREAM_STOPPED ) {\r
4324     errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
4325     error( RtAudioError::WARNING );\r
4326     return;\r
4327   }\r
4328 \r
4329   // inform stream thread by setting stream state to STREAM_STOPPING\r
4330   stream_.state = STREAM_STOPPING;\r
4331 \r
4332   // wait until stream thread is stopped\r
4333   while ( stream_.state != STREAM_STOPPED ) {\r
4334     Sleep( 1 );\r
4335   }\r
4336 \r
4337   // stop capture client if applicable\r
4338   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4339     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4340     if ( FAILED( hr ) ) {\r
4341       errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
4342       error( RtAudioError::DRIVER_ERROR );\r
4343       return;\r
4344     }\r
4345   }\r
4346 \r
4347   // stop render client if applicable\r
4348   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4349     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4350     if ( FAILED( hr ) ) {\r
4351       errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
4352       error( RtAudioError::DRIVER_ERROR );\r
4353       return;\r
4354     }\r
4355   }\r
4356 \r
4357   // close thread handle\r
4358   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4359     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
4360     error( RtAudioError::THREAD_ERROR );\r
4361     return;\r
4362   }\r
4363 \r
4364   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4365 }\r
4366 \r
4367 //-----------------------------------------------------------------------------\r
4368 \r
4369 bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
4370                                    unsigned int firstChannel, unsigned int sampleRate,\r
4371                                    RtAudioFormat format, unsigned int* bufferSize,\r
4372                                    RtAudio::StreamOptions* options )\r
4373 {\r
4374   bool methodResult = FAILURE;\r
4375   unsigned int captureDeviceCount = 0;\r
4376   unsigned int renderDeviceCount = 0;\r
4377 \r
4378   IMMDeviceCollection* captureDevices = NULL;\r
4379   IMMDeviceCollection* renderDevices = NULL;\r
4380   IMMDevice* devicePtr = NULL;\r
4381   WAVEFORMATEX* deviceFormat = NULL;\r
4382   unsigned int bufferBytes;\r
4383   stream_.state = STREAM_STOPPED;\r
4384 \r
4385   // create API Handle if not already created\r
4386   if ( !stream_.apiHandle )\r
4387     stream_.apiHandle = ( void* ) new WasapiHandle();\r
4388 \r
4389   // Count capture devices\r
4390   errorText_.clear();\r
4391   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4392   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4393   if ( FAILED( hr ) ) {\r
4394     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
4395     goto Exit;\r
4396   }\r
4397 \r
4398   hr = captureDevices->GetCount( &captureDeviceCount );\r
4399   if ( FAILED( hr ) ) {\r
4400     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
4401     goto Exit;\r
4402   }\r
4403 \r
4404   // Count render devices\r
4405   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4406   if ( FAILED( hr ) ) {\r
4407     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
4408     goto Exit;\r
4409   }\r
4410 \r
4411   hr = renderDevices->GetCount( &renderDeviceCount );\r
4412   if ( FAILED( hr ) ) {\r
4413     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
4414     goto Exit;\r
4415   }\r
4416 \r
4417   // validate device index\r
4418   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4419     errorType = RtAudioError::INVALID_USE;\r
4420     errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
4421     goto Exit;\r
4422   }\r
4423 \r
4424   // determine whether index falls within capture or render devices\r
4425   if ( device >= renderDeviceCount ) {\r
4426     if ( mode != INPUT ) {\r
4427       errorType = RtAudioError::INVALID_USE;\r
4428       errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
4429       goto Exit;\r
4430     }\r
4431 \r
4432     // retrieve captureAudioClient from devicePtr\r
4433     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4434 \r
4435     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4436     if ( FAILED( hr ) ) {\r
4437       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
4438       goto Exit;\r
4439     }\r
4440 \r
4441     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4442                               NULL, ( void** ) &captureAudioClient );\r
4443     if ( FAILED( hr ) ) {\r
4444       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4445       goto Exit;\r
4446     }\r
4447 \r
4448     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
4449     if ( FAILED( hr ) ) {\r
4450       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4451       goto Exit;\r
4452     }\r
4453 \r
4454     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4455     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4456   }\r
4457   else {\r
4458     if ( mode != OUTPUT ) {\r
4459       errorType = RtAudioError::INVALID_USE;\r
4460       errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
4461       goto Exit;\r
4462     }\r
4463 \r
4464     // retrieve renderAudioClient from devicePtr\r
4465     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4466 \r
4467     hr = renderDevices->Item( device, &devicePtr );\r
4468     if ( FAILED( hr ) ) {\r
4469       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
4470       goto Exit;\r
4471     }\r
4472 \r
4473     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4474                               NULL, ( void** ) &renderAudioClient );\r
4475     if ( FAILED( hr ) ) {\r
4476       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4477       goto Exit;\r
4478     }\r
4479 \r
4480     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
4481     if ( FAILED( hr ) ) {\r
4482       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4483       goto Exit;\r
4484     }\r
4485 \r
4486     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4487     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4488   }\r
4489 \r
4490   // fill stream data\r
4491   if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
4492        ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
4493     stream_.mode = DUPLEX;\r
4494   }\r
4495   else {\r
4496     stream_.mode = mode;\r
4497   }\r
4498 \r
4499   stream_.device[mode] = device;\r
4500   stream_.doByteSwap[mode] = false;\r
4501   stream_.sampleRate = sampleRate;\r
4502   stream_.bufferSize = *bufferSize;\r
4503   stream_.nBuffers = 1;\r
4504   stream_.nUserChannels[mode] = channels;\r
4505   stream_.channelOffset[mode] = firstChannel;\r
4506   stream_.userFormat = format;\r
4507   stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
4508 \r
4509   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
4510     stream_.userInterleaved = false;\r
4511   else\r
4512     stream_.userInterleaved = true;\r
4513   stream_.deviceInterleaved[mode] = true;\r
4514 \r
4515   // Set flags for buffer conversion.\r
4516   stream_.doConvertBuffer[mode] = false;\r
4517   if ( stream_.userFormat != stream_.deviceFormat[mode] ||\r
4518        stream_.nUserChannels != stream_.nDeviceChannels )\r
4519     stream_.doConvertBuffer[mode] = true;\r
4520   else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
4521             stream_.nUserChannels[mode] > 1 )\r
4522     stream_.doConvertBuffer[mode] = true;\r
4523 \r
4524   if ( stream_.doConvertBuffer[mode] )\r
4525     setConvertInfo( mode, 0 );\r
4526 \r
4527   // Allocate necessary internal buffers\r
4528   bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
4529 \r
4530   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
4531   if ( !stream_.userBuffer[mode] ) {\r
4532     errorType = RtAudioError::MEMORY_ERROR;\r
4533     errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
4534     goto Exit;\r
4535   }\r
4536 \r
4537   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
4538     stream_.callbackInfo.priority = 15;\r
4539   else\r
4540     stream_.callbackInfo.priority = 0;\r
4541 \r
4542   ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
4543   ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
4544 \r
4545   methodResult = SUCCESS;\r
4546 \r
4547 Exit:\r
4548   //clean up\r
4549   SAFE_RELEASE( captureDevices );\r
4550   SAFE_RELEASE( renderDevices );\r
4551   SAFE_RELEASE( devicePtr );\r
4552   CoTaskMemFree( deviceFormat );\r
4553 \r
4554   // if method failed, close the stream\r
4555   if ( methodResult == FAILURE )\r
4556     closeStream();\r
4557 \r
4558   if ( !errorText_.empty() )\r
4559     error( errorType );\r
4560   return methodResult;\r
4561 }\r
4562 \r
4563 //=============================================================================\r
4564 \r
4565 DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
4566 {\r
4567   if ( wasapiPtr )\r
4568     ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
4569 \r
4570   return 0;\r
4571 }\r
4572 \r
4573 DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
4574 {\r
4575   if ( wasapiPtr )\r
4576     ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
4577 \r
4578   return 0;\r
4579 }\r
4580 \r
4581 DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
4582 {\r
4583   if ( wasapiPtr )\r
4584     ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
4585 \r
4586   return 0;\r
4587 }\r
4588 \r
4589 //-----------------------------------------------------------------------------\r
4590 \r
4591 void RtApiWasapi::wasapiThread()\r
4592 {\r
4593   // as this is a new thread, we must CoInitialize it\r
4594   CoInitialize( NULL );\r
4595 \r
4596   HRESULT hr;\r
4597 \r
4598   IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4599   IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4600   IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
4601   IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
4602   HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
4603   HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
4604 \r
4605   WAVEFORMATEX* captureFormat = NULL;\r
4606   WAVEFORMATEX* renderFormat = NULL;\r
4607   float captureSrRatio = 0.0f;\r
4608   float renderSrRatio = 0.0f;\r
4609   WasapiBuffer captureBuffer;\r
4610   WasapiBuffer renderBuffer;\r
4611 \r
4612   // declare local stream variables\r
4613   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
4614   BYTE* streamBuffer = NULL;\r
4615   unsigned long captureFlags = 0;\r
4616   unsigned int bufferFrameCount = 0;\r
4617   unsigned int numFramesPadding = 0;\r
4618   unsigned int convBufferSize = 0;\r
4619   bool callbackPushed = false;\r
4620   bool callbackPulled = false;\r
4621   bool callbackStopped = false;\r
4622   int callbackResult = 0;\r
4623 \r
4624   // convBuffer is used to store converted buffers between WASAPI and the user\r
4625   char* convBuffer = NULL;\r
4626   unsigned int convBuffSize = 0;\r
4627   unsigned int deviceBuffSize = 0;\r
4628 \r
4629   errorText_.clear();\r
4630   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4631 \r
4632   // Attempt to assign "Pro Audio" characteristic to thread\r
4633   HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );\r
4634   if ( AvrtDll ) {\r
4635     DWORD taskIndex = 0;\r
4636     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
4637     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
4638     FreeLibrary( AvrtDll );\r
4639   }\r
4640 \r
4641   // start capture stream if applicable\r
4642   if ( captureAudioClient ) {\r
4643     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
4644     if ( FAILED( hr ) ) {\r
4645       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4646       goto Exit;\r
4647     }\r
4648 \r
4649     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
4650 \r
4651     // initialize capture stream according to desire buffer size\r
4652     float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
4653     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
4654 \r
4655     if ( !captureClient ) {\r
4656       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4657                                            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4658                                            desiredBufferPeriod,\r
4659                                            desiredBufferPeriod,\r
4660                                            captureFormat,\r
4661                                            NULL );\r
4662       if ( FAILED( hr ) ) {\r
4663         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
4664         goto Exit;\r
4665       }\r
4666 \r
4667       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
4668                                            ( void** ) &captureClient );\r
4669       if ( FAILED( hr ) ) {\r
4670         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
4671         goto Exit;\r
4672       }\r
4673 \r
4674       // configure captureEvent to trigger on every available capture buffer\r
4675       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4676       if ( !captureEvent ) {\r
4677         errorType = RtAudioError::SYSTEM_ERROR;\r
4678         errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
4679         goto Exit;\r
4680       }\r
4681 \r
4682       hr = captureAudioClient->SetEventHandle( captureEvent );\r
4683       if ( FAILED( hr ) ) {\r
4684         errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
4685         goto Exit;\r
4686       }\r
4687 \r
4688       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
4689       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
4690     }\r
4691 \r
4692     unsigned int inBufferSize = 0;\r
4693     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
4694     if ( FAILED( hr ) ) {\r
4695       errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
4696       goto Exit;\r
4697     }\r
4698 \r
4699     // scale outBufferSize according to stream->user sample rate ratio\r
4700     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
4701     inBufferSize *= stream_.nDeviceChannels[INPUT];\r
4702 \r
4703     // set captureBuffer size\r
4704     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
4705 \r
4706     // reset the capture stream\r
4707     hr = captureAudioClient->Reset();\r
4708     if ( FAILED( hr ) ) {\r
4709       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
4710       goto Exit;\r
4711     }\r
4712 \r
4713     // start the capture stream\r
4714     hr = captureAudioClient->Start();\r
4715     if ( FAILED( hr ) ) {\r
4716       errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
4717       goto Exit;\r
4718     }\r
4719   }\r
4720 \r
4721   // start render stream if applicable\r
4722   if ( renderAudioClient ) {\r
4723     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
4724     if ( FAILED( hr ) ) {\r
4725       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4726       goto Exit;\r
4727     }\r
4728 \r
4729     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
4730 \r
4731     // initialize render stream according to desire buffer size\r
4732     float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
4733     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
4734 \r
4735     if ( !renderClient ) {\r
4736       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4737                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4738                                           desiredBufferPeriod,\r
4739                                           desiredBufferPeriod,\r
4740                                           renderFormat,\r
4741                                           NULL );\r
4742       if ( FAILED( hr ) ) {\r
4743         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
4744         goto Exit;\r
4745       }\r
4746 \r
4747       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
4748                                           ( void** ) &renderClient );\r
4749       if ( FAILED( hr ) ) {\r
4750         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
4751         goto Exit;\r
4752       }\r
4753 \r
4754       // configure renderEvent to trigger on every available render buffer\r
4755       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4756       if ( !renderEvent ) {\r
4757         errorType = RtAudioError::SYSTEM_ERROR;\r
4758         errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
4759         goto Exit;\r
4760       }\r
4761 \r
4762       hr = renderAudioClient->SetEventHandle( renderEvent );\r
4763       if ( FAILED( hr ) ) {\r
4764         errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
4765         goto Exit;\r
4766       }\r
4767 \r
4768       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
4769       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
4770     }\r
4771 \r
4772     unsigned int outBufferSize = 0;\r
4773     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
4774     if ( FAILED( hr ) ) {\r
4775       errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
4776       goto Exit;\r
4777     }\r
4778 \r
4779     // scale inBufferSize according to user->stream sample rate ratio\r
4780     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
4781     outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
4782 \r
4783     // set renderBuffer size\r
4784     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4785 \r
4786     // reset the render stream\r
4787     hr = renderAudioClient->Reset();\r
4788     if ( FAILED( hr ) ) {\r
4789       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
4790       goto Exit;\r
4791     }\r
4792 \r
4793     // start the render stream\r
4794     hr = renderAudioClient->Start();\r
4795     if ( FAILED( hr ) ) {\r
4796       errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
4797       goto Exit;\r
4798     }\r
4799   }\r
4800 \r
4801   if ( stream_.mode == INPUT ) {\r
4802     convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4803     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4804   }\r
4805   else if ( stream_.mode == OUTPUT ) {\r
4806     convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4807     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4808   }\r
4809   else if ( stream_.mode == DUPLEX ) {\r
4810     convBuffSize = 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     deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4813                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4814   }\r
4815 \r
4816   convBuffer = ( char* ) malloc( convBuffSize );\r
4817   stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
4818   if ( !convBuffer || !stream_.deviceBuffer ) {\r
4819     errorType = RtAudioError::MEMORY_ERROR;\r
4820     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
4821     goto Exit;\r
4822   }\r
4823 \r
4824   // stream process loop\r
4825   while ( stream_.state != STREAM_STOPPING ) {\r
4826     if ( !callbackPulled ) {\r
4827       // Callback Input\r
4828       // ==============\r
4829       // 1. Pull callback buffer from inputBuffer\r
4830       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
4831       //                          Convert callback buffer to user format\r
4832 \r
4833       if ( captureAudioClient ) {\r
4834         // Pull callback buffer from inputBuffer\r
4835         callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
4836                                                    ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
4837                                                    stream_.deviceFormat[INPUT] );\r
4838 \r
4839         if ( callbackPulled ) {\r
4840           // Convert callback buffer to user sample rate\r
4841           convertBufferWasapi( stream_.deviceBuffer,\r
4842                                convBuffer,\r
4843                                stream_.nDeviceChannels[INPUT],\r
4844                                captureFormat->nSamplesPerSec,\r
4845                                stream_.sampleRate,\r
4846                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
4847                                convBufferSize,\r
4848                                stream_.deviceFormat[INPUT] );\r
4849 \r
4850           if ( stream_.doConvertBuffer[INPUT] ) {\r
4851             // Convert callback buffer to user format\r
4852             convertBuffer( stream_.userBuffer[INPUT],\r
4853                            stream_.deviceBuffer,\r
4854                            stream_.convertInfo[INPUT] );\r
4855           }\r
4856           else {\r
4857             // no further conversion, simple copy deviceBuffer to userBuffer\r
4858             memcpy( stream_.userBuffer[INPUT],\r
4859                     stream_.deviceBuffer,\r
4860                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
4861           }\r
4862         }\r
4863       }\r
4864       else {\r
4865         // if there is no capture stream, set callbackPulled flag\r
4866         callbackPulled = true;\r
4867       }\r
4868 \r
4869       // Execute Callback\r
4870       // ================\r
4871       // 1. Execute user callback method\r
4872       // 2. Handle return value from callback\r
4873 \r
4874       // if callback has not requested the stream to stop\r
4875       if ( callbackPulled && !callbackStopped ) {\r
4876         // Execute user callback method\r
4877         callbackResult = callback( stream_.userBuffer[OUTPUT],\r
4878                                    stream_.userBuffer[INPUT],\r
4879                                    stream_.bufferSize,\r
4880                                    getStreamTime(),\r
4881                                    captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
4882                                    stream_.callbackInfo.userData );\r
4883 \r
4884         // Handle return value from callback\r
4885         if ( callbackResult == 1 ) {\r
4886           // instantiate a thread to stop this thread\r
4887           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
4888           if ( !threadHandle ) {\r
4889             errorType = RtAudioError::THREAD_ERROR;\r
4890             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
4891             goto Exit;\r
4892           }\r
4893           else if ( !CloseHandle( threadHandle ) ) {\r
4894             errorType = RtAudioError::THREAD_ERROR;\r
4895             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
4896             goto Exit;\r
4897           }\r
4898 \r
4899           callbackStopped = true;\r
4900         }\r
4901         else if ( callbackResult == 2 ) {\r
4902           // instantiate a thread to stop this thread\r
4903           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
4904           if ( !threadHandle ) {\r
4905             errorType = RtAudioError::THREAD_ERROR;\r
4906             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
4907             goto Exit;\r
4908           }\r
4909           else if ( !CloseHandle( threadHandle ) ) {\r
4910             errorType = RtAudioError::THREAD_ERROR;\r
4911             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
4912             goto Exit;\r
4913           }\r
4914 \r
4915           callbackStopped = true;\r
4916         }\r
4917       }\r
4918     }\r
4919 \r
4920     // Callback Output\r
4921     // ===============\r
4922     // 1. Convert callback buffer to stream format\r
4923     // 2. Convert callback buffer to stream sample rate and channel count\r
4924     // 3. Push callback buffer into outputBuffer\r
4925 \r
4926     if ( renderAudioClient && callbackPulled ) {\r
4927       if ( stream_.doConvertBuffer[OUTPUT] ) {\r
4928         // Convert callback buffer to stream format\r
4929         convertBuffer( stream_.deviceBuffer,\r
4930                        stream_.userBuffer[OUTPUT],\r
4931                        stream_.convertInfo[OUTPUT] );\r
4932 \r
4933       }\r
4934 \r
4935       // Convert callback buffer to stream sample rate\r
4936       convertBufferWasapi( convBuffer,\r
4937                            stream_.deviceBuffer,\r
4938                            stream_.nDeviceChannels[OUTPUT],\r
4939                            stream_.sampleRate,\r
4940                            renderFormat->nSamplesPerSec,\r
4941                            stream_.bufferSize,\r
4942                            convBufferSize,\r
4943                            stream_.deviceFormat[OUTPUT] );\r
4944 \r
4945       // Push callback buffer into outputBuffer\r
4946       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
4947                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
4948                                                 stream_.deviceFormat[OUTPUT] );\r
4949     }\r
4950     else {\r
4951       // if there is no render stream, set callbackPushed flag\r
4952       callbackPushed = true;\r
4953     }\r
4954 \r
4955     // Stream Capture\r
4956     // ==============\r
4957     // 1. Get capture buffer from stream\r
4958     // 2. Push capture buffer into inputBuffer\r
4959     // 3. If 2. was successful: Release capture buffer\r
4960 \r
4961     if ( captureAudioClient ) {\r
4962       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
4963       if ( !callbackPulled ) {\r
4964         WaitForSingleObject( captureEvent, INFINITE );\r
4965       }\r
4966 \r
4967       // Get capture buffer from stream\r
4968       hr = captureClient->GetBuffer( &streamBuffer,\r
4969                                      &bufferFrameCount,\r
4970                                      &captureFlags, NULL, NULL );\r
4971       if ( FAILED( hr ) ) {\r
4972         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
4973         goto Exit;\r
4974       }\r
4975 \r
4976       if ( bufferFrameCount != 0 ) {\r
4977         // Push capture buffer into inputBuffer\r
4978         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
4979                                        bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
4980                                        stream_.deviceFormat[INPUT] ) )\r
4981         {\r
4982           // Release capture buffer\r
4983           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
4984           if ( FAILED( hr ) ) {\r
4985             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
4986             goto Exit;\r
4987           }\r
4988         }\r
4989         else\r
4990         {\r
4991           // Inform WASAPI that capture was unsuccessful\r
4992           hr = captureClient->ReleaseBuffer( 0 );\r
4993           if ( FAILED( hr ) ) {\r
4994             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
4995             goto Exit;\r
4996           }\r
4997         }\r
4998       }\r
4999       else\r
5000       {\r
5001         // Inform WASAPI that capture was unsuccessful\r
5002         hr = captureClient->ReleaseBuffer( 0 );\r
5003         if ( FAILED( hr ) ) {\r
5004           errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5005           goto Exit;\r
5006         }\r
5007       }\r
5008     }\r
5009 \r
5010     // Stream Render\r
5011     // =============\r
5012     // 1. Get render buffer from stream\r
5013     // 2. Pull next buffer from outputBuffer\r
5014     // 3. If 2. was successful: Fill render buffer with next buffer\r
5015     //                          Release render buffer\r
5016 \r
5017     if ( renderAudioClient ) {\r
5018       // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
5019       if ( callbackPulled && !callbackPushed ) {\r
5020         WaitForSingleObject( renderEvent, INFINITE );\r
5021       }\r
5022 \r
5023       // Get render buffer from stream\r
5024       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
5025       if ( FAILED( hr ) ) {\r
5026         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
5027         goto Exit;\r
5028       }\r
5029 \r
5030       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
5031       if ( FAILED( hr ) ) {\r
5032         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
5033         goto Exit;\r
5034       }\r
5035 \r
5036       bufferFrameCount -= numFramesPadding;\r
5037 \r
5038       if ( bufferFrameCount != 0 ) {\r
5039         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
5040         if ( FAILED( hr ) ) {\r
5041           errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
5042           goto Exit;\r
5043         }\r
5044 \r
5045         // Pull next buffer from outputBuffer\r
5046         // Fill render buffer with next buffer\r
5047         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
5048                                       bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
5049                                       stream_.deviceFormat[OUTPUT] ) )\r
5050         {\r
5051           // Release render buffer\r
5052           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
5053           if ( FAILED( hr ) ) {\r
5054             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5055             goto Exit;\r
5056           }\r
5057         }\r
5058         else\r
5059         {\r
5060           // Inform WASAPI that render was unsuccessful\r
5061           hr = renderClient->ReleaseBuffer( 0, 0 );\r
5062           if ( FAILED( hr ) ) {\r
5063             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5064             goto Exit;\r
5065           }\r
5066         }\r
5067       }\r
5068       else\r
5069       {\r
5070         // Inform WASAPI that render was unsuccessful\r
5071         hr = renderClient->ReleaseBuffer( 0, 0 );\r
5072         if ( FAILED( hr ) ) {\r
5073           errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5074           goto Exit;\r
5075         }\r
5076       }\r
5077     }\r
5078 \r
5079     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
5080     if ( callbackPushed ) {\r
5081       callbackPulled = false;\r
5082     }\r
5083 \r
5084     // tick stream time\r
5085     RtApi::tickStreamTime();\r
5086   }\r
5087 \r
5088 Exit:\r
5089   // clean up\r
5090   CoTaskMemFree( captureFormat );\r
5091   CoTaskMemFree( renderFormat );\r
5092 \r
5093   free ( convBuffer );\r
5094 \r
5095   CoUninitialize();\r
5096 \r
5097   // update stream state\r
5098   stream_.state = STREAM_STOPPED;\r
5099 \r
5100   if ( errorText_.empty() )\r
5101     return;\r
5102   else\r
5103     error( errorType );\r
5104 }\r
5105 \r
5106 //******************** End of __WINDOWS_WASAPI__ *********************//\r
5107 #endif\r
5108 \r
5109 \r
5110 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
5111 \r
5112 // Modified by Robin Davies, October 2005\r
5113 // - Improvements to DirectX pointer chasing. \r
5114 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
5115 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
5116 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
5117 // Changed device query structure for RtAudio 4.0.7, January 2010\r
5118 \r
5119 #include <dsound.h>\r
5120 #include <assert.h>\r
5121 #include <algorithm>\r
5122 \r
5123 #if defined(__MINGW32__)\r
5124   // missing from latest mingw winapi\r
5125 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
5126 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
5127 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
5128 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
5129 #endif\r
5130 \r
5131 #define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
5132 \r
5133 #ifdef _MSC_VER // if Microsoft Visual C++\r
5134 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
5135 #endif\r
5136 \r
5137 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
5138 {\r
5139   if ( pointer > bufferSize ) pointer -= bufferSize;\r
5140   if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
5141   if ( pointer < earlierPointer ) pointer += bufferSize;\r
5142   return pointer >= earlierPointer && pointer < laterPointer;\r
5143 }\r
5144 \r
5145 // A structure to hold various information related to the DirectSound\r
5146 // API implementation.\r
5147 struct DsHandle {\r
5148   unsigned int drainCounter; // Tracks callback counts when draining\r
5149   bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
5150   void *id[2];\r
5151   void *buffer[2];\r
5152   bool xrun[2];\r
5153   UINT bufferPointer[2];  \r
5154   DWORD dsBufferSize[2];\r
5155   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
5156   HANDLE condition;\r
5157 \r
5158   DsHandle()\r
5159     :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
5160 };\r
5161 \r
5162 // Declarations for utility functions, callbacks, and structures\r
5163 // specific to the DirectSound implementation.\r
5164 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
5165                                           LPCTSTR description,\r
5166                                           LPCTSTR module,\r
5167                                           LPVOID lpContext );\r
5168 \r
5169 static const char* getErrorString( int code );\r
5170 \r
5171 static unsigned __stdcall callbackHandler( void *ptr );\r
5172 \r
5173 struct DsDevice {\r
5174   LPGUID id[2];\r
5175   bool validId[2];\r
5176   bool found;\r
5177   std::string name;\r
5178 \r
5179   DsDevice()\r
5180   : found(false) { validId[0] = false; validId[1] = false; }\r
5181 };\r
5182 \r
5183 struct DsProbeData {\r
5184   bool isInput;\r
5185   std::vector<struct DsDevice>* dsDevices;\r
5186 };\r
5187 \r
5188 RtApiDs :: RtApiDs()\r
5189 {\r
5190   // Dsound will run both-threaded. If CoInitialize fails, then just\r
5191   // accept whatever the mainline chose for a threading model.\r
5192   coInitialized_ = false;\r
5193   HRESULT hr = CoInitialize( NULL );\r
5194   if ( !FAILED( hr ) ) coInitialized_ = true;\r
5195 }\r
5196 \r
5197 RtApiDs :: ~RtApiDs()\r
5198 {\r
5199   if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
5200   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
5201 }\r
5202 \r
5203 // The DirectSound default output is always the first device.\r
5204 unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
5205 {\r
5206   return 0;\r
5207 }\r
5208 \r
5209 // The DirectSound default input is always the first input device,\r
5210 // which is the first capture device enumerated.\r
5211 unsigned int RtApiDs :: getDefaultInputDevice( void )\r
5212 {\r
5213   return 0;\r
5214 }\r
5215 \r
5216 unsigned int RtApiDs :: getDeviceCount( void )\r
5217 {\r
5218   // Set query flag for previously found devices to false, so that we\r
5219   // can check for any devices that have disappeared.\r
5220   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5221     dsDevices[i].found = false;\r
5222 \r
5223   // Query DirectSound devices.\r
5224   struct DsProbeData probeInfo;\r
5225   probeInfo.isInput = false;\r
5226   probeInfo.dsDevices = &dsDevices;\r
5227   HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5228   if ( FAILED( result ) ) {\r
5229     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
5230     errorText_ = errorStream_.str();\r
5231     error( RtAudioError::WARNING );\r
5232   }\r
5233 \r
5234   // Query DirectSoundCapture devices.\r
5235   probeInfo.isInput = true;\r
5236   result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5237   if ( FAILED( result ) ) {\r
5238     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
5239     errorText_ = errorStream_.str();\r
5240     error( RtAudioError::WARNING );\r
5241   }\r
5242 \r
5243   // Clean out any devices that may have disappeared.\r
5244   std::vector< int > indices;\r
5245   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5246     if ( dsDevices[i].found == false ) indices.push_back( i );\r
5247   //unsigned int nErased = 0;\r
5248   for ( unsigned int i=0; i<indices.size(); i++ )\r
5249     dsDevices.erase( dsDevices.begin()+indices[i] );\r
5250   //dsDevices.erase( dsDevices.begin()-nErased++ );\r
5251 \r
5252   return static_cast<unsigned int>(dsDevices.size());\r
5253 }\r
5254 \r
5255 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
5256 {\r
5257   RtAudio::DeviceInfo info;\r
5258   info.probed = false;\r
5259 \r
5260   if ( dsDevices.size() == 0 ) {\r
5261     // Force a query of all devices\r
5262     getDeviceCount();\r
5263     if ( dsDevices.size() == 0 ) {\r
5264       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
5265       error( RtAudioError::INVALID_USE );\r
5266       return info;\r
5267     }\r
5268   }\r
5269 \r
5270   if ( device >= dsDevices.size() ) {\r
5271     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
5272     error( RtAudioError::INVALID_USE );\r
5273     return info;\r
5274   }\r
5275 \r
5276   HRESULT result;\r
5277   if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
5278 \r
5279   LPDIRECTSOUND output;\r
5280   DSCAPS outCaps;\r
5281   result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5282   if ( FAILED( result ) ) {\r
5283     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5284     errorText_ = errorStream_.str();\r
5285     error( RtAudioError::WARNING );\r
5286     goto probeInput;\r
5287   }\r
5288 \r
5289   outCaps.dwSize = sizeof( outCaps );\r
5290   result = output->GetCaps( &outCaps );\r
5291   if ( FAILED( result ) ) {\r
5292     output->Release();\r
5293     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
5294     errorText_ = errorStream_.str();\r
5295     error( RtAudioError::WARNING );\r
5296     goto probeInput;\r
5297   }\r
5298 \r
5299   // Get output channel information.\r
5300   info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
5301 \r
5302   // Get sample rate information.\r
5303   info.sampleRates.clear();\r
5304   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
5305     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
5306          SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )\r
5307       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
5308   }\r
5309 \r
5310   // Get format information.\r
5311   if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
5312   if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
5313 \r
5314   output->Release();\r
5315 \r
5316   if ( getDefaultOutputDevice() == device )\r
5317     info.isDefaultOutput = true;\r
5318 \r
5319   if ( dsDevices[ device ].validId[1] == false ) {\r
5320     info.name = dsDevices[ device ].name;\r
5321     info.probed = true;\r
5322     return info;\r
5323   }\r
5324 \r
5325  probeInput:\r
5326 \r
5327   LPDIRECTSOUNDCAPTURE input;\r
5328   result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5329   if ( FAILED( result ) ) {\r
5330     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5331     errorText_ = errorStream_.str();\r
5332     error( RtAudioError::WARNING );\r
5333     return info;\r
5334   }\r
5335 \r
5336   DSCCAPS inCaps;\r
5337   inCaps.dwSize = sizeof( inCaps );\r
5338   result = input->GetCaps( &inCaps );\r
5339   if ( FAILED( result ) ) {\r
5340     input->Release();\r
5341     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
5342     errorText_ = errorStream_.str();\r
5343     error( RtAudioError::WARNING );\r
5344     return info;\r
5345   }\r
5346 \r
5347   // Get input channel information.\r
5348   info.inputChannels = inCaps.dwChannels;\r
5349 \r
5350   // Get sample rate and format information.\r
5351   std::vector<unsigned int> rates;\r
5352   if ( inCaps.dwChannels >= 2 ) {\r
5353     if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5354     if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5355     if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5356     if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5357     if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5358     if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5359     if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5360     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5361 \r
5362     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5363       if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
5364       if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
5365       if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
5366       if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
5367     }\r
5368     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5369       if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
5370       if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
5371       if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
5372       if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
5373     }\r
5374   }\r
5375   else if ( inCaps.dwChannels == 1 ) {\r
5376     if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5377     if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5378     if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5379     if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5380     if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5381     if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5382     if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5383     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5384 \r
5385     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5386       if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
5387       if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
5388       if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
5389       if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
5390     }\r
5391     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5392       if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
5393       if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
5394       if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
5395       if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
5396     }\r
5397   }\r
5398   else info.inputChannels = 0; // technically, this would be an error\r
5399 \r
5400   input->Release();\r
5401 \r
5402   if ( info.inputChannels == 0 ) return info;\r
5403 \r
5404   // Copy the supported rates to the info structure but avoid duplication.\r
5405   bool found;\r
5406   for ( unsigned int i=0; i<rates.size(); i++ ) {\r
5407     found = false;\r
5408     for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
5409       if ( rates[i] == info.sampleRates[j] ) {\r
5410         found = true;\r
5411         break;\r
5412       }\r
5413     }\r
5414     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
5415   }\r
5416   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
5417 \r
5418   // If device opens for both playback and capture, we determine the channels.\r
5419   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
5420     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
5421 \r
5422   if ( device == 0 ) info.isDefaultInput = true;\r
5423 \r
5424   // Copy name and return.\r
5425   info.name = dsDevices[ device ].name;\r
5426   info.probed = true;\r
5427   return info;\r
5428 }\r
5429 \r
5430 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
5431                                  unsigned int firstChannel, unsigned int sampleRate,\r
5432                                  RtAudioFormat format, unsigned int *bufferSize,\r
5433                                  RtAudio::StreamOptions *options )\r
5434 {\r
5435   if ( channels + firstChannel > 2 ) {\r
5436     errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
5437     return FAILURE;\r
5438   }\r
5439 \r
5440   size_t nDevices = dsDevices.size();\r
5441   if ( nDevices == 0 ) {\r
5442     // This should not happen because a check is made before this function is called.\r
5443     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
5444     return FAILURE;\r
5445   }\r
5446 \r
5447   if ( device >= nDevices ) {\r
5448     // This should not happen because a check is made before this function is called.\r
5449     errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
5450     return FAILURE;\r
5451   }\r
5452 \r
5453   if ( mode == OUTPUT ) {\r
5454     if ( dsDevices[ device ].validId[0] == false ) {\r
5455       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
5456       errorText_ = errorStream_.str();\r
5457       return FAILURE;\r
5458     }\r
5459   }\r
5460   else { // mode == INPUT\r
5461     if ( dsDevices[ device ].validId[1] == false ) {\r
5462       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
5463       errorText_ = errorStream_.str();\r
5464       return FAILURE;\r
5465     }\r
5466   }\r
5467 \r
5468   // According to a note in PortAudio, using GetDesktopWindow()\r
5469   // instead of GetForegroundWindow() is supposed to avoid problems\r
5470   // that occur when the application's window is not the foreground\r
5471   // window.  Also, if the application window closes before the\r
5472   // DirectSound buffer, DirectSound can crash.  In the past, I had\r
5473   // problems when using GetDesktopWindow() but it seems fine now\r
5474   // (January 2010).  I'll leave it commented here.\r
5475   // HWND hWnd = GetForegroundWindow();\r
5476   HWND hWnd = GetDesktopWindow();\r
5477 \r
5478   // Check the numberOfBuffers parameter and limit the lowest value to\r
5479   // two.  This is a judgement call and a value of two is probably too\r
5480   // low for capture, but it should work for playback.\r
5481   int nBuffers = 0;\r
5482   if ( options ) nBuffers = options->numberOfBuffers;\r
5483   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
5484   if ( nBuffers < 2 ) nBuffers = 3;\r
5485 \r
5486   // Check the lower range of the user-specified buffer size and set\r
5487   // (arbitrarily) to a lower bound of 32.\r
5488   if ( *bufferSize < 32 ) *bufferSize = 32;\r
5489 \r
5490   // Create the wave format structure.  The data format setting will\r
5491   // be determined later.\r
5492   WAVEFORMATEX waveFormat;\r
5493   ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
5494   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
5495   waveFormat.nChannels = channels + firstChannel;\r
5496   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
5497 \r
5498   // Determine the device buffer size. By default, we'll use the value\r
5499   // defined above (32K), but we will grow it to make allowances for\r
5500   // very large software buffer sizes.\r
5501   DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
5502   DWORD dsPointerLeadTime = 0;\r
5503 \r
5504   void *ohandle = 0, *bhandle = 0;\r
5505   HRESULT result;\r
5506   if ( mode == OUTPUT ) {\r
5507 \r
5508     LPDIRECTSOUND output;\r
5509     result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5510     if ( FAILED( result ) ) {\r
5511       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5512       errorText_ = errorStream_.str();\r
5513       return FAILURE;\r
5514     }\r
5515 \r
5516     DSCAPS outCaps;\r
5517     outCaps.dwSize = sizeof( outCaps );\r
5518     result = output->GetCaps( &outCaps );\r
5519     if ( FAILED( result ) ) {\r
5520       output->Release();\r
5521       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
5522       errorText_ = errorStream_.str();\r
5523       return FAILURE;\r
5524     }\r
5525 \r
5526     // Check channel information.\r
5527     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
5528       errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
5529       errorText_ = errorStream_.str();\r
5530       return FAILURE;\r
5531     }\r
5532 \r
5533     // Check format information.  Use 16-bit format unless not\r
5534     // supported or user requests 8-bit.\r
5535     if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
5536          !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
5537       waveFormat.wBitsPerSample = 16;\r
5538       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5539     }\r
5540     else {\r
5541       waveFormat.wBitsPerSample = 8;\r
5542       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5543     }\r
5544     stream_.userFormat = format;\r
5545 \r
5546     // Update wave format structure and buffer information.\r
5547     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5548     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5549     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5550 \r
5551     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5552     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5553       dsBufferSize *= 2;\r
5554 \r
5555     // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
5556     // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
5557     // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
5558     result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
5559     if ( FAILED( result ) ) {\r
5560       output->Release();\r
5561       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
5562       errorText_ = errorStream_.str();\r
5563       return FAILURE;\r
5564     }\r
5565 \r
5566     // Even though we will write to the secondary buffer, we need to\r
5567     // access the primary buffer to set the correct output format\r
5568     // (since the default is 8-bit, 22 kHz!).  Setup the DS primary\r
5569     // buffer description.\r
5570     DSBUFFERDESC bufferDescription;\r
5571     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5572     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5573     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
5574 \r
5575     // Obtain the primary buffer\r
5576     LPDIRECTSOUNDBUFFER buffer;\r
5577     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5578     if ( FAILED( result ) ) {\r
5579       output->Release();\r
5580       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
5581       errorText_ = errorStream_.str();\r
5582       return FAILURE;\r
5583     }\r
5584 \r
5585     // Set the primary DS buffer sound format.\r
5586     result = buffer->SetFormat( &waveFormat );\r
5587     if ( FAILED( result ) ) {\r
5588       output->Release();\r
5589       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
5590       errorText_ = errorStream_.str();\r
5591       return FAILURE;\r
5592     }\r
5593 \r
5594     // Setup the secondary DS buffer description.\r
5595     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5596     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5597     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5598                                   DSBCAPS_GLOBALFOCUS |\r
5599                                   DSBCAPS_GETCURRENTPOSITION2 |\r
5600                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
5601     bufferDescription.dwBufferBytes = dsBufferSize;\r
5602     bufferDescription.lpwfxFormat = &waveFormat;\r
5603 \r
5604     // Try to create the secondary DS buffer.  If that doesn't work,\r
5605     // try to use software mixing.  Otherwise, there's a problem.\r
5606     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5607     if ( FAILED( result ) ) {\r
5608       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5609                                     DSBCAPS_GLOBALFOCUS |\r
5610                                     DSBCAPS_GETCURRENTPOSITION2 |\r
5611                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
5612       result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5613       if ( FAILED( result ) ) {\r
5614         output->Release();\r
5615         errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
5616         errorText_ = errorStream_.str();\r
5617         return FAILURE;\r
5618       }\r
5619     }\r
5620 \r
5621     // Get the buffer size ... might be different from what we specified.\r
5622     DSBCAPS dsbcaps;\r
5623     dsbcaps.dwSize = sizeof( DSBCAPS );\r
5624     result = buffer->GetCaps( &dsbcaps );\r
5625     if ( FAILED( result ) ) {\r
5626       output->Release();\r
5627       buffer->Release();\r
5628       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5629       errorText_ = errorStream_.str();\r
5630       return FAILURE;\r
5631     }\r
5632 \r
5633     dsBufferSize = dsbcaps.dwBufferBytes;\r
5634 \r
5635     // Lock the DS buffer\r
5636     LPVOID audioPtr;\r
5637     DWORD dataLen;\r
5638     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5639     if ( FAILED( result ) ) {\r
5640       output->Release();\r
5641       buffer->Release();\r
5642       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
5643       errorText_ = errorStream_.str();\r
5644       return FAILURE;\r
5645     }\r
5646 \r
5647     // Zero the DS buffer\r
5648     ZeroMemory( audioPtr, dataLen );\r
5649 \r
5650     // Unlock the DS buffer\r
5651     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5652     if ( FAILED( result ) ) {\r
5653       output->Release();\r
5654       buffer->Release();\r
5655       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
5656       errorText_ = errorStream_.str();\r
5657       return FAILURE;\r
5658     }\r
5659 \r
5660     ohandle = (void *) output;\r
5661     bhandle = (void *) buffer;\r
5662   }\r
5663 \r
5664   if ( mode == INPUT ) {\r
5665 \r
5666     LPDIRECTSOUNDCAPTURE input;\r
5667     result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5668     if ( FAILED( result ) ) {\r
5669       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5670       errorText_ = errorStream_.str();\r
5671       return FAILURE;\r
5672     }\r
5673 \r
5674     DSCCAPS inCaps;\r
5675     inCaps.dwSize = sizeof( inCaps );\r
5676     result = input->GetCaps( &inCaps );\r
5677     if ( FAILED( result ) ) {\r
5678       input->Release();\r
5679       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
5680       errorText_ = errorStream_.str();\r
5681       return FAILURE;\r
5682     }\r
5683 \r
5684     // Check channel information.\r
5685     if ( inCaps.dwChannels < channels + firstChannel ) {\r
5686       errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
5687       return FAILURE;\r
5688     }\r
5689 \r
5690     // Check format information.  Use 16-bit format unless user\r
5691     // requests 8-bit.\r
5692     DWORD deviceFormats;\r
5693     if ( channels + firstChannel == 2 ) {\r
5694       deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
5695       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5696         waveFormat.wBitsPerSample = 8;\r
5697         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5698       }\r
5699       else { // assume 16-bit is supported\r
5700         waveFormat.wBitsPerSample = 16;\r
5701         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5702       }\r
5703     }\r
5704     else { // channel == 1\r
5705       deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
5706       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5707         waveFormat.wBitsPerSample = 8;\r
5708         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5709       }\r
5710       else { // assume 16-bit is supported\r
5711         waveFormat.wBitsPerSample = 16;\r
5712         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5713       }\r
5714     }\r
5715     stream_.userFormat = format;\r
5716 \r
5717     // Update wave format structure and buffer information.\r
5718     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5719     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5720     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5721 \r
5722     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5723     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5724       dsBufferSize *= 2;\r
5725 \r
5726     // Setup the secondary DS buffer description.\r
5727     DSCBUFFERDESC bufferDescription;\r
5728     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
5729     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
5730     bufferDescription.dwFlags = 0;\r
5731     bufferDescription.dwReserved = 0;\r
5732     bufferDescription.dwBufferBytes = dsBufferSize;\r
5733     bufferDescription.lpwfxFormat = &waveFormat;\r
5734 \r
5735     // Create the capture buffer.\r
5736     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
5737     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
5738     if ( FAILED( result ) ) {\r
5739       input->Release();\r
5740       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
5741       errorText_ = errorStream_.str();\r
5742       return FAILURE;\r
5743     }\r
5744 \r
5745     // Get the buffer size ... might be different from what we specified.\r
5746     DSCBCAPS dscbcaps;\r
5747     dscbcaps.dwSize = sizeof( DSCBCAPS );\r
5748     result = buffer->GetCaps( &dscbcaps );\r
5749     if ( FAILED( result ) ) {\r
5750       input->Release();\r
5751       buffer->Release();\r
5752       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5753       errorText_ = errorStream_.str();\r
5754       return FAILURE;\r
5755     }\r
5756 \r
5757     dsBufferSize = dscbcaps.dwBufferBytes;\r
5758 \r
5759     // NOTE: We could have a problem here if this is a duplex stream\r
5760     // and the play and capture hardware buffer sizes are different\r
5761     // (I'm actually not sure if that is a problem or not).\r
5762     // Currently, we are not verifying that.\r
5763 \r
5764     // Lock the capture buffer\r
5765     LPVOID audioPtr;\r
5766     DWORD dataLen;\r
5767     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5768     if ( FAILED( result ) ) {\r
5769       input->Release();\r
5770       buffer->Release();\r
5771       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
5772       errorText_ = errorStream_.str();\r
5773       return FAILURE;\r
5774     }\r
5775 \r
5776     // Zero the buffer\r
5777     ZeroMemory( audioPtr, dataLen );\r
5778 \r
5779     // Unlock the buffer\r
5780     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5781     if ( FAILED( result ) ) {\r
5782       input->Release();\r
5783       buffer->Release();\r
5784       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
5785       errorText_ = errorStream_.str();\r
5786       return FAILURE;\r
5787     }\r
5788 \r
5789     ohandle = (void *) input;\r
5790     bhandle = (void *) buffer;\r
5791   }\r
5792 \r
5793   // Set various stream parameters\r
5794   DsHandle *handle = 0;\r
5795   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
5796   stream_.nUserChannels[mode] = channels;\r
5797   stream_.bufferSize = *bufferSize;\r
5798   stream_.channelOffset[mode] = firstChannel;\r
5799   stream_.deviceInterleaved[mode] = true;\r
5800   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
5801   else stream_.userInterleaved = true;\r
5802 \r
5803   // Set flag for buffer conversion\r
5804   stream_.doConvertBuffer[mode] = false;\r
5805   if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
5806     stream_.doConvertBuffer[mode] = true;\r
5807   if (stream_.userFormat != stream_.deviceFormat[mode])\r
5808     stream_.doConvertBuffer[mode] = true;\r
5809   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
5810        stream_.nUserChannels[mode] > 1 )\r
5811     stream_.doConvertBuffer[mode] = true;\r
5812 \r
5813   // Allocate necessary internal buffers\r
5814   long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
5815   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
5816   if ( stream_.userBuffer[mode] == NULL ) {\r
5817     errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
5818     goto error;\r
5819   }\r
5820 \r
5821   if ( stream_.doConvertBuffer[mode] ) {\r
5822 \r
5823     bool makeBuffer = true;\r
5824     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
5825     if ( mode == INPUT ) {\r
5826       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
5827         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
5828         if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
5829       }\r
5830     }\r
5831 \r
5832     if ( makeBuffer ) {\r
5833       bufferBytes *= *bufferSize;\r
5834       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
5835       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
5836       if ( stream_.deviceBuffer == NULL ) {\r
5837         errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
5838         goto error;\r
5839       }\r
5840     }\r
5841   }\r
5842 \r
5843   // Allocate our DsHandle structures for the stream.\r
5844   if ( stream_.apiHandle == 0 ) {\r
5845     try {\r
5846       handle = new DsHandle;\r
5847     }\r
5848     catch ( std::bad_alloc& ) {\r
5849       errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
5850       goto error;\r
5851     }\r
5852 \r
5853     // Create a manual-reset event.\r
5854     handle->condition = CreateEvent( NULL,   // no security\r
5855                                      TRUE,   // manual-reset\r
5856                                      FALSE,  // non-signaled initially\r
5857                                      NULL ); // unnamed\r
5858     stream_.apiHandle = (void *) handle;\r
5859   }\r
5860   else\r
5861     handle = (DsHandle *) stream_.apiHandle;\r
5862   handle->id[mode] = ohandle;\r
5863   handle->buffer[mode] = bhandle;\r
5864   handle->dsBufferSize[mode] = dsBufferSize;\r
5865   handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
5866 \r
5867   stream_.device[mode] = device;\r
5868   stream_.state = STREAM_STOPPED;\r
5869   if ( stream_.mode == OUTPUT && mode == INPUT )\r
5870     // We had already set up an output stream.\r
5871     stream_.mode = DUPLEX;\r
5872   else\r
5873     stream_.mode = mode;\r
5874   stream_.nBuffers = nBuffers;\r
5875   stream_.sampleRate = sampleRate;\r
5876 \r
5877   // Setup the buffer conversion information structure.\r
5878   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
5879 \r
5880   // Setup the callback thread.\r
5881   if ( stream_.callbackInfo.isRunning == false ) {\r
5882     unsigned threadId;\r
5883     stream_.callbackInfo.isRunning = true;\r
5884     stream_.callbackInfo.object = (void *) this;\r
5885     stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
5886                                                   &stream_.callbackInfo, 0, &threadId );\r
5887     if ( stream_.callbackInfo.thread == 0 ) {\r
5888       errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
5889       goto error;\r
5890     }\r
5891 \r
5892     // Boost DS thread priority\r
5893     SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
5894   }\r
5895   return SUCCESS;\r
5896 \r
5897  error:\r
5898   if ( handle ) {\r
5899     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5900       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5901       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5902       if ( buffer ) buffer->Release();\r
5903       object->Release();\r
5904     }\r
5905     if ( handle->buffer[1] ) {\r
5906       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5907       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5908       if ( buffer ) buffer->Release();\r
5909       object->Release();\r
5910     }\r
5911     CloseHandle( handle->condition );\r
5912     delete handle;\r
5913     stream_.apiHandle = 0;\r
5914   }\r
5915 \r
5916   for ( int i=0; i<2; i++ ) {\r
5917     if ( stream_.userBuffer[i] ) {\r
5918       free( stream_.userBuffer[i] );\r
5919       stream_.userBuffer[i] = 0;\r
5920     }\r
5921   }\r
5922 \r
5923   if ( stream_.deviceBuffer ) {\r
5924     free( stream_.deviceBuffer );\r
5925     stream_.deviceBuffer = 0;\r
5926   }\r
5927 \r
5928   stream_.state = STREAM_CLOSED;\r
5929   return FAILURE;\r
5930 }\r
5931 \r
5932 void RtApiDs :: closeStream()\r
5933 {\r
5934   if ( stream_.state == STREAM_CLOSED ) {\r
5935     errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
5936     error( RtAudioError::WARNING );\r
5937     return;\r
5938   }\r
5939 \r
5940   // Stop the callback thread.\r
5941   stream_.callbackInfo.isRunning = false;\r
5942   WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
5943   CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
5944 \r
5945   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
5946   if ( handle ) {\r
5947     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5948       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5949       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5950       if ( buffer ) {\r
5951         buffer->Stop();\r
5952         buffer->Release();\r
5953       }\r
5954       object->Release();\r
5955     }\r
5956     if ( handle->buffer[1] ) {\r
5957       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5958       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5959       if ( buffer ) {\r
5960         buffer->Stop();\r
5961         buffer->Release();\r
5962       }\r
5963       object->Release();\r
5964     }\r
5965     CloseHandle( handle->condition );\r
5966     delete handle;\r
5967     stream_.apiHandle = 0;\r
5968   }\r
5969 \r
5970   for ( int i=0; i<2; i++ ) {\r
5971     if ( stream_.userBuffer[i] ) {\r
5972       free( stream_.userBuffer[i] );\r
5973       stream_.userBuffer[i] = 0;\r
5974     }\r
5975   }\r
5976 \r
5977   if ( stream_.deviceBuffer ) {\r
5978     free( stream_.deviceBuffer );\r
5979     stream_.deviceBuffer = 0;\r
5980   }\r
5981 \r
5982   stream_.mode = UNINITIALIZED;\r
5983   stream_.state = STREAM_CLOSED;\r
5984 }\r
5985 \r
5986 void RtApiDs :: startStream()\r
5987 {\r
5988   verifyStream();\r
5989   if ( stream_.state == STREAM_RUNNING ) {\r
5990     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
5991     error( RtAudioError::WARNING );\r
5992     return;\r
5993   }\r
5994 \r
5995   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
5996 \r
5997   // Increase scheduler frequency on lesser windows (a side-effect of\r
5998   // increasing timer accuracy).  On greater windows (Win2K or later),\r
5999   // this is already in effect.\r
6000   timeBeginPeriod( 1 ); \r
6001 \r
6002   buffersRolling = false;\r
6003   duplexPrerollBytes = 0;\r
6004 \r
6005   if ( stream_.mode == DUPLEX ) {\r
6006     // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
6007     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
6008   }\r
6009 \r
6010   HRESULT result = 0;\r
6011   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6012 \r
6013     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6014     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
6015     if ( FAILED( result ) ) {\r
6016       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
6017       errorText_ = errorStream_.str();\r
6018       goto unlock;\r
6019     }\r
6020   }\r
6021 \r
6022   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6023 \r
6024     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6025     result = buffer->Start( DSCBSTART_LOOPING );\r
6026     if ( FAILED( result ) ) {\r
6027       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
6028       errorText_ = errorStream_.str();\r
6029       goto unlock;\r
6030     }\r
6031   }\r
6032 \r
6033   handle->drainCounter = 0;\r
6034   handle->internalDrain = false;\r
6035   ResetEvent( handle->condition );\r
6036   stream_.state = STREAM_RUNNING;\r
6037 \r
6038  unlock:\r
6039   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6040 }\r
6041 \r
6042 void RtApiDs :: stopStream()\r
6043 {\r
6044   verifyStream();\r
6045   if ( stream_.state == STREAM_STOPPED ) {\r
6046     errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
6047     error( RtAudioError::WARNING );\r
6048     return;\r
6049   }\r
6050 \r
6051   HRESULT result = 0;\r
6052   LPVOID audioPtr;\r
6053   DWORD dataLen;\r
6054   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6055   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6056     if ( handle->drainCounter == 0 ) {\r
6057       handle->drainCounter = 2;\r
6058       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
6059     }\r
6060 \r
6061     stream_.state = STREAM_STOPPED;\r
6062 \r
6063     MUTEX_LOCK( &stream_.mutex );\r
6064 \r
6065     // Stop the buffer and clear memory\r
6066     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6067     result = buffer->Stop();\r
6068     if ( FAILED( result ) ) {\r
6069       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
6070       errorText_ = errorStream_.str();\r
6071       goto unlock;\r
6072     }\r
6073 \r
6074     // Lock the buffer and clear it so that if we start to play again,\r
6075     // we won't have old data playing.\r
6076     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6077     if ( FAILED( result ) ) {\r
6078       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
6079       errorText_ = errorStream_.str();\r
6080       goto unlock;\r
6081     }\r
6082 \r
6083     // Zero the DS buffer\r
6084     ZeroMemory( audioPtr, dataLen );\r
6085 \r
6086     // Unlock the DS buffer\r
6087     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6088     if ( FAILED( result ) ) {\r
6089       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
6090       errorText_ = errorStream_.str();\r
6091       goto unlock;\r
6092     }\r
6093 \r
6094     // If we start playing again, we must begin at beginning of buffer.\r
6095     handle->bufferPointer[0] = 0;\r
6096   }\r
6097 \r
6098   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6099     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6100     audioPtr = NULL;\r
6101     dataLen = 0;\r
6102 \r
6103     stream_.state = STREAM_STOPPED;\r
6104 \r
6105     if ( stream_.mode != DUPLEX )\r
6106       MUTEX_LOCK( &stream_.mutex );\r
6107 \r
6108     result = buffer->Stop();\r
6109     if ( FAILED( result ) ) {\r
6110       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
6111       errorText_ = errorStream_.str();\r
6112       goto unlock;\r
6113     }\r
6114 \r
6115     // Lock the buffer and clear it so that if we start to play again,\r
6116     // we won't have old data playing.\r
6117     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6118     if ( FAILED( result ) ) {\r
6119       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
6120       errorText_ = errorStream_.str();\r
6121       goto unlock;\r
6122     }\r
6123 \r
6124     // Zero the DS buffer\r
6125     ZeroMemory( audioPtr, dataLen );\r
6126 \r
6127     // Unlock the DS buffer\r
6128     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6129     if ( FAILED( result ) ) {\r
6130       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
6131       errorText_ = errorStream_.str();\r
6132       goto unlock;\r
6133     }\r
6134 \r
6135     // If we start recording again, we must begin at beginning of buffer.\r
6136     handle->bufferPointer[1] = 0;\r
6137   }\r
6138 \r
6139  unlock:\r
6140   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
6141   MUTEX_UNLOCK( &stream_.mutex );\r
6142 \r
6143   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6144 }\r
6145 \r
6146 void RtApiDs :: abortStream()\r
6147 {\r
6148   verifyStream();\r
6149   if ( stream_.state == STREAM_STOPPED ) {\r
6150     errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
6151     error( RtAudioError::WARNING );\r
6152     return;\r
6153   }\r
6154 \r
6155   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6156   handle->drainCounter = 2;\r
6157 \r
6158   stopStream();\r
6159 }\r
6160 \r
6161 void RtApiDs :: callbackEvent()\r
6162 {\r
6163   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
6164     Sleep( 50 ); // sleep 50 milliseconds\r
6165     return;\r
6166   }\r
6167 \r
6168   if ( stream_.state == STREAM_CLOSED ) {\r
6169     errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
6170     error( RtAudioError::WARNING );\r
6171     return;\r
6172   }\r
6173 \r
6174   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
6175   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6176 \r
6177   // Check if we were draining the stream and signal is finished.\r
6178   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
6179 \r
6180     stream_.state = STREAM_STOPPING;\r
6181     if ( handle->internalDrain == false )\r
6182       SetEvent( handle->condition );\r
6183     else\r
6184       stopStream();\r
6185     return;\r
6186   }\r
6187 \r
6188   // Invoke user callback to get fresh output data UNLESS we are\r
6189   // draining stream.\r
6190   if ( handle->drainCounter == 0 ) {\r
6191     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
6192     double streamTime = getStreamTime();\r
6193     RtAudioStreamStatus status = 0;\r
6194     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
6195       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
6196       handle->xrun[0] = false;\r
6197     }\r
6198     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
6199       status |= RTAUDIO_INPUT_OVERFLOW;\r
6200       handle->xrun[1] = false;\r
6201     }\r
6202     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
6203                                   stream_.bufferSize, streamTime, status, info->userData );\r
6204     if ( cbReturnValue == 2 ) {\r
6205       stream_.state = STREAM_STOPPING;\r
6206       handle->drainCounter = 2;\r
6207       abortStream();\r
6208       return;\r
6209     }\r
6210     else if ( cbReturnValue == 1 ) {\r
6211       handle->drainCounter = 1;\r
6212       handle->internalDrain = true;\r
6213     }\r
6214   }\r
6215 \r
6216   HRESULT result;\r
6217   DWORD currentWritePointer, safeWritePointer;\r
6218   DWORD currentReadPointer, safeReadPointer;\r
6219   UINT nextWritePointer;\r
6220 \r
6221   LPVOID buffer1 = NULL;\r
6222   LPVOID buffer2 = NULL;\r
6223   DWORD bufferSize1 = 0;\r
6224   DWORD bufferSize2 = 0;\r
6225 \r
6226   char *buffer;\r
6227   long bufferBytes;\r
6228 \r
6229   MUTEX_LOCK( &stream_.mutex );\r
6230   if ( stream_.state == STREAM_STOPPED ) {\r
6231     MUTEX_UNLOCK( &stream_.mutex );\r
6232     return;\r
6233   }\r
6234 \r
6235   if ( buffersRolling == false ) {\r
6236     if ( stream_.mode == DUPLEX ) {\r
6237       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6238 \r
6239       // It takes a while for the devices to get rolling. As a result,\r
6240       // there's no guarantee that the capture and write device pointers\r
6241       // will move in lockstep.  Wait here for both devices to start\r
6242       // rolling, and then set our buffer pointers accordingly.\r
6243       // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
6244       // bytes later than the write buffer.\r
6245 \r
6246       // Stub: a serious risk of having a pre-emptive scheduling round\r
6247       // take place between the two GetCurrentPosition calls... but I'm\r
6248       // really not sure how to solve the problem.  Temporarily boost to\r
6249       // Realtime priority, maybe; but I'm not sure what priority the\r
6250       // DirectSound service threads run at. We *should* be roughly\r
6251       // within a ms or so of correct.\r
6252 \r
6253       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6254       LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6255 \r
6256       DWORD startSafeWritePointer, startSafeReadPointer;\r
6257 \r
6258       result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
6259       if ( FAILED( result ) ) {\r
6260         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6261         errorText_ = errorStream_.str();\r
6262         error( RtAudioError::SYSTEM_ERROR );\r
6263         return;\r
6264       }\r
6265       result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
6266       if ( FAILED( result ) ) {\r
6267         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6268         errorText_ = errorStream_.str();\r
6269         error( RtAudioError::SYSTEM_ERROR );\r
6270         return;\r
6271       }\r
6272       while ( true ) {\r
6273         result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
6274         if ( FAILED( result ) ) {\r
6275           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6276           errorText_ = errorStream_.str();\r
6277           error( RtAudioError::SYSTEM_ERROR );\r
6278           return;\r
6279         }\r
6280         result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
6281         if ( FAILED( result ) ) {\r
6282           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6283           errorText_ = errorStream_.str();\r
6284           error( RtAudioError::SYSTEM_ERROR );\r
6285           return;\r
6286         }\r
6287         if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
6288         Sleep( 1 );\r
6289       }\r
6290 \r
6291       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6292 \r
6293       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6294       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6295       handle->bufferPointer[1] = safeReadPointer;\r
6296     }\r
6297     else if ( stream_.mode == OUTPUT ) {\r
6298 \r
6299       // Set the proper nextWritePosition after initial startup.\r
6300       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6301       result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6302       if ( FAILED( result ) ) {\r
6303         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6304         errorText_ = errorStream_.str();\r
6305         error( RtAudioError::SYSTEM_ERROR );\r
6306         return;\r
6307       }\r
6308       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6309       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6310     }\r
6311 \r
6312     buffersRolling = true;\r
6313   }\r
6314 \r
6315   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6316     \r
6317     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6318 \r
6319     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
6320       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6321       bufferBytes *= formatBytes( stream_.userFormat );\r
6322       memset( stream_.userBuffer[0], 0, bufferBytes );\r
6323     }\r
6324 \r
6325     // Setup parameters and do buffer conversion if necessary.\r
6326     if ( stream_.doConvertBuffer[0] ) {\r
6327       buffer = stream_.deviceBuffer;\r
6328       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
6329       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
6330       bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
6331     }\r
6332     else {\r
6333       buffer = stream_.userBuffer[0];\r
6334       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6335       bufferBytes *= formatBytes( stream_.userFormat );\r
6336     }\r
6337 \r
6338     // No byte swapping necessary in DirectSound implementation.\r
6339 \r
6340     // Ahhh ... windoze.  16-bit data is signed but 8-bit data is\r
6341     // unsigned.  So, we need to convert our signed 8-bit data here to\r
6342     // unsigned.\r
6343     if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
6344       for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
6345 \r
6346     DWORD dsBufferSize = handle->dsBufferSize[0];\r
6347     nextWritePointer = handle->bufferPointer[0];\r
6348 \r
6349     DWORD endWrite, leadPointer;\r
6350     while ( true ) {\r
6351       // Find out where the read and "safe write" pointers are.\r
6352       result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6353       if ( FAILED( result ) ) {\r
6354         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6355         errorText_ = errorStream_.str();\r
6356         error( RtAudioError::SYSTEM_ERROR );\r
6357         return;\r
6358       }\r
6359 \r
6360       // We will copy our output buffer into the region between\r
6361       // safeWritePointer and leadPointer.  If leadPointer is not\r
6362       // beyond the next endWrite position, wait until it is.\r
6363       leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
6364       //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
6365       if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
6366       if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
6367       endWrite = nextWritePointer + bufferBytes;\r
6368 \r
6369       // Check whether the entire write region is behind the play pointer.\r
6370       if ( leadPointer >= endWrite ) break;\r
6371 \r
6372       // If we are here, then we must wait until the leadPointer advances\r
6373       // beyond the end of our next write region. We use the\r
6374       // Sleep() function to suspend operation until that happens.\r
6375       double millis = ( endWrite - leadPointer ) * 1000.0;\r
6376       millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
6377       if ( millis < 1.0 ) millis = 1.0;\r
6378       Sleep( (DWORD) millis );\r
6379     }\r
6380 \r
6381     if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
6382          || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
6383       // We've strayed into the forbidden zone ... resync the read pointer.\r
6384       handle->xrun[0] = true;\r
6385       nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
6386       if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
6387       handle->bufferPointer[0] = nextWritePointer;\r
6388       endWrite = nextWritePointer + bufferBytes;\r
6389     }\r
6390 \r
6391     // Lock free space in the buffer\r
6392     result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
6393                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6394     if ( FAILED( result ) ) {\r
6395       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
6396       errorText_ = errorStream_.str();\r
6397       error( RtAudioError::SYSTEM_ERROR );\r
6398       return;\r
6399     }\r
6400 \r
6401     // Copy our buffer into the DS buffer\r
6402     CopyMemory( buffer1, buffer, bufferSize1 );\r
6403     if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
6404 \r
6405     // Update our buffer offset and unlock sound buffer\r
6406     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6407     if ( FAILED( result ) ) {\r
6408       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
6409       errorText_ = errorStream_.str();\r
6410       error( RtAudioError::SYSTEM_ERROR );\r
6411       return;\r
6412     }\r
6413     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6414     handle->bufferPointer[0] = nextWritePointer;\r
6415   }\r
6416 \r
6417   // Don't bother draining input\r
6418   if ( handle->drainCounter ) {\r
6419     handle->drainCounter++;\r
6420     goto unlock;\r
6421   }\r
6422 \r
6423   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6424 \r
6425     // Setup parameters.\r
6426     if ( stream_.doConvertBuffer[1] ) {\r
6427       buffer = stream_.deviceBuffer;\r
6428       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
6429       bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
6430     }\r
6431     else {\r
6432       buffer = stream_.userBuffer[1];\r
6433       bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
6434       bufferBytes *= formatBytes( stream_.userFormat );\r
6435     }\r
6436 \r
6437     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6438     long nextReadPointer = handle->bufferPointer[1];\r
6439     DWORD dsBufferSize = handle->dsBufferSize[1];\r
6440 \r
6441     // Find out where the write and "safe read" pointers are.\r
6442     result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6443     if ( FAILED( result ) ) {\r
6444       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6445       errorText_ = errorStream_.str();\r
6446       error( RtAudioError::SYSTEM_ERROR );\r
6447       return;\r
6448     }\r
6449 \r
6450     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6451     DWORD endRead = nextReadPointer + bufferBytes;\r
6452 \r
6453     // Handling depends on whether we are INPUT or DUPLEX. \r
6454     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
6455     // then a wait here will drag the write pointers into the forbidden zone.\r
6456     // \r
6457     // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
6458     // it's in a safe position. This causes dropouts, but it seems to be the only \r
6459     // practical way to sync up the read and write pointers reliably, given the \r
6460     // the very complex relationship between phase and increment of the read and write \r
6461     // pointers.\r
6462     //\r
6463     // In order to minimize audible dropouts in DUPLEX mode, we will\r
6464     // provide a pre-roll period of 0.5 seconds in which we return\r
6465     // zeros from the read buffer while the pointers sync up.\r
6466 \r
6467     if ( stream_.mode == DUPLEX ) {\r
6468       if ( safeReadPointer < endRead ) {\r
6469         if ( duplexPrerollBytes <= 0 ) {\r
6470           // Pre-roll time over. Be more agressive.\r
6471           int adjustment = endRead-safeReadPointer;\r
6472 \r
6473           handle->xrun[1] = true;\r
6474           // Two cases:\r
6475           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
6476           //     and perform fine adjustments later.\r
6477           //   - small adjustments: back off by twice as much.\r
6478           if ( adjustment >= 2*bufferBytes )\r
6479             nextReadPointer = safeReadPointer-2*bufferBytes;\r
6480           else\r
6481             nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
6482 \r
6483           if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6484 \r
6485         }\r
6486         else {\r
6487           // In pre=roll time. Just do it.\r
6488           nextReadPointer = safeReadPointer - bufferBytes;\r
6489           while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6490         }\r
6491         endRead = nextReadPointer + bufferBytes;\r
6492       }\r
6493     }\r
6494     else { // mode == INPUT\r
6495       while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
6496         // See comments for playback.\r
6497         double millis = (endRead - safeReadPointer) * 1000.0;\r
6498         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
6499         if ( millis < 1.0 ) millis = 1.0;\r
6500         Sleep( (DWORD) millis );\r
6501 \r
6502         // Wake up and find out where we are now.\r
6503         result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6504         if ( FAILED( result ) ) {\r
6505           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6506           errorText_ = errorStream_.str();\r
6507           error( RtAudioError::SYSTEM_ERROR );\r
6508           return;\r
6509         }\r
6510       \r
6511         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6512       }\r
6513     }\r
6514 \r
6515     // Lock free space in the buffer\r
6516     result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
6517                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6518     if ( FAILED( result ) ) {\r
6519       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
6520       errorText_ = errorStream_.str();\r
6521       error( RtAudioError::SYSTEM_ERROR );\r
6522       return;\r
6523     }\r
6524 \r
6525     if ( duplexPrerollBytes <= 0 ) {\r
6526       // Copy our buffer into the DS buffer\r
6527       CopyMemory( buffer, buffer1, bufferSize1 );\r
6528       if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
6529     }\r
6530     else {\r
6531       memset( buffer, 0, bufferSize1 );\r
6532       if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
6533       duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
6534     }\r
6535 \r
6536     // Update our buffer offset and unlock sound buffer\r
6537     nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6538     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6539     if ( FAILED( result ) ) {\r
6540       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
6541       errorText_ = errorStream_.str();\r
6542       error( RtAudioError::SYSTEM_ERROR );\r
6543       return;\r
6544     }\r
6545     handle->bufferPointer[1] = nextReadPointer;\r
6546 \r
6547     // No byte swapping necessary in DirectSound implementation.\r
6548 \r
6549     // If necessary, convert 8-bit data from unsigned to signed.\r
6550     if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
6551       for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
6552 \r
6553     // Do buffer conversion if necessary.\r
6554     if ( stream_.doConvertBuffer[1] )\r
6555       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
6556   }\r
6557 \r
6558  unlock:\r
6559   MUTEX_UNLOCK( &stream_.mutex );\r
6560   RtApi::tickStreamTime();\r
6561 }\r
6562 \r
6563 // Definitions for utility functions and callbacks\r
6564 // specific to the DirectSound implementation.\r
6565 \r
6566 static unsigned __stdcall callbackHandler( void *ptr )\r
6567 {\r
6568   CallbackInfo *info = (CallbackInfo *) ptr;\r
6569   RtApiDs *object = (RtApiDs *) info->object;\r
6570   bool* isRunning = &info->isRunning;\r
6571 \r
6572   while ( *isRunning == true ) {\r
6573     object->callbackEvent();\r
6574   }\r
6575 \r
6576   _endthreadex( 0 );\r
6577   return 0;\r
6578 }\r
6579 \r
6580 #include "tchar.h"\r
6581 \r
6582 static std::string convertTChar( LPCTSTR name )\r
6583 {\r
6584 #if defined( UNICODE ) || defined( _UNICODE )\r
6585   int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);\r
6586   std::string s( length-1, '\0' );\r
6587   WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);\r
6588 #else\r
6589   std::string s( name );\r
6590 #endif\r
6591 \r
6592   return s;\r
6593 }\r
6594 \r
6595 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
6596                                           LPCTSTR description,\r
6597                                           LPCTSTR /*module*/,\r
6598                                           LPVOID lpContext )\r
6599 {\r
6600   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
6601   std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
6602 \r
6603   HRESULT hr;\r
6604   bool validDevice = false;\r
6605   if ( probeInfo.isInput == true ) {\r
6606     DSCCAPS caps;\r
6607     LPDIRECTSOUNDCAPTURE object;\r
6608 \r
6609     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
6610     if ( hr != DS_OK ) return TRUE;\r
6611 \r
6612     caps.dwSize = sizeof(caps);\r
6613     hr = object->GetCaps( &caps );\r
6614     if ( hr == DS_OK ) {\r
6615       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
6616         validDevice = true;\r
6617     }\r
6618     object->Release();\r
6619   }\r
6620   else {\r
6621     DSCAPS caps;\r
6622     LPDIRECTSOUND object;\r
6623     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
6624     if ( hr != DS_OK ) return TRUE;\r
6625 \r
6626     caps.dwSize = sizeof(caps);\r
6627     hr = object->GetCaps( &caps );\r
6628     if ( hr == DS_OK ) {\r
6629       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
6630         validDevice = true;\r
6631     }\r
6632     object->Release();\r
6633   }\r
6634 \r
6635   // If good device, then save its name and guid.\r
6636   std::string name = convertTChar( description );\r
6637   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
6638   if ( lpguid == NULL )\r
6639     name = "Default Device";\r
6640   if ( validDevice ) {\r
6641     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
6642       if ( dsDevices[i].name == name ) {\r
6643         dsDevices[i].found = true;\r
6644         if ( probeInfo.isInput ) {\r
6645           dsDevices[i].id[1] = lpguid;\r
6646           dsDevices[i].validId[1] = true;\r
6647         }\r
6648         else {\r
6649           dsDevices[i].id[0] = lpguid;\r
6650           dsDevices[i].validId[0] = true;\r
6651         }\r
6652         return TRUE;\r
6653       }\r
6654     }\r
6655 \r
6656     DsDevice device;\r
6657     device.name = name;\r
6658     device.found = true;\r
6659     if ( probeInfo.isInput ) {\r
6660       device.id[1] = lpguid;\r
6661       device.validId[1] = true;\r
6662     }\r
6663     else {\r
6664       device.id[0] = lpguid;\r
6665       device.validId[0] = true;\r
6666     }\r
6667     dsDevices.push_back( device );\r
6668   }\r
6669 \r
6670   return TRUE;\r
6671 }\r
6672 \r
6673 static const char* getErrorString( int code )\r
6674 {\r
6675   switch ( code ) {\r
6676 \r
6677   case DSERR_ALLOCATED:\r
6678     return "Already allocated";\r
6679 \r
6680   case DSERR_CONTROLUNAVAIL:\r
6681     return "Control unavailable";\r
6682 \r
6683   case DSERR_INVALIDPARAM:\r
6684     return "Invalid parameter";\r
6685 \r
6686   case DSERR_INVALIDCALL:\r
6687     return "Invalid call";\r
6688 \r
6689   case DSERR_GENERIC:\r
6690     return "Generic error";\r
6691 \r
6692   case DSERR_PRIOLEVELNEEDED:\r
6693     return "Priority level needed";\r
6694 \r
6695   case DSERR_OUTOFMEMORY:\r
6696     return "Out of memory";\r
6697 \r
6698   case DSERR_BADFORMAT:\r
6699     return "The sample rate or the channel format is not supported";\r
6700 \r
6701   case DSERR_UNSUPPORTED:\r
6702     return "Not supported";\r
6703 \r
6704   case DSERR_NODRIVER:\r
6705     return "No driver";\r
6706 \r
6707   case DSERR_ALREADYINITIALIZED:\r
6708     return "Already initialized";\r
6709 \r
6710   case DSERR_NOAGGREGATION:\r
6711     return "No aggregation";\r
6712 \r
6713   case DSERR_BUFFERLOST:\r
6714     return "Buffer lost";\r
6715 \r
6716   case DSERR_OTHERAPPHASPRIO:\r
6717     return "Another application already has priority";\r
6718 \r
6719   case DSERR_UNINITIALIZED:\r
6720     return "Uninitialized";\r
6721 \r
6722   default:\r
6723     return "DirectSound unknown error";\r
6724   }\r
6725 }\r
6726 //******************** End of __WINDOWS_DS__ *********************//\r
6727 #endif\r
6728 \r
6729 \r
6730 #if defined(__LINUX_ALSA__)\r
6731 \r
6732 #include <alsa/asoundlib.h>\r
6733 #include <unistd.h>\r
6734 \r
6735   // A structure to hold various information related to the ALSA API\r
6736   // implementation.\r
6737 struct AlsaHandle {\r
6738   snd_pcm_t *handles[2];\r
6739   bool synchronized;\r
6740   bool xrun[2];\r
6741   pthread_cond_t runnable_cv;\r
6742   bool runnable;\r
6743 \r
6744   AlsaHandle()\r
6745     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
6746 };\r
6747 \r
6748 static void *alsaCallbackHandler( void * ptr );\r
6749 \r
6750 RtApiAlsa :: RtApiAlsa()\r
6751 {\r
6752   // Nothing to do here.\r
6753 }\r
6754 \r
6755 RtApiAlsa :: ~RtApiAlsa()\r
6756 {\r
6757   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
6758 }\r
6759 \r
6760 unsigned int RtApiAlsa :: getDeviceCount( void )\r
6761 {\r
6762   unsigned nDevices = 0;\r
6763   int result, subdevice, card;\r
6764   char name[64];\r
6765   snd_ctl_t *handle;\r
6766 \r
6767   // Count cards and devices\r
6768   card = -1;\r
6769   snd_card_next( &card );\r
6770   while ( card >= 0 ) {\r
6771     sprintf( name, "hw:%d", card );\r
6772     result = snd_ctl_open( &handle, name, 0 );\r
6773     if ( result < 0 ) {\r
6774       errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6775       errorText_ = errorStream_.str();\r
6776       error( RtAudioError::WARNING );\r
6777       goto nextcard;\r
6778     }\r
6779     subdevice = -1;\r
6780     while( 1 ) {\r
6781       result = snd_ctl_pcm_next_device( handle, &subdevice );\r
6782       if ( result < 0 ) {\r
6783         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6784         errorText_ = errorStream_.str();\r
6785         error( RtAudioError::WARNING );\r
6786         break;\r
6787       }\r
6788       if ( subdevice < 0 )\r
6789         break;\r
6790       nDevices++;\r
6791     }\r
6792   nextcard:\r
6793     snd_ctl_close( handle );\r
6794     snd_card_next( &card );\r
6795   }\r
6796 \r
6797   result = snd_ctl_open( &handle, "default", 0 );\r
6798   if (result == 0) {\r
6799     nDevices++;\r
6800     snd_ctl_close( handle );\r
6801   }\r
6802 \r
6803   return nDevices;\r
6804 }\r
6805 \r
6806 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
6807 {\r
6808   RtAudio::DeviceInfo info;\r
6809   info.probed = false;\r
6810 \r
6811   unsigned nDevices = 0;\r
6812   int result, subdevice, card;\r
6813   char name[64];\r
6814   snd_ctl_t *chandle;\r
6815 \r
6816   // Count cards and devices\r
6817   card = -1;\r
6818   subdevice = -1;\r
6819   snd_card_next( &card );\r
6820   while ( card >= 0 ) {\r
6821     sprintf( name, "hw:%d", card );\r
6822     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6823     if ( result < 0 ) {\r
6824       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6825       errorText_ = errorStream_.str();\r
6826       error( RtAudioError::WARNING );\r
6827       goto nextcard;\r
6828     }\r
6829     subdevice = -1;\r
6830     while( 1 ) {\r
6831       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
6832       if ( result < 0 ) {\r
6833         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6834         errorText_ = errorStream_.str();\r
6835         error( RtAudioError::WARNING );\r
6836         break;\r
6837       }\r
6838       if ( subdevice < 0 ) break;\r
6839       if ( nDevices == device ) {\r
6840         sprintf( name, "hw:%d,%d", card, subdevice );\r
6841         goto foundDevice;\r
6842       }\r
6843       nDevices++;\r
6844     }\r
6845   nextcard:\r
6846     snd_ctl_close( chandle );\r
6847     snd_card_next( &card );\r
6848   }\r
6849 \r
6850   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
6851   if ( result == 0 ) {\r
6852     if ( nDevices == device ) {\r
6853       strcpy( name, "default" );\r
6854       goto foundDevice;\r
6855     }\r
6856     nDevices++;\r
6857   }\r
6858 \r
6859   if ( nDevices == 0 ) {\r
6860     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
6861     error( RtAudioError::INVALID_USE );\r
6862     return info;\r
6863   }\r
6864 \r
6865   if ( device >= nDevices ) {\r
6866     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
6867     error( RtAudioError::INVALID_USE );\r
6868     return info;\r
6869   }\r
6870 \r
6871  foundDevice:\r
6872 \r
6873   // If a stream is already open, we cannot probe the stream devices.\r
6874   // Thus, use the saved results.\r
6875   if ( stream_.state != STREAM_CLOSED &&\r
6876        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
6877     snd_ctl_close( chandle );\r
6878     if ( device >= devices_.size() ) {\r
6879       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
6880       error( RtAudioError::WARNING );\r
6881       return info;\r
6882     }\r
6883     return devices_[ device ];\r
6884   }\r
6885 \r
6886   int openMode = SND_PCM_ASYNC;\r
6887   snd_pcm_stream_t stream;\r
6888   snd_pcm_info_t *pcminfo;\r
6889   snd_pcm_info_alloca( &pcminfo );\r
6890   snd_pcm_t *phandle;\r
6891   snd_pcm_hw_params_t *params;\r
6892   snd_pcm_hw_params_alloca( &params );\r
6893 \r
6894   // First try for playback unless default device (which has subdev -1)\r
6895   stream = SND_PCM_STREAM_PLAYBACK;\r
6896   snd_pcm_info_set_stream( pcminfo, stream );\r
6897   if ( subdevice != -1 ) {\r
6898     snd_pcm_info_set_device( pcminfo, subdevice );\r
6899     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
6900 \r
6901     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6902     if ( result < 0 ) {\r
6903       // Device probably doesn't support playback.\r
6904       goto captureProbe;\r
6905     }\r
6906   }\r
6907 \r
6908   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
6909   if ( result < 0 ) {\r
6910     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6911     errorText_ = errorStream_.str();\r
6912     error( RtAudioError::WARNING );\r
6913     goto captureProbe;\r
6914   }\r
6915 \r
6916   // The device is open ... fill the parameter structure.\r
6917   result = snd_pcm_hw_params_any( phandle, params );\r
6918   if ( result < 0 ) {\r
6919     snd_pcm_close( phandle );\r
6920     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6921     errorText_ = errorStream_.str();\r
6922     error( RtAudioError::WARNING );\r
6923     goto captureProbe;\r
6924   }\r
6925 \r
6926   // Get output channel information.\r
6927   unsigned int value;\r
6928   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6929   if ( result < 0 ) {\r
6930     snd_pcm_close( phandle );\r
6931     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
6932     errorText_ = errorStream_.str();\r
6933     error( RtAudioError::WARNING );\r
6934     goto captureProbe;\r
6935   }\r
6936   info.outputChannels = value;\r
6937   snd_pcm_close( phandle );\r
6938 \r
6939  captureProbe:\r
6940   stream = SND_PCM_STREAM_CAPTURE;\r
6941   snd_pcm_info_set_stream( pcminfo, stream );\r
6942 \r
6943   // Now try for capture unless default device (with subdev = -1)\r
6944   if ( subdevice != -1 ) {\r
6945     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6946     snd_ctl_close( chandle );\r
6947     if ( result < 0 ) {\r
6948       // Device probably doesn't support capture.\r
6949       if ( info.outputChannels == 0 ) return info;\r
6950       goto probeParameters;\r
6951     }\r
6952   }\r
6953   else\r
6954     snd_ctl_close( chandle );\r
6955 \r
6956   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
6957   if ( result < 0 ) {\r
6958     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6959     errorText_ = errorStream_.str();\r
6960     error( RtAudioError::WARNING );\r
6961     if ( info.outputChannels == 0 ) return info;\r
6962     goto probeParameters;\r
6963   }\r
6964 \r
6965   // The device is open ... fill the parameter structure.\r
6966   result = snd_pcm_hw_params_any( phandle, params );\r
6967   if ( result < 0 ) {\r
6968     snd_pcm_close( phandle );\r
6969     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6970     errorText_ = errorStream_.str();\r
6971     error( RtAudioError::WARNING );\r
6972     if ( info.outputChannels == 0 ) return info;\r
6973     goto probeParameters;\r
6974   }\r
6975 \r
6976   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6977   if ( result < 0 ) {\r
6978     snd_pcm_close( phandle );\r
6979     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
6980     errorText_ = errorStream_.str();\r
6981     error( RtAudioError::WARNING );\r
6982     if ( info.outputChannels == 0 ) return info;\r
6983     goto probeParameters;\r
6984   }\r
6985   info.inputChannels = value;\r
6986   snd_pcm_close( phandle );\r
6987 \r
6988   // If device opens for both playback and capture, we determine the channels.\r
6989   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
6990     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
6991 \r
6992   // ALSA doesn't provide default devices so we'll use the first available one.\r
6993   if ( device == 0 && info.outputChannels > 0 )\r
6994     info.isDefaultOutput = true;\r
6995   if ( device == 0 && info.inputChannels > 0 )\r
6996     info.isDefaultInput = true;\r
6997 \r
6998  probeParameters:\r
6999   // At this point, we just need to figure out the supported data\r
7000   // formats and sample rates.  We'll proceed by opening the device in\r
7001   // the direction with the maximum number of channels, or playback if\r
7002   // they are equal.  This might limit our sample rate options, but so\r
7003   // be it.\r
7004 \r
7005   if ( info.outputChannels >= info.inputChannels )\r
7006     stream = SND_PCM_STREAM_PLAYBACK;\r
7007   else\r
7008     stream = SND_PCM_STREAM_CAPTURE;\r
7009   snd_pcm_info_set_stream( pcminfo, stream );\r
7010 \r
7011   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7012   if ( result < 0 ) {\r
7013     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7014     errorText_ = errorStream_.str();\r
7015     error( RtAudioError::WARNING );\r
7016     return info;\r
7017   }\r
7018 \r
7019   // The device is open ... fill the parameter structure.\r
7020   result = snd_pcm_hw_params_any( phandle, params );\r
7021   if ( result < 0 ) {\r
7022     snd_pcm_close( phandle );\r
7023     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7024     errorText_ = errorStream_.str();\r
7025     error( RtAudioError::WARNING );\r
7026     return info;\r
7027   }\r
7028 \r
7029   // Test our discrete set of sample rate values.\r
7030   info.sampleRates.clear();\r
7031   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7032     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )\r
7033       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7034   }\r
7035   if ( info.sampleRates.size() == 0 ) {\r
7036     snd_pcm_close( phandle );\r
7037     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7038     errorText_ = errorStream_.str();\r
7039     error( RtAudioError::WARNING );\r
7040     return info;\r
7041   }\r
7042 \r
7043   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7044   snd_pcm_format_t format;\r
7045   info.nativeFormats = 0;\r
7046   format = SND_PCM_FORMAT_S8;\r
7047   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7048     info.nativeFormats |= RTAUDIO_SINT8;\r
7049   format = SND_PCM_FORMAT_S16;\r
7050   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7051     info.nativeFormats |= RTAUDIO_SINT16;\r
7052   format = SND_PCM_FORMAT_S24;\r
7053   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7054     info.nativeFormats |= RTAUDIO_SINT24;\r
7055   format = SND_PCM_FORMAT_S32;\r
7056   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7057     info.nativeFormats |= RTAUDIO_SINT32;\r
7058   format = SND_PCM_FORMAT_FLOAT;\r
7059   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7060     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7061   format = SND_PCM_FORMAT_FLOAT64;\r
7062   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7063     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7064 \r
7065   // Check that we have at least one supported format\r
7066   if ( info.nativeFormats == 0 ) {\r
7067     snd_pcm_close( phandle );\r
7068     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7069     errorText_ = errorStream_.str();\r
7070     error( RtAudioError::WARNING );\r
7071     return info;\r
7072   }\r
7073 \r
7074   // Get the device name\r
7075   char *cardname;\r
7076   result = snd_card_get_name( card, &cardname );\r
7077   if ( result >= 0 ) {\r
7078     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7079     free( cardname );\r
7080   }\r
7081   info.name = name;\r
7082 \r
7083   // That's all ... close the device and return\r
7084   snd_pcm_close( phandle );\r
7085   info.probed = true;\r
7086   return info;\r
7087 }\r
7088 \r
7089 void RtApiAlsa :: saveDeviceInfo( void )\r
7090 {\r
7091   devices_.clear();\r
7092 \r
7093   unsigned int nDevices = getDeviceCount();\r
7094   devices_.resize( nDevices );\r
7095   for ( unsigned int i=0; i<nDevices; i++ )\r
7096     devices_[i] = getDeviceInfo( i );\r
7097 }\r
7098 \r
7099 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7100                                    unsigned int firstChannel, unsigned int sampleRate,\r
7101                                    RtAudioFormat format, unsigned int *bufferSize,\r
7102                                    RtAudio::StreamOptions *options )\r
7103 \r
7104 {\r
7105 #if defined(__RTAUDIO_DEBUG__)\r
7106   snd_output_t *out;\r
7107   snd_output_stdio_attach(&out, stderr, 0);\r
7108 #endif\r
7109 \r
7110   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7111 \r
7112   unsigned nDevices = 0;\r
7113   int result, subdevice, card;\r
7114   char name[64];\r
7115   snd_ctl_t *chandle;\r
7116 \r
7117   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7118     snprintf(name, sizeof(name), "%s", "default");\r
7119   else {\r
7120     // Count cards and devices\r
7121     card = -1;\r
7122     snd_card_next( &card );\r
7123     while ( card >= 0 ) {\r
7124       sprintf( name, "hw:%d", card );\r
7125       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7126       if ( result < 0 ) {\r
7127         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7128         errorText_ = errorStream_.str();\r
7129         return FAILURE;\r
7130       }\r
7131       subdevice = -1;\r
7132       while( 1 ) {\r
7133         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7134         if ( result < 0 ) break;\r
7135         if ( subdevice < 0 ) break;\r
7136         if ( nDevices == device ) {\r
7137           sprintf( name, "hw:%d,%d", card, subdevice );\r
7138           snd_ctl_close( chandle );\r
7139           goto foundDevice;\r
7140         }\r
7141         nDevices++;\r
7142       }\r
7143       snd_ctl_close( chandle );\r
7144       snd_card_next( &card );\r
7145     }\r
7146 \r
7147     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7148     if ( result == 0 ) {\r
7149       if ( nDevices == device ) {\r
7150         strcpy( name, "default" );\r
7151         goto foundDevice;\r
7152       }\r
7153       nDevices++;\r
7154     }\r
7155 \r
7156     if ( nDevices == 0 ) {\r
7157       // This should not happen because a check is made before this function is called.\r
7158       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7159       return FAILURE;\r
7160     }\r
7161 \r
7162     if ( device >= nDevices ) {\r
7163       // This should not happen because a check is made before this function is called.\r
7164       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7165       return FAILURE;\r
7166     }\r
7167   }\r
7168 \r
7169  foundDevice:\r
7170 \r
7171   // The getDeviceInfo() function will not work for a device that is\r
7172   // already open.  Thus, we'll probe the system before opening a\r
7173   // stream and save the results for use by getDeviceInfo().\r
7174   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7175     this->saveDeviceInfo();\r
7176 \r
7177   snd_pcm_stream_t stream;\r
7178   if ( mode == OUTPUT )\r
7179     stream = SND_PCM_STREAM_PLAYBACK;\r
7180   else\r
7181     stream = SND_PCM_STREAM_CAPTURE;\r
7182 \r
7183   snd_pcm_t *phandle;\r
7184   int openMode = SND_PCM_ASYNC;\r
7185   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7186   if ( result < 0 ) {\r
7187     if ( mode == OUTPUT )\r
7188       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7189     else\r
7190       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7191     errorText_ = errorStream_.str();\r
7192     return FAILURE;\r
7193   }\r
7194 \r
7195   // Fill the parameter structure.\r
7196   snd_pcm_hw_params_t *hw_params;\r
7197   snd_pcm_hw_params_alloca( &hw_params );\r
7198   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7199   if ( result < 0 ) {\r
7200     snd_pcm_close( phandle );\r
7201     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7202     errorText_ = errorStream_.str();\r
7203     return FAILURE;\r
7204   }\r
7205 \r
7206 #if defined(__RTAUDIO_DEBUG__)\r
7207   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7208   snd_pcm_hw_params_dump( hw_params, out );\r
7209 #endif\r
7210 \r
7211   // Set access ... check user preference.\r
7212   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7213     stream_.userInterleaved = false;\r
7214     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7215     if ( result < 0 ) {\r
7216       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7217       stream_.deviceInterleaved[mode] =  true;\r
7218     }\r
7219     else\r
7220       stream_.deviceInterleaved[mode] = false;\r
7221   }\r
7222   else {\r
7223     stream_.userInterleaved = true;\r
7224     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7225     if ( result < 0 ) {\r
7226       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7227       stream_.deviceInterleaved[mode] =  false;\r
7228     }\r
7229     else\r
7230       stream_.deviceInterleaved[mode] =  true;\r
7231   }\r
7232 \r
7233   if ( result < 0 ) {\r
7234     snd_pcm_close( phandle );\r
7235     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7236     errorText_ = errorStream_.str();\r
7237     return FAILURE;\r
7238   }\r
7239 \r
7240   // Determine how to set the device format.\r
7241   stream_.userFormat = format;\r
7242   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7243 \r
7244   if ( format == RTAUDIO_SINT8 )\r
7245     deviceFormat = SND_PCM_FORMAT_S8;\r
7246   else if ( format == RTAUDIO_SINT16 )\r
7247     deviceFormat = SND_PCM_FORMAT_S16;\r
7248   else if ( format == RTAUDIO_SINT24 )\r
7249     deviceFormat = SND_PCM_FORMAT_S24;\r
7250   else if ( format == RTAUDIO_SINT32 )\r
7251     deviceFormat = SND_PCM_FORMAT_S32;\r
7252   else if ( format == RTAUDIO_FLOAT32 )\r
7253     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7254   else if ( format == RTAUDIO_FLOAT64 )\r
7255     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7256 \r
7257   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7258     stream_.deviceFormat[mode] = format;\r
7259     goto setFormat;\r
7260   }\r
7261 \r
7262   // The user requested format is not natively supported by the device.\r
7263   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7264   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7265     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7266     goto setFormat;\r
7267   }\r
7268 \r
7269   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7270   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7271     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7272     goto setFormat;\r
7273   }\r
7274 \r
7275   deviceFormat = SND_PCM_FORMAT_S32;\r
7276   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7277     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7278     goto setFormat;\r
7279   }\r
7280 \r
7281   deviceFormat = SND_PCM_FORMAT_S24;\r
7282   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7283     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7284     goto setFormat;\r
7285   }\r
7286 \r
7287   deviceFormat = SND_PCM_FORMAT_S16;\r
7288   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7289     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7290     goto setFormat;\r
7291   }\r
7292 \r
7293   deviceFormat = SND_PCM_FORMAT_S8;\r
7294   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7295     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7296     goto setFormat;\r
7297   }\r
7298 \r
7299   // If we get here, no supported format was found.\r
7300   snd_pcm_close( phandle );\r
7301   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7302   errorText_ = errorStream_.str();\r
7303   return FAILURE;\r
7304 \r
7305  setFormat:\r
7306   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7307   if ( result < 0 ) {\r
7308     snd_pcm_close( phandle );\r
7309     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7310     errorText_ = errorStream_.str();\r
7311     return FAILURE;\r
7312   }\r
7313 \r
7314   // Determine whether byte-swaping is necessary.\r
7315   stream_.doByteSwap[mode] = false;\r
7316   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7317     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7318     if ( result == 0 )\r
7319       stream_.doByteSwap[mode] = true;\r
7320     else if (result < 0) {\r
7321       snd_pcm_close( phandle );\r
7322       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7323       errorText_ = errorStream_.str();\r
7324       return FAILURE;\r
7325     }\r
7326   }\r
7327 \r
7328   // Set the sample rate.\r
7329   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7330   if ( result < 0 ) {\r
7331     snd_pcm_close( phandle );\r
7332     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7333     errorText_ = errorStream_.str();\r
7334     return FAILURE;\r
7335   }\r
7336 \r
7337   // Determine the number of channels for this device.  We support a possible\r
7338   // minimum device channel number > than the value requested by the user.\r
7339   stream_.nUserChannels[mode] = channels;\r
7340   unsigned int value;\r
7341   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7342   unsigned int deviceChannels = value;\r
7343   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7344     snd_pcm_close( phandle );\r
7345     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7346     errorText_ = errorStream_.str();\r
7347     return FAILURE;\r
7348   }\r
7349 \r
7350   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7351   if ( result < 0 ) {\r
7352     snd_pcm_close( phandle );\r
7353     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7354     errorText_ = errorStream_.str();\r
7355     return FAILURE;\r
7356   }\r
7357   deviceChannels = value;\r
7358   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7359   stream_.nDeviceChannels[mode] = deviceChannels;\r
7360 \r
7361   // Set the device channels.\r
7362   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7363   if ( result < 0 ) {\r
7364     snd_pcm_close( phandle );\r
7365     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7366     errorText_ = errorStream_.str();\r
7367     return FAILURE;\r
7368   }\r
7369 \r
7370   // Set the buffer (or period) size.\r
7371   int dir = 0;\r
7372   snd_pcm_uframes_t periodSize = *bufferSize;\r
7373   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7374   if ( result < 0 ) {\r
7375     snd_pcm_close( phandle );\r
7376     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7377     errorText_ = errorStream_.str();\r
7378     return FAILURE;\r
7379   }\r
7380   *bufferSize = periodSize;\r
7381 \r
7382   // Set the buffer number, which in ALSA is referred to as the "period".\r
7383   unsigned int periods = 0;\r
7384   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7385   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7386   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7387   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7388   if ( result < 0 ) {\r
7389     snd_pcm_close( phandle );\r
7390     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7391     errorText_ = errorStream_.str();\r
7392     return FAILURE;\r
7393   }\r
7394 \r
7395   // If attempting to setup a duplex stream, the bufferSize parameter\r
7396   // MUST be the same in both directions!\r
7397   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7398     snd_pcm_close( phandle );\r
7399     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7400     errorText_ = errorStream_.str();\r
7401     return FAILURE;\r
7402   }\r
7403 \r
7404   stream_.bufferSize = *bufferSize;\r
7405 \r
7406   // Install the hardware configuration\r
7407   result = snd_pcm_hw_params( phandle, hw_params );\r
7408   if ( result < 0 ) {\r
7409     snd_pcm_close( phandle );\r
7410     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7411     errorText_ = errorStream_.str();\r
7412     return FAILURE;\r
7413   }\r
7414 \r
7415 #if defined(__RTAUDIO_DEBUG__)\r
7416   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7417   snd_pcm_hw_params_dump( hw_params, out );\r
7418 #endif\r
7419 \r
7420   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7421   snd_pcm_sw_params_t *sw_params = NULL;\r
7422   snd_pcm_sw_params_alloca( &sw_params );\r
7423   snd_pcm_sw_params_current( phandle, sw_params );\r
7424   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7425   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7426   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7427 \r
7428   // The following two settings were suggested by Theo Veenker\r
7429   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7430   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7431 \r
7432   // here are two options for a fix\r
7433   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7434   snd_pcm_uframes_t val;\r
7435   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7436   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7437 \r
7438   result = snd_pcm_sw_params( phandle, sw_params );\r
7439   if ( result < 0 ) {\r
7440     snd_pcm_close( phandle );\r
7441     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7442     errorText_ = errorStream_.str();\r
7443     return FAILURE;\r
7444   }\r
7445 \r
7446 #if defined(__RTAUDIO_DEBUG__)\r
7447   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7448   snd_pcm_sw_params_dump( sw_params, out );\r
7449 #endif\r
7450 \r
7451   // Set flags for buffer conversion\r
7452   stream_.doConvertBuffer[mode] = false;\r
7453   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7454     stream_.doConvertBuffer[mode] = true;\r
7455   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7456     stream_.doConvertBuffer[mode] = true;\r
7457   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7458        stream_.nUserChannels[mode] > 1 )\r
7459     stream_.doConvertBuffer[mode] = true;\r
7460 \r
7461   // Allocate the ApiHandle if necessary and then save.\r
7462   AlsaHandle *apiInfo = 0;\r
7463   if ( stream_.apiHandle == 0 ) {\r
7464     try {\r
7465       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7466     }\r
7467     catch ( std::bad_alloc& ) {\r
7468       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7469       goto error;\r
7470     }\r
7471 \r
7472     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7473       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7474       goto error;\r
7475     }\r
7476 \r
7477     stream_.apiHandle = (void *) apiInfo;\r
7478     apiInfo->handles[0] = 0;\r
7479     apiInfo->handles[1] = 0;\r
7480   }\r
7481   else {\r
7482     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7483   }\r
7484   apiInfo->handles[mode] = phandle;\r
7485   phandle = 0;\r
7486 \r
7487   // Allocate necessary internal buffers.\r
7488   unsigned long bufferBytes;\r
7489   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7490   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7491   if ( stream_.userBuffer[mode] == NULL ) {\r
7492     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7493     goto error;\r
7494   }\r
7495 \r
7496   if ( stream_.doConvertBuffer[mode] ) {\r
7497 \r
7498     bool makeBuffer = true;\r
7499     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7500     if ( mode == INPUT ) {\r
7501       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7502         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7503         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7504       }\r
7505     }\r
7506 \r
7507     if ( makeBuffer ) {\r
7508       bufferBytes *= *bufferSize;\r
7509       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7510       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7511       if ( stream_.deviceBuffer == NULL ) {\r
7512         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7513         goto error;\r
7514       }\r
7515     }\r
7516   }\r
7517 \r
7518   stream_.sampleRate = sampleRate;\r
7519   stream_.nBuffers = periods;\r
7520   stream_.device[mode] = device;\r
7521   stream_.state = STREAM_STOPPED;\r
7522 \r
7523   // Setup the buffer conversion information structure.\r
7524   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7525 \r
7526   // Setup thread if necessary.\r
7527   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7528     // We had already set up an output stream.\r
7529     stream_.mode = DUPLEX;\r
7530     // Link the streams if possible.\r
7531     apiInfo->synchronized = false;\r
7532     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7533       apiInfo->synchronized = true;\r
7534     else {\r
7535       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7536       error( RtAudioError::WARNING );\r
7537     }\r
7538   }\r
7539   else {\r
7540     stream_.mode = mode;\r
7541 \r
7542     // Setup callback thread.\r
7543     stream_.callbackInfo.object = (void *) this;\r
7544 \r
7545     // Set the thread attributes for joinable and realtime scheduling\r
7546     // priority (optional).  The higher priority will only take affect\r
7547     // if the program is run as root or suid. Note, under Linux\r
7548     // processes with CAP_SYS_NICE privilege, a user can change\r
7549     // scheduling policy and priority (thus need not be root). See\r
7550     // POSIX "capabilities".\r
7551     pthread_attr_t attr;\r
7552     pthread_attr_init( &attr );\r
7553     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7554 \r
7555 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7556     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7557       // We previously attempted to increase the audio callback priority\r
7558       // to SCHED_RR here via the attributes.  However, while no errors\r
7559       // were reported in doing so, it did not work.  So, now this is\r
7560       // done in the alsaCallbackHandler function.\r
7561       stream_.callbackInfo.doRealtime = true;\r
7562       int priority = options->priority;\r
7563       int min = sched_get_priority_min( SCHED_RR );\r
7564       int max = sched_get_priority_max( SCHED_RR );\r
7565       if ( priority < min ) priority = min;\r
7566       else if ( priority > max ) priority = max;\r
7567       stream_.callbackInfo.priority = priority;\r
7568     }\r
7569 #endif\r
7570 \r
7571     stream_.callbackInfo.isRunning = true;\r
7572     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7573     pthread_attr_destroy( &attr );\r
7574     if ( result ) {\r
7575       stream_.callbackInfo.isRunning = false;\r
7576       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7577       goto error;\r
7578     }\r
7579   }\r
7580 \r
7581   return SUCCESS;\r
7582 \r
7583  error:\r
7584   if ( apiInfo ) {\r
7585     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7586     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7587     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7588     delete apiInfo;\r
7589     stream_.apiHandle = 0;\r
7590   }\r
7591 \r
7592   if ( phandle) snd_pcm_close( phandle );\r
7593 \r
7594   for ( int i=0; i<2; i++ ) {\r
7595     if ( stream_.userBuffer[i] ) {\r
7596       free( stream_.userBuffer[i] );\r
7597       stream_.userBuffer[i] = 0;\r
7598     }\r
7599   }\r
7600 \r
7601   if ( stream_.deviceBuffer ) {\r
7602     free( stream_.deviceBuffer );\r
7603     stream_.deviceBuffer = 0;\r
7604   }\r
7605 \r
7606   stream_.state = STREAM_CLOSED;\r
7607   return FAILURE;\r
7608 }\r
7609 \r
7610 void RtApiAlsa :: closeStream()\r
7611 {\r
7612   if ( stream_.state == STREAM_CLOSED ) {\r
7613     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7614     error( RtAudioError::WARNING );\r
7615     return;\r
7616   }\r
7617 \r
7618   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7619   stream_.callbackInfo.isRunning = false;\r
7620   MUTEX_LOCK( &stream_.mutex );\r
7621   if ( stream_.state == STREAM_STOPPED ) {\r
7622     apiInfo->runnable = true;\r
7623     pthread_cond_signal( &apiInfo->runnable_cv );\r
7624   }\r
7625   MUTEX_UNLOCK( &stream_.mutex );\r
7626   pthread_join( stream_.callbackInfo.thread, NULL );\r
7627 \r
7628   if ( stream_.state == STREAM_RUNNING ) {\r
7629     stream_.state = STREAM_STOPPED;\r
7630     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7631       snd_pcm_drop( apiInfo->handles[0] );\r
7632     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7633       snd_pcm_drop( apiInfo->handles[1] );\r
7634   }\r
7635 \r
7636   if ( apiInfo ) {\r
7637     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7638     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7639     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7640     delete apiInfo;\r
7641     stream_.apiHandle = 0;\r
7642   }\r
7643 \r
7644   for ( int i=0; i<2; i++ ) {\r
7645     if ( stream_.userBuffer[i] ) {\r
7646       free( stream_.userBuffer[i] );\r
7647       stream_.userBuffer[i] = 0;\r
7648     }\r
7649   }\r
7650 \r
7651   if ( stream_.deviceBuffer ) {\r
7652     free( stream_.deviceBuffer );\r
7653     stream_.deviceBuffer = 0;\r
7654   }\r
7655 \r
7656   stream_.mode = UNINITIALIZED;\r
7657   stream_.state = STREAM_CLOSED;\r
7658 }\r
7659 \r
7660 void RtApiAlsa :: startStream()\r
7661 {\r
7662   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7663 \r
7664   verifyStream();\r
7665   if ( stream_.state == STREAM_RUNNING ) {\r
7666     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7667     error( RtAudioError::WARNING );\r
7668     return;\r
7669   }\r
7670 \r
7671   MUTEX_LOCK( &stream_.mutex );\r
7672 \r
7673   int result = 0;\r
7674   snd_pcm_state_t state;\r
7675   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7676   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7677   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7678     state = snd_pcm_state( handle[0] );\r
7679     if ( state != SND_PCM_STATE_PREPARED ) {\r
7680       result = snd_pcm_prepare( handle[0] );\r
7681       if ( result < 0 ) {\r
7682         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7683         errorText_ = errorStream_.str();\r
7684         goto unlock;\r
7685       }\r
7686     }\r
7687   }\r
7688 \r
7689   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7690     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7691     state = snd_pcm_state( handle[1] );\r
7692     if ( state != SND_PCM_STATE_PREPARED ) {\r
7693       result = snd_pcm_prepare( handle[1] );\r
7694       if ( result < 0 ) {\r
7695         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7696         errorText_ = errorStream_.str();\r
7697         goto unlock;\r
7698       }\r
7699     }\r
7700   }\r
7701 \r
7702   stream_.state = STREAM_RUNNING;\r
7703 \r
7704  unlock:\r
7705   apiInfo->runnable = true;\r
7706   pthread_cond_signal( &apiInfo->runnable_cv );\r
7707   MUTEX_UNLOCK( &stream_.mutex );\r
7708 \r
7709   if ( result >= 0 ) return;\r
7710   error( RtAudioError::SYSTEM_ERROR );\r
7711 }\r
7712 \r
7713 void RtApiAlsa :: stopStream()\r
7714 {\r
7715   verifyStream();\r
7716   if ( stream_.state == STREAM_STOPPED ) {\r
7717     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7718     error( RtAudioError::WARNING );\r
7719     return;\r
7720   }\r
7721 \r
7722   stream_.state = STREAM_STOPPED;\r
7723   MUTEX_LOCK( &stream_.mutex );\r
7724 \r
7725   int result = 0;\r
7726   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7727   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7728   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7729     if ( apiInfo->synchronized ) \r
7730       result = snd_pcm_drop( handle[0] );\r
7731     else\r
7732       result = snd_pcm_drain( handle[0] );\r
7733     if ( result < 0 ) {\r
7734       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7735       errorText_ = errorStream_.str();\r
7736       goto unlock;\r
7737     }\r
7738   }\r
7739 \r
7740   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7741     result = snd_pcm_drop( handle[1] );\r
7742     if ( result < 0 ) {\r
7743       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7744       errorText_ = errorStream_.str();\r
7745       goto unlock;\r
7746     }\r
7747   }\r
7748 \r
7749  unlock:\r
7750   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7751   MUTEX_UNLOCK( &stream_.mutex );\r
7752 \r
7753   if ( result >= 0 ) return;\r
7754   error( RtAudioError::SYSTEM_ERROR );\r
7755 }\r
7756 \r
7757 void RtApiAlsa :: abortStream()\r
7758 {\r
7759   verifyStream();\r
7760   if ( stream_.state == STREAM_STOPPED ) {\r
7761     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7762     error( RtAudioError::WARNING );\r
7763     return;\r
7764   }\r
7765 \r
7766   stream_.state = STREAM_STOPPED;\r
7767   MUTEX_LOCK( &stream_.mutex );\r
7768 \r
7769   int result = 0;\r
7770   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7771   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7772   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7773     result = snd_pcm_drop( handle[0] );\r
7774     if ( result < 0 ) {\r
7775       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7776       errorText_ = errorStream_.str();\r
7777       goto unlock;\r
7778     }\r
7779   }\r
7780 \r
7781   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7782     result = snd_pcm_drop( handle[1] );\r
7783     if ( result < 0 ) {\r
7784       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7785       errorText_ = errorStream_.str();\r
7786       goto unlock;\r
7787     }\r
7788   }\r
7789 \r
7790  unlock:\r
7791   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7792   MUTEX_UNLOCK( &stream_.mutex );\r
7793 \r
7794   if ( result >= 0 ) return;\r
7795   error( RtAudioError::SYSTEM_ERROR );\r
7796 }\r
7797 \r
7798 void RtApiAlsa :: callbackEvent()\r
7799 {\r
7800   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7801   if ( stream_.state == STREAM_STOPPED ) {\r
7802     MUTEX_LOCK( &stream_.mutex );\r
7803     while ( !apiInfo->runnable )\r
7804       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7805 \r
7806     if ( stream_.state != STREAM_RUNNING ) {\r
7807       MUTEX_UNLOCK( &stream_.mutex );\r
7808       return;\r
7809     }\r
7810     MUTEX_UNLOCK( &stream_.mutex );\r
7811   }\r
7812 \r
7813   if ( stream_.state == STREAM_CLOSED ) {\r
7814     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7815     error( RtAudioError::WARNING );\r
7816     return;\r
7817   }\r
7818 \r
7819   int doStopStream = 0;\r
7820   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7821   double streamTime = getStreamTime();\r
7822   RtAudioStreamStatus status = 0;\r
7823   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7824     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
7825     apiInfo->xrun[0] = false;\r
7826   }\r
7827   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
7828     status |= RTAUDIO_INPUT_OVERFLOW;\r
7829     apiInfo->xrun[1] = false;\r
7830   }\r
7831   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
7832                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
7833 \r
7834   if ( doStopStream == 2 ) {\r
7835     abortStream();\r
7836     return;\r
7837   }\r
7838 \r
7839   MUTEX_LOCK( &stream_.mutex );\r
7840 \r
7841   // The state might change while waiting on a mutex.\r
7842   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
7843 \r
7844   int result;\r
7845   char *buffer;\r
7846   int channels;\r
7847   snd_pcm_t **handle;\r
7848   snd_pcm_sframes_t frames;\r
7849   RtAudioFormat format;\r
7850   handle = (snd_pcm_t **) apiInfo->handles;\r
7851 \r
7852   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
7853 \r
7854     // Setup parameters.\r
7855     if ( stream_.doConvertBuffer[1] ) {\r
7856       buffer = stream_.deviceBuffer;\r
7857       channels = stream_.nDeviceChannels[1];\r
7858       format = stream_.deviceFormat[1];\r
7859     }\r
7860     else {\r
7861       buffer = stream_.userBuffer[1];\r
7862       channels = stream_.nUserChannels[1];\r
7863       format = stream_.userFormat;\r
7864     }\r
7865 \r
7866     // Read samples from device in interleaved/non-interleaved format.\r
7867     if ( stream_.deviceInterleaved[1] )\r
7868       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
7869     else {\r
7870       void *bufs[channels];\r
7871       size_t offset = stream_.bufferSize * formatBytes( format );\r
7872       for ( int i=0; i<channels; i++ )\r
7873         bufs[i] = (void *) (buffer + (i * offset));\r
7874       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
7875     }\r
7876 \r
7877     if ( result < (int) stream_.bufferSize ) {\r
7878       // Either an error or overrun occured.\r
7879       if ( result == -EPIPE ) {\r
7880         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
7881         if ( state == SND_PCM_STATE_XRUN ) {\r
7882           apiInfo->xrun[1] = true;\r
7883           result = snd_pcm_prepare( handle[1] );\r
7884           if ( result < 0 ) {\r
7885             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
7886             errorText_ = errorStream_.str();\r
7887           }\r
7888         }\r
7889         else {\r
7890           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7891           errorText_ = errorStream_.str();\r
7892         }\r
7893       }\r
7894       else {\r
7895         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
7896         errorText_ = errorStream_.str();\r
7897       }\r
7898       error( RtAudioError::WARNING );\r
7899       goto tryOutput;\r
7900     }\r
7901 \r
7902     // Do byte swapping if necessary.\r
7903     if ( stream_.doByteSwap[1] )\r
7904       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
7905 \r
7906     // Do buffer conversion if necessary.\r
7907     if ( stream_.doConvertBuffer[1] )\r
7908       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
7909 \r
7910     // Check stream latency\r
7911     result = snd_pcm_delay( handle[1], &frames );\r
7912     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
7913   }\r
7914 \r
7915  tryOutput:\r
7916 \r
7917   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7918 \r
7919     // Setup parameters and do buffer conversion if necessary.\r
7920     if ( stream_.doConvertBuffer[0] ) {\r
7921       buffer = stream_.deviceBuffer;\r
7922       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
7923       channels = stream_.nDeviceChannels[0];\r
7924       format = stream_.deviceFormat[0];\r
7925     }\r
7926     else {\r
7927       buffer = stream_.userBuffer[0];\r
7928       channels = stream_.nUserChannels[0];\r
7929       format = stream_.userFormat;\r
7930     }\r
7931 \r
7932     // Do byte swapping if necessary.\r
7933     if ( stream_.doByteSwap[0] )\r
7934       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
7935 \r
7936     // Write samples to device in interleaved/non-interleaved format.\r
7937     if ( stream_.deviceInterleaved[0] )\r
7938       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
7939     else {\r
7940       void *bufs[channels];\r
7941       size_t offset = stream_.bufferSize * formatBytes( format );\r
7942       for ( int i=0; i<channels; i++ )\r
7943         bufs[i] = (void *) (buffer + (i * offset));\r
7944       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
7945     }\r
7946 \r
7947     if ( result < (int) stream_.bufferSize ) {\r
7948       // Either an error or underrun occured.\r
7949       if ( result == -EPIPE ) {\r
7950         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
7951         if ( state == SND_PCM_STATE_XRUN ) {\r
7952           apiInfo->xrun[0] = true;\r
7953           result = snd_pcm_prepare( handle[0] );\r
7954           if ( result < 0 ) {\r
7955             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
7956             errorText_ = errorStream_.str();\r
7957           }\r
7958         }\r
7959         else {\r
7960           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7961           errorText_ = errorStream_.str();\r
7962         }\r
7963       }\r
7964       else {\r
7965         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
7966         errorText_ = errorStream_.str();\r
7967       }\r
7968       error( RtAudioError::WARNING );\r
7969       goto unlock;\r
7970     }\r
7971 \r
7972     // Check stream latency\r
7973     result = snd_pcm_delay( handle[0], &frames );\r
7974     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
7975   }\r
7976 \r
7977  unlock:\r
7978   MUTEX_UNLOCK( &stream_.mutex );\r
7979 \r
7980   RtApi::tickStreamTime();\r
7981   if ( doStopStream == 1 ) this->stopStream();\r
7982 }\r
7983 \r
7984 static void *alsaCallbackHandler( void *ptr )\r
7985 {\r
7986   CallbackInfo *info = (CallbackInfo *) ptr;\r
7987   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
7988   bool *isRunning = &info->isRunning;\r
7989 \r
7990 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7991   if ( &info->doRealtime ) {\r
7992     pthread_t tID = pthread_self();      // ID of this thread\r
7993     sched_param prio = { info->priority }; // scheduling priority of thread\r
7994     pthread_setschedparam( tID, SCHED_RR, &prio );\r
7995   }\r
7996 #endif\r
7997 \r
7998   while ( *isRunning == true ) {\r
7999     pthread_testcancel();\r
8000     object->callbackEvent();\r
8001   }\r
8002 \r
8003   pthread_exit( NULL );\r
8004 }\r
8005 \r
8006 //******************** End of __LINUX_ALSA__ *********************//\r
8007 #endif\r
8008 \r
8009 #if defined(__LINUX_PULSE__)\r
8010 \r
8011 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8012 // and Tristan Matthews.\r
8013 \r
8014 #include <pulse/error.h>\r
8015 #include <pulse/simple.h>\r
8016 #include <cstdio>\r
8017 \r
8018 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8019                                                       44100, 48000, 96000, 0};\r
8020 \r
8021 struct rtaudio_pa_format_mapping_t {\r
8022   RtAudioFormat rtaudio_format;\r
8023   pa_sample_format_t pa_format;\r
8024 };\r
8025 \r
8026 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8027   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8028   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8029   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8030   {0, PA_SAMPLE_INVALID}};\r
8031 \r
8032 struct PulseAudioHandle {\r
8033   pa_simple *s_play;\r
8034   pa_simple *s_rec;\r
8035   pthread_t thread;\r
8036   pthread_cond_t runnable_cv;\r
8037   bool runnable;\r
8038   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8039 };\r
8040 \r
8041 RtApiPulse::~RtApiPulse()\r
8042 {\r
8043   if ( stream_.state != STREAM_CLOSED )\r
8044     closeStream();\r
8045 }\r
8046 \r
8047 unsigned int RtApiPulse::getDeviceCount( void )\r
8048 {\r
8049   return 1;\r
8050 }\r
8051 \r
8052 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8053 {\r
8054   RtAudio::DeviceInfo info;\r
8055   info.probed = true;\r
8056   info.name = "PulseAudio";\r
8057   info.outputChannels = 2;\r
8058   info.inputChannels = 2;\r
8059   info.duplexChannels = 2;\r
8060   info.isDefaultOutput = true;\r
8061   info.isDefaultInput = true;\r
8062 \r
8063   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8064     info.sampleRates.push_back( *sr );\r
8065 \r
8066   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8067 \r
8068   return info;\r
8069 }\r
8070 \r
8071 static void *pulseaudio_callback( void * user )\r
8072 {\r
8073   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8074   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8075   volatile bool *isRunning = &cbi->isRunning;\r
8076 \r
8077   while ( *isRunning ) {\r
8078     pthread_testcancel();\r
8079     context->callbackEvent();\r
8080   }\r
8081 \r
8082   pthread_exit( NULL );\r
8083 }\r
8084 \r
8085 void RtApiPulse::closeStream( void )\r
8086 {\r
8087   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8088 \r
8089   stream_.callbackInfo.isRunning = false;\r
8090   if ( pah ) {\r
8091     MUTEX_LOCK( &stream_.mutex );\r
8092     if ( stream_.state == STREAM_STOPPED ) {\r
8093       pah->runnable = true;\r
8094       pthread_cond_signal( &pah->runnable_cv );\r
8095     }\r
8096     MUTEX_UNLOCK( &stream_.mutex );\r
8097 \r
8098     pthread_join( pah->thread, 0 );\r
8099     if ( pah->s_play ) {\r
8100       pa_simple_flush( pah->s_play, NULL );\r
8101       pa_simple_free( pah->s_play );\r
8102     }\r
8103     if ( pah->s_rec )\r
8104       pa_simple_free( pah->s_rec );\r
8105 \r
8106     pthread_cond_destroy( &pah->runnable_cv );\r
8107     delete pah;\r
8108     stream_.apiHandle = 0;\r
8109   }\r
8110 \r
8111   if ( stream_.userBuffer[0] ) {\r
8112     free( stream_.userBuffer[0] );\r
8113     stream_.userBuffer[0] = 0;\r
8114   }\r
8115   if ( stream_.userBuffer[1] ) {\r
8116     free( stream_.userBuffer[1] );\r
8117     stream_.userBuffer[1] = 0;\r
8118   }\r
8119 \r
8120   stream_.state = STREAM_CLOSED;\r
8121   stream_.mode = UNINITIALIZED;\r
8122 }\r
8123 \r
8124 void RtApiPulse::callbackEvent( void )\r
8125 {\r
8126   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8127 \r
8128   if ( stream_.state == STREAM_STOPPED ) {\r
8129     MUTEX_LOCK( &stream_.mutex );\r
8130     while ( !pah->runnable )\r
8131       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8132 \r
8133     if ( stream_.state != STREAM_RUNNING ) {\r
8134       MUTEX_UNLOCK( &stream_.mutex );\r
8135       return;\r
8136     }\r
8137     MUTEX_UNLOCK( &stream_.mutex );\r
8138   }\r
8139 \r
8140   if ( stream_.state == STREAM_CLOSED ) {\r
8141     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8142       "this shouldn't happen!";\r
8143     error( RtAudioError::WARNING );\r
8144     return;\r
8145   }\r
8146 \r
8147   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8148   double streamTime = getStreamTime();\r
8149   RtAudioStreamStatus status = 0;\r
8150   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8151                                stream_.bufferSize, streamTime, status,\r
8152                                stream_.callbackInfo.userData );\r
8153 \r
8154   if ( doStopStream == 2 ) {\r
8155     abortStream();\r
8156     return;\r
8157   }\r
8158 \r
8159   MUTEX_LOCK( &stream_.mutex );\r
8160   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8161   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8162 \r
8163   if ( stream_.state != STREAM_RUNNING )\r
8164     goto unlock;\r
8165 \r
8166   int pa_error;\r
8167   size_t bytes;\r
8168   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8169     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8170         convertBuffer( stream_.deviceBuffer,\r
8171                        stream_.userBuffer[OUTPUT],\r
8172                        stream_.convertInfo[OUTPUT] );\r
8173         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8174                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8175     } else\r
8176         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8177                 formatBytes( stream_.userFormat );\r
8178 \r
8179     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8180       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8181         pa_strerror( pa_error ) << ".";\r
8182       errorText_ = errorStream_.str();\r
8183       error( RtAudioError::WARNING );\r
8184     }\r
8185   }\r
8186 \r
8187   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8188     if ( stream_.doConvertBuffer[INPUT] )\r
8189       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8190         formatBytes( stream_.deviceFormat[INPUT] );\r
8191     else\r
8192       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8193         formatBytes( stream_.userFormat );\r
8194             \r
8195     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8196       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8197         pa_strerror( pa_error ) << ".";\r
8198       errorText_ = errorStream_.str();\r
8199       error( RtAudioError::WARNING );\r
8200     }\r
8201     if ( stream_.doConvertBuffer[INPUT] ) {\r
8202       convertBuffer( stream_.userBuffer[INPUT],\r
8203                      stream_.deviceBuffer,\r
8204                      stream_.convertInfo[INPUT] );\r
8205     }\r
8206   }\r
8207 \r
8208  unlock:\r
8209   MUTEX_UNLOCK( &stream_.mutex );\r
8210   RtApi::tickStreamTime();\r
8211 \r
8212   if ( doStopStream == 1 )\r
8213     stopStream();\r
8214 }\r
8215 \r
8216 void RtApiPulse::startStream( void )\r
8217 {\r
8218   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8219 \r
8220   if ( stream_.state == STREAM_CLOSED ) {\r
8221     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8222     error( RtAudioError::INVALID_USE );\r
8223     return;\r
8224   }\r
8225   if ( stream_.state == STREAM_RUNNING ) {\r
8226     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8227     error( RtAudioError::WARNING );\r
8228     return;\r
8229   }\r
8230 \r
8231   MUTEX_LOCK( &stream_.mutex );\r
8232 \r
8233   stream_.state = STREAM_RUNNING;\r
8234 \r
8235   pah->runnable = true;\r
8236   pthread_cond_signal( &pah->runnable_cv );\r
8237   MUTEX_UNLOCK( &stream_.mutex );\r
8238 }\r
8239 \r
8240 void RtApiPulse::stopStream( void )\r
8241 {\r
8242   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8243 \r
8244   if ( stream_.state == STREAM_CLOSED ) {\r
8245     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8246     error( RtAudioError::INVALID_USE );\r
8247     return;\r
8248   }\r
8249   if ( stream_.state == STREAM_STOPPED ) {\r
8250     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8251     error( RtAudioError::WARNING );\r
8252     return;\r
8253   }\r
8254 \r
8255   stream_.state = STREAM_STOPPED;\r
8256   MUTEX_LOCK( &stream_.mutex );\r
8257 \r
8258   if ( pah && pah->s_play ) {\r
8259     int pa_error;\r
8260     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8261       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8262         pa_strerror( pa_error ) << ".";\r
8263       errorText_ = errorStream_.str();\r
8264       MUTEX_UNLOCK( &stream_.mutex );\r
8265       error( RtAudioError::SYSTEM_ERROR );\r
8266       return;\r
8267     }\r
8268   }\r
8269 \r
8270   stream_.state = STREAM_STOPPED;\r
8271   MUTEX_UNLOCK( &stream_.mutex );\r
8272 }\r
8273 \r
8274 void RtApiPulse::abortStream( void )\r
8275 {\r
8276   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8277 \r
8278   if ( stream_.state == STREAM_CLOSED ) {\r
8279     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8280     error( RtAudioError::INVALID_USE );\r
8281     return;\r
8282   }\r
8283   if ( stream_.state == STREAM_STOPPED ) {\r
8284     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8285     error( RtAudioError::WARNING );\r
8286     return;\r
8287   }\r
8288 \r
8289   stream_.state = STREAM_STOPPED;\r
8290   MUTEX_LOCK( &stream_.mutex );\r
8291 \r
8292   if ( pah && pah->s_play ) {\r
8293     int pa_error;\r
8294     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8295       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8296         pa_strerror( pa_error ) << ".";\r
8297       errorText_ = errorStream_.str();\r
8298       MUTEX_UNLOCK( &stream_.mutex );\r
8299       error( RtAudioError::SYSTEM_ERROR );\r
8300       return;\r
8301     }\r
8302   }\r
8303 \r
8304   stream_.state = STREAM_STOPPED;\r
8305   MUTEX_UNLOCK( &stream_.mutex );\r
8306 }\r
8307 \r
8308 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8309                                   unsigned int channels, unsigned int firstChannel,\r
8310                                   unsigned int sampleRate, RtAudioFormat format,\r
8311                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8312 {\r
8313   PulseAudioHandle *pah = 0;\r
8314   unsigned long bufferBytes = 0;\r
8315   pa_sample_spec ss;\r
8316 \r
8317   if ( device != 0 ) return false;\r
8318   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8319   if ( channels != 1 && channels != 2 ) {\r
8320     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
8321     return false;\r
8322   }\r
8323   ss.channels = channels;\r
8324 \r
8325   if ( firstChannel != 0 ) return false;\r
8326 \r
8327   bool sr_found = false;\r
8328   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8329     if ( sampleRate == *sr ) {\r
8330       sr_found = true;\r
8331       stream_.sampleRate = sampleRate;\r
8332       ss.rate = sampleRate;\r
8333       break;\r
8334     }\r
8335   }\r
8336   if ( !sr_found ) {\r
8337     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8338     return false;\r
8339   }\r
8340 \r
8341   bool sf_found = 0;\r
8342   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8343         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8344     if ( format == sf->rtaudio_format ) {\r
8345       sf_found = true;\r
8346       stream_.userFormat = sf->rtaudio_format;\r
8347       stream_.deviceFormat[mode] = stream_.userFormat;\r
8348       ss.format = sf->pa_format;\r
8349       break;\r
8350     }\r
8351   }\r
8352   if ( !sf_found ) { // Use internal data format conversion.\r
8353     stream_.userFormat = format;\r
8354     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8355     ss.format = PA_SAMPLE_FLOAT32LE;\r
8356   }\r
8357 \r
8358   // Set other stream parameters.\r
8359   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8360   else stream_.userInterleaved = true;\r
8361   stream_.deviceInterleaved[mode] = true;\r
8362   stream_.nBuffers = 1;\r
8363   stream_.doByteSwap[mode] = false;\r
8364   stream_.nUserChannels[mode] = channels;\r
8365   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8366   stream_.channelOffset[mode] = 0;\r
8367   std::string streamName = "RtAudio";\r
8368 \r
8369   // Set flags for buffer conversion.\r
8370   stream_.doConvertBuffer[mode] = false;\r
8371   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8372     stream_.doConvertBuffer[mode] = true;\r
8373   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8374     stream_.doConvertBuffer[mode] = true;\r
8375 \r
8376   // Allocate necessary internal buffers.\r
8377   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8378   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8379   if ( stream_.userBuffer[mode] == NULL ) {\r
8380     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8381     goto error;\r
8382   }\r
8383   stream_.bufferSize = *bufferSize;\r
8384 \r
8385   if ( stream_.doConvertBuffer[mode] ) {\r
8386 \r
8387     bool makeBuffer = true;\r
8388     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8389     if ( mode == INPUT ) {\r
8390       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8391         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8392         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8393       }\r
8394     }\r
8395 \r
8396     if ( makeBuffer ) {\r
8397       bufferBytes *= *bufferSize;\r
8398       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8399       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8400       if ( stream_.deviceBuffer == NULL ) {\r
8401         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8402         goto error;\r
8403       }\r
8404     }\r
8405   }\r
8406 \r
8407   stream_.device[mode] = device;\r
8408 \r
8409   // Setup the buffer conversion information structure.\r
8410   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8411 \r
8412   if ( !stream_.apiHandle ) {\r
8413     PulseAudioHandle *pah = new PulseAudioHandle;\r
8414     if ( !pah ) {\r
8415       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8416       goto error;\r
8417     }\r
8418 \r
8419     stream_.apiHandle = pah;\r
8420     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8421       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8422       goto error;\r
8423     }\r
8424   }\r
8425   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8426 \r
8427   int error;\r
8428   if ( !options->streamName.empty() ) streamName = options->streamName;\r
8429   switch ( mode ) {\r
8430   case INPUT:\r
8431     pa_buffer_attr buffer_attr;\r
8432     buffer_attr.fragsize = bufferBytes;\r
8433     buffer_attr.maxlength = -1;\r
8434 \r
8435     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8436     if ( !pah->s_rec ) {\r
8437       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8438       goto error;\r
8439     }\r
8440     break;\r
8441   case OUTPUT:\r
8442     pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
8443     if ( !pah->s_play ) {\r
8444       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8445       goto error;\r
8446     }\r
8447     break;\r
8448   default:\r
8449     goto error;\r
8450   }\r
8451 \r
8452   if ( stream_.mode == UNINITIALIZED )\r
8453     stream_.mode = mode;\r
8454   else if ( stream_.mode == mode )\r
8455     goto error;\r
8456   else\r
8457     stream_.mode = DUPLEX;\r
8458 \r
8459   if ( !stream_.callbackInfo.isRunning ) {\r
8460     stream_.callbackInfo.object = this;\r
8461     stream_.callbackInfo.isRunning = true;\r
8462     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8463       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8464       goto error;\r
8465     }\r
8466   }\r
8467 \r
8468   stream_.state = STREAM_STOPPED;\r
8469   return true;\r
8470  \r
8471  error:\r
8472   if ( pah && stream_.callbackInfo.isRunning ) {\r
8473     pthread_cond_destroy( &pah->runnable_cv );\r
8474     delete pah;\r
8475     stream_.apiHandle = 0;\r
8476   }\r
8477 \r
8478   for ( int i=0; i<2; i++ ) {\r
8479     if ( stream_.userBuffer[i] ) {\r
8480       free( stream_.userBuffer[i] );\r
8481       stream_.userBuffer[i] = 0;\r
8482     }\r
8483   }\r
8484 \r
8485   if ( stream_.deviceBuffer ) {\r
8486     free( stream_.deviceBuffer );\r
8487     stream_.deviceBuffer = 0;\r
8488   }\r
8489 \r
8490   return FAILURE;\r
8491 }\r
8492 \r
8493 //******************** End of __LINUX_PULSE__ *********************//\r
8494 #endif\r
8495 \r
8496 #if defined(__LINUX_OSS__)\r
8497 \r
8498 #include <unistd.h>\r
8499 #include <sys/ioctl.h>\r
8500 #include <unistd.h>\r
8501 #include <fcntl.h>\r
8502 #include <sys/soundcard.h>\r
8503 #include <errno.h>\r
8504 #include <math.h>\r
8505 \r
8506 static void *ossCallbackHandler(void * ptr);\r
8507 \r
8508 // A structure to hold various information related to the OSS API\r
8509 // implementation.\r
8510 struct OssHandle {\r
8511   int id[2];    // device ids\r
8512   bool xrun[2];\r
8513   bool triggered;\r
8514   pthread_cond_t runnable;\r
8515 \r
8516   OssHandle()\r
8517     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8518 };\r
8519 \r
8520 RtApiOss :: RtApiOss()\r
8521 {\r
8522   // Nothing to do here.\r
8523 }\r
8524 \r
8525 RtApiOss :: ~RtApiOss()\r
8526 {\r
8527   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8528 }\r
8529 \r
8530 unsigned int RtApiOss :: getDeviceCount( void )\r
8531 {\r
8532   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8533   if ( mixerfd == -1 ) {\r
8534     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8535     error( RtAudioError::WARNING );\r
8536     return 0;\r
8537   }\r
8538 \r
8539   oss_sysinfo sysinfo;\r
8540   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8541     close( mixerfd );\r
8542     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8543     error( RtAudioError::WARNING );\r
8544     return 0;\r
8545   }\r
8546 \r
8547   close( mixerfd );\r
8548   return sysinfo.numaudios;\r
8549 }\r
8550 \r
8551 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8552 {\r
8553   RtAudio::DeviceInfo info;\r
8554   info.probed = false;\r
8555 \r
8556   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8557   if ( mixerfd == -1 ) {\r
8558     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8559     error( RtAudioError::WARNING );\r
8560     return info;\r
8561   }\r
8562 \r
8563   oss_sysinfo sysinfo;\r
8564   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8565   if ( result == -1 ) {\r
8566     close( mixerfd );\r
8567     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8568     error( RtAudioError::WARNING );\r
8569     return info;\r
8570   }\r
8571 \r
8572   unsigned nDevices = sysinfo.numaudios;\r
8573   if ( nDevices == 0 ) {\r
8574     close( mixerfd );\r
8575     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8576     error( RtAudioError::INVALID_USE );\r
8577     return info;\r
8578   }\r
8579 \r
8580   if ( device >= nDevices ) {\r
8581     close( mixerfd );\r
8582     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8583     error( RtAudioError::INVALID_USE );\r
8584     return info;\r
8585   }\r
8586 \r
8587   oss_audioinfo ainfo;\r
8588   ainfo.dev = device;\r
8589   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8590   close( mixerfd );\r
8591   if ( result == -1 ) {\r
8592     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8593     errorText_ = errorStream_.str();\r
8594     error( RtAudioError::WARNING );\r
8595     return info;\r
8596   }\r
8597 \r
8598   // Probe channels\r
8599   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8600   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8601   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8602     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8603       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8604   }\r
8605 \r
8606   // Probe data formats ... do for input\r
8607   unsigned long mask = ainfo.iformats;\r
8608   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8609     info.nativeFormats |= RTAUDIO_SINT16;\r
8610   if ( mask & AFMT_S8 )\r
8611     info.nativeFormats |= RTAUDIO_SINT8;\r
8612   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8613     info.nativeFormats |= RTAUDIO_SINT32;\r
8614   if ( mask & AFMT_FLOAT )\r
8615     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8616   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8617     info.nativeFormats |= RTAUDIO_SINT24;\r
8618 \r
8619   // Check that we have at least one supported format\r
8620   if ( info.nativeFormats == 0 ) {\r
8621     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8622     errorText_ = errorStream_.str();\r
8623     error( RtAudioError::WARNING );\r
8624     return info;\r
8625   }\r
8626 \r
8627   // Probe the supported sample rates.\r
8628   info.sampleRates.clear();\r
8629   if ( ainfo.nrates ) {\r
8630     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8631       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8632         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8633           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8634           break;\r
8635         }\r
8636       }\r
8637     }\r
8638   }\r
8639   else {\r
8640     // Check min and max rate values;\r
8641     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8642       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )\r
8643         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8644     }\r
8645   }\r
8646 \r
8647   if ( info.sampleRates.size() == 0 ) {\r
8648     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8649     errorText_ = errorStream_.str();\r
8650     error( RtAudioError::WARNING );\r
8651   }\r
8652   else {\r
8653     info.probed = true;\r
8654     info.name = ainfo.name;\r
8655   }\r
8656 \r
8657   return info;\r
8658 }\r
8659 \r
8660 \r
8661 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8662                                   unsigned int firstChannel, unsigned int sampleRate,\r
8663                                   RtAudioFormat format, unsigned int *bufferSize,\r
8664                                   RtAudio::StreamOptions *options )\r
8665 {\r
8666   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8667   if ( mixerfd == -1 ) {\r
8668     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8669     return FAILURE;\r
8670   }\r
8671 \r
8672   oss_sysinfo sysinfo;\r
8673   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8674   if ( result == -1 ) {\r
8675     close( mixerfd );\r
8676     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8677     return FAILURE;\r
8678   }\r
8679 \r
8680   unsigned nDevices = sysinfo.numaudios;\r
8681   if ( nDevices == 0 ) {\r
8682     // This should not happen because a check is made before this function is called.\r
8683     close( mixerfd );\r
8684     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8685     return FAILURE;\r
8686   }\r
8687 \r
8688   if ( device >= nDevices ) {\r
8689     // This should not happen because a check is made before this function is called.\r
8690     close( mixerfd );\r
8691     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8692     return FAILURE;\r
8693   }\r
8694 \r
8695   oss_audioinfo ainfo;\r
8696   ainfo.dev = device;\r
8697   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8698   close( mixerfd );\r
8699   if ( result == -1 ) {\r
8700     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8701     errorText_ = errorStream_.str();\r
8702     return FAILURE;\r
8703   }\r
8704 \r
8705   // Check if device supports input or output\r
8706   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8707        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8708     if ( mode == OUTPUT )\r
8709       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8710     else\r
8711       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8712     errorText_ = errorStream_.str();\r
8713     return FAILURE;\r
8714   }\r
8715 \r
8716   int flags = 0;\r
8717   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8718   if ( mode == OUTPUT )\r
8719     flags |= O_WRONLY;\r
8720   else { // mode == INPUT\r
8721     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8722       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8723       close( handle->id[0] );\r
8724       handle->id[0] = 0;\r
8725       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8726         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8727         errorText_ = errorStream_.str();\r
8728         return FAILURE;\r
8729       }\r
8730       // Check that the number previously set channels is the same.\r
8731       if ( stream_.nUserChannels[0] != channels ) {\r
8732         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8733         errorText_ = errorStream_.str();\r
8734         return FAILURE;\r
8735       }\r
8736       flags |= O_RDWR;\r
8737     }\r
8738     else\r
8739       flags |= O_RDONLY;\r
8740   }\r
8741 \r
8742   // Set exclusive access if specified.\r
8743   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8744 \r
8745   // Try to open the device.\r
8746   int fd;\r
8747   fd = open( ainfo.devnode, flags, 0 );\r
8748   if ( fd == -1 ) {\r
8749     if ( errno == EBUSY )\r
8750       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8751     else\r
8752       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8753     errorText_ = errorStream_.str();\r
8754     return FAILURE;\r
8755   }\r
8756 \r
8757   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8758   /*\r
8759     if ( flags | O_RDWR ) {\r
8760     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
8761     if ( result == -1) {\r
8762     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
8763     errorText_ = errorStream_.str();\r
8764     return FAILURE;\r
8765     }\r
8766     }\r
8767   */\r
8768 \r
8769   // Check the device channel support.\r
8770   stream_.nUserChannels[mode] = channels;\r
8771   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
8772     close( fd );\r
8773     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
8774     errorText_ = errorStream_.str();\r
8775     return FAILURE;\r
8776   }\r
8777 \r
8778   // Set the number of channels.\r
8779   int deviceChannels = channels + firstChannel;\r
8780   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
8781   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
8782     close( fd );\r
8783     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
8784     errorText_ = errorStream_.str();\r
8785     return FAILURE;\r
8786   }\r
8787   stream_.nDeviceChannels[mode] = deviceChannels;\r
8788 \r
8789   // Get the data format mask\r
8790   int mask;\r
8791   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
8792   if ( result == -1 ) {\r
8793     close( fd );\r
8794     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
8795     errorText_ = errorStream_.str();\r
8796     return FAILURE;\r
8797   }\r
8798 \r
8799   // Determine how to set the device format.\r
8800   stream_.userFormat = format;\r
8801   int deviceFormat = -1;\r
8802   stream_.doByteSwap[mode] = false;\r
8803   if ( format == RTAUDIO_SINT8 ) {\r
8804     if ( mask & AFMT_S8 ) {\r
8805       deviceFormat = AFMT_S8;\r
8806       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8807     }\r
8808   }\r
8809   else if ( format == RTAUDIO_SINT16 ) {\r
8810     if ( mask & AFMT_S16_NE ) {\r
8811       deviceFormat = AFMT_S16_NE;\r
8812       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8813     }\r
8814     else if ( mask & AFMT_S16_OE ) {\r
8815       deviceFormat = AFMT_S16_OE;\r
8816       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8817       stream_.doByteSwap[mode] = true;\r
8818     }\r
8819   }\r
8820   else if ( format == RTAUDIO_SINT24 ) {\r
8821     if ( mask & AFMT_S24_NE ) {\r
8822       deviceFormat = AFMT_S24_NE;\r
8823       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8824     }\r
8825     else if ( mask & AFMT_S24_OE ) {\r
8826       deviceFormat = AFMT_S24_OE;\r
8827       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8828       stream_.doByteSwap[mode] = true;\r
8829     }\r
8830   }\r
8831   else if ( format == RTAUDIO_SINT32 ) {\r
8832     if ( mask & AFMT_S32_NE ) {\r
8833       deviceFormat = AFMT_S32_NE;\r
8834       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8835     }\r
8836     else if ( mask & AFMT_S32_OE ) {\r
8837       deviceFormat = AFMT_S32_OE;\r
8838       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8839       stream_.doByteSwap[mode] = true;\r
8840     }\r
8841   }\r
8842 \r
8843   if ( deviceFormat == -1 ) {\r
8844     // The user requested format is not natively supported by the device.\r
8845     if ( mask & AFMT_S16_NE ) {\r
8846       deviceFormat = AFMT_S16_NE;\r
8847       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8848     }\r
8849     else if ( mask & AFMT_S32_NE ) {\r
8850       deviceFormat = AFMT_S32_NE;\r
8851       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8852     }\r
8853     else if ( mask & AFMT_S24_NE ) {\r
8854       deviceFormat = AFMT_S24_NE;\r
8855       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8856     }\r
8857     else if ( mask & AFMT_S16_OE ) {\r
8858       deviceFormat = AFMT_S16_OE;\r
8859       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8860       stream_.doByteSwap[mode] = true;\r
8861     }\r
8862     else if ( mask & AFMT_S32_OE ) {\r
8863       deviceFormat = AFMT_S32_OE;\r
8864       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8865       stream_.doByteSwap[mode] = true;\r
8866     }\r
8867     else if ( mask & AFMT_S24_OE ) {\r
8868       deviceFormat = AFMT_S24_OE;\r
8869       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8870       stream_.doByteSwap[mode] = true;\r
8871     }\r
8872     else if ( mask & AFMT_S8) {\r
8873       deviceFormat = AFMT_S8;\r
8874       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8875     }\r
8876   }\r
8877 \r
8878   if ( stream_.deviceFormat[mode] == 0 ) {\r
8879     // This really shouldn't happen ...\r
8880     close( fd );\r
8881     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8882     errorText_ = errorStream_.str();\r
8883     return FAILURE;\r
8884   }\r
8885 \r
8886   // Set the data format.\r
8887   int temp = deviceFormat;\r
8888   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
8889   if ( result == -1 || deviceFormat != temp ) {\r
8890     close( fd );\r
8891     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
8892     errorText_ = errorStream_.str();\r
8893     return FAILURE;\r
8894   }\r
8895 \r
8896   // Attempt to set the buffer size.  According to OSS, the minimum\r
8897   // number of buffers is two.  The supposed minimum buffer size is 16\r
8898   // bytes, so that will be our lower bound.  The argument to this\r
8899   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
8900   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
8901   // We'll check the actual value used near the end of the setup\r
8902   // procedure.\r
8903   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
8904   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
8905   int buffers = 0;\r
8906   if ( options ) buffers = options->numberOfBuffers;\r
8907   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
8908   if ( buffers < 2 ) buffers = 3;\r
8909   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
8910   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
8911   if ( result == -1 ) {\r
8912     close( fd );\r
8913     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
8914     errorText_ = errorStream_.str();\r
8915     return FAILURE;\r
8916   }\r
8917   stream_.nBuffers = buffers;\r
8918 \r
8919   // Save buffer size (in sample frames).\r
8920   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
8921   stream_.bufferSize = *bufferSize;\r
8922 \r
8923   // Set the sample rate.\r
8924   int srate = sampleRate;\r
8925   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
8926   if ( result == -1 ) {\r
8927     close( fd );\r
8928     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
8929     errorText_ = errorStream_.str();\r
8930     return FAILURE;\r
8931   }\r
8932 \r
8933   // Verify the sample rate setup worked.\r
8934   if ( abs( srate - sampleRate ) > 100 ) {\r
8935     close( fd );\r
8936     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
8937     errorText_ = errorStream_.str();\r
8938     return FAILURE;\r
8939   }\r
8940   stream_.sampleRate = sampleRate;\r
8941 \r
8942   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8943     // We're doing duplex setup here.\r
8944     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
8945     stream_.nDeviceChannels[0] = deviceChannels;\r
8946   }\r
8947 \r
8948   // Set interleaving parameters.\r
8949   stream_.userInterleaved = true;\r
8950   stream_.deviceInterleaved[mode] =  true;\r
8951   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
8952     stream_.userInterleaved = false;\r
8953 \r
8954   // Set flags for buffer conversion\r
8955   stream_.doConvertBuffer[mode] = false;\r
8956   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8957     stream_.doConvertBuffer[mode] = true;\r
8958   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8959     stream_.doConvertBuffer[mode] = true;\r
8960   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
8961        stream_.nUserChannels[mode] > 1 )\r
8962     stream_.doConvertBuffer[mode] = true;\r
8963 \r
8964   // Allocate the stream handles if necessary and then save.\r
8965   if ( stream_.apiHandle == 0 ) {\r
8966     try {\r
8967       handle = new OssHandle;\r
8968     }\r
8969     catch ( std::bad_alloc& ) {\r
8970       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
8971       goto error;\r
8972     }\r
8973 \r
8974     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
8975       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
8976       goto error;\r
8977     }\r
8978 \r
8979     stream_.apiHandle = (void *) handle;\r
8980   }\r
8981   else {\r
8982     handle = (OssHandle *) stream_.apiHandle;\r
8983   }\r
8984   handle->id[mode] = fd;\r
8985 \r
8986   // Allocate necessary internal buffers.\r
8987   unsigned long bufferBytes;\r
8988   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8989   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8990   if ( stream_.userBuffer[mode] == NULL ) {\r
8991     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
8992     goto error;\r
8993   }\r
8994 \r
8995   if ( stream_.doConvertBuffer[mode] ) {\r
8996 \r
8997     bool makeBuffer = true;\r
8998     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8999     if ( mode == INPUT ) {\r
9000       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
9001         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
9002         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
9003       }\r
9004     }\r
9005 \r
9006     if ( makeBuffer ) {\r
9007       bufferBytes *= *bufferSize;\r
9008       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9009       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9010       if ( stream_.deviceBuffer == NULL ) {\r
9011         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9012         goto error;\r
9013       }\r
9014     }\r
9015   }\r
9016 \r
9017   stream_.device[mode] = device;\r
9018   stream_.state = STREAM_STOPPED;\r
9019 \r
9020   // Setup the buffer conversion information structure.\r
9021   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9022 \r
9023   // Setup thread if necessary.\r
9024   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9025     // We had already set up an output stream.\r
9026     stream_.mode = DUPLEX;\r
9027     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9028   }\r
9029   else {\r
9030     stream_.mode = mode;\r
9031 \r
9032     // Setup callback thread.\r
9033     stream_.callbackInfo.object = (void *) this;\r
9034 \r
9035     // Set the thread attributes for joinable and realtime scheduling\r
9036     // priority.  The higher priority will only take affect if the\r
9037     // program is run as root or suid.\r
9038     pthread_attr_t attr;\r
9039     pthread_attr_init( &attr );\r
9040     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9041 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9042     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9043       struct sched_param param;\r
9044       int priority = options->priority;\r
9045       int min = sched_get_priority_min( SCHED_RR );\r
9046       int max = sched_get_priority_max( SCHED_RR );\r
9047       if ( priority < min ) priority = min;\r
9048       else if ( priority > max ) priority = max;\r
9049       param.sched_priority = priority;\r
9050       pthread_attr_setschedparam( &attr, &param );\r
9051       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9052     }\r
9053     else\r
9054       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9055 #else\r
9056     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9057 #endif\r
9058 \r
9059     stream_.callbackInfo.isRunning = true;\r
9060     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9061     pthread_attr_destroy( &attr );\r
9062     if ( result ) {\r
9063       stream_.callbackInfo.isRunning = false;\r
9064       errorText_ = "RtApiOss::error creating callback thread!";\r
9065       goto error;\r
9066     }\r
9067   }\r
9068 \r
9069   return SUCCESS;\r
9070 \r
9071  error:\r
9072   if ( handle ) {\r
9073     pthread_cond_destroy( &handle->runnable );\r
9074     if ( handle->id[0] ) close( handle->id[0] );\r
9075     if ( handle->id[1] ) close( handle->id[1] );\r
9076     delete handle;\r
9077     stream_.apiHandle = 0;\r
9078   }\r
9079 \r
9080   for ( int i=0; i<2; i++ ) {\r
9081     if ( stream_.userBuffer[i] ) {\r
9082       free( stream_.userBuffer[i] );\r
9083       stream_.userBuffer[i] = 0;\r
9084     }\r
9085   }\r
9086 \r
9087   if ( stream_.deviceBuffer ) {\r
9088     free( stream_.deviceBuffer );\r
9089     stream_.deviceBuffer = 0;\r
9090   }\r
9091 \r
9092   return FAILURE;\r
9093 }\r
9094 \r
9095 void RtApiOss :: closeStream()\r
9096 {\r
9097   if ( stream_.state == STREAM_CLOSED ) {\r
9098     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9099     error( RtAudioError::WARNING );\r
9100     return;\r
9101   }\r
9102 \r
9103   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9104   stream_.callbackInfo.isRunning = false;\r
9105   MUTEX_LOCK( &stream_.mutex );\r
9106   if ( stream_.state == STREAM_STOPPED )\r
9107     pthread_cond_signal( &handle->runnable );\r
9108   MUTEX_UNLOCK( &stream_.mutex );\r
9109   pthread_join( stream_.callbackInfo.thread, NULL );\r
9110 \r
9111   if ( stream_.state == STREAM_RUNNING ) {\r
9112     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9113       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9114     else\r
9115       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9116     stream_.state = STREAM_STOPPED;\r
9117   }\r
9118 \r
9119   if ( handle ) {\r
9120     pthread_cond_destroy( &handle->runnable );\r
9121     if ( handle->id[0] ) close( handle->id[0] );\r
9122     if ( handle->id[1] ) close( handle->id[1] );\r
9123     delete handle;\r
9124     stream_.apiHandle = 0;\r
9125   }\r
9126 \r
9127   for ( int i=0; i<2; i++ ) {\r
9128     if ( stream_.userBuffer[i] ) {\r
9129       free( stream_.userBuffer[i] );\r
9130       stream_.userBuffer[i] = 0;\r
9131     }\r
9132   }\r
9133 \r
9134   if ( stream_.deviceBuffer ) {\r
9135     free( stream_.deviceBuffer );\r
9136     stream_.deviceBuffer = 0;\r
9137   }\r
9138 \r
9139   stream_.mode = UNINITIALIZED;\r
9140   stream_.state = STREAM_CLOSED;\r
9141 }\r
9142 \r
9143 void RtApiOss :: startStream()\r
9144 {\r
9145   verifyStream();\r
9146   if ( stream_.state == STREAM_RUNNING ) {\r
9147     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9148     error( RtAudioError::WARNING );\r
9149     return;\r
9150   }\r
9151 \r
9152   MUTEX_LOCK( &stream_.mutex );\r
9153 \r
9154   stream_.state = STREAM_RUNNING;\r
9155 \r
9156   // No need to do anything else here ... OSS automatically starts\r
9157   // when fed samples.\r
9158 \r
9159   MUTEX_UNLOCK( &stream_.mutex );\r
9160 \r
9161   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9162   pthread_cond_signal( &handle->runnable );\r
9163 }\r
9164 \r
9165 void RtApiOss :: stopStream()\r
9166 {\r
9167   verifyStream();\r
9168   if ( stream_.state == STREAM_STOPPED ) {\r
9169     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9170     error( RtAudioError::WARNING );\r
9171     return;\r
9172   }\r
9173 \r
9174   MUTEX_LOCK( &stream_.mutex );\r
9175 \r
9176   // The state might change while waiting on a mutex.\r
9177   if ( stream_.state == STREAM_STOPPED ) {\r
9178     MUTEX_UNLOCK( &stream_.mutex );\r
9179     return;\r
9180   }\r
9181 \r
9182   int result = 0;\r
9183   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9184   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9185 \r
9186     // Flush the output with zeros a few times.\r
9187     char *buffer;\r
9188     int samples;\r
9189     RtAudioFormat format;\r
9190 \r
9191     if ( stream_.doConvertBuffer[0] ) {\r
9192       buffer = stream_.deviceBuffer;\r
9193       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9194       format = stream_.deviceFormat[0];\r
9195     }\r
9196     else {\r
9197       buffer = stream_.userBuffer[0];\r
9198       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9199       format = stream_.userFormat;\r
9200     }\r
9201 \r
9202     memset( buffer, 0, samples * formatBytes(format) );\r
9203     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9204       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9205       if ( result == -1 ) {\r
9206         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9207         error( RtAudioError::WARNING );\r
9208       }\r
9209     }\r
9210 \r
9211     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9212     if ( result == -1 ) {\r
9213       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9214       errorText_ = errorStream_.str();\r
9215       goto unlock;\r
9216     }\r
9217     handle->triggered = false;\r
9218   }\r
9219 \r
9220   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9221     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9222     if ( result == -1 ) {\r
9223       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9224       errorText_ = errorStream_.str();\r
9225       goto unlock;\r
9226     }\r
9227   }\r
9228 \r
9229  unlock:\r
9230   stream_.state = STREAM_STOPPED;\r
9231   MUTEX_UNLOCK( &stream_.mutex );\r
9232 \r
9233   if ( result != -1 ) return;\r
9234   error( RtAudioError::SYSTEM_ERROR );\r
9235 }\r
9236 \r
9237 void RtApiOss :: abortStream()\r
9238 {\r
9239   verifyStream();\r
9240   if ( stream_.state == STREAM_STOPPED ) {\r
9241     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9242     error( RtAudioError::WARNING );\r
9243     return;\r
9244   }\r
9245 \r
9246   MUTEX_LOCK( &stream_.mutex );\r
9247 \r
9248   // The state might change while waiting on a mutex.\r
9249   if ( stream_.state == STREAM_STOPPED ) {\r
9250     MUTEX_UNLOCK( &stream_.mutex );\r
9251     return;\r
9252   }\r
9253 \r
9254   int result = 0;\r
9255   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9256   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9257     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9258     if ( result == -1 ) {\r
9259       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9260       errorText_ = errorStream_.str();\r
9261       goto unlock;\r
9262     }\r
9263     handle->triggered = false;\r
9264   }\r
9265 \r
9266   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9267     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9268     if ( result == -1 ) {\r
9269       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9270       errorText_ = errorStream_.str();\r
9271       goto unlock;\r
9272     }\r
9273   }\r
9274 \r
9275  unlock:\r
9276   stream_.state = STREAM_STOPPED;\r
9277   MUTEX_UNLOCK( &stream_.mutex );\r
9278 \r
9279   if ( result != -1 ) return;\r
9280   error( RtAudioError::SYSTEM_ERROR );\r
9281 }\r
9282 \r
9283 void RtApiOss :: callbackEvent()\r
9284 {\r
9285   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9286   if ( stream_.state == STREAM_STOPPED ) {\r
9287     MUTEX_LOCK( &stream_.mutex );\r
9288     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9289     if ( stream_.state != STREAM_RUNNING ) {\r
9290       MUTEX_UNLOCK( &stream_.mutex );\r
9291       return;\r
9292     }\r
9293     MUTEX_UNLOCK( &stream_.mutex );\r
9294   }\r
9295 \r
9296   if ( stream_.state == STREAM_CLOSED ) {\r
9297     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9298     error( RtAudioError::WARNING );\r
9299     return;\r
9300   }\r
9301 \r
9302   // Invoke user callback to get fresh output data.\r
9303   int doStopStream = 0;\r
9304   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9305   double streamTime = getStreamTime();\r
9306   RtAudioStreamStatus status = 0;\r
9307   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9308     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9309     handle->xrun[0] = false;\r
9310   }\r
9311   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9312     status |= RTAUDIO_INPUT_OVERFLOW;\r
9313     handle->xrun[1] = false;\r
9314   }\r
9315   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9316                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9317   if ( doStopStream == 2 ) {\r
9318     this->abortStream();\r
9319     return;\r
9320   }\r
9321 \r
9322   MUTEX_LOCK( &stream_.mutex );\r
9323 \r
9324   // The state might change while waiting on a mutex.\r
9325   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9326 \r
9327   int result;\r
9328   char *buffer;\r
9329   int samples;\r
9330   RtAudioFormat format;\r
9331 \r
9332   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9333 \r
9334     // Setup parameters and do buffer conversion if necessary.\r
9335     if ( stream_.doConvertBuffer[0] ) {\r
9336       buffer = stream_.deviceBuffer;\r
9337       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9338       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9339       format = stream_.deviceFormat[0];\r
9340     }\r
9341     else {\r
9342       buffer = stream_.userBuffer[0];\r
9343       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9344       format = stream_.userFormat;\r
9345     }\r
9346 \r
9347     // Do byte swapping if necessary.\r
9348     if ( stream_.doByteSwap[0] )\r
9349       byteSwapBuffer( buffer, samples, format );\r
9350 \r
9351     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9352       int trig = 0;\r
9353       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9354       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9355       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9356       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9357       handle->triggered = true;\r
9358     }\r
9359     else\r
9360       // Write samples to device.\r
9361       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9362 \r
9363     if ( result == -1 ) {\r
9364       // We'll assume this is an underrun, though there isn't a\r
9365       // specific means for determining that.\r
9366       handle->xrun[0] = true;\r
9367       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9368       error( RtAudioError::WARNING );\r
9369       // Continue on to input section.\r
9370     }\r
9371   }\r
9372 \r
9373   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9374 \r
9375     // Setup parameters.\r
9376     if ( stream_.doConvertBuffer[1] ) {\r
9377       buffer = stream_.deviceBuffer;\r
9378       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9379       format = stream_.deviceFormat[1];\r
9380     }\r
9381     else {\r
9382       buffer = stream_.userBuffer[1];\r
9383       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9384       format = stream_.userFormat;\r
9385     }\r
9386 \r
9387     // Read samples from device.\r
9388     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9389 \r
9390     if ( result == -1 ) {\r
9391       // We'll assume this is an overrun, though there isn't a\r
9392       // specific means for determining that.\r
9393       handle->xrun[1] = true;\r
9394       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9395       error( RtAudioError::WARNING );\r
9396       goto unlock;\r
9397     }\r
9398 \r
9399     // Do byte swapping if necessary.\r
9400     if ( stream_.doByteSwap[1] )\r
9401       byteSwapBuffer( buffer, samples, format );\r
9402 \r
9403     // Do buffer conversion if necessary.\r
9404     if ( stream_.doConvertBuffer[1] )\r
9405       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9406   }\r
9407 \r
9408  unlock:\r
9409   MUTEX_UNLOCK( &stream_.mutex );\r
9410 \r
9411   RtApi::tickStreamTime();\r
9412   if ( doStopStream == 1 ) this->stopStream();\r
9413 }\r
9414 \r
9415 static void *ossCallbackHandler( void *ptr )\r
9416 {\r
9417   CallbackInfo *info = (CallbackInfo *) ptr;\r
9418   RtApiOss *object = (RtApiOss *) info->object;\r
9419   bool *isRunning = &info->isRunning;\r
9420 \r
9421   while ( *isRunning == true ) {\r
9422     pthread_testcancel();\r
9423     object->callbackEvent();\r
9424   }\r
9425 \r
9426   pthread_exit( NULL );\r
9427 }\r
9428 \r
9429 //******************** End of __LINUX_OSS__ *********************//\r
9430 #endif\r
9431 \r
9432 \r
9433 // *************************************************** //\r
9434 //\r
9435 // Protected common (OS-independent) RtAudio methods.\r
9436 //\r
9437 // *************************************************** //\r
9438 \r
9439 // This method can be modified to control the behavior of error\r
9440 // message printing.\r
9441 void RtApi :: error( RtAudioError::Type type )\r
9442 {\r
9443   errorStream_.str(""); // clear the ostringstream\r
9444 \r
9445   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9446   if ( errorCallback ) {\r
9447     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9448 \r
9449     if ( firstErrorOccurred_ )\r
9450       return;\r
9451 \r
9452     firstErrorOccurred_ = true;\r
9453     const std::string errorMessage = errorText_;\r
9454 \r
9455     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9456       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9457       abortStream();\r
9458     }\r
9459 \r
9460     errorCallback( type, errorMessage );\r
9461     firstErrorOccurred_ = false;\r
9462     return;\r
9463   }\r
9464 \r
9465   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9466     std::cerr << '\n' << errorText_ << "\n\n";\r
9467   else if ( type != RtAudioError::WARNING )\r
9468     throw( RtAudioError( errorText_, type ) );\r
9469 }\r
9470 \r
9471 void RtApi :: verifyStream()\r
9472 {\r
9473   if ( stream_.state == STREAM_CLOSED ) {\r
9474     errorText_ = "RtApi:: a stream is not open!";\r
9475     error( RtAudioError::INVALID_USE );\r
9476   }\r
9477 }\r
9478 \r
9479 void RtApi :: clearStreamInfo()\r
9480 {\r
9481   stream_.mode = UNINITIALIZED;\r
9482   stream_.state = STREAM_CLOSED;\r
9483   stream_.sampleRate = 0;\r
9484   stream_.bufferSize = 0;\r
9485   stream_.nBuffers = 0;\r
9486   stream_.userFormat = 0;\r
9487   stream_.userInterleaved = true;\r
9488   stream_.streamTime = 0.0;\r
9489   stream_.apiHandle = 0;\r
9490   stream_.deviceBuffer = 0;\r
9491   stream_.callbackInfo.callback = 0;\r
9492   stream_.callbackInfo.userData = 0;\r
9493   stream_.callbackInfo.isRunning = false;\r
9494   stream_.callbackInfo.errorCallback = 0;\r
9495   for ( int i=0; i<2; i++ ) {\r
9496     stream_.device[i] = 11111;\r
9497     stream_.doConvertBuffer[i] = false;\r
9498     stream_.deviceInterleaved[i] = true;\r
9499     stream_.doByteSwap[i] = false;\r
9500     stream_.nUserChannels[i] = 0;\r
9501     stream_.nDeviceChannels[i] = 0;\r
9502     stream_.channelOffset[i] = 0;\r
9503     stream_.deviceFormat[i] = 0;\r
9504     stream_.latency[i] = 0;\r
9505     stream_.userBuffer[i] = 0;\r
9506     stream_.convertInfo[i].channels = 0;\r
9507     stream_.convertInfo[i].inJump = 0;\r
9508     stream_.convertInfo[i].outJump = 0;\r
9509     stream_.convertInfo[i].inFormat = 0;\r
9510     stream_.convertInfo[i].outFormat = 0;\r
9511     stream_.convertInfo[i].inOffset.clear();\r
9512     stream_.convertInfo[i].outOffset.clear();\r
9513   }\r
9514 }\r
9515 \r
9516 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9517 {\r
9518   if ( format == RTAUDIO_SINT16 )\r
9519     return 2;\r
9520   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9521     return 4;\r
9522   else if ( format == RTAUDIO_FLOAT64 )\r
9523     return 8;\r
9524   else if ( format == RTAUDIO_SINT24 )\r
9525     return 3;\r
9526   else if ( format == RTAUDIO_SINT8 )\r
9527     return 1;\r
9528 \r
9529   errorText_ = "RtApi::formatBytes: undefined format.";\r
9530   error( RtAudioError::WARNING );\r
9531 \r
9532   return 0;\r
9533 }\r
9534 \r
9535 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9536 {\r
9537   if ( mode == INPUT ) { // convert device to user buffer\r
9538     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9539     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9540     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9541     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9542   }\r
9543   else { // convert user to device buffer\r
9544     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9545     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9546     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9547     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9548   }\r
9549 \r
9550   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9551     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9552   else\r
9553     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9554 \r
9555   // Set up the interleave/deinterleave offsets.\r
9556   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9557     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9558          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9559       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9560         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9561         stream_.convertInfo[mode].outOffset.push_back( k );\r
9562         stream_.convertInfo[mode].inJump = 1;\r
9563       }\r
9564     }\r
9565     else {\r
9566       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9567         stream_.convertInfo[mode].inOffset.push_back( k );\r
9568         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9569         stream_.convertInfo[mode].outJump = 1;\r
9570       }\r
9571     }\r
9572   }\r
9573   else { // no (de)interleaving\r
9574     if ( stream_.userInterleaved ) {\r
9575       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9576         stream_.convertInfo[mode].inOffset.push_back( k );\r
9577         stream_.convertInfo[mode].outOffset.push_back( k );\r
9578       }\r
9579     }\r
9580     else {\r
9581       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9582         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9583         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9584         stream_.convertInfo[mode].inJump = 1;\r
9585         stream_.convertInfo[mode].outJump = 1;\r
9586       }\r
9587     }\r
9588   }\r
9589 \r
9590   // Add channel offset.\r
9591   if ( firstChannel > 0 ) {\r
9592     if ( stream_.deviceInterleaved[mode] ) {\r
9593       if ( mode == OUTPUT ) {\r
9594         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9595           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9596       }\r
9597       else {\r
9598         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9599           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9600       }\r
9601     }\r
9602     else {\r
9603       if ( mode == OUTPUT ) {\r
9604         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9605           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9606       }\r
9607       else {\r
9608         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9609           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9610       }\r
9611     }\r
9612   }\r
9613 }\r
9614 \r
9615 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9616 {\r
9617   // This function does format conversion, input/output channel compensation, and\r
9618   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9619   // the lower three bytes of a 32-bit integer.\r
9620 \r
9621   // Clear our device buffer when in/out duplex device channels are different\r
9622   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9623        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9624     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9625 \r
9626   int j;\r
9627   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9628     Float64 scale;\r
9629     Float64 *out = (Float64 *)outBuffer;\r
9630 \r
9631     if (info.inFormat == RTAUDIO_SINT8) {\r
9632       signed char *in = (signed char *)inBuffer;\r
9633       scale = 1.0 / 127.5;\r
9634       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9635         for (j=0; j<info.channels; j++) {\r
9636           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9637           out[info.outOffset[j]] += 0.5;\r
9638           out[info.outOffset[j]] *= scale;\r
9639         }\r
9640         in += info.inJump;\r
9641         out += info.outJump;\r
9642       }\r
9643     }\r
9644     else if (info.inFormat == RTAUDIO_SINT16) {\r
9645       Int16 *in = (Int16 *)inBuffer;\r
9646       scale = 1.0 / 32767.5;\r
9647       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9648         for (j=0; j<info.channels; j++) {\r
9649           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9650           out[info.outOffset[j]] += 0.5;\r
9651           out[info.outOffset[j]] *= scale;\r
9652         }\r
9653         in += info.inJump;\r
9654         out += info.outJump;\r
9655       }\r
9656     }\r
9657     else if (info.inFormat == RTAUDIO_SINT24) {\r
9658       Int24 *in = (Int24 *)inBuffer;\r
9659       scale = 1.0 / 8388607.5;\r
9660       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9661         for (j=0; j<info.channels; j++) {\r
9662           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9663           out[info.outOffset[j]] += 0.5;\r
9664           out[info.outOffset[j]] *= scale;\r
9665         }\r
9666         in += info.inJump;\r
9667         out += info.outJump;\r
9668       }\r
9669     }\r
9670     else if (info.inFormat == RTAUDIO_SINT32) {\r
9671       Int32 *in = (Int32 *)inBuffer;\r
9672       scale = 1.0 / 2147483647.5;\r
9673       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9674         for (j=0; j<info.channels; j++) {\r
9675           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9676           out[info.outOffset[j]] += 0.5;\r
9677           out[info.outOffset[j]] *= scale;\r
9678         }\r
9679         in += info.inJump;\r
9680         out += info.outJump;\r
9681       }\r
9682     }\r
9683     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9684       Float32 *in = (Float32 *)inBuffer;\r
9685       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9686         for (j=0; j<info.channels; j++) {\r
9687           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9688         }\r
9689         in += info.inJump;\r
9690         out += info.outJump;\r
9691       }\r
9692     }\r
9693     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9694       // Channel compensation and/or (de)interleaving only.\r
9695       Float64 *in = (Float64 *)inBuffer;\r
9696       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9697         for (j=0; j<info.channels; j++) {\r
9698           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9699         }\r
9700         in += info.inJump;\r
9701         out += info.outJump;\r
9702       }\r
9703     }\r
9704   }\r
9705   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9706     Float32 scale;\r
9707     Float32 *out = (Float32 *)outBuffer;\r
9708 \r
9709     if (info.inFormat == RTAUDIO_SINT8) {\r
9710       signed char *in = (signed char *)inBuffer;\r
9711       scale = (Float32) ( 1.0 / 127.5 );\r
9712       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9713         for (j=0; j<info.channels; j++) {\r
9714           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9715           out[info.outOffset[j]] += 0.5;\r
9716           out[info.outOffset[j]] *= scale;\r
9717         }\r
9718         in += info.inJump;\r
9719         out += info.outJump;\r
9720       }\r
9721     }\r
9722     else if (info.inFormat == RTAUDIO_SINT16) {\r
9723       Int16 *in = (Int16 *)inBuffer;\r
9724       scale = (Float32) ( 1.0 / 32767.5 );\r
9725       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9726         for (j=0; j<info.channels; j++) {\r
9727           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9728           out[info.outOffset[j]] += 0.5;\r
9729           out[info.outOffset[j]] *= scale;\r
9730         }\r
9731         in += info.inJump;\r
9732         out += info.outJump;\r
9733       }\r
9734     }\r
9735     else if (info.inFormat == RTAUDIO_SINT24) {\r
9736       Int24 *in = (Int24 *)inBuffer;\r
9737       scale = (Float32) ( 1.0 / 8388607.5 );\r
9738       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9739         for (j=0; j<info.channels; j++) {\r
9740           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9741           out[info.outOffset[j]] += 0.5;\r
9742           out[info.outOffset[j]] *= scale;\r
9743         }\r
9744         in += info.inJump;\r
9745         out += info.outJump;\r
9746       }\r
9747     }\r
9748     else if (info.inFormat == RTAUDIO_SINT32) {\r
9749       Int32 *in = (Int32 *)inBuffer;\r
9750       scale = (Float32) ( 1.0 / 2147483647.5 );\r
9751       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9752         for (j=0; j<info.channels; j++) {\r
9753           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9754           out[info.outOffset[j]] += 0.5;\r
9755           out[info.outOffset[j]] *= scale;\r
9756         }\r
9757         in += info.inJump;\r
9758         out += info.outJump;\r
9759       }\r
9760     }\r
9761     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9762       // Channel compensation and/or (de)interleaving only.\r
9763       Float32 *in = (Float32 *)inBuffer;\r
9764       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9765         for (j=0; j<info.channels; j++) {\r
9766           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9767         }\r
9768         in += info.inJump;\r
9769         out += info.outJump;\r
9770       }\r
9771     }\r
9772     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9773       Float64 *in = (Float64 *)inBuffer;\r
9774       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9775         for (j=0; j<info.channels; j++) {\r
9776           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9777         }\r
9778         in += info.inJump;\r
9779         out += info.outJump;\r
9780       }\r
9781     }\r
9782   }\r
9783   else if (info.outFormat == RTAUDIO_SINT32) {\r
9784     Int32 *out = (Int32 *)outBuffer;\r
9785     if (info.inFormat == RTAUDIO_SINT8) {\r
9786       signed char *in = (signed char *)inBuffer;\r
9787       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9788         for (j=0; j<info.channels; j++) {\r
9789           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9790           out[info.outOffset[j]] <<= 24;\r
9791         }\r
9792         in += info.inJump;\r
9793         out += info.outJump;\r
9794       }\r
9795     }\r
9796     else if (info.inFormat == RTAUDIO_SINT16) {\r
9797       Int16 *in = (Int16 *)inBuffer;\r
9798       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9799         for (j=0; j<info.channels; j++) {\r
9800           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9801           out[info.outOffset[j]] <<= 16;\r
9802         }\r
9803         in += info.inJump;\r
9804         out += info.outJump;\r
9805       }\r
9806     }\r
9807     else if (info.inFormat == RTAUDIO_SINT24) {\r
9808       Int24 *in = (Int24 *)inBuffer;\r
9809       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9810         for (j=0; j<info.channels; j++) {\r
9811           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
9812           out[info.outOffset[j]] <<= 8;\r
9813         }\r
9814         in += info.inJump;\r
9815         out += info.outJump;\r
9816       }\r
9817     }\r
9818     else if (info.inFormat == RTAUDIO_SINT32) {\r
9819       // Channel compensation and/or (de)interleaving only.\r
9820       Int32 *in = (Int32 *)inBuffer;\r
9821       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9822         for (j=0; j<info.channels; j++) {\r
9823           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9824         }\r
9825         in += info.inJump;\r
9826         out += info.outJump;\r
9827       }\r
9828     }\r
9829     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9830       Float32 *in = (Float32 *)inBuffer;\r
9831       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9832         for (j=0; j<info.channels; j++) {\r
9833           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9834         }\r
9835         in += info.inJump;\r
9836         out += info.outJump;\r
9837       }\r
9838     }\r
9839     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9840       Float64 *in = (Float64 *)inBuffer;\r
9841       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9842         for (j=0; j<info.channels; j++) {\r
9843           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9844         }\r
9845         in += info.inJump;\r
9846         out += info.outJump;\r
9847       }\r
9848     }\r
9849   }\r
9850   else if (info.outFormat == RTAUDIO_SINT24) {\r
9851     Int24 *out = (Int24 *)outBuffer;\r
9852     if (info.inFormat == RTAUDIO_SINT8) {\r
9853       signed char *in = (signed char *)inBuffer;\r
9854       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9855         for (j=0; j<info.channels; j++) {\r
9856           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
9857           //out[info.outOffset[j]] <<= 16;\r
9858         }\r
9859         in += info.inJump;\r
9860         out += info.outJump;\r
9861       }\r
9862     }\r
9863     else if (info.inFormat == RTAUDIO_SINT16) {\r
9864       Int16 *in = (Int16 *)inBuffer;\r
9865       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9866         for (j=0; j<info.channels; j++) {\r
9867           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
9868           //out[info.outOffset[j]] <<= 8;\r
9869         }\r
9870         in += info.inJump;\r
9871         out += info.outJump;\r
9872       }\r
9873     }\r
9874     else if (info.inFormat == RTAUDIO_SINT24) {\r
9875       // Channel compensation and/or (de)interleaving only.\r
9876       Int24 *in = (Int24 *)inBuffer;\r
9877       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9878         for (j=0; j<info.channels; j++) {\r
9879           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9880         }\r
9881         in += info.inJump;\r
9882         out += info.outJump;\r
9883       }\r
9884     }\r
9885     else if (info.inFormat == RTAUDIO_SINT32) {\r
9886       Int32 *in = (Int32 *)inBuffer;\r
9887       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9888         for (j=0; j<info.channels; j++) {\r
9889           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
9890           //out[info.outOffset[j]] >>= 8;\r
9891         }\r
9892         in += info.inJump;\r
9893         out += info.outJump;\r
9894       }\r
9895     }\r
9896     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9897       Float32 *in = (Float32 *)inBuffer;\r
9898       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9899         for (j=0; j<info.channels; j++) {\r
9900           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9901         }\r
9902         in += info.inJump;\r
9903         out += info.outJump;\r
9904       }\r
9905     }\r
9906     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9907       Float64 *in = (Float64 *)inBuffer;\r
9908       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9909         for (j=0; j<info.channels; j++) {\r
9910           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9911         }\r
9912         in += info.inJump;\r
9913         out += info.outJump;\r
9914       }\r
9915     }\r
9916   }\r
9917   else if (info.outFormat == RTAUDIO_SINT16) {\r
9918     Int16 *out = (Int16 *)outBuffer;\r
9919     if (info.inFormat == RTAUDIO_SINT8) {\r
9920       signed char *in = (signed char *)inBuffer;\r
9921       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9922         for (j=0; j<info.channels; j++) {\r
9923           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
9924           out[info.outOffset[j]] <<= 8;\r
9925         }\r
9926         in += info.inJump;\r
9927         out += info.outJump;\r
9928       }\r
9929     }\r
9930     else if (info.inFormat == RTAUDIO_SINT16) {\r
9931       // Channel compensation and/or (de)interleaving only.\r
9932       Int16 *in = (Int16 *)inBuffer;\r
9933       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9934         for (j=0; j<info.channels; j++) {\r
9935           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9936         }\r
9937         in += info.inJump;\r
9938         out += info.outJump;\r
9939       }\r
9940     }\r
9941     else if (info.inFormat == RTAUDIO_SINT24) {\r
9942       Int24 *in = (Int24 *)inBuffer;\r
9943       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9944         for (j=0; j<info.channels; j++) {\r
9945           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
9946         }\r
9947         in += info.inJump;\r
9948         out += info.outJump;\r
9949       }\r
9950     }\r
9951     else if (info.inFormat == RTAUDIO_SINT32) {\r
9952       Int32 *in = (Int32 *)inBuffer;\r
9953       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9954         for (j=0; j<info.channels; j++) {\r
9955           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
9956         }\r
9957         in += info.inJump;\r
9958         out += info.outJump;\r
9959       }\r
9960     }\r
9961     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9962       Float32 *in = (Float32 *)inBuffer;\r
9963       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9964         for (j=0; j<info.channels; j++) {\r
9965           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9966         }\r
9967         in += info.inJump;\r
9968         out += info.outJump;\r
9969       }\r
9970     }\r
9971     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9972       Float64 *in = (Float64 *)inBuffer;\r
9973       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9974         for (j=0; j<info.channels; j++) {\r
9975           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9976         }\r
9977         in += info.inJump;\r
9978         out += info.outJump;\r
9979       }\r
9980     }\r
9981   }\r
9982   else if (info.outFormat == RTAUDIO_SINT8) {\r
9983     signed char *out = (signed char *)outBuffer;\r
9984     if (info.inFormat == RTAUDIO_SINT8) {\r
9985       // Channel compensation and/or (de)interleaving only.\r
9986       signed char *in = (signed char *)inBuffer;\r
9987       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9988         for (j=0; j<info.channels; j++) {\r
9989           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9990         }\r
9991         in += info.inJump;\r
9992         out += info.outJump;\r
9993       }\r
9994     }\r
9995     if (info.inFormat == RTAUDIO_SINT16) {\r
9996       Int16 *in = (Int16 *)inBuffer;\r
9997       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9998         for (j=0; j<info.channels; j++) {\r
9999           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
10000         }\r
10001         in += info.inJump;\r
10002         out += info.outJump;\r
10003       }\r
10004     }\r
10005     else if (info.inFormat == RTAUDIO_SINT24) {\r
10006       Int24 *in = (Int24 *)inBuffer;\r
10007       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10008         for (j=0; j<info.channels; j++) {\r
10009           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10010         }\r
10011         in += info.inJump;\r
10012         out += info.outJump;\r
10013       }\r
10014     }\r
10015     else if (info.inFormat == RTAUDIO_SINT32) {\r
10016       Int32 *in = (Int32 *)inBuffer;\r
10017       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10018         for (j=0; j<info.channels; j++) {\r
10019           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10020         }\r
10021         in += info.inJump;\r
10022         out += info.outJump;\r
10023       }\r
10024     }\r
10025     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10026       Float32 *in = (Float32 *)inBuffer;\r
10027       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10028         for (j=0; j<info.channels; j++) {\r
10029           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10030         }\r
10031         in += info.inJump;\r
10032         out += info.outJump;\r
10033       }\r
10034     }\r
10035     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10036       Float64 *in = (Float64 *)inBuffer;\r
10037       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10038         for (j=0; j<info.channels; j++) {\r
10039           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10040         }\r
10041         in += info.inJump;\r
10042         out += info.outJump;\r
10043       }\r
10044     }\r
10045   }\r
10046 }\r
10047 \r
10048 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10049 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10050 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10051 \r
10052 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10053 {\r
10054   register char val;\r
10055   register char *ptr;\r
10056 \r
10057   ptr = buffer;\r
10058   if ( format == RTAUDIO_SINT16 ) {\r
10059     for ( unsigned int i=0; i<samples; i++ ) {\r
10060       // Swap 1st and 2nd bytes.\r
10061       val = *(ptr);\r
10062       *(ptr) = *(ptr+1);\r
10063       *(ptr+1) = val;\r
10064 \r
10065       // Increment 2 bytes.\r
10066       ptr += 2;\r
10067     }\r
10068   }\r
10069   else if ( format == RTAUDIO_SINT32 ||\r
10070             format == RTAUDIO_FLOAT32 ) {\r
10071     for ( unsigned int i=0; i<samples; i++ ) {\r
10072       // Swap 1st and 4th bytes.\r
10073       val = *(ptr);\r
10074       *(ptr) = *(ptr+3);\r
10075       *(ptr+3) = val;\r
10076 \r
10077       // Swap 2nd and 3rd bytes.\r
10078       ptr += 1;\r
10079       val = *(ptr);\r
10080       *(ptr) = *(ptr+1);\r
10081       *(ptr+1) = val;\r
10082 \r
10083       // Increment 3 more bytes.\r
10084       ptr += 3;\r
10085     }\r
10086   }\r
10087   else if ( format == RTAUDIO_SINT24 ) {\r
10088     for ( unsigned int i=0; i<samples; i++ ) {\r
10089       // Swap 1st and 3rd bytes.\r
10090       val = *(ptr);\r
10091       *(ptr) = *(ptr+2);\r
10092       *(ptr+2) = val;\r
10093 \r
10094       // Increment 2 more bytes.\r
10095       ptr += 2;\r
10096     }\r
10097   }\r
10098   else if ( format == RTAUDIO_FLOAT64 ) {\r
10099     for ( unsigned int i=0; i<samples; i++ ) {\r
10100       // Swap 1st and 8th bytes\r
10101       val = *(ptr);\r
10102       *(ptr) = *(ptr+7);\r
10103       *(ptr+7) = val;\r
10104 \r
10105       // Swap 2nd and 7th bytes\r
10106       ptr += 1;\r
10107       val = *(ptr);\r
10108       *(ptr) = *(ptr+5);\r
10109       *(ptr+5) = val;\r
10110 \r
10111       // Swap 3rd and 6th bytes\r
10112       ptr += 1;\r
10113       val = *(ptr);\r
10114       *(ptr) = *(ptr+3);\r
10115       *(ptr+3) = val;\r
10116 \r
10117       // Swap 4th and 5th bytes\r
10118       ptr += 1;\r
10119       val = *(ptr);\r
10120       *(ptr) = *(ptr+1);\r
10121       *(ptr+1) = val;\r
10122 \r
10123       // Increment 5 more bytes.\r
10124       ptr += 5;\r
10125     }\r
10126   }\r
10127 }\r
10128 \r
10129   // Indentation settings for Vim and Emacs\r
10130   //\r
10131   // Local Variables:\r
10132   // c-basic-offset: 2\r
10133   // indent-tabs-mode: nil\r
10134   // End:\r
10135   //\r
10136   // vim: et sts=2 sw=2\r
10137 \r