Added new setStreamTime function; Documentation updates for 4.1.1 release.
[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   snd_card_next( &card );\r
6819   while ( card >= 0 ) {\r
6820     sprintf( name, "hw:%d", card );\r
6821     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6822     if ( result < 0 ) {\r
6823       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6824       errorText_ = errorStream_.str();\r
6825       error( RtAudioError::WARNING );\r
6826       goto nextcard;\r
6827     }\r
6828     subdevice = -1;\r
6829     while( 1 ) {\r
6830       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
6831       if ( result < 0 ) {\r
6832         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6833         errorText_ = errorStream_.str();\r
6834         error( RtAudioError::WARNING );\r
6835         break;\r
6836       }\r
6837       if ( subdevice < 0 ) break;\r
6838       if ( nDevices == device ) {\r
6839         sprintf( name, "hw:%d,%d", card, subdevice );\r
6840         goto foundDevice;\r
6841       }\r
6842       nDevices++;\r
6843     }\r
6844   nextcard:\r
6845     snd_ctl_close( chandle );\r
6846     snd_card_next( &card );\r
6847   }\r
6848 \r
6849   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
6850   if ( result == 0 ) {\r
6851     if ( nDevices == device ) {\r
6852       strcpy( name, "default" );\r
6853       goto foundDevice;\r
6854     }\r
6855     nDevices++;\r
6856   }\r
6857 \r
6858   if ( nDevices == 0 ) {\r
6859     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
6860     error( RtAudioError::INVALID_USE );\r
6861     return info;\r
6862   }\r
6863 \r
6864   if ( device >= nDevices ) {\r
6865     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
6866     error( RtAudioError::INVALID_USE );\r
6867     return info;\r
6868   }\r
6869 \r
6870  foundDevice:\r
6871 \r
6872   // If a stream is already open, we cannot probe the stream devices.\r
6873   // Thus, use the saved results.\r
6874   if ( stream_.state != STREAM_CLOSED &&\r
6875        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
6876     snd_ctl_close( chandle );\r
6877     if ( device >= devices_.size() ) {\r
6878       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
6879       error( RtAudioError::WARNING );\r
6880       return info;\r
6881     }\r
6882     return devices_[ device ];\r
6883   }\r
6884 \r
6885   int openMode = SND_PCM_ASYNC;\r
6886   snd_pcm_stream_t stream;\r
6887   snd_pcm_info_t *pcminfo;\r
6888   snd_pcm_info_alloca( &pcminfo );\r
6889   snd_pcm_t *phandle;\r
6890   snd_pcm_hw_params_t *params;\r
6891   snd_pcm_hw_params_alloca( &params );\r
6892 \r
6893   // First try for playback unless default device (which has subdev -1)\r
6894   stream = SND_PCM_STREAM_PLAYBACK;\r
6895   snd_pcm_info_set_stream( pcminfo, stream );\r
6896   if ( subdevice != -1 ) {\r
6897     snd_pcm_info_set_device( pcminfo, subdevice );\r
6898     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
6899 \r
6900     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6901     if ( result < 0 ) {\r
6902       // Device probably doesn't support playback.\r
6903       goto captureProbe;\r
6904     }\r
6905   }\r
6906 \r
6907   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
6908   if ( result < 0 ) {\r
6909     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6910     errorText_ = errorStream_.str();\r
6911     error( RtAudioError::WARNING );\r
6912     goto captureProbe;\r
6913   }\r
6914 \r
6915   // The device is open ... fill the parameter structure.\r
6916   result = snd_pcm_hw_params_any( phandle, params );\r
6917   if ( result < 0 ) {\r
6918     snd_pcm_close( phandle );\r
6919     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6920     errorText_ = errorStream_.str();\r
6921     error( RtAudioError::WARNING );\r
6922     goto captureProbe;\r
6923   }\r
6924 \r
6925   // Get output channel information.\r
6926   unsigned int value;\r
6927   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6928   if ( result < 0 ) {\r
6929     snd_pcm_close( phandle );\r
6930     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
6931     errorText_ = errorStream_.str();\r
6932     error( RtAudioError::WARNING );\r
6933     goto captureProbe;\r
6934   }\r
6935   info.outputChannels = value;\r
6936   snd_pcm_close( phandle );\r
6937 \r
6938  captureProbe:\r
6939   stream = SND_PCM_STREAM_CAPTURE;\r
6940   snd_pcm_info_set_stream( pcminfo, stream );\r
6941 \r
6942   // Now try for capture unless default device (with subdev = -1)\r
6943   if ( subdevice != -1 ) {\r
6944     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6945     snd_ctl_close( chandle );\r
6946     if ( result < 0 ) {\r
6947       // Device probably doesn't support capture.\r
6948       if ( info.outputChannels == 0 ) return info;\r
6949       goto probeParameters;\r
6950     }\r
6951   }\r
6952   else\r
6953     snd_ctl_close( chandle );\r
6954 \r
6955   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
6956   if ( result < 0 ) {\r
6957     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6958     errorText_ = errorStream_.str();\r
6959     error( RtAudioError::WARNING );\r
6960     if ( info.outputChannels == 0 ) return info;\r
6961     goto probeParameters;\r
6962   }\r
6963 \r
6964   // The device is open ... fill the parameter structure.\r
6965   result = snd_pcm_hw_params_any( phandle, params );\r
6966   if ( result < 0 ) {\r
6967     snd_pcm_close( phandle );\r
6968     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6969     errorText_ = errorStream_.str();\r
6970     error( RtAudioError::WARNING );\r
6971     if ( info.outputChannels == 0 ) return info;\r
6972     goto probeParameters;\r
6973   }\r
6974 \r
6975   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6976   if ( result < 0 ) {\r
6977     snd_pcm_close( phandle );\r
6978     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
6979     errorText_ = errorStream_.str();\r
6980     error( RtAudioError::WARNING );\r
6981     if ( info.outputChannels == 0 ) return info;\r
6982     goto probeParameters;\r
6983   }\r
6984   info.inputChannels = value;\r
6985   snd_pcm_close( phandle );\r
6986 \r
6987   // If device opens for both playback and capture, we determine the channels.\r
6988   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
6989     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
6990 \r
6991   // ALSA doesn't provide default devices so we'll use the first available one.\r
6992   if ( device == 0 && info.outputChannels > 0 )\r
6993     info.isDefaultOutput = true;\r
6994   if ( device == 0 && info.inputChannels > 0 )\r
6995     info.isDefaultInput = true;\r
6996 \r
6997  probeParameters:\r
6998   // At this point, we just need to figure out the supported data\r
6999   // formats and sample rates.  We'll proceed by opening the device in\r
7000   // the direction with the maximum number of channels, or playback if\r
7001   // they are equal.  This might limit our sample rate options, but so\r
7002   // be it.\r
7003 \r
7004   if ( info.outputChannels >= info.inputChannels )\r
7005     stream = SND_PCM_STREAM_PLAYBACK;\r
7006   else\r
7007     stream = SND_PCM_STREAM_CAPTURE;\r
7008   snd_pcm_info_set_stream( pcminfo, stream );\r
7009 \r
7010   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7011   if ( result < 0 ) {\r
7012     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7013     errorText_ = errorStream_.str();\r
7014     error( RtAudioError::WARNING );\r
7015     return info;\r
7016   }\r
7017 \r
7018   // The device is open ... fill the parameter structure.\r
7019   result = snd_pcm_hw_params_any( phandle, params );\r
7020   if ( result < 0 ) {\r
7021     snd_pcm_close( phandle );\r
7022     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7023     errorText_ = errorStream_.str();\r
7024     error( RtAudioError::WARNING );\r
7025     return info;\r
7026   }\r
7027 \r
7028   // Test our discrete set of sample rate values.\r
7029   info.sampleRates.clear();\r
7030   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7031     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )\r
7032       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7033   }\r
7034   if ( info.sampleRates.size() == 0 ) {\r
7035     snd_pcm_close( phandle );\r
7036     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7037     errorText_ = errorStream_.str();\r
7038     error( RtAudioError::WARNING );\r
7039     return info;\r
7040   }\r
7041 \r
7042   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7043   snd_pcm_format_t format;\r
7044   info.nativeFormats = 0;\r
7045   format = SND_PCM_FORMAT_S8;\r
7046   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7047     info.nativeFormats |= RTAUDIO_SINT8;\r
7048   format = SND_PCM_FORMAT_S16;\r
7049   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7050     info.nativeFormats |= RTAUDIO_SINT16;\r
7051   format = SND_PCM_FORMAT_S24;\r
7052   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7053     info.nativeFormats |= RTAUDIO_SINT24;\r
7054   format = SND_PCM_FORMAT_S32;\r
7055   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7056     info.nativeFormats |= RTAUDIO_SINT32;\r
7057   format = SND_PCM_FORMAT_FLOAT;\r
7058   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7059     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7060   format = SND_PCM_FORMAT_FLOAT64;\r
7061   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7062     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7063 \r
7064   // Check that we have at least one supported format\r
7065   if ( info.nativeFormats == 0 ) {\r
7066     snd_pcm_close( phandle );\r
7067     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7068     errorText_ = errorStream_.str();\r
7069     error( RtAudioError::WARNING );\r
7070     return info;\r
7071   }\r
7072 \r
7073   // Get the device name\r
7074   char *cardname;\r
7075   result = snd_card_get_name( card, &cardname );\r
7076   if ( result >= 0 ) {\r
7077     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7078     free( cardname );\r
7079   }\r
7080   info.name = name;\r
7081 \r
7082   // That's all ... close the device and return\r
7083   snd_pcm_close( phandle );\r
7084   info.probed = true;\r
7085   return info;\r
7086 }\r
7087 \r
7088 void RtApiAlsa :: saveDeviceInfo( void )\r
7089 {\r
7090   devices_.clear();\r
7091 \r
7092   unsigned int nDevices = getDeviceCount();\r
7093   devices_.resize( nDevices );\r
7094   for ( unsigned int i=0; i<nDevices; i++ )\r
7095     devices_[i] = getDeviceInfo( i );\r
7096 }\r
7097 \r
7098 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7099                                    unsigned int firstChannel, unsigned int sampleRate,\r
7100                                    RtAudioFormat format, unsigned int *bufferSize,\r
7101                                    RtAudio::StreamOptions *options )\r
7102 \r
7103 {\r
7104 #if defined(__RTAUDIO_DEBUG__)\r
7105   snd_output_t *out;\r
7106   snd_output_stdio_attach(&out, stderr, 0);\r
7107 #endif\r
7108 \r
7109   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7110 \r
7111   unsigned nDevices = 0;\r
7112   int result, subdevice, card;\r
7113   char name[64];\r
7114   snd_ctl_t *chandle;\r
7115 \r
7116   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7117     snprintf(name, sizeof(name), "%s", "default");\r
7118   else {\r
7119     // Count cards and devices\r
7120     card = -1;\r
7121     snd_card_next( &card );\r
7122     while ( card >= 0 ) {\r
7123       sprintf( name, "hw:%d", card );\r
7124       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7125       if ( result < 0 ) {\r
7126         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7127         errorText_ = errorStream_.str();\r
7128         return FAILURE;\r
7129       }\r
7130       subdevice = -1;\r
7131       while( 1 ) {\r
7132         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7133         if ( result < 0 ) break;\r
7134         if ( subdevice < 0 ) break;\r
7135         if ( nDevices == device ) {\r
7136           sprintf( name, "hw:%d,%d", card, subdevice );\r
7137           snd_ctl_close( chandle );\r
7138           goto foundDevice;\r
7139         }\r
7140         nDevices++;\r
7141       }\r
7142       snd_ctl_close( chandle );\r
7143       snd_card_next( &card );\r
7144     }\r
7145 \r
7146     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7147     if ( result == 0 ) {\r
7148       if ( nDevices == device ) {\r
7149         strcpy( name, "default" );\r
7150         goto foundDevice;\r
7151       }\r
7152       nDevices++;\r
7153     }\r
7154 \r
7155     if ( nDevices == 0 ) {\r
7156       // This should not happen because a check is made before this function is called.\r
7157       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7158       return FAILURE;\r
7159     }\r
7160 \r
7161     if ( device >= nDevices ) {\r
7162       // This should not happen because a check is made before this function is called.\r
7163       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7164       return FAILURE;\r
7165     }\r
7166   }\r
7167 \r
7168  foundDevice:\r
7169 \r
7170   // The getDeviceInfo() function will not work for a device that is\r
7171   // already open.  Thus, we'll probe the system before opening a\r
7172   // stream and save the results for use by getDeviceInfo().\r
7173   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7174     this->saveDeviceInfo();\r
7175 \r
7176   snd_pcm_stream_t stream;\r
7177   if ( mode == OUTPUT )\r
7178     stream = SND_PCM_STREAM_PLAYBACK;\r
7179   else\r
7180     stream = SND_PCM_STREAM_CAPTURE;\r
7181 \r
7182   snd_pcm_t *phandle;\r
7183   int openMode = SND_PCM_ASYNC;\r
7184   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7185   if ( result < 0 ) {\r
7186     if ( mode == OUTPUT )\r
7187       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7188     else\r
7189       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7190     errorText_ = errorStream_.str();\r
7191     return FAILURE;\r
7192   }\r
7193 \r
7194   // Fill the parameter structure.\r
7195   snd_pcm_hw_params_t *hw_params;\r
7196   snd_pcm_hw_params_alloca( &hw_params );\r
7197   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7198   if ( result < 0 ) {\r
7199     snd_pcm_close( phandle );\r
7200     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7201     errorText_ = errorStream_.str();\r
7202     return FAILURE;\r
7203   }\r
7204 \r
7205 #if defined(__RTAUDIO_DEBUG__)\r
7206   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7207   snd_pcm_hw_params_dump( hw_params, out );\r
7208 #endif\r
7209 \r
7210   // Set access ... check user preference.\r
7211   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7212     stream_.userInterleaved = false;\r
7213     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7214     if ( result < 0 ) {\r
7215       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7216       stream_.deviceInterleaved[mode] =  true;\r
7217     }\r
7218     else\r
7219       stream_.deviceInterleaved[mode] = false;\r
7220   }\r
7221   else {\r
7222     stream_.userInterleaved = true;\r
7223     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7224     if ( result < 0 ) {\r
7225       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7226       stream_.deviceInterleaved[mode] =  false;\r
7227     }\r
7228     else\r
7229       stream_.deviceInterleaved[mode] =  true;\r
7230   }\r
7231 \r
7232   if ( result < 0 ) {\r
7233     snd_pcm_close( phandle );\r
7234     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7235     errorText_ = errorStream_.str();\r
7236     return FAILURE;\r
7237   }\r
7238 \r
7239   // Determine how to set the device format.\r
7240   stream_.userFormat = format;\r
7241   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7242 \r
7243   if ( format == RTAUDIO_SINT8 )\r
7244     deviceFormat = SND_PCM_FORMAT_S8;\r
7245   else if ( format == RTAUDIO_SINT16 )\r
7246     deviceFormat = SND_PCM_FORMAT_S16;\r
7247   else if ( format == RTAUDIO_SINT24 )\r
7248     deviceFormat = SND_PCM_FORMAT_S24;\r
7249   else if ( format == RTAUDIO_SINT32 )\r
7250     deviceFormat = SND_PCM_FORMAT_S32;\r
7251   else if ( format == RTAUDIO_FLOAT32 )\r
7252     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7253   else if ( format == RTAUDIO_FLOAT64 )\r
7254     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7255 \r
7256   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7257     stream_.deviceFormat[mode] = format;\r
7258     goto setFormat;\r
7259   }\r
7260 \r
7261   // The user requested format is not natively supported by the device.\r
7262   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7263   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7264     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7265     goto setFormat;\r
7266   }\r
7267 \r
7268   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7269   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7270     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7271     goto setFormat;\r
7272   }\r
7273 \r
7274   deviceFormat = SND_PCM_FORMAT_S32;\r
7275   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7276     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7277     goto setFormat;\r
7278   }\r
7279 \r
7280   deviceFormat = SND_PCM_FORMAT_S24;\r
7281   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7282     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7283     goto setFormat;\r
7284   }\r
7285 \r
7286   deviceFormat = SND_PCM_FORMAT_S16;\r
7287   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7288     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7289     goto setFormat;\r
7290   }\r
7291 \r
7292   deviceFormat = SND_PCM_FORMAT_S8;\r
7293   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7294     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7295     goto setFormat;\r
7296   }\r
7297 \r
7298   // If we get here, no supported format was found.\r
7299   snd_pcm_close( phandle );\r
7300   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7301   errorText_ = errorStream_.str();\r
7302   return FAILURE;\r
7303 \r
7304  setFormat:\r
7305   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7306   if ( result < 0 ) {\r
7307     snd_pcm_close( phandle );\r
7308     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7309     errorText_ = errorStream_.str();\r
7310     return FAILURE;\r
7311   }\r
7312 \r
7313   // Determine whether byte-swaping is necessary.\r
7314   stream_.doByteSwap[mode] = false;\r
7315   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7316     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7317     if ( result == 0 )\r
7318       stream_.doByteSwap[mode] = true;\r
7319     else if (result < 0) {\r
7320       snd_pcm_close( phandle );\r
7321       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7322       errorText_ = errorStream_.str();\r
7323       return FAILURE;\r
7324     }\r
7325   }\r
7326 \r
7327   // Set the sample rate.\r
7328   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7329   if ( result < 0 ) {\r
7330     snd_pcm_close( phandle );\r
7331     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7332     errorText_ = errorStream_.str();\r
7333     return FAILURE;\r
7334   }\r
7335 \r
7336   // Determine the number of channels for this device.  We support a possible\r
7337   // minimum device channel number > than the value requested by the user.\r
7338   stream_.nUserChannels[mode] = channels;\r
7339   unsigned int value;\r
7340   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7341   unsigned int deviceChannels = value;\r
7342   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7343     snd_pcm_close( phandle );\r
7344     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7345     errorText_ = errorStream_.str();\r
7346     return FAILURE;\r
7347   }\r
7348 \r
7349   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7350   if ( result < 0 ) {\r
7351     snd_pcm_close( phandle );\r
7352     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7353     errorText_ = errorStream_.str();\r
7354     return FAILURE;\r
7355   }\r
7356   deviceChannels = value;\r
7357   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7358   stream_.nDeviceChannels[mode] = deviceChannels;\r
7359 \r
7360   // Set the device channels.\r
7361   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7362   if ( result < 0 ) {\r
7363     snd_pcm_close( phandle );\r
7364     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7365     errorText_ = errorStream_.str();\r
7366     return FAILURE;\r
7367   }\r
7368 \r
7369   // Set the buffer (or period) size.\r
7370   int dir = 0;\r
7371   snd_pcm_uframes_t periodSize = *bufferSize;\r
7372   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7373   if ( result < 0 ) {\r
7374     snd_pcm_close( phandle );\r
7375     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7376     errorText_ = errorStream_.str();\r
7377     return FAILURE;\r
7378   }\r
7379   *bufferSize = periodSize;\r
7380 \r
7381   // Set the buffer number, which in ALSA is referred to as the "period".\r
7382   unsigned int periods = 0;\r
7383   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7384   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7385   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7386   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7387   if ( result < 0 ) {\r
7388     snd_pcm_close( phandle );\r
7389     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7390     errorText_ = errorStream_.str();\r
7391     return FAILURE;\r
7392   }\r
7393 \r
7394   // If attempting to setup a duplex stream, the bufferSize parameter\r
7395   // MUST be the same in both directions!\r
7396   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7397     snd_pcm_close( phandle );\r
7398     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7399     errorText_ = errorStream_.str();\r
7400     return FAILURE;\r
7401   }\r
7402 \r
7403   stream_.bufferSize = *bufferSize;\r
7404 \r
7405   // Install the hardware configuration\r
7406   result = snd_pcm_hw_params( phandle, hw_params );\r
7407   if ( result < 0 ) {\r
7408     snd_pcm_close( phandle );\r
7409     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7410     errorText_ = errorStream_.str();\r
7411     return FAILURE;\r
7412   }\r
7413 \r
7414 #if defined(__RTAUDIO_DEBUG__)\r
7415   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7416   snd_pcm_hw_params_dump( hw_params, out );\r
7417 #endif\r
7418 \r
7419   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7420   snd_pcm_sw_params_t *sw_params = NULL;\r
7421   snd_pcm_sw_params_alloca( &sw_params );\r
7422   snd_pcm_sw_params_current( phandle, sw_params );\r
7423   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7424   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7425   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7426 \r
7427   // The following two settings were suggested by Theo Veenker\r
7428   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7429   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7430 \r
7431   // here are two options for a fix\r
7432   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7433   snd_pcm_uframes_t val;\r
7434   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7435   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7436 \r
7437   result = snd_pcm_sw_params( phandle, sw_params );\r
7438   if ( result < 0 ) {\r
7439     snd_pcm_close( phandle );\r
7440     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7441     errorText_ = errorStream_.str();\r
7442     return FAILURE;\r
7443   }\r
7444 \r
7445 #if defined(__RTAUDIO_DEBUG__)\r
7446   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7447   snd_pcm_sw_params_dump( sw_params, out );\r
7448 #endif\r
7449 \r
7450   // Set flags for buffer conversion\r
7451   stream_.doConvertBuffer[mode] = false;\r
7452   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7453     stream_.doConvertBuffer[mode] = true;\r
7454   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7455     stream_.doConvertBuffer[mode] = true;\r
7456   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7457        stream_.nUserChannels[mode] > 1 )\r
7458     stream_.doConvertBuffer[mode] = true;\r
7459 \r
7460   // Allocate the ApiHandle if necessary and then save.\r
7461   AlsaHandle *apiInfo = 0;\r
7462   if ( stream_.apiHandle == 0 ) {\r
7463     try {\r
7464       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7465     }\r
7466     catch ( std::bad_alloc& ) {\r
7467       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7468       goto error;\r
7469     }\r
7470 \r
7471     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7472       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7473       goto error;\r
7474     }\r
7475 \r
7476     stream_.apiHandle = (void *) apiInfo;\r
7477     apiInfo->handles[0] = 0;\r
7478     apiInfo->handles[1] = 0;\r
7479   }\r
7480   else {\r
7481     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7482   }\r
7483   apiInfo->handles[mode] = phandle;\r
7484   phandle = 0;\r
7485 \r
7486   // Allocate necessary internal buffers.\r
7487   unsigned long bufferBytes;\r
7488   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7489   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7490   if ( stream_.userBuffer[mode] == NULL ) {\r
7491     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7492     goto error;\r
7493   }\r
7494 \r
7495   if ( stream_.doConvertBuffer[mode] ) {\r
7496 \r
7497     bool makeBuffer = true;\r
7498     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7499     if ( mode == INPUT ) {\r
7500       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7501         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7502         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7503       }\r
7504     }\r
7505 \r
7506     if ( makeBuffer ) {\r
7507       bufferBytes *= *bufferSize;\r
7508       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7509       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7510       if ( stream_.deviceBuffer == NULL ) {\r
7511         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7512         goto error;\r
7513       }\r
7514     }\r
7515   }\r
7516 \r
7517   stream_.sampleRate = sampleRate;\r
7518   stream_.nBuffers = periods;\r
7519   stream_.device[mode] = device;\r
7520   stream_.state = STREAM_STOPPED;\r
7521 \r
7522   // Setup the buffer conversion information structure.\r
7523   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7524 \r
7525   // Setup thread if necessary.\r
7526   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7527     // We had already set up an output stream.\r
7528     stream_.mode = DUPLEX;\r
7529     // Link the streams if possible.\r
7530     apiInfo->synchronized = false;\r
7531     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7532       apiInfo->synchronized = true;\r
7533     else {\r
7534       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7535       error( RtAudioError::WARNING );\r
7536     }\r
7537   }\r
7538   else {\r
7539     stream_.mode = mode;\r
7540 \r
7541     // Setup callback thread.\r
7542     stream_.callbackInfo.object = (void *) this;\r
7543 \r
7544     // Set the thread attributes for joinable and realtime scheduling\r
7545     // priority (optional).  The higher priority will only take affect\r
7546     // if the program is run as root or suid. Note, under Linux\r
7547     // processes with CAP_SYS_NICE privilege, a user can change\r
7548     // scheduling policy and priority (thus need not be root). See\r
7549     // POSIX "capabilities".\r
7550     pthread_attr_t attr;\r
7551     pthread_attr_init( &attr );\r
7552     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7553 \r
7554 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7555     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7556       // We previously attempted to increase the audio callback priority\r
7557       // to SCHED_RR here via the attributes.  However, while no errors\r
7558       // were reported in doing so, it did not work.  So, now this is\r
7559       // done in the alsaCallbackHandler function.\r
7560       stream_.callbackInfo.doRealtime = true;\r
7561       int priority = options->priority;\r
7562       int min = sched_get_priority_min( SCHED_RR );\r
7563       int max = sched_get_priority_max( SCHED_RR );\r
7564       if ( priority < min ) priority = min;\r
7565       else if ( priority > max ) priority = max;\r
7566       stream_.callbackInfo.priority = priority;\r
7567     }\r
7568 #endif\r
7569 \r
7570     stream_.callbackInfo.isRunning = true;\r
7571     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7572     pthread_attr_destroy( &attr );\r
7573     if ( result ) {\r
7574       stream_.callbackInfo.isRunning = false;\r
7575       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7576       goto error;\r
7577     }\r
7578   }\r
7579 \r
7580   return SUCCESS;\r
7581 \r
7582  error:\r
7583   if ( apiInfo ) {\r
7584     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7585     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7586     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7587     delete apiInfo;\r
7588     stream_.apiHandle = 0;\r
7589   }\r
7590 \r
7591   if ( phandle) snd_pcm_close( phandle );\r
7592 \r
7593   for ( int i=0; i<2; i++ ) {\r
7594     if ( stream_.userBuffer[i] ) {\r
7595       free( stream_.userBuffer[i] );\r
7596       stream_.userBuffer[i] = 0;\r
7597     }\r
7598   }\r
7599 \r
7600   if ( stream_.deviceBuffer ) {\r
7601     free( stream_.deviceBuffer );\r
7602     stream_.deviceBuffer = 0;\r
7603   }\r
7604 \r
7605   stream_.state = STREAM_CLOSED;\r
7606   return FAILURE;\r
7607 }\r
7608 \r
7609 void RtApiAlsa :: closeStream()\r
7610 {\r
7611   if ( stream_.state == STREAM_CLOSED ) {\r
7612     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7613     error( RtAudioError::WARNING );\r
7614     return;\r
7615   }\r
7616 \r
7617   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7618   stream_.callbackInfo.isRunning = false;\r
7619   MUTEX_LOCK( &stream_.mutex );\r
7620   if ( stream_.state == STREAM_STOPPED ) {\r
7621     apiInfo->runnable = true;\r
7622     pthread_cond_signal( &apiInfo->runnable_cv );\r
7623   }\r
7624   MUTEX_UNLOCK( &stream_.mutex );\r
7625   pthread_join( stream_.callbackInfo.thread, NULL );\r
7626 \r
7627   if ( stream_.state == STREAM_RUNNING ) {\r
7628     stream_.state = STREAM_STOPPED;\r
7629     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7630       snd_pcm_drop( apiInfo->handles[0] );\r
7631     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7632       snd_pcm_drop( apiInfo->handles[1] );\r
7633   }\r
7634 \r
7635   if ( apiInfo ) {\r
7636     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7637     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7638     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7639     delete apiInfo;\r
7640     stream_.apiHandle = 0;\r
7641   }\r
7642 \r
7643   for ( int i=0; i<2; i++ ) {\r
7644     if ( stream_.userBuffer[i] ) {\r
7645       free( stream_.userBuffer[i] );\r
7646       stream_.userBuffer[i] = 0;\r
7647     }\r
7648   }\r
7649 \r
7650   if ( stream_.deviceBuffer ) {\r
7651     free( stream_.deviceBuffer );\r
7652     stream_.deviceBuffer = 0;\r
7653   }\r
7654 \r
7655   stream_.mode = UNINITIALIZED;\r
7656   stream_.state = STREAM_CLOSED;\r
7657 }\r
7658 \r
7659 void RtApiAlsa :: startStream()\r
7660 {\r
7661   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7662 \r
7663   verifyStream();\r
7664   if ( stream_.state == STREAM_RUNNING ) {\r
7665     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7666     error( RtAudioError::WARNING );\r
7667     return;\r
7668   }\r
7669 \r
7670   MUTEX_LOCK( &stream_.mutex );\r
7671 \r
7672   int result = 0;\r
7673   snd_pcm_state_t state;\r
7674   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7675   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7676   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7677     state = snd_pcm_state( handle[0] );\r
7678     if ( state != SND_PCM_STATE_PREPARED ) {\r
7679       result = snd_pcm_prepare( handle[0] );\r
7680       if ( result < 0 ) {\r
7681         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7682         errorText_ = errorStream_.str();\r
7683         goto unlock;\r
7684       }\r
7685     }\r
7686   }\r
7687 \r
7688   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7689     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7690     state = snd_pcm_state( handle[1] );\r
7691     if ( state != SND_PCM_STATE_PREPARED ) {\r
7692       result = snd_pcm_prepare( handle[1] );\r
7693       if ( result < 0 ) {\r
7694         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7695         errorText_ = errorStream_.str();\r
7696         goto unlock;\r
7697       }\r
7698     }\r
7699   }\r
7700 \r
7701   stream_.state = STREAM_RUNNING;\r
7702 \r
7703  unlock:\r
7704   apiInfo->runnable = true;\r
7705   pthread_cond_signal( &apiInfo->runnable_cv );\r
7706   MUTEX_UNLOCK( &stream_.mutex );\r
7707 \r
7708   if ( result >= 0 ) return;\r
7709   error( RtAudioError::SYSTEM_ERROR );\r
7710 }\r
7711 \r
7712 void RtApiAlsa :: stopStream()\r
7713 {\r
7714   verifyStream();\r
7715   if ( stream_.state == STREAM_STOPPED ) {\r
7716     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7717     error( RtAudioError::WARNING );\r
7718     return;\r
7719   }\r
7720 \r
7721   stream_.state = STREAM_STOPPED;\r
7722   MUTEX_LOCK( &stream_.mutex );\r
7723 \r
7724   int result = 0;\r
7725   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7726   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7727   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7728     if ( apiInfo->synchronized ) \r
7729       result = snd_pcm_drop( handle[0] );\r
7730     else\r
7731       result = snd_pcm_drain( handle[0] );\r
7732     if ( result < 0 ) {\r
7733       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7734       errorText_ = errorStream_.str();\r
7735       goto unlock;\r
7736     }\r
7737   }\r
7738 \r
7739   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7740     result = snd_pcm_drop( handle[1] );\r
7741     if ( result < 0 ) {\r
7742       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7743       errorText_ = errorStream_.str();\r
7744       goto unlock;\r
7745     }\r
7746   }\r
7747 \r
7748  unlock:\r
7749   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7750   MUTEX_UNLOCK( &stream_.mutex );\r
7751 \r
7752   if ( result >= 0 ) return;\r
7753   error( RtAudioError::SYSTEM_ERROR );\r
7754 }\r
7755 \r
7756 void RtApiAlsa :: abortStream()\r
7757 {\r
7758   verifyStream();\r
7759   if ( stream_.state == STREAM_STOPPED ) {\r
7760     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7761     error( RtAudioError::WARNING );\r
7762     return;\r
7763   }\r
7764 \r
7765   stream_.state = STREAM_STOPPED;\r
7766   MUTEX_LOCK( &stream_.mutex );\r
7767 \r
7768   int result = 0;\r
7769   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7770   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7771   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7772     result = snd_pcm_drop( handle[0] );\r
7773     if ( result < 0 ) {\r
7774       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7775       errorText_ = errorStream_.str();\r
7776       goto unlock;\r
7777     }\r
7778   }\r
7779 \r
7780   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7781     result = snd_pcm_drop( handle[1] );\r
7782     if ( result < 0 ) {\r
7783       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7784       errorText_ = errorStream_.str();\r
7785       goto unlock;\r
7786     }\r
7787   }\r
7788 \r
7789  unlock:\r
7790   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7791   MUTEX_UNLOCK( &stream_.mutex );\r
7792 \r
7793   if ( result >= 0 ) return;\r
7794   error( RtAudioError::SYSTEM_ERROR );\r
7795 }\r
7796 \r
7797 void RtApiAlsa :: callbackEvent()\r
7798 {\r
7799   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7800   if ( stream_.state == STREAM_STOPPED ) {\r
7801     MUTEX_LOCK( &stream_.mutex );\r
7802     while ( !apiInfo->runnable )\r
7803       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7804 \r
7805     if ( stream_.state != STREAM_RUNNING ) {\r
7806       MUTEX_UNLOCK( &stream_.mutex );\r
7807       return;\r
7808     }\r
7809     MUTEX_UNLOCK( &stream_.mutex );\r
7810   }\r
7811 \r
7812   if ( stream_.state == STREAM_CLOSED ) {\r
7813     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7814     error( RtAudioError::WARNING );\r
7815     return;\r
7816   }\r
7817 \r
7818   int doStopStream = 0;\r
7819   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7820   double streamTime = getStreamTime();\r
7821   RtAudioStreamStatus status = 0;\r
7822   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7823     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
7824     apiInfo->xrun[0] = false;\r
7825   }\r
7826   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
7827     status |= RTAUDIO_INPUT_OVERFLOW;\r
7828     apiInfo->xrun[1] = false;\r
7829   }\r
7830   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
7831                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
7832 \r
7833   if ( doStopStream == 2 ) {\r
7834     abortStream();\r
7835     return;\r
7836   }\r
7837 \r
7838   MUTEX_LOCK( &stream_.mutex );\r
7839 \r
7840   // The state might change while waiting on a mutex.\r
7841   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
7842 \r
7843   int result;\r
7844   char *buffer;\r
7845   int channels;\r
7846   snd_pcm_t **handle;\r
7847   snd_pcm_sframes_t frames;\r
7848   RtAudioFormat format;\r
7849   handle = (snd_pcm_t **) apiInfo->handles;\r
7850 \r
7851   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
7852 \r
7853     // Setup parameters.\r
7854     if ( stream_.doConvertBuffer[1] ) {\r
7855       buffer = stream_.deviceBuffer;\r
7856       channels = stream_.nDeviceChannels[1];\r
7857       format = stream_.deviceFormat[1];\r
7858     }\r
7859     else {\r
7860       buffer = stream_.userBuffer[1];\r
7861       channels = stream_.nUserChannels[1];\r
7862       format = stream_.userFormat;\r
7863     }\r
7864 \r
7865     // Read samples from device in interleaved/non-interleaved format.\r
7866     if ( stream_.deviceInterleaved[1] )\r
7867       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
7868     else {\r
7869       void *bufs[channels];\r
7870       size_t offset = stream_.bufferSize * formatBytes( format );\r
7871       for ( int i=0; i<channels; i++ )\r
7872         bufs[i] = (void *) (buffer + (i * offset));\r
7873       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
7874     }\r
7875 \r
7876     if ( result < (int) stream_.bufferSize ) {\r
7877       // Either an error or overrun occured.\r
7878       if ( result == -EPIPE ) {\r
7879         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
7880         if ( state == SND_PCM_STATE_XRUN ) {\r
7881           apiInfo->xrun[1] = true;\r
7882           result = snd_pcm_prepare( handle[1] );\r
7883           if ( result < 0 ) {\r
7884             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
7885             errorText_ = errorStream_.str();\r
7886           }\r
7887         }\r
7888         else {\r
7889           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7890           errorText_ = errorStream_.str();\r
7891         }\r
7892       }\r
7893       else {\r
7894         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
7895         errorText_ = errorStream_.str();\r
7896       }\r
7897       error( RtAudioError::WARNING );\r
7898       goto tryOutput;\r
7899     }\r
7900 \r
7901     // Do byte swapping if necessary.\r
7902     if ( stream_.doByteSwap[1] )\r
7903       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
7904 \r
7905     // Do buffer conversion if necessary.\r
7906     if ( stream_.doConvertBuffer[1] )\r
7907       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
7908 \r
7909     // Check stream latency\r
7910     result = snd_pcm_delay( handle[1], &frames );\r
7911     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
7912   }\r
7913 \r
7914  tryOutput:\r
7915 \r
7916   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7917 \r
7918     // Setup parameters and do buffer conversion if necessary.\r
7919     if ( stream_.doConvertBuffer[0] ) {\r
7920       buffer = stream_.deviceBuffer;\r
7921       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
7922       channels = stream_.nDeviceChannels[0];\r
7923       format = stream_.deviceFormat[0];\r
7924     }\r
7925     else {\r
7926       buffer = stream_.userBuffer[0];\r
7927       channels = stream_.nUserChannels[0];\r
7928       format = stream_.userFormat;\r
7929     }\r
7930 \r
7931     // Do byte swapping if necessary.\r
7932     if ( stream_.doByteSwap[0] )\r
7933       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
7934 \r
7935     // Write samples to device in interleaved/non-interleaved format.\r
7936     if ( stream_.deviceInterleaved[0] )\r
7937       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
7938     else {\r
7939       void *bufs[channels];\r
7940       size_t offset = stream_.bufferSize * formatBytes( format );\r
7941       for ( int i=0; i<channels; i++ )\r
7942         bufs[i] = (void *) (buffer + (i * offset));\r
7943       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
7944     }\r
7945 \r
7946     if ( result < (int) stream_.bufferSize ) {\r
7947       // Either an error or underrun occured.\r
7948       if ( result == -EPIPE ) {\r
7949         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
7950         if ( state == SND_PCM_STATE_XRUN ) {\r
7951           apiInfo->xrun[0] = true;\r
7952           result = snd_pcm_prepare( handle[0] );\r
7953           if ( result < 0 ) {\r
7954             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
7955             errorText_ = errorStream_.str();\r
7956           }\r
7957         }\r
7958         else {\r
7959           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7960           errorText_ = errorStream_.str();\r
7961         }\r
7962       }\r
7963       else {\r
7964         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
7965         errorText_ = errorStream_.str();\r
7966       }\r
7967       error( RtAudioError::WARNING );\r
7968       goto unlock;\r
7969     }\r
7970 \r
7971     // Check stream latency\r
7972     result = snd_pcm_delay( handle[0], &frames );\r
7973     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
7974   }\r
7975 \r
7976  unlock:\r
7977   MUTEX_UNLOCK( &stream_.mutex );\r
7978 \r
7979   RtApi::tickStreamTime();\r
7980   if ( doStopStream == 1 ) this->stopStream();\r
7981 }\r
7982 \r
7983 static void *alsaCallbackHandler( void *ptr )\r
7984 {\r
7985   CallbackInfo *info = (CallbackInfo *) ptr;\r
7986   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
7987   bool *isRunning = &info->isRunning;\r
7988 \r
7989 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7990   if ( &info->doRealtime ) {\r
7991     pthread_t tID = pthread_self();      // ID of this thread\r
7992     sched_param prio = { info->priority }; // scheduling priority of thread\r
7993     pthread_setschedparam( tID, SCHED_RR, &prio );\r
7994   }\r
7995 #endif\r
7996 \r
7997   while ( *isRunning == true ) {\r
7998     pthread_testcancel();\r
7999     object->callbackEvent();\r
8000   }\r
8001 \r
8002   pthread_exit( NULL );\r
8003 }\r
8004 \r
8005 //******************** End of __LINUX_ALSA__ *********************//\r
8006 #endif\r
8007 \r
8008 #if defined(__LINUX_PULSE__)\r
8009 \r
8010 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8011 // and Tristan Matthews.\r
8012 \r
8013 #include <pulse/error.h>\r
8014 #include <pulse/simple.h>\r
8015 #include <cstdio>\r
8016 \r
8017 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8018                                                       44100, 48000, 96000, 0};\r
8019 \r
8020 struct rtaudio_pa_format_mapping_t {\r
8021   RtAudioFormat rtaudio_format;\r
8022   pa_sample_format_t pa_format;\r
8023 };\r
8024 \r
8025 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8026   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8027   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8028   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8029   {0, PA_SAMPLE_INVALID}};\r
8030 \r
8031 struct PulseAudioHandle {\r
8032   pa_simple *s_play;\r
8033   pa_simple *s_rec;\r
8034   pthread_t thread;\r
8035   pthread_cond_t runnable_cv;\r
8036   bool runnable;\r
8037   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8038 };\r
8039 \r
8040 RtApiPulse::~RtApiPulse()\r
8041 {\r
8042   if ( stream_.state != STREAM_CLOSED )\r
8043     closeStream();\r
8044 }\r
8045 \r
8046 unsigned int RtApiPulse::getDeviceCount( void )\r
8047 {\r
8048   return 1;\r
8049 }\r
8050 \r
8051 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8052 {\r
8053   RtAudio::DeviceInfo info;\r
8054   info.probed = true;\r
8055   info.name = "PulseAudio";\r
8056   info.outputChannels = 2;\r
8057   info.inputChannels = 2;\r
8058   info.duplexChannels = 2;\r
8059   info.isDefaultOutput = true;\r
8060   info.isDefaultInput = true;\r
8061 \r
8062   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8063     info.sampleRates.push_back( *sr );\r
8064 \r
8065   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8066 \r
8067   return info;\r
8068 }\r
8069 \r
8070 static void *pulseaudio_callback( void * user )\r
8071 {\r
8072   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8073   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8074   volatile bool *isRunning = &cbi->isRunning;\r
8075 \r
8076   while ( *isRunning ) {\r
8077     pthread_testcancel();\r
8078     context->callbackEvent();\r
8079   }\r
8080 \r
8081   pthread_exit( NULL );\r
8082 }\r
8083 \r
8084 void RtApiPulse::closeStream( void )\r
8085 {\r
8086   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8087 \r
8088   stream_.callbackInfo.isRunning = false;\r
8089   if ( pah ) {\r
8090     MUTEX_LOCK( &stream_.mutex );\r
8091     if ( stream_.state == STREAM_STOPPED ) {\r
8092       pah->runnable = true;\r
8093       pthread_cond_signal( &pah->runnable_cv );\r
8094     }\r
8095     MUTEX_UNLOCK( &stream_.mutex );\r
8096 \r
8097     pthread_join( pah->thread, 0 );\r
8098     if ( pah->s_play ) {\r
8099       pa_simple_flush( pah->s_play, NULL );\r
8100       pa_simple_free( pah->s_play );\r
8101     }\r
8102     if ( pah->s_rec )\r
8103       pa_simple_free( pah->s_rec );\r
8104 \r
8105     pthread_cond_destroy( &pah->runnable_cv );\r
8106     delete pah;\r
8107     stream_.apiHandle = 0;\r
8108   }\r
8109 \r
8110   if ( stream_.userBuffer[0] ) {\r
8111     free( stream_.userBuffer[0] );\r
8112     stream_.userBuffer[0] = 0;\r
8113   }\r
8114   if ( stream_.userBuffer[1] ) {\r
8115     free( stream_.userBuffer[1] );\r
8116     stream_.userBuffer[1] = 0;\r
8117   }\r
8118 \r
8119   stream_.state = STREAM_CLOSED;\r
8120   stream_.mode = UNINITIALIZED;\r
8121 }\r
8122 \r
8123 void RtApiPulse::callbackEvent( void )\r
8124 {\r
8125   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8126 \r
8127   if ( stream_.state == STREAM_STOPPED ) {\r
8128     MUTEX_LOCK( &stream_.mutex );\r
8129     while ( !pah->runnable )\r
8130       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8131 \r
8132     if ( stream_.state != STREAM_RUNNING ) {\r
8133       MUTEX_UNLOCK( &stream_.mutex );\r
8134       return;\r
8135     }\r
8136     MUTEX_UNLOCK( &stream_.mutex );\r
8137   }\r
8138 \r
8139   if ( stream_.state == STREAM_CLOSED ) {\r
8140     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8141       "this shouldn't happen!";\r
8142     error( RtAudioError::WARNING );\r
8143     return;\r
8144   }\r
8145 \r
8146   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8147   double streamTime = getStreamTime();\r
8148   RtAudioStreamStatus status = 0;\r
8149   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8150                                stream_.bufferSize, streamTime, status,\r
8151                                stream_.callbackInfo.userData );\r
8152 \r
8153   if ( doStopStream == 2 ) {\r
8154     abortStream();\r
8155     return;\r
8156   }\r
8157 \r
8158   MUTEX_LOCK( &stream_.mutex );\r
8159   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8160   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8161 \r
8162   if ( stream_.state != STREAM_RUNNING )\r
8163     goto unlock;\r
8164 \r
8165   int pa_error;\r
8166   size_t bytes;\r
8167   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8168     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8169         convertBuffer( stream_.deviceBuffer,\r
8170                        stream_.userBuffer[OUTPUT],\r
8171                        stream_.convertInfo[OUTPUT] );\r
8172         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8173                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8174     } else\r
8175         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8176                 formatBytes( stream_.userFormat );\r
8177 \r
8178     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8179       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8180         pa_strerror( pa_error ) << ".";\r
8181       errorText_ = errorStream_.str();\r
8182       error( RtAudioError::WARNING );\r
8183     }\r
8184   }\r
8185 \r
8186   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8187     if ( stream_.doConvertBuffer[INPUT] )\r
8188       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8189         formatBytes( stream_.deviceFormat[INPUT] );\r
8190     else\r
8191       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8192         formatBytes( stream_.userFormat );\r
8193             \r
8194     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8195       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8196         pa_strerror( pa_error ) << ".";\r
8197       errorText_ = errorStream_.str();\r
8198       error( RtAudioError::WARNING );\r
8199     }\r
8200     if ( stream_.doConvertBuffer[INPUT] ) {\r
8201       convertBuffer( stream_.userBuffer[INPUT],\r
8202                      stream_.deviceBuffer,\r
8203                      stream_.convertInfo[INPUT] );\r
8204     }\r
8205   }\r
8206 \r
8207  unlock:\r
8208   MUTEX_UNLOCK( &stream_.mutex );\r
8209   RtApi::tickStreamTime();\r
8210 \r
8211   if ( doStopStream == 1 )\r
8212     stopStream();\r
8213 }\r
8214 \r
8215 void RtApiPulse::startStream( void )\r
8216 {\r
8217   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8218 \r
8219   if ( stream_.state == STREAM_CLOSED ) {\r
8220     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8221     error( RtAudioError::INVALID_USE );\r
8222     return;\r
8223   }\r
8224   if ( stream_.state == STREAM_RUNNING ) {\r
8225     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8226     error( RtAudioError::WARNING );\r
8227     return;\r
8228   }\r
8229 \r
8230   MUTEX_LOCK( &stream_.mutex );\r
8231 \r
8232   stream_.state = STREAM_RUNNING;\r
8233 \r
8234   pah->runnable = true;\r
8235   pthread_cond_signal( &pah->runnable_cv );\r
8236   MUTEX_UNLOCK( &stream_.mutex );\r
8237 }\r
8238 \r
8239 void RtApiPulse::stopStream( void )\r
8240 {\r
8241   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8242 \r
8243   if ( stream_.state == STREAM_CLOSED ) {\r
8244     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8245     error( RtAudioError::INVALID_USE );\r
8246     return;\r
8247   }\r
8248   if ( stream_.state == STREAM_STOPPED ) {\r
8249     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8250     error( RtAudioError::WARNING );\r
8251     return;\r
8252   }\r
8253 \r
8254   stream_.state = STREAM_STOPPED;\r
8255   MUTEX_LOCK( &stream_.mutex );\r
8256 \r
8257   if ( pah && pah->s_play ) {\r
8258     int pa_error;\r
8259     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8260       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8261         pa_strerror( pa_error ) << ".";\r
8262       errorText_ = errorStream_.str();\r
8263       MUTEX_UNLOCK( &stream_.mutex );\r
8264       error( RtAudioError::SYSTEM_ERROR );\r
8265       return;\r
8266     }\r
8267   }\r
8268 \r
8269   stream_.state = STREAM_STOPPED;\r
8270   MUTEX_UNLOCK( &stream_.mutex );\r
8271 }\r
8272 \r
8273 void RtApiPulse::abortStream( void )\r
8274 {\r
8275   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8276 \r
8277   if ( stream_.state == STREAM_CLOSED ) {\r
8278     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8279     error( RtAudioError::INVALID_USE );\r
8280     return;\r
8281   }\r
8282   if ( stream_.state == STREAM_STOPPED ) {\r
8283     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8284     error( RtAudioError::WARNING );\r
8285     return;\r
8286   }\r
8287 \r
8288   stream_.state = STREAM_STOPPED;\r
8289   MUTEX_LOCK( &stream_.mutex );\r
8290 \r
8291   if ( pah && pah->s_play ) {\r
8292     int pa_error;\r
8293     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8294       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8295         pa_strerror( pa_error ) << ".";\r
8296       errorText_ = errorStream_.str();\r
8297       MUTEX_UNLOCK( &stream_.mutex );\r
8298       error( RtAudioError::SYSTEM_ERROR );\r
8299       return;\r
8300     }\r
8301   }\r
8302 \r
8303   stream_.state = STREAM_STOPPED;\r
8304   MUTEX_UNLOCK( &stream_.mutex );\r
8305 }\r
8306 \r
8307 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8308                                   unsigned int channels, unsigned int firstChannel,\r
8309                                   unsigned int sampleRate, RtAudioFormat format,\r
8310                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8311 {\r
8312   PulseAudioHandle *pah = 0;\r
8313   unsigned long bufferBytes = 0;\r
8314   pa_sample_spec ss;\r
8315 \r
8316   if ( device != 0 ) return false;\r
8317   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8318   if ( channels != 1 && channels != 2 ) {\r
8319     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
8320     return false;\r
8321   }\r
8322   ss.channels = channels;\r
8323 \r
8324   if ( firstChannel != 0 ) return false;\r
8325 \r
8326   bool sr_found = false;\r
8327   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8328     if ( sampleRate == *sr ) {\r
8329       sr_found = true;\r
8330       stream_.sampleRate = sampleRate;\r
8331       ss.rate = sampleRate;\r
8332       break;\r
8333     }\r
8334   }\r
8335   if ( !sr_found ) {\r
8336     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8337     return false;\r
8338   }\r
8339 \r
8340   bool sf_found = 0;\r
8341   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8342         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8343     if ( format == sf->rtaudio_format ) {\r
8344       sf_found = true;\r
8345       stream_.userFormat = sf->rtaudio_format;\r
8346       stream_.deviceFormat[mode] = stream_.userFormat;\r
8347       ss.format = sf->pa_format;\r
8348       break;\r
8349     }\r
8350   }\r
8351   if ( !sf_found ) { // Use internal data format conversion.\r
8352     stream_.userFormat = format;\r
8353     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8354     ss.format = PA_SAMPLE_FLOAT32LE;\r
8355   }\r
8356 \r
8357   // Set other stream parameters.\r
8358   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8359   else stream_.userInterleaved = true;\r
8360   stream_.deviceInterleaved[mode] = true;\r
8361   stream_.nBuffers = 1;\r
8362   stream_.doByteSwap[mode] = false;\r
8363   stream_.nUserChannels[mode] = channels;\r
8364   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8365   stream_.channelOffset[mode] = 0;\r
8366   std::string streamName = "RtAudio";\r
8367 \r
8368   // Set flags for buffer conversion.\r
8369   stream_.doConvertBuffer[mode] = false;\r
8370   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8371     stream_.doConvertBuffer[mode] = true;\r
8372   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8373     stream_.doConvertBuffer[mode] = true;\r
8374 \r
8375   // Allocate necessary internal buffers.\r
8376   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8377   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8378   if ( stream_.userBuffer[mode] == NULL ) {\r
8379     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8380     goto error;\r
8381   }\r
8382   stream_.bufferSize = *bufferSize;\r
8383 \r
8384   if ( stream_.doConvertBuffer[mode] ) {\r
8385 \r
8386     bool makeBuffer = true;\r
8387     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8388     if ( mode == INPUT ) {\r
8389       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8390         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8391         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8392       }\r
8393     }\r
8394 \r
8395     if ( makeBuffer ) {\r
8396       bufferBytes *= *bufferSize;\r
8397       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8398       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8399       if ( stream_.deviceBuffer == NULL ) {\r
8400         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8401         goto error;\r
8402       }\r
8403     }\r
8404   }\r
8405 \r
8406   stream_.device[mode] = device;\r
8407 \r
8408   // Setup the buffer conversion information structure.\r
8409   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8410 \r
8411   if ( !stream_.apiHandle ) {\r
8412     PulseAudioHandle *pah = new PulseAudioHandle;\r
8413     if ( !pah ) {\r
8414       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8415       goto error;\r
8416     }\r
8417 \r
8418     stream_.apiHandle = pah;\r
8419     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8420       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8421       goto error;\r
8422     }\r
8423   }\r
8424   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8425 \r
8426   int error;\r
8427   if ( !options->streamName.empty() ) streamName = options->streamName;\r
8428   switch ( mode ) {\r
8429   case INPUT:\r
8430     pa_buffer_attr buffer_attr;\r
8431     buffer_attr.fragsize = bufferBytes;\r
8432     buffer_attr.maxlength = -1;\r
8433 \r
8434     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8435     if ( !pah->s_rec ) {\r
8436       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8437       goto error;\r
8438     }\r
8439     break;\r
8440   case OUTPUT:\r
8441     pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
8442     if ( !pah->s_play ) {\r
8443       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8444       goto error;\r
8445     }\r
8446     break;\r
8447   default:\r
8448     goto error;\r
8449   }\r
8450 \r
8451   if ( stream_.mode == UNINITIALIZED )\r
8452     stream_.mode = mode;\r
8453   else if ( stream_.mode == mode )\r
8454     goto error;\r
8455   else\r
8456     stream_.mode = DUPLEX;\r
8457 \r
8458   if ( !stream_.callbackInfo.isRunning ) {\r
8459     stream_.callbackInfo.object = this;\r
8460     stream_.callbackInfo.isRunning = true;\r
8461     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8462       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8463       goto error;\r
8464     }\r
8465   }\r
8466 \r
8467   stream_.state = STREAM_STOPPED;\r
8468   return true;\r
8469  \r
8470  error:\r
8471   if ( pah && stream_.callbackInfo.isRunning ) {\r
8472     pthread_cond_destroy( &pah->runnable_cv );\r
8473     delete pah;\r
8474     stream_.apiHandle = 0;\r
8475   }\r
8476 \r
8477   for ( int i=0; i<2; i++ ) {\r
8478     if ( stream_.userBuffer[i] ) {\r
8479       free( stream_.userBuffer[i] );\r
8480       stream_.userBuffer[i] = 0;\r
8481     }\r
8482   }\r
8483 \r
8484   if ( stream_.deviceBuffer ) {\r
8485     free( stream_.deviceBuffer );\r
8486     stream_.deviceBuffer = 0;\r
8487   }\r
8488 \r
8489   return FAILURE;\r
8490 }\r
8491 \r
8492 //******************** End of __LINUX_PULSE__ *********************//\r
8493 #endif\r
8494 \r
8495 #if defined(__LINUX_OSS__)\r
8496 \r
8497 #include <unistd.h>\r
8498 #include <sys/ioctl.h>\r
8499 #include <unistd.h>\r
8500 #include <fcntl.h>\r
8501 #include <sys/soundcard.h>\r
8502 #include <errno.h>\r
8503 #include <math.h>\r
8504 \r
8505 static void *ossCallbackHandler(void * ptr);\r
8506 \r
8507 // A structure to hold various information related to the OSS API\r
8508 // implementation.\r
8509 struct OssHandle {\r
8510   int id[2];    // device ids\r
8511   bool xrun[2];\r
8512   bool triggered;\r
8513   pthread_cond_t runnable;\r
8514 \r
8515   OssHandle()\r
8516     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8517 };\r
8518 \r
8519 RtApiOss :: RtApiOss()\r
8520 {\r
8521   // Nothing to do here.\r
8522 }\r
8523 \r
8524 RtApiOss :: ~RtApiOss()\r
8525 {\r
8526   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8527 }\r
8528 \r
8529 unsigned int RtApiOss :: getDeviceCount( void )\r
8530 {\r
8531   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8532   if ( mixerfd == -1 ) {\r
8533     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8534     error( RtAudioError::WARNING );\r
8535     return 0;\r
8536   }\r
8537 \r
8538   oss_sysinfo sysinfo;\r
8539   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8540     close( mixerfd );\r
8541     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8542     error( RtAudioError::WARNING );\r
8543     return 0;\r
8544   }\r
8545 \r
8546   close( mixerfd );\r
8547   return sysinfo.numaudios;\r
8548 }\r
8549 \r
8550 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8551 {\r
8552   RtAudio::DeviceInfo info;\r
8553   info.probed = false;\r
8554 \r
8555   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8556   if ( mixerfd == -1 ) {\r
8557     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8558     error( RtAudioError::WARNING );\r
8559     return info;\r
8560   }\r
8561 \r
8562   oss_sysinfo sysinfo;\r
8563   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8564   if ( result == -1 ) {\r
8565     close( mixerfd );\r
8566     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8567     error( RtAudioError::WARNING );\r
8568     return info;\r
8569   }\r
8570 \r
8571   unsigned nDevices = sysinfo.numaudios;\r
8572   if ( nDevices == 0 ) {\r
8573     close( mixerfd );\r
8574     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8575     error( RtAudioError::INVALID_USE );\r
8576     return info;\r
8577   }\r
8578 \r
8579   if ( device >= nDevices ) {\r
8580     close( mixerfd );\r
8581     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8582     error( RtAudioError::INVALID_USE );\r
8583     return info;\r
8584   }\r
8585 \r
8586   oss_audioinfo ainfo;\r
8587   ainfo.dev = device;\r
8588   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8589   close( mixerfd );\r
8590   if ( result == -1 ) {\r
8591     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8592     errorText_ = errorStream_.str();\r
8593     error( RtAudioError::WARNING );\r
8594     return info;\r
8595   }\r
8596 \r
8597   // Probe channels\r
8598   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8599   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8600   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8601     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8602       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8603   }\r
8604 \r
8605   // Probe data formats ... do for input\r
8606   unsigned long mask = ainfo.iformats;\r
8607   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8608     info.nativeFormats |= RTAUDIO_SINT16;\r
8609   if ( mask & AFMT_S8 )\r
8610     info.nativeFormats |= RTAUDIO_SINT8;\r
8611   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8612     info.nativeFormats |= RTAUDIO_SINT32;\r
8613   if ( mask & AFMT_FLOAT )\r
8614     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8615   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8616     info.nativeFormats |= RTAUDIO_SINT24;\r
8617 \r
8618   // Check that we have at least one supported format\r
8619   if ( info.nativeFormats == 0 ) {\r
8620     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8621     errorText_ = errorStream_.str();\r
8622     error( RtAudioError::WARNING );\r
8623     return info;\r
8624   }\r
8625 \r
8626   // Probe the supported sample rates.\r
8627   info.sampleRates.clear();\r
8628   if ( ainfo.nrates ) {\r
8629     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8630       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8631         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8632           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8633           break;\r
8634         }\r
8635       }\r
8636     }\r
8637   }\r
8638   else {\r
8639     // Check min and max rate values;\r
8640     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8641       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )\r
8642         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8643     }\r
8644   }\r
8645 \r
8646   if ( info.sampleRates.size() == 0 ) {\r
8647     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8648     errorText_ = errorStream_.str();\r
8649     error( RtAudioError::WARNING );\r
8650   }\r
8651   else {\r
8652     info.probed = true;\r
8653     info.name = ainfo.name;\r
8654   }\r
8655 \r
8656   return info;\r
8657 }\r
8658 \r
8659 \r
8660 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8661                                   unsigned int firstChannel, unsigned int sampleRate,\r
8662                                   RtAudioFormat format, unsigned int *bufferSize,\r
8663                                   RtAudio::StreamOptions *options )\r
8664 {\r
8665   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8666   if ( mixerfd == -1 ) {\r
8667     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8668     return FAILURE;\r
8669   }\r
8670 \r
8671   oss_sysinfo sysinfo;\r
8672   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8673   if ( result == -1 ) {\r
8674     close( mixerfd );\r
8675     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8676     return FAILURE;\r
8677   }\r
8678 \r
8679   unsigned nDevices = sysinfo.numaudios;\r
8680   if ( nDevices == 0 ) {\r
8681     // This should not happen because a check is made before this function is called.\r
8682     close( mixerfd );\r
8683     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8684     return FAILURE;\r
8685   }\r
8686 \r
8687   if ( device >= nDevices ) {\r
8688     // This should not happen because a check is made before this function is called.\r
8689     close( mixerfd );\r
8690     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8691     return FAILURE;\r
8692   }\r
8693 \r
8694   oss_audioinfo ainfo;\r
8695   ainfo.dev = device;\r
8696   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8697   close( mixerfd );\r
8698   if ( result == -1 ) {\r
8699     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8700     errorText_ = errorStream_.str();\r
8701     return FAILURE;\r
8702   }\r
8703 \r
8704   // Check if device supports input or output\r
8705   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8706        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8707     if ( mode == OUTPUT )\r
8708       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8709     else\r
8710       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8711     errorText_ = errorStream_.str();\r
8712     return FAILURE;\r
8713   }\r
8714 \r
8715   int flags = 0;\r
8716   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8717   if ( mode == OUTPUT )\r
8718     flags |= O_WRONLY;\r
8719   else { // mode == INPUT\r
8720     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8721       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8722       close( handle->id[0] );\r
8723       handle->id[0] = 0;\r
8724       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8725         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8726         errorText_ = errorStream_.str();\r
8727         return FAILURE;\r
8728       }\r
8729       // Check that the number previously set channels is the same.\r
8730       if ( stream_.nUserChannels[0] != channels ) {\r
8731         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8732         errorText_ = errorStream_.str();\r
8733         return FAILURE;\r
8734       }\r
8735       flags |= O_RDWR;\r
8736     }\r
8737     else\r
8738       flags |= O_RDONLY;\r
8739   }\r
8740 \r
8741   // Set exclusive access if specified.\r
8742   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8743 \r
8744   // Try to open the device.\r
8745   int fd;\r
8746   fd = open( ainfo.devnode, flags, 0 );\r
8747   if ( fd == -1 ) {\r
8748     if ( errno == EBUSY )\r
8749       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8750     else\r
8751       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8752     errorText_ = errorStream_.str();\r
8753     return FAILURE;\r
8754   }\r
8755 \r
8756   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8757   /*\r
8758     if ( flags | O_RDWR ) {\r
8759     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
8760     if ( result == -1) {\r
8761     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
8762     errorText_ = errorStream_.str();\r
8763     return FAILURE;\r
8764     }\r
8765     }\r
8766   */\r
8767 \r
8768   // Check the device channel support.\r
8769   stream_.nUserChannels[mode] = channels;\r
8770   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
8771     close( fd );\r
8772     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
8773     errorText_ = errorStream_.str();\r
8774     return FAILURE;\r
8775   }\r
8776 \r
8777   // Set the number of channels.\r
8778   int deviceChannels = channels + firstChannel;\r
8779   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
8780   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
8781     close( fd );\r
8782     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
8783     errorText_ = errorStream_.str();\r
8784     return FAILURE;\r
8785   }\r
8786   stream_.nDeviceChannels[mode] = deviceChannels;\r
8787 \r
8788   // Get the data format mask\r
8789   int mask;\r
8790   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
8791   if ( result == -1 ) {\r
8792     close( fd );\r
8793     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
8794     errorText_ = errorStream_.str();\r
8795     return FAILURE;\r
8796   }\r
8797 \r
8798   // Determine how to set the device format.\r
8799   stream_.userFormat = format;\r
8800   int deviceFormat = -1;\r
8801   stream_.doByteSwap[mode] = false;\r
8802   if ( format == RTAUDIO_SINT8 ) {\r
8803     if ( mask & AFMT_S8 ) {\r
8804       deviceFormat = AFMT_S8;\r
8805       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8806     }\r
8807   }\r
8808   else if ( format == RTAUDIO_SINT16 ) {\r
8809     if ( mask & AFMT_S16_NE ) {\r
8810       deviceFormat = AFMT_S16_NE;\r
8811       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8812     }\r
8813     else if ( mask & AFMT_S16_OE ) {\r
8814       deviceFormat = AFMT_S16_OE;\r
8815       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8816       stream_.doByteSwap[mode] = true;\r
8817     }\r
8818   }\r
8819   else if ( format == RTAUDIO_SINT24 ) {\r
8820     if ( mask & AFMT_S24_NE ) {\r
8821       deviceFormat = AFMT_S24_NE;\r
8822       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8823     }\r
8824     else if ( mask & AFMT_S24_OE ) {\r
8825       deviceFormat = AFMT_S24_OE;\r
8826       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8827       stream_.doByteSwap[mode] = true;\r
8828     }\r
8829   }\r
8830   else if ( format == RTAUDIO_SINT32 ) {\r
8831     if ( mask & AFMT_S32_NE ) {\r
8832       deviceFormat = AFMT_S32_NE;\r
8833       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8834     }\r
8835     else if ( mask & AFMT_S32_OE ) {\r
8836       deviceFormat = AFMT_S32_OE;\r
8837       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8838       stream_.doByteSwap[mode] = true;\r
8839     }\r
8840   }\r
8841 \r
8842   if ( deviceFormat == -1 ) {\r
8843     // The user requested format is not natively supported by the device.\r
8844     if ( mask & AFMT_S16_NE ) {\r
8845       deviceFormat = AFMT_S16_NE;\r
8846       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8847     }\r
8848     else if ( mask & AFMT_S32_NE ) {\r
8849       deviceFormat = AFMT_S32_NE;\r
8850       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8851     }\r
8852     else if ( mask & AFMT_S24_NE ) {\r
8853       deviceFormat = AFMT_S24_NE;\r
8854       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8855     }\r
8856     else if ( mask & AFMT_S16_OE ) {\r
8857       deviceFormat = AFMT_S16_OE;\r
8858       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8859       stream_.doByteSwap[mode] = true;\r
8860     }\r
8861     else if ( mask & AFMT_S32_OE ) {\r
8862       deviceFormat = AFMT_S32_OE;\r
8863       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8864       stream_.doByteSwap[mode] = true;\r
8865     }\r
8866     else if ( mask & AFMT_S24_OE ) {\r
8867       deviceFormat = AFMT_S24_OE;\r
8868       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8869       stream_.doByteSwap[mode] = true;\r
8870     }\r
8871     else if ( mask & AFMT_S8) {\r
8872       deviceFormat = AFMT_S8;\r
8873       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8874     }\r
8875   }\r
8876 \r
8877   if ( stream_.deviceFormat[mode] == 0 ) {\r
8878     // This really shouldn't happen ...\r
8879     close( fd );\r
8880     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8881     errorText_ = errorStream_.str();\r
8882     return FAILURE;\r
8883   }\r
8884 \r
8885   // Set the data format.\r
8886   int temp = deviceFormat;\r
8887   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
8888   if ( result == -1 || deviceFormat != temp ) {\r
8889     close( fd );\r
8890     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
8891     errorText_ = errorStream_.str();\r
8892     return FAILURE;\r
8893   }\r
8894 \r
8895   // Attempt to set the buffer size.  According to OSS, the minimum\r
8896   // number of buffers is two.  The supposed minimum buffer size is 16\r
8897   // bytes, so that will be our lower bound.  The argument to this\r
8898   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
8899   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
8900   // We'll check the actual value used near the end of the setup\r
8901   // procedure.\r
8902   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
8903   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
8904   int buffers = 0;\r
8905   if ( options ) buffers = options->numberOfBuffers;\r
8906   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
8907   if ( buffers < 2 ) buffers = 3;\r
8908   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
8909   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
8910   if ( result == -1 ) {\r
8911     close( fd );\r
8912     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
8913     errorText_ = errorStream_.str();\r
8914     return FAILURE;\r
8915   }\r
8916   stream_.nBuffers = buffers;\r
8917 \r
8918   // Save buffer size (in sample frames).\r
8919   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
8920   stream_.bufferSize = *bufferSize;\r
8921 \r
8922   // Set the sample rate.\r
8923   int srate = sampleRate;\r
8924   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
8925   if ( result == -1 ) {\r
8926     close( fd );\r
8927     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
8928     errorText_ = errorStream_.str();\r
8929     return FAILURE;\r
8930   }\r
8931 \r
8932   // Verify the sample rate setup worked.\r
8933   if ( abs( srate - sampleRate ) > 100 ) {\r
8934     close( fd );\r
8935     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
8936     errorText_ = errorStream_.str();\r
8937     return FAILURE;\r
8938   }\r
8939   stream_.sampleRate = sampleRate;\r
8940 \r
8941   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8942     // We're doing duplex setup here.\r
8943     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
8944     stream_.nDeviceChannels[0] = deviceChannels;\r
8945   }\r
8946 \r
8947   // Set interleaving parameters.\r
8948   stream_.userInterleaved = true;\r
8949   stream_.deviceInterleaved[mode] =  true;\r
8950   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
8951     stream_.userInterleaved = false;\r
8952 \r
8953   // Set flags for buffer conversion\r
8954   stream_.doConvertBuffer[mode] = false;\r
8955   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8956     stream_.doConvertBuffer[mode] = true;\r
8957   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8958     stream_.doConvertBuffer[mode] = true;\r
8959   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
8960        stream_.nUserChannels[mode] > 1 )\r
8961     stream_.doConvertBuffer[mode] = true;\r
8962 \r
8963   // Allocate the stream handles if necessary and then save.\r
8964   if ( stream_.apiHandle == 0 ) {\r
8965     try {\r
8966       handle = new OssHandle;\r
8967     }\r
8968     catch ( std::bad_alloc& ) {\r
8969       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
8970       goto error;\r
8971     }\r
8972 \r
8973     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
8974       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
8975       goto error;\r
8976     }\r
8977 \r
8978     stream_.apiHandle = (void *) handle;\r
8979   }\r
8980   else {\r
8981     handle = (OssHandle *) stream_.apiHandle;\r
8982   }\r
8983   handle->id[mode] = fd;\r
8984 \r
8985   // Allocate necessary internal buffers.\r
8986   unsigned long bufferBytes;\r
8987   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8988   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8989   if ( stream_.userBuffer[mode] == NULL ) {\r
8990     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
8991     goto error;\r
8992   }\r
8993 \r
8994   if ( stream_.doConvertBuffer[mode] ) {\r
8995 \r
8996     bool makeBuffer = true;\r
8997     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8998     if ( mode == INPUT ) {\r
8999       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
9000         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
9001         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
9002       }\r
9003     }\r
9004 \r
9005     if ( makeBuffer ) {\r
9006       bufferBytes *= *bufferSize;\r
9007       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9008       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9009       if ( stream_.deviceBuffer == NULL ) {\r
9010         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9011         goto error;\r
9012       }\r
9013     }\r
9014   }\r
9015 \r
9016   stream_.device[mode] = device;\r
9017   stream_.state = STREAM_STOPPED;\r
9018 \r
9019   // Setup the buffer conversion information structure.\r
9020   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9021 \r
9022   // Setup thread if necessary.\r
9023   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9024     // We had already set up an output stream.\r
9025     stream_.mode = DUPLEX;\r
9026     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9027   }\r
9028   else {\r
9029     stream_.mode = mode;\r
9030 \r
9031     // Setup callback thread.\r
9032     stream_.callbackInfo.object = (void *) this;\r
9033 \r
9034     // Set the thread attributes for joinable and realtime scheduling\r
9035     // priority.  The higher priority will only take affect if the\r
9036     // program is run as root or suid.\r
9037     pthread_attr_t attr;\r
9038     pthread_attr_init( &attr );\r
9039     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9040 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9041     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9042       struct sched_param param;\r
9043       int priority = options->priority;\r
9044       int min = sched_get_priority_min( SCHED_RR );\r
9045       int max = sched_get_priority_max( SCHED_RR );\r
9046       if ( priority < min ) priority = min;\r
9047       else if ( priority > max ) priority = max;\r
9048       param.sched_priority = priority;\r
9049       pthread_attr_setschedparam( &attr, &param );\r
9050       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9051     }\r
9052     else\r
9053       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9054 #else\r
9055     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9056 #endif\r
9057 \r
9058     stream_.callbackInfo.isRunning = true;\r
9059     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9060     pthread_attr_destroy( &attr );\r
9061     if ( result ) {\r
9062       stream_.callbackInfo.isRunning = false;\r
9063       errorText_ = "RtApiOss::error creating callback thread!";\r
9064       goto error;\r
9065     }\r
9066   }\r
9067 \r
9068   return SUCCESS;\r
9069 \r
9070  error:\r
9071   if ( handle ) {\r
9072     pthread_cond_destroy( &handle->runnable );\r
9073     if ( handle->id[0] ) close( handle->id[0] );\r
9074     if ( handle->id[1] ) close( handle->id[1] );\r
9075     delete handle;\r
9076     stream_.apiHandle = 0;\r
9077   }\r
9078 \r
9079   for ( int i=0; i<2; i++ ) {\r
9080     if ( stream_.userBuffer[i] ) {\r
9081       free( stream_.userBuffer[i] );\r
9082       stream_.userBuffer[i] = 0;\r
9083     }\r
9084   }\r
9085 \r
9086   if ( stream_.deviceBuffer ) {\r
9087     free( stream_.deviceBuffer );\r
9088     stream_.deviceBuffer = 0;\r
9089   }\r
9090 \r
9091   return FAILURE;\r
9092 }\r
9093 \r
9094 void RtApiOss :: closeStream()\r
9095 {\r
9096   if ( stream_.state == STREAM_CLOSED ) {\r
9097     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9098     error( RtAudioError::WARNING );\r
9099     return;\r
9100   }\r
9101 \r
9102   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9103   stream_.callbackInfo.isRunning = false;\r
9104   MUTEX_LOCK( &stream_.mutex );\r
9105   if ( stream_.state == STREAM_STOPPED )\r
9106     pthread_cond_signal( &handle->runnable );\r
9107   MUTEX_UNLOCK( &stream_.mutex );\r
9108   pthread_join( stream_.callbackInfo.thread, NULL );\r
9109 \r
9110   if ( stream_.state == STREAM_RUNNING ) {\r
9111     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9112       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9113     else\r
9114       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9115     stream_.state = STREAM_STOPPED;\r
9116   }\r
9117 \r
9118   if ( handle ) {\r
9119     pthread_cond_destroy( &handle->runnable );\r
9120     if ( handle->id[0] ) close( handle->id[0] );\r
9121     if ( handle->id[1] ) close( handle->id[1] );\r
9122     delete handle;\r
9123     stream_.apiHandle = 0;\r
9124   }\r
9125 \r
9126   for ( int i=0; i<2; i++ ) {\r
9127     if ( stream_.userBuffer[i] ) {\r
9128       free( stream_.userBuffer[i] );\r
9129       stream_.userBuffer[i] = 0;\r
9130     }\r
9131   }\r
9132 \r
9133   if ( stream_.deviceBuffer ) {\r
9134     free( stream_.deviceBuffer );\r
9135     stream_.deviceBuffer = 0;\r
9136   }\r
9137 \r
9138   stream_.mode = UNINITIALIZED;\r
9139   stream_.state = STREAM_CLOSED;\r
9140 }\r
9141 \r
9142 void RtApiOss :: startStream()\r
9143 {\r
9144   verifyStream();\r
9145   if ( stream_.state == STREAM_RUNNING ) {\r
9146     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9147     error( RtAudioError::WARNING );\r
9148     return;\r
9149   }\r
9150 \r
9151   MUTEX_LOCK( &stream_.mutex );\r
9152 \r
9153   stream_.state = STREAM_RUNNING;\r
9154 \r
9155   // No need to do anything else here ... OSS automatically starts\r
9156   // when fed samples.\r
9157 \r
9158   MUTEX_UNLOCK( &stream_.mutex );\r
9159 \r
9160   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9161   pthread_cond_signal( &handle->runnable );\r
9162 }\r
9163 \r
9164 void RtApiOss :: stopStream()\r
9165 {\r
9166   verifyStream();\r
9167   if ( stream_.state == STREAM_STOPPED ) {\r
9168     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9169     error( RtAudioError::WARNING );\r
9170     return;\r
9171   }\r
9172 \r
9173   MUTEX_LOCK( &stream_.mutex );\r
9174 \r
9175   // The state might change while waiting on a mutex.\r
9176   if ( stream_.state == STREAM_STOPPED ) {\r
9177     MUTEX_UNLOCK( &stream_.mutex );\r
9178     return;\r
9179   }\r
9180 \r
9181   int result = 0;\r
9182   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9183   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9184 \r
9185     // Flush the output with zeros a few times.\r
9186     char *buffer;\r
9187     int samples;\r
9188     RtAudioFormat format;\r
9189 \r
9190     if ( stream_.doConvertBuffer[0] ) {\r
9191       buffer = stream_.deviceBuffer;\r
9192       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9193       format = stream_.deviceFormat[0];\r
9194     }\r
9195     else {\r
9196       buffer = stream_.userBuffer[0];\r
9197       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9198       format = stream_.userFormat;\r
9199     }\r
9200 \r
9201     memset( buffer, 0, samples * formatBytes(format) );\r
9202     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9203       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9204       if ( result == -1 ) {\r
9205         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9206         error( RtAudioError::WARNING );\r
9207       }\r
9208     }\r
9209 \r
9210     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9211     if ( result == -1 ) {\r
9212       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9213       errorText_ = errorStream_.str();\r
9214       goto unlock;\r
9215     }\r
9216     handle->triggered = false;\r
9217   }\r
9218 \r
9219   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9220     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9221     if ( result == -1 ) {\r
9222       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9223       errorText_ = errorStream_.str();\r
9224       goto unlock;\r
9225     }\r
9226   }\r
9227 \r
9228  unlock:\r
9229   stream_.state = STREAM_STOPPED;\r
9230   MUTEX_UNLOCK( &stream_.mutex );\r
9231 \r
9232   if ( result != -1 ) return;\r
9233   error( RtAudioError::SYSTEM_ERROR );\r
9234 }\r
9235 \r
9236 void RtApiOss :: abortStream()\r
9237 {\r
9238   verifyStream();\r
9239   if ( stream_.state == STREAM_STOPPED ) {\r
9240     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9241     error( RtAudioError::WARNING );\r
9242     return;\r
9243   }\r
9244 \r
9245   MUTEX_LOCK( &stream_.mutex );\r
9246 \r
9247   // The state might change while waiting on a mutex.\r
9248   if ( stream_.state == STREAM_STOPPED ) {\r
9249     MUTEX_UNLOCK( &stream_.mutex );\r
9250     return;\r
9251   }\r
9252 \r
9253   int result = 0;\r
9254   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9255   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9256     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9257     if ( result == -1 ) {\r
9258       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9259       errorText_ = errorStream_.str();\r
9260       goto unlock;\r
9261     }\r
9262     handle->triggered = false;\r
9263   }\r
9264 \r
9265   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9266     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9267     if ( result == -1 ) {\r
9268       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9269       errorText_ = errorStream_.str();\r
9270       goto unlock;\r
9271     }\r
9272   }\r
9273 \r
9274  unlock:\r
9275   stream_.state = STREAM_STOPPED;\r
9276   MUTEX_UNLOCK( &stream_.mutex );\r
9277 \r
9278   if ( result != -1 ) return;\r
9279   error( RtAudioError::SYSTEM_ERROR );\r
9280 }\r
9281 \r
9282 void RtApiOss :: callbackEvent()\r
9283 {\r
9284   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9285   if ( stream_.state == STREAM_STOPPED ) {\r
9286     MUTEX_LOCK( &stream_.mutex );\r
9287     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9288     if ( stream_.state != STREAM_RUNNING ) {\r
9289       MUTEX_UNLOCK( &stream_.mutex );\r
9290       return;\r
9291     }\r
9292     MUTEX_UNLOCK( &stream_.mutex );\r
9293   }\r
9294 \r
9295   if ( stream_.state == STREAM_CLOSED ) {\r
9296     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9297     error( RtAudioError::WARNING );\r
9298     return;\r
9299   }\r
9300 \r
9301   // Invoke user callback to get fresh output data.\r
9302   int doStopStream = 0;\r
9303   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9304   double streamTime = getStreamTime();\r
9305   RtAudioStreamStatus status = 0;\r
9306   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9307     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9308     handle->xrun[0] = false;\r
9309   }\r
9310   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9311     status |= RTAUDIO_INPUT_OVERFLOW;\r
9312     handle->xrun[1] = false;\r
9313   }\r
9314   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9315                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9316   if ( doStopStream == 2 ) {\r
9317     this->abortStream();\r
9318     return;\r
9319   }\r
9320 \r
9321   MUTEX_LOCK( &stream_.mutex );\r
9322 \r
9323   // The state might change while waiting on a mutex.\r
9324   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9325 \r
9326   int result;\r
9327   char *buffer;\r
9328   int samples;\r
9329   RtAudioFormat format;\r
9330 \r
9331   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9332 \r
9333     // Setup parameters and do buffer conversion if necessary.\r
9334     if ( stream_.doConvertBuffer[0] ) {\r
9335       buffer = stream_.deviceBuffer;\r
9336       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9337       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9338       format = stream_.deviceFormat[0];\r
9339     }\r
9340     else {\r
9341       buffer = stream_.userBuffer[0];\r
9342       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9343       format = stream_.userFormat;\r
9344     }\r
9345 \r
9346     // Do byte swapping if necessary.\r
9347     if ( stream_.doByteSwap[0] )\r
9348       byteSwapBuffer( buffer, samples, format );\r
9349 \r
9350     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9351       int trig = 0;\r
9352       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9353       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9354       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9355       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9356       handle->triggered = true;\r
9357     }\r
9358     else\r
9359       // Write samples to device.\r
9360       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9361 \r
9362     if ( result == -1 ) {\r
9363       // We'll assume this is an underrun, though there isn't a\r
9364       // specific means for determining that.\r
9365       handle->xrun[0] = true;\r
9366       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9367       error( RtAudioError::WARNING );\r
9368       // Continue on to input section.\r
9369     }\r
9370   }\r
9371 \r
9372   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9373 \r
9374     // Setup parameters.\r
9375     if ( stream_.doConvertBuffer[1] ) {\r
9376       buffer = stream_.deviceBuffer;\r
9377       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9378       format = stream_.deviceFormat[1];\r
9379     }\r
9380     else {\r
9381       buffer = stream_.userBuffer[1];\r
9382       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9383       format = stream_.userFormat;\r
9384     }\r
9385 \r
9386     // Read samples from device.\r
9387     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9388 \r
9389     if ( result == -1 ) {\r
9390       // We'll assume this is an overrun, though there isn't a\r
9391       // specific means for determining that.\r
9392       handle->xrun[1] = true;\r
9393       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9394       error( RtAudioError::WARNING );\r
9395       goto unlock;\r
9396     }\r
9397 \r
9398     // Do byte swapping if necessary.\r
9399     if ( stream_.doByteSwap[1] )\r
9400       byteSwapBuffer( buffer, samples, format );\r
9401 \r
9402     // Do buffer conversion if necessary.\r
9403     if ( stream_.doConvertBuffer[1] )\r
9404       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9405   }\r
9406 \r
9407  unlock:\r
9408   MUTEX_UNLOCK( &stream_.mutex );\r
9409 \r
9410   RtApi::tickStreamTime();\r
9411   if ( doStopStream == 1 ) this->stopStream();\r
9412 }\r
9413 \r
9414 static void *ossCallbackHandler( void *ptr )\r
9415 {\r
9416   CallbackInfo *info = (CallbackInfo *) ptr;\r
9417   RtApiOss *object = (RtApiOss *) info->object;\r
9418   bool *isRunning = &info->isRunning;\r
9419 \r
9420   while ( *isRunning == true ) {\r
9421     pthread_testcancel();\r
9422     object->callbackEvent();\r
9423   }\r
9424 \r
9425   pthread_exit( NULL );\r
9426 }\r
9427 \r
9428 //******************** End of __LINUX_OSS__ *********************//\r
9429 #endif\r
9430 \r
9431 \r
9432 // *************************************************** //\r
9433 //\r
9434 // Protected common (OS-independent) RtAudio methods.\r
9435 //\r
9436 // *************************************************** //\r
9437 \r
9438 // This method can be modified to control the behavior of error\r
9439 // message printing.\r
9440 void RtApi :: error( RtAudioError::Type type )\r
9441 {\r
9442   errorStream_.str(""); // clear the ostringstream\r
9443 \r
9444   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9445   if ( errorCallback ) {\r
9446     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9447 \r
9448     if ( firstErrorOccurred_ )\r
9449       return;\r
9450 \r
9451     firstErrorOccurred_ = true;\r
9452     const std::string errorMessage = errorText_;\r
9453 \r
9454     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9455       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9456       abortStream();\r
9457     }\r
9458 \r
9459     errorCallback( type, errorMessage );\r
9460     firstErrorOccurred_ = false;\r
9461     return;\r
9462   }\r
9463 \r
9464   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9465     std::cerr << '\n' << errorText_ << "\n\n";\r
9466   else if ( type != RtAudioError::WARNING )\r
9467     throw( RtAudioError( errorText_, type ) );\r
9468 }\r
9469 \r
9470 void RtApi :: verifyStream()\r
9471 {\r
9472   if ( stream_.state == STREAM_CLOSED ) {\r
9473     errorText_ = "RtApi:: a stream is not open!";\r
9474     error( RtAudioError::INVALID_USE );\r
9475   }\r
9476 }\r
9477 \r
9478 void RtApi :: clearStreamInfo()\r
9479 {\r
9480   stream_.mode = UNINITIALIZED;\r
9481   stream_.state = STREAM_CLOSED;\r
9482   stream_.sampleRate = 0;\r
9483   stream_.bufferSize = 0;\r
9484   stream_.nBuffers = 0;\r
9485   stream_.userFormat = 0;\r
9486   stream_.userInterleaved = true;\r
9487   stream_.streamTime = 0.0;\r
9488   stream_.apiHandle = 0;\r
9489   stream_.deviceBuffer = 0;\r
9490   stream_.callbackInfo.callback = 0;\r
9491   stream_.callbackInfo.userData = 0;\r
9492   stream_.callbackInfo.isRunning = false;\r
9493   stream_.callbackInfo.errorCallback = 0;\r
9494   for ( int i=0; i<2; i++ ) {\r
9495     stream_.device[i] = 11111;\r
9496     stream_.doConvertBuffer[i] = false;\r
9497     stream_.deviceInterleaved[i] = true;\r
9498     stream_.doByteSwap[i] = false;\r
9499     stream_.nUserChannels[i] = 0;\r
9500     stream_.nDeviceChannels[i] = 0;\r
9501     stream_.channelOffset[i] = 0;\r
9502     stream_.deviceFormat[i] = 0;\r
9503     stream_.latency[i] = 0;\r
9504     stream_.userBuffer[i] = 0;\r
9505     stream_.convertInfo[i].channels = 0;\r
9506     stream_.convertInfo[i].inJump = 0;\r
9507     stream_.convertInfo[i].outJump = 0;\r
9508     stream_.convertInfo[i].inFormat = 0;\r
9509     stream_.convertInfo[i].outFormat = 0;\r
9510     stream_.convertInfo[i].inOffset.clear();\r
9511     stream_.convertInfo[i].outOffset.clear();\r
9512   }\r
9513 }\r
9514 \r
9515 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9516 {\r
9517   if ( format == RTAUDIO_SINT16 )\r
9518     return 2;\r
9519   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9520     return 4;\r
9521   else if ( format == RTAUDIO_FLOAT64 )\r
9522     return 8;\r
9523   else if ( format == RTAUDIO_SINT24 )\r
9524     return 3;\r
9525   else if ( format == RTAUDIO_SINT8 )\r
9526     return 1;\r
9527 \r
9528   errorText_ = "RtApi::formatBytes: undefined format.";\r
9529   error( RtAudioError::WARNING );\r
9530 \r
9531   return 0;\r
9532 }\r
9533 \r
9534 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9535 {\r
9536   if ( mode == INPUT ) { // convert device to user buffer\r
9537     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9538     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9539     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9540     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9541   }\r
9542   else { // convert user to device buffer\r
9543     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9544     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9545     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9546     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9547   }\r
9548 \r
9549   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9550     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9551   else\r
9552     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9553 \r
9554   // Set up the interleave/deinterleave offsets.\r
9555   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9556     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9557          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9558       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9559         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9560         stream_.convertInfo[mode].outOffset.push_back( k );\r
9561         stream_.convertInfo[mode].inJump = 1;\r
9562       }\r
9563     }\r
9564     else {\r
9565       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9566         stream_.convertInfo[mode].inOffset.push_back( k );\r
9567         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9568         stream_.convertInfo[mode].outJump = 1;\r
9569       }\r
9570     }\r
9571   }\r
9572   else { // no (de)interleaving\r
9573     if ( stream_.userInterleaved ) {\r
9574       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9575         stream_.convertInfo[mode].inOffset.push_back( k );\r
9576         stream_.convertInfo[mode].outOffset.push_back( k );\r
9577       }\r
9578     }\r
9579     else {\r
9580       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9581         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9582         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9583         stream_.convertInfo[mode].inJump = 1;\r
9584         stream_.convertInfo[mode].outJump = 1;\r
9585       }\r
9586     }\r
9587   }\r
9588 \r
9589   // Add channel offset.\r
9590   if ( firstChannel > 0 ) {\r
9591     if ( stream_.deviceInterleaved[mode] ) {\r
9592       if ( mode == OUTPUT ) {\r
9593         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9594           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9595       }\r
9596       else {\r
9597         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9598           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9599       }\r
9600     }\r
9601     else {\r
9602       if ( mode == OUTPUT ) {\r
9603         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9604           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9605       }\r
9606       else {\r
9607         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9608           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9609       }\r
9610     }\r
9611   }\r
9612 }\r
9613 \r
9614 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9615 {\r
9616   // This function does format conversion, input/output channel compensation, and\r
9617   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9618   // the lower three bytes of a 32-bit integer.\r
9619 \r
9620   // Clear our device buffer when in/out duplex device channels are different\r
9621   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9622        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9623     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9624 \r
9625   int j;\r
9626   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9627     Float64 scale;\r
9628     Float64 *out = (Float64 *)outBuffer;\r
9629 \r
9630     if (info.inFormat == RTAUDIO_SINT8) {\r
9631       signed char *in = (signed char *)inBuffer;\r
9632       scale = 1.0 / 127.5;\r
9633       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9634         for (j=0; j<info.channels; j++) {\r
9635           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9636           out[info.outOffset[j]] += 0.5;\r
9637           out[info.outOffset[j]] *= scale;\r
9638         }\r
9639         in += info.inJump;\r
9640         out += info.outJump;\r
9641       }\r
9642     }\r
9643     else if (info.inFormat == RTAUDIO_SINT16) {\r
9644       Int16 *in = (Int16 *)inBuffer;\r
9645       scale = 1.0 / 32767.5;\r
9646       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9647         for (j=0; j<info.channels; j++) {\r
9648           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9649           out[info.outOffset[j]] += 0.5;\r
9650           out[info.outOffset[j]] *= scale;\r
9651         }\r
9652         in += info.inJump;\r
9653         out += info.outJump;\r
9654       }\r
9655     }\r
9656     else if (info.inFormat == RTAUDIO_SINT24) {\r
9657       Int24 *in = (Int24 *)inBuffer;\r
9658       scale = 1.0 / 8388607.5;\r
9659       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9660         for (j=0; j<info.channels; j++) {\r
9661           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9662           out[info.outOffset[j]] += 0.5;\r
9663           out[info.outOffset[j]] *= scale;\r
9664         }\r
9665         in += info.inJump;\r
9666         out += info.outJump;\r
9667       }\r
9668     }\r
9669     else if (info.inFormat == RTAUDIO_SINT32) {\r
9670       Int32 *in = (Int32 *)inBuffer;\r
9671       scale = 1.0 / 2147483647.5;\r
9672       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9673         for (j=0; j<info.channels; j++) {\r
9674           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9675           out[info.outOffset[j]] += 0.5;\r
9676           out[info.outOffset[j]] *= scale;\r
9677         }\r
9678         in += info.inJump;\r
9679         out += info.outJump;\r
9680       }\r
9681     }\r
9682     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9683       Float32 *in = (Float32 *)inBuffer;\r
9684       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9685         for (j=0; j<info.channels; j++) {\r
9686           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9687         }\r
9688         in += info.inJump;\r
9689         out += info.outJump;\r
9690       }\r
9691     }\r
9692     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9693       // Channel compensation and/or (de)interleaving only.\r
9694       Float64 *in = (Float64 *)inBuffer;\r
9695       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9696         for (j=0; j<info.channels; j++) {\r
9697           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9698         }\r
9699         in += info.inJump;\r
9700         out += info.outJump;\r
9701       }\r
9702     }\r
9703   }\r
9704   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9705     Float32 scale;\r
9706     Float32 *out = (Float32 *)outBuffer;\r
9707 \r
9708     if (info.inFormat == RTAUDIO_SINT8) {\r
9709       signed char *in = (signed char *)inBuffer;\r
9710       scale = (Float32) ( 1.0 / 127.5 );\r
9711       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9712         for (j=0; j<info.channels; j++) {\r
9713           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9714           out[info.outOffset[j]] += 0.5;\r
9715           out[info.outOffset[j]] *= scale;\r
9716         }\r
9717         in += info.inJump;\r
9718         out += info.outJump;\r
9719       }\r
9720     }\r
9721     else if (info.inFormat == RTAUDIO_SINT16) {\r
9722       Int16 *in = (Int16 *)inBuffer;\r
9723       scale = (Float32) ( 1.0 / 32767.5 );\r
9724       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9725         for (j=0; j<info.channels; j++) {\r
9726           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9727           out[info.outOffset[j]] += 0.5;\r
9728           out[info.outOffset[j]] *= scale;\r
9729         }\r
9730         in += info.inJump;\r
9731         out += info.outJump;\r
9732       }\r
9733     }\r
9734     else if (info.inFormat == RTAUDIO_SINT24) {\r
9735       Int24 *in = (Int24 *)inBuffer;\r
9736       scale = (Float32) ( 1.0 / 8388607.5 );\r
9737       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9738         for (j=0; j<info.channels; j++) {\r
9739           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9740           out[info.outOffset[j]] += 0.5;\r
9741           out[info.outOffset[j]] *= scale;\r
9742         }\r
9743         in += info.inJump;\r
9744         out += info.outJump;\r
9745       }\r
9746     }\r
9747     else if (info.inFormat == RTAUDIO_SINT32) {\r
9748       Int32 *in = (Int32 *)inBuffer;\r
9749       scale = (Float32) ( 1.0 / 2147483647.5 );\r
9750       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9751         for (j=0; j<info.channels; j++) {\r
9752           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9753           out[info.outOffset[j]] += 0.5;\r
9754           out[info.outOffset[j]] *= scale;\r
9755         }\r
9756         in += info.inJump;\r
9757         out += info.outJump;\r
9758       }\r
9759     }\r
9760     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9761       // Channel compensation and/or (de)interleaving only.\r
9762       Float32 *in = (Float32 *)inBuffer;\r
9763       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9764         for (j=0; j<info.channels; j++) {\r
9765           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9766         }\r
9767         in += info.inJump;\r
9768         out += info.outJump;\r
9769       }\r
9770     }\r
9771     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9772       Float64 *in = (Float64 *)inBuffer;\r
9773       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9774         for (j=0; j<info.channels; j++) {\r
9775           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9776         }\r
9777         in += info.inJump;\r
9778         out += info.outJump;\r
9779       }\r
9780     }\r
9781   }\r
9782   else if (info.outFormat == RTAUDIO_SINT32) {\r
9783     Int32 *out = (Int32 *)outBuffer;\r
9784     if (info.inFormat == RTAUDIO_SINT8) {\r
9785       signed char *in = (signed char *)inBuffer;\r
9786       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9787         for (j=0; j<info.channels; j++) {\r
9788           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9789           out[info.outOffset[j]] <<= 24;\r
9790         }\r
9791         in += info.inJump;\r
9792         out += info.outJump;\r
9793       }\r
9794     }\r
9795     else if (info.inFormat == RTAUDIO_SINT16) {\r
9796       Int16 *in = (Int16 *)inBuffer;\r
9797       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9798         for (j=0; j<info.channels; j++) {\r
9799           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9800           out[info.outOffset[j]] <<= 16;\r
9801         }\r
9802         in += info.inJump;\r
9803         out += info.outJump;\r
9804       }\r
9805     }\r
9806     else if (info.inFormat == RTAUDIO_SINT24) {\r
9807       Int24 *in = (Int24 *)inBuffer;\r
9808       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9809         for (j=0; j<info.channels; j++) {\r
9810           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
9811           out[info.outOffset[j]] <<= 8;\r
9812         }\r
9813         in += info.inJump;\r
9814         out += info.outJump;\r
9815       }\r
9816     }\r
9817     else if (info.inFormat == RTAUDIO_SINT32) {\r
9818       // Channel compensation and/or (de)interleaving only.\r
9819       Int32 *in = (Int32 *)inBuffer;\r
9820       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9821         for (j=0; j<info.channels; j++) {\r
9822           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9823         }\r
9824         in += info.inJump;\r
9825         out += info.outJump;\r
9826       }\r
9827     }\r
9828     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9829       Float32 *in = (Float32 *)inBuffer;\r
9830       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9831         for (j=0; j<info.channels; j++) {\r
9832           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9833         }\r
9834         in += info.inJump;\r
9835         out += info.outJump;\r
9836       }\r
9837     }\r
9838     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9839       Float64 *in = (Float64 *)inBuffer;\r
9840       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9841         for (j=0; j<info.channels; j++) {\r
9842           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9843         }\r
9844         in += info.inJump;\r
9845         out += info.outJump;\r
9846       }\r
9847     }\r
9848   }\r
9849   else if (info.outFormat == RTAUDIO_SINT24) {\r
9850     Int24 *out = (Int24 *)outBuffer;\r
9851     if (info.inFormat == RTAUDIO_SINT8) {\r
9852       signed char *in = (signed char *)inBuffer;\r
9853       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9854         for (j=0; j<info.channels; j++) {\r
9855           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
9856           //out[info.outOffset[j]] <<= 16;\r
9857         }\r
9858         in += info.inJump;\r
9859         out += info.outJump;\r
9860       }\r
9861     }\r
9862     else if (info.inFormat == RTAUDIO_SINT16) {\r
9863       Int16 *in = (Int16 *)inBuffer;\r
9864       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9865         for (j=0; j<info.channels; j++) {\r
9866           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
9867           //out[info.outOffset[j]] <<= 8;\r
9868         }\r
9869         in += info.inJump;\r
9870         out += info.outJump;\r
9871       }\r
9872     }\r
9873     else if (info.inFormat == RTAUDIO_SINT24) {\r
9874       // Channel compensation and/or (de)interleaving only.\r
9875       Int24 *in = (Int24 *)inBuffer;\r
9876       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9877         for (j=0; j<info.channels; j++) {\r
9878           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9879         }\r
9880         in += info.inJump;\r
9881         out += info.outJump;\r
9882       }\r
9883     }\r
9884     else if (info.inFormat == RTAUDIO_SINT32) {\r
9885       Int32 *in = (Int32 *)inBuffer;\r
9886       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9887         for (j=0; j<info.channels; j++) {\r
9888           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
9889           //out[info.outOffset[j]] >>= 8;\r
9890         }\r
9891         in += info.inJump;\r
9892         out += info.outJump;\r
9893       }\r
9894     }\r
9895     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9896       Float32 *in = (Float32 *)inBuffer;\r
9897       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9898         for (j=0; j<info.channels; j++) {\r
9899           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9900         }\r
9901         in += info.inJump;\r
9902         out += info.outJump;\r
9903       }\r
9904     }\r
9905     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9906       Float64 *in = (Float64 *)inBuffer;\r
9907       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9908         for (j=0; j<info.channels; j++) {\r
9909           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9910         }\r
9911         in += info.inJump;\r
9912         out += info.outJump;\r
9913       }\r
9914     }\r
9915   }\r
9916   else if (info.outFormat == RTAUDIO_SINT16) {\r
9917     Int16 *out = (Int16 *)outBuffer;\r
9918     if (info.inFormat == RTAUDIO_SINT8) {\r
9919       signed char *in = (signed char *)inBuffer;\r
9920       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9921         for (j=0; j<info.channels; j++) {\r
9922           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
9923           out[info.outOffset[j]] <<= 8;\r
9924         }\r
9925         in += info.inJump;\r
9926         out += info.outJump;\r
9927       }\r
9928     }\r
9929     else if (info.inFormat == RTAUDIO_SINT16) {\r
9930       // Channel compensation and/or (de)interleaving only.\r
9931       Int16 *in = (Int16 *)inBuffer;\r
9932       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9933         for (j=0; j<info.channels; j++) {\r
9934           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9935         }\r
9936         in += info.inJump;\r
9937         out += info.outJump;\r
9938       }\r
9939     }\r
9940     else if (info.inFormat == RTAUDIO_SINT24) {\r
9941       Int24 *in = (Int24 *)inBuffer;\r
9942       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9943         for (j=0; j<info.channels; j++) {\r
9944           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
9945         }\r
9946         in += info.inJump;\r
9947         out += info.outJump;\r
9948       }\r
9949     }\r
9950     else if (info.inFormat == RTAUDIO_SINT32) {\r
9951       Int32 *in = (Int32 *)inBuffer;\r
9952       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9953         for (j=0; j<info.channels; j++) {\r
9954           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
9955         }\r
9956         in += info.inJump;\r
9957         out += info.outJump;\r
9958       }\r
9959     }\r
9960     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9961       Float32 *in = (Float32 *)inBuffer;\r
9962       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9963         for (j=0; j<info.channels; j++) {\r
9964           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9965         }\r
9966         in += info.inJump;\r
9967         out += info.outJump;\r
9968       }\r
9969     }\r
9970     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9971       Float64 *in = (Float64 *)inBuffer;\r
9972       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9973         for (j=0; j<info.channels; j++) {\r
9974           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9975         }\r
9976         in += info.inJump;\r
9977         out += info.outJump;\r
9978       }\r
9979     }\r
9980   }\r
9981   else if (info.outFormat == RTAUDIO_SINT8) {\r
9982     signed char *out = (signed char *)outBuffer;\r
9983     if (info.inFormat == RTAUDIO_SINT8) {\r
9984       // Channel compensation and/or (de)interleaving only.\r
9985       signed char *in = (signed char *)inBuffer;\r
9986       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9987         for (j=0; j<info.channels; j++) {\r
9988           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9989         }\r
9990         in += info.inJump;\r
9991         out += info.outJump;\r
9992       }\r
9993     }\r
9994     if (info.inFormat == RTAUDIO_SINT16) {\r
9995       Int16 *in = (Int16 *)inBuffer;\r
9996       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9997         for (j=0; j<info.channels; j++) {\r
9998           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
9999         }\r
10000         in += info.inJump;\r
10001         out += info.outJump;\r
10002       }\r
10003     }\r
10004     else if (info.inFormat == RTAUDIO_SINT24) {\r
10005       Int24 *in = (Int24 *)inBuffer;\r
10006       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10007         for (j=0; j<info.channels; j++) {\r
10008           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10009         }\r
10010         in += info.inJump;\r
10011         out += info.outJump;\r
10012       }\r
10013     }\r
10014     else if (info.inFormat == RTAUDIO_SINT32) {\r
10015       Int32 *in = (Int32 *)inBuffer;\r
10016       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10017         for (j=0; j<info.channels; j++) {\r
10018           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10019         }\r
10020         in += info.inJump;\r
10021         out += info.outJump;\r
10022       }\r
10023     }\r
10024     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10025       Float32 *in = (Float32 *)inBuffer;\r
10026       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10027         for (j=0; j<info.channels; j++) {\r
10028           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10029         }\r
10030         in += info.inJump;\r
10031         out += info.outJump;\r
10032       }\r
10033     }\r
10034     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10035       Float64 *in = (Float64 *)inBuffer;\r
10036       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10037         for (j=0; j<info.channels; j++) {\r
10038           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10039         }\r
10040         in += info.inJump;\r
10041         out += info.outJump;\r
10042       }\r
10043     }\r
10044   }\r
10045 }\r
10046 \r
10047 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10048 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10049 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10050 \r
10051 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10052 {\r
10053   register char val;\r
10054   register char *ptr;\r
10055 \r
10056   ptr = buffer;\r
10057   if ( format == RTAUDIO_SINT16 ) {\r
10058     for ( unsigned int i=0; i<samples; i++ ) {\r
10059       // Swap 1st and 2nd bytes.\r
10060       val = *(ptr);\r
10061       *(ptr) = *(ptr+1);\r
10062       *(ptr+1) = val;\r
10063 \r
10064       // Increment 2 bytes.\r
10065       ptr += 2;\r
10066     }\r
10067   }\r
10068   else if ( format == RTAUDIO_SINT32 ||\r
10069             format == RTAUDIO_FLOAT32 ) {\r
10070     for ( unsigned int i=0; i<samples; i++ ) {\r
10071       // Swap 1st and 4th bytes.\r
10072       val = *(ptr);\r
10073       *(ptr) = *(ptr+3);\r
10074       *(ptr+3) = val;\r
10075 \r
10076       // Swap 2nd and 3rd bytes.\r
10077       ptr += 1;\r
10078       val = *(ptr);\r
10079       *(ptr) = *(ptr+1);\r
10080       *(ptr+1) = val;\r
10081 \r
10082       // Increment 3 more bytes.\r
10083       ptr += 3;\r
10084     }\r
10085   }\r
10086   else if ( format == RTAUDIO_SINT24 ) {\r
10087     for ( unsigned int i=0; i<samples; i++ ) {\r
10088       // Swap 1st and 3rd bytes.\r
10089       val = *(ptr);\r
10090       *(ptr) = *(ptr+2);\r
10091       *(ptr+2) = val;\r
10092 \r
10093       // Increment 2 more bytes.\r
10094       ptr += 2;\r
10095     }\r
10096   }\r
10097   else if ( format == RTAUDIO_FLOAT64 ) {\r
10098     for ( unsigned int i=0; i<samples; i++ ) {\r
10099       // Swap 1st and 8th bytes\r
10100       val = *(ptr);\r
10101       *(ptr) = *(ptr+7);\r
10102       *(ptr+7) = val;\r
10103 \r
10104       // Swap 2nd and 7th bytes\r
10105       ptr += 1;\r
10106       val = *(ptr);\r
10107       *(ptr) = *(ptr+5);\r
10108       *(ptr+5) = val;\r
10109 \r
10110       // Swap 3rd and 6th bytes\r
10111       ptr += 1;\r
10112       val = *(ptr);\r
10113       *(ptr) = *(ptr+3);\r
10114       *(ptr+3) = val;\r
10115 \r
10116       // Swap 4th and 5th bytes\r
10117       ptr += 1;\r
10118       val = *(ptr);\r
10119       *(ptr) = *(ptr+1);\r
10120       *(ptr+1) = val;\r
10121 \r
10122       // Increment 5 more bytes.\r
10123       ptr += 5;\r
10124     }\r
10125   }\r
10126 }\r
10127 \r
10128   // Indentation settings for Vim and Emacs\r
10129   //\r
10130   // Local Variables:\r
10131   // c-basic-offset: 2\r
10132   // indent-tabs-mode: nil\r
10133   // End:\r
10134   //\r
10135   // vim: et sts=2 sw=2\r
10136 \r