44cf4edb497d3743594f56a349693a6e1f83a23f
[rtaudio-cdist.git] / RtAudio.cpp
1 /************************************************************************/\r
2 /*! \class RtAudio\r
3     \brief Realtime audio i/o C++ classes.\r
4 \r
5     RtAudio provides a common API (Application Programming Interface)\r
6     for realtime audio input/output across Linux (native ALSA, Jack,\r
7     and OSS), Macintosh OS X (CoreAudio and Jack), and Windows\r
8     (DirectSound, ASIO and WASAPI) operating systems.\r
9 \r
10     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
11 \r
12     RtAudio: realtime audio i/o C++ classes\r
13     Copyright (c) 2001-2014 Gary P. Scavone\r
14 \r
15     Permission is hereby granted, free of charge, to any person\r
16     obtaining a copy of this software and associated documentation files\r
17     (the "Software"), to deal in the Software without restriction,\r
18     including without limitation the rights to use, copy, modify, merge,\r
19     publish, distribute, sublicense, and/or sell copies of the Software,\r
20     and to permit persons to whom the Software is furnished to do so,\r
21     subject to the following conditions:\r
22 \r
23     The above copyright notice and this permission notice shall be\r
24     included in all copies or substantial portions of the Software.\r
25 \r
26     Any person wishing to distribute modifications to the Software is\r
27     asked to send the modifications to the original developer so that\r
28     they can be incorporated into the canonical version.  This is,\r
29     however, not a binding provision of this license.\r
30 \r
31     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
32     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
33     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
34     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
35     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
36     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
37     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
38 */\r
39 /************************************************************************/\r
40 \r
41 // RtAudio: Version 4.1.1pre\r
42 \r
43 #include "RtAudio.h"\r
44 #include <iostream>\r
45 #include <cstdlib>\r
46 #include <cstring>\r
47 #include <climits>\r
48 \r
49 // Static variable definitions.\r
50 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
51 const unsigned int RtApi::SAMPLE_RATES[] = {\r
52   4000, 5512, 8000, 9600, 11025, 16000, 22050,\r
53   32000, 44100, 48000, 88200, 96000, 176400, 192000\r
54 };\r
55 \r
56 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)\r
57   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
58   #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)\r
59   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
60   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)\r
61 #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
62   // pthread API\r
63   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
64   #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)\r
65   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)\r
66   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)\r
67 #else\r
68   #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions\r
69   #define MUTEX_DESTROY(A)    abs(*A) // dummy definitions\r
70 #endif\r
71 \r
72 // *************************************************** //\r
73 //\r
74 // RtAudio definitions.\r
75 //\r
76 // *************************************************** //\r
77 \r
78 std::string RtAudio :: getVersion( void ) throw()\r
79 {\r
80   return RTAUDIO_VERSION;\r
81 }\r
82 \r
83 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()\r
84 {\r
85   apis.clear();\r
86 \r
87   // The order here will control the order of RtAudio's API search in\r
88   // the constructor.\r
89 #if defined(__UNIX_JACK__)\r
90   apis.push_back( UNIX_JACK );\r
91 #endif\r
92 #if defined(__LINUX_ALSA__)\r
93   apis.push_back( LINUX_ALSA );\r
94 #endif\r
95 #if defined(__LINUX_PULSE__)\r
96   apis.push_back( LINUX_PULSE );\r
97 #endif\r
98 #if defined(__LINUX_OSS__)\r
99   apis.push_back( LINUX_OSS );\r
100 #endif\r
101 #if defined(__WINDOWS_ASIO__)\r
102   apis.push_back( WINDOWS_ASIO );\r
103 #endif\r
104 #if defined(__WINDOWS_WASAPI__)\r
105   apis.push_back( WINDOWS_WASAPI );\r
106 #endif\r
107 #if defined(__WINDOWS_DS__)\r
108   apis.push_back( WINDOWS_DS );\r
109 #endif\r
110 #if defined(__MACOSX_CORE__)\r
111   apis.push_back( MACOSX_CORE );\r
112 #endif\r
113 #if defined(__RTAUDIO_DUMMY__)\r
114   apis.push_back( RTAUDIO_DUMMY );\r
115 #endif\r
116 }\r
117 \r
118 void RtAudio :: openRtApi( RtAudio::Api api )\r
119 {\r
120   if ( rtapi_ )\r
121     delete rtapi_;\r
122   rtapi_ = 0;\r
123 \r
124 #if defined(__UNIX_JACK__)\r
125   if ( api == UNIX_JACK )\r
126     rtapi_ = new RtApiJack();\r
127 #endif\r
128 #if defined(__LINUX_ALSA__)\r
129   if ( api == LINUX_ALSA )\r
130     rtapi_ = new RtApiAlsa();\r
131 #endif\r
132 #if defined(__LINUX_PULSE__)\r
133   if ( api == LINUX_PULSE )\r
134     rtapi_ = new RtApiPulse();\r
135 #endif\r
136 #if defined(__LINUX_OSS__)\r
137   if ( api == LINUX_OSS )\r
138     rtapi_ = new RtApiOss();\r
139 #endif\r
140 #if defined(__WINDOWS_ASIO__)\r
141   if ( api == WINDOWS_ASIO )\r
142     rtapi_ = new RtApiAsio();\r
143 #endif\r
144 #if defined(__WINDOWS_WASAPI__)\r
145   if ( api == WINDOWS_WASAPI )\r
146     rtapi_ = new RtApiWasapi();\r
147 #endif\r
148 #if defined(__WINDOWS_DS__)\r
149   if ( api == WINDOWS_DS )\r
150     rtapi_ = new RtApiDs();\r
151 #endif\r
152 #if defined(__MACOSX_CORE__)\r
153   if ( api == MACOSX_CORE )\r
154     rtapi_ = new RtApiCore();\r
155 #endif\r
156 #if defined(__RTAUDIO_DUMMY__)\r
157   if ( api == RTAUDIO_DUMMY )\r
158     rtapi_ = new RtApiDummy();\r
159 #endif\r
160 }\r
161 \r
162 RtAudio :: RtAudio( RtAudio::Api api )\r
163 {\r
164   rtapi_ = 0;\r
165 \r
166   if ( api != UNSPECIFIED ) {\r
167     // Attempt to open the specified API.\r
168     openRtApi( api );\r
169     if ( rtapi_ ) return;\r
170 \r
171     // No compiled support for specified API value.  Issue a debug\r
172     // warning and continue as if no API was specified.\r
173     std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;\r
174   }\r
175 \r
176   // Iterate through the compiled APIs and return as soon as we find\r
177   // one with at least one device or we reach the end of the list.\r
178   std::vector< RtAudio::Api > apis;\r
179   getCompiledApi( apis );\r
180   for ( unsigned int i=0; i<apis.size(); i++ ) {\r
181     openRtApi( apis[i] );\r
182     if ( rtapi_->getDeviceCount() ) break;\r
183   }\r
184 \r
185   if ( rtapi_ ) return;\r
186 \r
187   // It should not be possible to get here because the preprocessor\r
188   // definition __RTAUDIO_DUMMY__ is automatically defined if no\r
189   // API-specific definitions are passed to the compiler. But just in\r
190   // case something weird happens, we'll thow an error.\r
191   std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
192   throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );\r
193 }\r
194 \r
195 RtAudio :: ~RtAudio() throw()\r
196 {\r
197   if ( rtapi_ )\r
198     delete rtapi_;\r
199 }\r
200 \r
201 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,\r
202                             RtAudio::StreamParameters *inputParameters,\r
203                             RtAudioFormat format, unsigned int sampleRate,\r
204                             unsigned int *bufferFrames,\r
205                             RtAudioCallback callback, void *userData,\r
206                             RtAudio::StreamOptions *options,\r
207                             RtAudioErrorCallback errorCallback )\r
208 {\r
209   return rtapi_->openStream( outputParameters, inputParameters, format,\r
210                              sampleRate, bufferFrames, callback,\r
211                              userData, options, errorCallback );\r
212 }\r
213 \r
214 // *************************************************** //\r
215 //\r
216 // Public RtApi definitions (see end of file for\r
217 // private or protected utility functions).\r
218 //\r
219 // *************************************************** //\r
220 \r
221 RtApi :: RtApi()\r
222 {\r
223   stream_.state = STREAM_CLOSED;\r
224   stream_.mode = UNINITIALIZED;\r
225   stream_.apiHandle = 0;\r
226   stream_.userBuffer[0] = 0;\r
227   stream_.userBuffer[1] = 0;\r
228   MUTEX_INITIALIZE( &stream_.mutex );\r
229   showWarnings_ = true;\r
230   firstErrorOccurred_ = false;\r
231 }\r
232 \r
233 RtApi :: ~RtApi()\r
234 {\r
235   MUTEX_DESTROY( &stream_.mutex );\r
236 }\r
237 \r
238 void RtApi :: openStream( RtAudio::StreamParameters *oParams,\r
239                           RtAudio::StreamParameters *iParams,\r
240                           RtAudioFormat format, unsigned int sampleRate,\r
241                           unsigned int *bufferFrames,\r
242                           RtAudioCallback callback, void *userData,\r
243                           RtAudio::StreamOptions *options,\r
244                           RtAudioErrorCallback errorCallback )\r
245 {\r
246   if ( stream_.state != STREAM_CLOSED ) {\r
247     errorText_ = "RtApi::openStream: a stream is already open!";\r
248     error( RtAudioError::INVALID_USE );\r
249     return;\r
250   }\r
251 \r
252   // Clear stream information potentially left from a previously open stream.\r
253   clearStreamInfo();\r
254 \r
255   if ( oParams && oParams->nChannels < 1 ) {\r
256     errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";\r
257     error( RtAudioError::INVALID_USE );\r
258     return;\r
259   }\r
260 \r
261   if ( iParams && iParams->nChannels < 1 ) {\r
262     errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";\r
263     error( RtAudioError::INVALID_USE );\r
264     return;\r
265   }\r
266 \r
267   if ( oParams == NULL && iParams == NULL ) {\r
268     errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";\r
269     error( RtAudioError::INVALID_USE );\r
270     return;\r
271   }\r
272 \r
273   if ( formatBytes(format) == 0 ) {\r
274     errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";\r
275     error( RtAudioError::INVALID_USE );\r
276     return;\r
277   }\r
278 \r
279   unsigned int nDevices = getDeviceCount();\r
280   unsigned int oChannels = 0;\r
281   if ( oParams ) {\r
282     oChannels = oParams->nChannels;\r
283     if ( oParams->deviceId >= nDevices ) {\r
284       errorText_ = "RtApi::openStream: output device parameter value is invalid.";\r
285       error( RtAudioError::INVALID_USE );\r
286       return;\r
287     }\r
288   }\r
289 \r
290   unsigned int iChannels = 0;\r
291   if ( iParams ) {\r
292     iChannels = iParams->nChannels;\r
293     if ( iParams->deviceId >= nDevices ) {\r
294       errorText_ = "RtApi::openStream: input device parameter value is invalid.";\r
295       error( RtAudioError::INVALID_USE );\r
296       return;\r
297     }\r
298   }\r
299 \r
300   bool result;\r
301 \r
302   if ( oChannels > 0 ) {\r
303 \r
304     result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,\r
305                               sampleRate, format, bufferFrames, options );\r
306     if ( result == false ) {\r
307       error( RtAudioError::SYSTEM_ERROR );\r
308       return;\r
309     }\r
310   }\r
311 \r
312   if ( iChannels > 0 ) {\r
313 \r
314     result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,\r
315                               sampleRate, format, bufferFrames, options );\r
316     if ( result == false ) {\r
317       if ( oChannels > 0 ) closeStream();\r
318       error( RtAudioError::SYSTEM_ERROR );\r
319       return;\r
320     }\r
321   }\r
322 \r
323   stream_.callbackInfo.callback = (void *) callback;\r
324   stream_.callbackInfo.userData = userData;\r
325   stream_.callbackInfo.errorCallback = (void *) errorCallback;\r
326 \r
327   if ( options ) options->numberOfBuffers = stream_.nBuffers;\r
328   stream_.state = STREAM_STOPPED;\r
329 }\r
330 \r
331 unsigned int RtApi :: getDefaultInputDevice( void )\r
332 {\r
333   // Should be implemented in subclasses if possible.\r
334   return 0;\r
335 }\r
336 \r
337 unsigned int RtApi :: getDefaultOutputDevice( void )\r
338 {\r
339   // Should be implemented in subclasses if possible.\r
340   return 0;\r
341 }\r
342 \r
343 void RtApi :: closeStream( void )\r
344 {\r
345   // MUST be implemented in subclasses!\r
346   return;\r
347 }\r
348 \r
349 bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,\r
350                                unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,\r
351                                RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,\r
352                                RtAudio::StreamOptions * /*options*/ )\r
353 {\r
354   // MUST be implemented in subclasses!\r
355   return FAILURE;\r
356 }\r
357 \r
358 void RtApi :: tickStreamTime( void )\r
359 {\r
360   // Subclasses that do not provide their own implementation of\r
361   // getStreamTime should call this function once per buffer I/O to\r
362   // provide basic stream time support.\r
363 \r
364   stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );\r
365 \r
366 #if defined( HAVE_GETTIMEOFDAY )\r
367   gettimeofday( &stream_.lastTickTimestamp, NULL );\r
368 #endif\r
369 }\r
370 \r
371 long RtApi :: getStreamLatency( void )\r
372 {\r
373   verifyStream();\r
374 \r
375   long totalLatency = 0;\r
376   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
377     totalLatency = stream_.latency[0];\r
378   if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
379     totalLatency += stream_.latency[1];\r
380 \r
381   return totalLatency;\r
382 }\r
383 \r
384 double RtApi :: getStreamTime( void )\r
385 {\r
386   verifyStream();\r
387 \r
388 #if defined( HAVE_GETTIMEOFDAY )\r
389   // Return a very accurate estimate of the stream time by\r
390   // adding in the elapsed time since the last tick.\r
391   struct timeval then;\r
392   struct timeval now;\r
393 \r
394   if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )\r
395     return stream_.streamTime;\r
396 \r
397   gettimeofday( &now, NULL );\r
398   then = stream_.lastTickTimestamp;\r
399   return stream_.streamTime +\r
400     ((now.tv_sec + 0.000001 * now.tv_usec) -\r
401      (then.tv_sec + 0.000001 * then.tv_usec));     \r
402 #else\r
403   return stream_.streamTime;\r
404 #endif\r
405 }\r
406 \r
407 unsigned int RtApi :: getStreamSampleRate( void )\r
408 {\r
409  verifyStream();\r
410 \r
411  return stream_.sampleRate;\r
412 }\r
413 \r
414 \r
415 // *************************************************** //\r
416 //\r
417 // OS/API-specific methods.\r
418 //\r
419 // *************************************************** //\r
420 \r
421 #if defined(__MACOSX_CORE__)\r
422 \r
423 // The OS X CoreAudio API is designed to use a separate callback\r
424 // procedure for each of its audio devices.  A single RtAudio duplex\r
425 // stream using two different devices is supported here, though it\r
426 // cannot be guaranteed to always behave correctly because we cannot\r
427 // synchronize these two callbacks.\r
428 //\r
429 // A property listener is installed for over/underrun information.\r
430 // However, no functionality is currently provided to allow property\r
431 // listeners to trigger user handlers because it is unclear what could\r
432 // be done if a critical stream parameter (buffer size, sample rate,\r
433 // device disconnect) notification arrived.  The listeners entail\r
434 // quite a bit of extra code and most likely, a user program wouldn't\r
435 // be prepared for the result anyway.  However, we do provide a flag\r
436 // to the client callback function to inform of an over/underrun.\r
437 \r
438 // A structure to hold various information related to the CoreAudio API\r
439 // implementation.\r
440 struct CoreHandle {\r
441   AudioDeviceID id[2];    // device ids\r
442 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
443   AudioDeviceIOProcID procId[2];\r
444 #endif\r
445   UInt32 iStream[2];      // device stream index (or first if using multiple)\r
446   UInt32 nStreams[2];     // number of streams to use\r
447   bool xrun[2];\r
448   char *deviceBuffer;\r
449   pthread_cond_t condition;\r
450   int drainCounter;       // Tracks callback counts when draining\r
451   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
452 \r
453   CoreHandle()\r
454     :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
455 };\r
456 \r
457 RtApiCore:: RtApiCore()\r
458 {\r
459 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
460   // This is a largely undocumented but absolutely necessary\r
461   // requirement starting with OS-X 10.6.  If not called, queries and\r
462   // updates to various audio device properties are not handled\r
463   // correctly.\r
464   CFRunLoopRef theRunLoop = NULL;\r
465   AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
466                                           kAudioObjectPropertyScopeGlobal,\r
467                                           kAudioObjectPropertyElementMaster };\r
468   OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
469   if ( result != noErr ) {\r
470     errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
471     error( RtAudioError::WARNING );\r
472   }\r
473 #endif\r
474 }\r
475 \r
476 RtApiCore :: ~RtApiCore()\r
477 {\r
478   // The subclass destructor gets called before the base class\r
479   // destructor, so close an existing stream before deallocating\r
480   // apiDeviceId memory.\r
481   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
482 }\r
483 \r
484 unsigned int RtApiCore :: getDeviceCount( void )\r
485 {\r
486   // Find out how many audio devices there are, if any.\r
487   UInt32 dataSize;\r
488   AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
489   OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );\r
490   if ( result != noErr ) {\r
491     errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";\r
492     error( RtAudioError::WARNING );\r
493     return 0;\r
494   }\r
495 \r
496   return dataSize / sizeof( AudioDeviceID );\r
497 }\r
498 \r
499 unsigned int RtApiCore :: getDefaultInputDevice( void )\r
500 {\r
501   unsigned int nDevices = getDeviceCount();\r
502   if ( nDevices <= 1 ) return 0;\r
503 \r
504   AudioDeviceID id;\r
505   UInt32 dataSize = sizeof( AudioDeviceID );\r
506   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
507   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
508   if ( result != noErr ) {\r
509     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";\r
510     error( RtAudioError::WARNING );\r
511     return 0;\r
512   }\r
513 \r
514   dataSize *= nDevices;\r
515   AudioDeviceID deviceList[ nDevices ];\r
516   property.mSelector = kAudioHardwarePropertyDevices;\r
517   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
518   if ( result != noErr ) {\r
519     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";\r
520     error( RtAudioError::WARNING );\r
521     return 0;\r
522   }\r
523 \r
524   for ( unsigned int i=0; i<nDevices; i++ )\r
525     if ( id == deviceList[i] ) return i;\r
526 \r
527   errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";\r
528   error( RtAudioError::WARNING );\r
529   return 0;\r
530 }\r
531 \r
532 unsigned int RtApiCore :: getDefaultOutputDevice( void )\r
533 {\r
534   unsigned int nDevices = getDeviceCount();\r
535   if ( nDevices <= 1 ) return 0;\r
536 \r
537   AudioDeviceID id;\r
538   UInt32 dataSize = sizeof( AudioDeviceID );\r
539   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
540   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
541   if ( result != noErr ) {\r
542     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";\r
543     error( RtAudioError::WARNING );\r
544     return 0;\r
545   }\r
546 \r
547   dataSize = sizeof( AudioDeviceID ) * nDevices;\r
548   AudioDeviceID deviceList[ nDevices ];\r
549   property.mSelector = kAudioHardwarePropertyDevices;\r
550   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
551   if ( result != noErr ) {\r
552     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";\r
553     error( RtAudioError::WARNING );\r
554     return 0;\r
555   }\r
556 \r
557   for ( unsigned int i=0; i<nDevices; i++ )\r
558     if ( id == deviceList[i] ) return i;\r
559 \r
560   errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";\r
561   error( RtAudioError::WARNING );\r
562   return 0;\r
563 }\r
564 \r
565 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )\r
566 {\r
567   RtAudio::DeviceInfo info;\r
568   info.probed = false;\r
569 \r
570   // Get device ID\r
571   unsigned int nDevices = getDeviceCount();\r
572   if ( nDevices == 0 ) {\r
573     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
574     error( RtAudioError::INVALID_USE );\r
575     return info;\r
576   }\r
577 \r
578   if ( device >= nDevices ) {\r
579     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
580     error( RtAudioError::INVALID_USE );\r
581     return info;\r
582   }\r
583 \r
584   AudioDeviceID deviceList[ nDevices ];\r
585   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
586   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
587                                           kAudioObjectPropertyScopeGlobal,\r
588                                           kAudioObjectPropertyElementMaster };\r
589   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
590                                                 0, NULL, &dataSize, (void *) &deviceList );\r
591   if ( result != noErr ) {\r
592     errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";\r
593     error( RtAudioError::WARNING );\r
594     return info;\r
595   }\r
596 \r
597   AudioDeviceID id = deviceList[ device ];\r
598 \r
599   // Get the device name.\r
600   info.name.erase();\r
601   CFStringRef cfname;\r
602   dataSize = sizeof( CFStringRef );\r
603   property.mSelector = kAudioObjectPropertyManufacturer;\r
604   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
605   if ( result != noErr ) {\r
606     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";\r
607     errorText_ = errorStream_.str();\r
608     error( RtAudioError::WARNING );\r
609     return info;\r
610   }\r
611 \r
612   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
613   int length = CFStringGetLength(cfname);\r
614   char *mname = (char *)malloc(length * 3 + 1);\r
615 #if defined( UNICODE ) || defined( _UNICODE )\r
616   CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
617 #else\r
618   CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
619 #endif\r
620   info.name.append( (const char *)mname, strlen(mname) );\r
621   info.name.append( ": " );\r
622   CFRelease( cfname );\r
623   free(mname);\r
624 \r
625   property.mSelector = kAudioObjectPropertyName;\r
626   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
627   if ( result != noErr ) {\r
628     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";\r
629     errorText_ = errorStream_.str();\r
630     error( RtAudioError::WARNING );\r
631     return info;\r
632   }\r
633 \r
634   //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
635   length = CFStringGetLength(cfname);\r
636   char *name = (char *)malloc(length * 3 + 1);\r
637 #if defined( UNICODE ) || defined( _UNICODE )\r
638   CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
639 #else\r
640   CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
641 #endif\r
642   info.name.append( (const char *)name, strlen(name) );\r
643   CFRelease( cfname );\r
644   free(name);\r
645 \r
646   // Get the output stream "configuration".\r
647   AudioBufferList       *bufferList = nil;\r
648   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
649   property.mScope = kAudioDevicePropertyScopeOutput;\r
650   //  property.mElement = kAudioObjectPropertyElementWildcard;\r
651   dataSize = 0;\r
652   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
653   if ( result != noErr || dataSize == 0 ) {\r
654     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";\r
655     errorText_ = errorStream_.str();\r
656     error( RtAudioError::WARNING );\r
657     return info;\r
658   }\r
659 \r
660   // Allocate the AudioBufferList.\r
661   bufferList = (AudioBufferList *) malloc( dataSize );\r
662   if ( bufferList == NULL ) {\r
663     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";\r
664     error( RtAudioError::WARNING );\r
665     return info;\r
666   }\r
667 \r
668   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
669   if ( result != noErr || dataSize == 0 ) {\r
670     free( bufferList );\r
671     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";\r
672     errorText_ = errorStream_.str();\r
673     error( RtAudioError::WARNING );\r
674     return info;\r
675   }\r
676 \r
677   // Get output channel information.\r
678   unsigned int i, nStreams = bufferList->mNumberBuffers;\r
679   for ( i=0; i<nStreams; i++ )\r
680     info.outputChannels += bufferList->mBuffers[i].mNumberChannels;\r
681   free( bufferList );\r
682 \r
683   // Get the input stream "configuration".\r
684   property.mScope = kAudioDevicePropertyScopeInput;\r
685   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
686   if ( result != noErr || dataSize == 0 ) {\r
687     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";\r
688     errorText_ = errorStream_.str();\r
689     error( RtAudioError::WARNING );\r
690     return info;\r
691   }\r
692 \r
693   // Allocate the AudioBufferList.\r
694   bufferList = (AudioBufferList *) malloc( dataSize );\r
695   if ( bufferList == NULL ) {\r
696     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";\r
697     error( RtAudioError::WARNING );\r
698     return info;\r
699   }\r
700 \r
701   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
702   if (result != noErr || dataSize == 0) {\r
703     free( bufferList );\r
704     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";\r
705     errorText_ = errorStream_.str();\r
706     error( RtAudioError::WARNING );\r
707     return info;\r
708   }\r
709 \r
710   // Get input channel information.\r
711   nStreams = bufferList->mNumberBuffers;\r
712   for ( i=0; i<nStreams; i++ )\r
713     info.inputChannels += bufferList->mBuffers[i].mNumberChannels;\r
714   free( bufferList );\r
715 \r
716   // If device opens for both playback and capture, we determine the channels.\r
717   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
718     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
719 \r
720   // Probe the device sample rates.\r
721   bool isInput = false;\r
722   if ( info.outputChannels == 0 ) isInput = true;\r
723 \r
724   // Determine the supported sample rates.\r
725   property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\r
726   if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;\r
727   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
728   if ( result != kAudioHardwareNoError || dataSize == 0 ) {\r
729     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";\r
730     errorText_ = errorStream_.str();\r
731     error( RtAudioError::WARNING );\r
732     return info;\r
733   }\r
734 \r
735   UInt32 nRanges = dataSize / sizeof( AudioValueRange );\r
736   AudioValueRange rangeList[ nRanges ];\r
737   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );\r
738   if ( result != kAudioHardwareNoError ) {\r
739     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";\r
740     errorText_ = errorStream_.str();\r
741     error( RtAudioError::WARNING );\r
742     return info;\r
743   }\r
744 \r
745   // The sample rate reporting mechanism is a bit of a mystery.  It\r
746   // seems that it can either return individual rates or a range of\r
747   // rates.  I assume that if the min / max range values are the same,\r
748   // then that represents a single supported rate and if the min / max\r
749   // range values are different, the device supports an arbitrary\r
750   // range of values (though there might be multiple ranges, so we'll\r
751   // use the most conservative range).\r
752   Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
753   bool haveValueRange = false;\r
754   info.sampleRates.clear();\r
755   for ( UInt32 i=0; i<nRanges; i++ ) {\r
756     if ( rangeList[i].mMinimum == rangeList[i].mMaximum )\r
757       info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );\r
758     else {\r
759       haveValueRange = true;\r
760       if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
761       if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
762     }\r
763   }\r
764 \r
765   if ( haveValueRange ) {\r
766     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
767       if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )\r
768         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
769     }\r
770   }\r
771 \r
772   // Sort and remove any redundant values\r
773   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
774   info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
775 \r
776   if ( info.sampleRates.size() == 0 ) {\r
777     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
778     errorText_ = errorStream_.str();\r
779     error( RtAudioError::WARNING );\r
780     return info;\r
781   }\r
782 \r
783   // CoreAudio always uses 32-bit floating point data for PCM streams.\r
784   // Thus, any other "physical" formats supported by the device are of\r
785   // no interest to the client.\r
786   info.nativeFormats = RTAUDIO_FLOAT32;\r
787 \r
788   if ( info.outputChannels > 0 )\r
789     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
790   if ( info.inputChannels > 0 )\r
791     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
792 \r
793   info.probed = true;\r
794   return info;\r
795 }\r
796 \r
797 static OSStatus callbackHandler( AudioDeviceID inDevice,\r
798                                  const AudioTimeStamp* /*inNow*/,\r
799                                  const AudioBufferList* inInputData,\r
800                                  const AudioTimeStamp* /*inInputTime*/,\r
801                                  AudioBufferList* outOutputData,\r
802                                  const AudioTimeStamp* /*inOutputTime*/,\r
803                                  void* infoPointer )\r
804 {\r
805   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
806 \r
807   RtApiCore *object = (RtApiCore *) info->object;\r
808   if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )\r
809     return kAudioHardwareUnspecifiedError;\r
810   else\r
811     return kAudioHardwareNoError;\r
812 }\r
813 \r
814 static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
815                               UInt32 nAddresses,\r
816                               const AudioObjectPropertyAddress properties[],\r
817                               void* handlePointer )\r
818 {\r
819   CoreHandle *handle = (CoreHandle *) handlePointer;\r
820   for ( UInt32 i=0; i<nAddresses; i++ ) {\r
821     if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {\r
822       if ( properties[i].mScope == kAudioDevicePropertyScopeInput )\r
823         handle->xrun[1] = true;\r
824       else\r
825         handle->xrun[0] = true;\r
826     }\r
827   }\r
828 \r
829   return kAudioHardwareNoError;\r
830 }\r
831 \r
832 static OSStatus rateListener( AudioObjectID inDevice,\r
833                               UInt32 /*nAddresses*/,\r
834                               const AudioObjectPropertyAddress /*properties*/[],\r
835                               void* ratePointer )\r
836 {\r
837   Float64 *rate = (Float64 *) ratePointer;\r
838   UInt32 dataSize = sizeof( Float64 );\r
839   AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
840                                           kAudioObjectPropertyScopeGlobal,\r
841                                           kAudioObjectPropertyElementMaster };\r
842   AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
843   return kAudioHardwareNoError;\r
844 }\r
845 \r
846 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
847                                    unsigned int firstChannel, unsigned int sampleRate,\r
848                                    RtAudioFormat format, unsigned int *bufferSize,\r
849                                    RtAudio::StreamOptions *options )\r
850 {\r
851   // Get device ID\r
852   unsigned int nDevices = getDeviceCount();\r
853   if ( nDevices == 0 ) {\r
854     // This should not happen because a check is made before this function is called.\r
855     errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";\r
856     return FAILURE;\r
857   }\r
858 \r
859   if ( device >= nDevices ) {\r
860     // This should not happen because a check is made before this function is called.\r
861     errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";\r
862     return FAILURE;\r
863   }\r
864 \r
865   AudioDeviceID deviceList[ nDevices ];\r
866   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
867   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
868                                           kAudioObjectPropertyScopeGlobal,\r
869                                           kAudioObjectPropertyElementMaster };\r
870   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
871                                                 0, NULL, &dataSize, (void *) &deviceList );\r
872   if ( result != noErr ) {\r
873     errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";\r
874     return FAILURE;\r
875   }\r
876 \r
877   AudioDeviceID id = deviceList[ device ];\r
878 \r
879   // Setup for stream mode.\r
880   bool isInput = false;\r
881   if ( mode == INPUT ) {\r
882     isInput = true;\r
883     property.mScope = kAudioDevicePropertyScopeInput;\r
884   }\r
885   else\r
886     property.mScope = kAudioDevicePropertyScopeOutput;\r
887 \r
888   // Get the stream "configuration".\r
889   AudioBufferList       *bufferList = nil;\r
890   dataSize = 0;\r
891   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
892   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
893   if ( result != noErr || dataSize == 0 ) {\r
894     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";\r
895     errorText_ = errorStream_.str();\r
896     return FAILURE;\r
897   }\r
898 \r
899   // Allocate the AudioBufferList.\r
900   bufferList = (AudioBufferList *) malloc( dataSize );\r
901   if ( bufferList == NULL ) {\r
902     errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";\r
903     return FAILURE;\r
904   }\r
905 \r
906   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
907   if (result != noErr || dataSize == 0) {\r
908     free( bufferList );\r
909     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
910     errorText_ = errorStream_.str();\r
911     return FAILURE;\r
912   }\r
913 \r
914   // Search for one or more streams that contain the desired number of\r
915   // channels. CoreAudio devices can have an arbitrary number of\r
916   // streams and each stream can have an arbitrary number of channels.\r
917   // For each stream, a single buffer of interleaved samples is\r
918   // provided.  RtAudio prefers the use of one stream of interleaved\r
919   // data or multiple consecutive single-channel streams.  However, we\r
920   // now support multiple consecutive multi-channel streams of\r
921   // interleaved data as well.\r
922   UInt32 iStream, offsetCounter = firstChannel;\r
923   UInt32 nStreams = bufferList->mNumberBuffers;\r
924   bool monoMode = false;\r
925   bool foundStream = false;\r
926 \r
927   // First check that the device supports the requested number of\r
928   // channels.\r
929   UInt32 deviceChannels = 0;\r
930   for ( iStream=0; iStream<nStreams; iStream++ )\r
931     deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;\r
932 \r
933   if ( deviceChannels < ( channels + firstChannel ) ) {\r
934     free( bufferList );\r
935     errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";\r
936     errorText_ = errorStream_.str();\r
937     return FAILURE;\r
938   }\r
939 \r
940   // Look for a single stream meeting our needs.\r
941   UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;\r
942   for ( iStream=0; iStream<nStreams; iStream++ ) {\r
943     streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
944     if ( streamChannels >= channels + offsetCounter ) {\r
945       firstStream = iStream;\r
946       channelOffset = offsetCounter;\r
947       foundStream = true;\r
948       break;\r
949     }\r
950     if ( streamChannels > offsetCounter ) break;\r
951     offsetCounter -= streamChannels;\r
952   }\r
953 \r
954   // If we didn't find a single stream above, then we should be able\r
955   // to meet the channel specification with multiple streams.\r
956   if ( foundStream == false ) {\r
957     monoMode = true;\r
958     offsetCounter = firstChannel;\r
959     for ( iStream=0; iStream<nStreams; iStream++ ) {\r
960       streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
961       if ( streamChannels > offsetCounter ) break;\r
962       offsetCounter -= streamChannels;\r
963     }\r
964 \r
965     firstStream = iStream;\r
966     channelOffset = offsetCounter;\r
967     Int32 channelCounter = channels + offsetCounter - streamChannels;\r
968 \r
969     if ( streamChannels > 1 ) monoMode = false;\r
970     while ( channelCounter > 0 ) {\r
971       streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;\r
972       if ( streamChannels > 1 ) monoMode = false;\r
973       channelCounter -= streamChannels;\r
974       streamCount++;\r
975     }\r
976   }\r
977 \r
978   free( bufferList );\r
979 \r
980   // Determine the buffer size.\r
981   AudioValueRange       bufferRange;\r
982   dataSize = sizeof( AudioValueRange );\r
983   property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\r
984   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );\r
985 \r
986   if ( result != noErr ) {\r
987     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";\r
988     errorText_ = errorStream_.str();\r
989     return FAILURE;\r
990   }\r
991 \r
992   if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
993   else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;\r
994   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
995 \r
996   // Set the buffer size.  For multiple streams, I'm assuming we only\r
997   // need to make this setting for the master channel.\r
998   UInt32 theSize = (UInt32) *bufferSize;\r
999   dataSize = sizeof( UInt32 );\r
1000   property.mSelector = kAudioDevicePropertyBufferFrameSize;\r
1001   result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );\r
1002 \r
1003   if ( result != noErr ) {\r
1004     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";\r
1005     errorText_ = errorStream_.str();\r
1006     return FAILURE;\r
1007   }\r
1008 \r
1009   // If attempting to setup a duplex stream, the bufferSize parameter\r
1010   // MUST be the same in both directions!\r
1011   *bufferSize = theSize;\r
1012   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
1013     errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";\r
1014     errorText_ = errorStream_.str();\r
1015     return FAILURE;\r
1016   }\r
1017 \r
1018   stream_.bufferSize = *bufferSize;\r
1019   stream_.nBuffers = 1;\r
1020 \r
1021   // Try to set "hog" mode ... it's not clear to me this is working.\r
1022   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
1023     pid_t hog_pid;\r
1024     dataSize = sizeof( hog_pid );\r
1025     property.mSelector = kAudioDevicePropertyHogMode;\r
1026     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );\r
1027     if ( result != noErr ) {\r
1028       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";\r
1029       errorText_ = errorStream_.str();\r
1030       return FAILURE;\r
1031     }\r
1032 \r
1033     if ( hog_pid != getpid() ) {\r
1034       hog_pid = getpid();\r
1035       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );\r
1036       if ( result != noErr ) {\r
1037         errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";\r
1038         errorText_ = errorStream_.str();\r
1039         return FAILURE;\r
1040       }\r
1041     }\r
1042   }\r
1043 \r
1044   // Check and if necessary, change the sample rate for the device.\r
1045   Float64 nominalRate;\r
1046   dataSize = sizeof( Float64 );\r
1047   property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
1048   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
1049   if ( result != noErr ) {\r
1050     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
1051     errorText_ = errorStream_.str();\r
1052     return FAILURE;\r
1053   }\r
1054 \r
1055   // Only change the sample rate if off by more than 1 Hz.\r
1056   if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
1057 \r
1058     // Set a property listener for the sample rate change\r
1059     Float64 reportedRate = 0.0;\r
1060     AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
1061     result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1062     if ( result != noErr ) {\r
1063       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
1064       errorText_ = errorStream_.str();\r
1065       return FAILURE;\r
1066     }\r
1067 \r
1068     nominalRate = (Float64) sampleRate;\r
1069     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
1070     if ( result != noErr ) {\r
1071       AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1072       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
1073       errorText_ = errorStream_.str();\r
1074       return FAILURE;\r
1075     }\r
1076 \r
1077     // Now wait until the reported nominal rate is what we just set.\r
1078     UInt32 microCounter = 0;\r
1079     while ( reportedRate != nominalRate ) {\r
1080       microCounter += 5000;\r
1081       if ( microCounter > 5000000 ) break;\r
1082       usleep( 5000 );\r
1083     }\r
1084 \r
1085     // Remove the property listener.\r
1086     AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1087 \r
1088     if ( microCounter > 5000000 ) {\r
1089       errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
1090       errorText_ = errorStream_.str();\r
1091       return FAILURE;\r
1092     }\r
1093   }\r
1094 \r
1095   // Now set the stream format for all streams.  Also, check the\r
1096   // physical format of the device and change that if necessary.\r
1097   AudioStreamBasicDescription   description;\r
1098   dataSize = sizeof( AudioStreamBasicDescription );\r
1099   property.mSelector = kAudioStreamPropertyVirtualFormat;\r
1100   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
1101   if ( result != noErr ) {\r
1102     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
1103     errorText_ = errorStream_.str();\r
1104     return FAILURE;\r
1105   }\r
1106 \r
1107   // Set the sample rate and data format id.  However, only make the\r
1108   // change if the sample rate is not within 1.0 of the desired\r
1109   // rate and the format is not linear pcm.\r
1110   bool updateFormat = false;\r
1111   if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
1112     description.mSampleRate = (Float64) sampleRate;\r
1113     updateFormat = true;\r
1114   }\r
1115 \r
1116   if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
1117     description.mFormatID = kAudioFormatLinearPCM;\r
1118     updateFormat = true;\r
1119   }\r
1120 \r
1121   if ( updateFormat ) {\r
1122     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
1123     if ( result != noErr ) {\r
1124       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
1125       errorText_ = errorStream_.str();\r
1126       return FAILURE;\r
1127     }\r
1128   }\r
1129 \r
1130   // Now check the physical format.\r
1131   property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
1132   result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );\r
1133   if ( result != noErr ) {\r
1134     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
1135     errorText_ = errorStream_.str();\r
1136     return FAILURE;\r
1137   }\r
1138 \r
1139   //std::cout << "Current physical stream format:" << std::endl;\r
1140   //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
1141   //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1142   //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
1143   //std::cout << "   sample rate = " << description.mSampleRate << std::endl;\r
1144 \r
1145   if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
1146     description.mFormatID = kAudioFormatLinearPCM;\r
1147     //description.mSampleRate = (Float64) sampleRate;\r
1148     AudioStreamBasicDescription testDescription = description;\r
1149     UInt32 formatFlags;\r
1150 \r
1151     // We'll try higher bit rates first and then work our way down.\r
1152     std::vector< std::pair<UInt32, UInt32>  > physicalFormats;\r
1153     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;\r
1154     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1155     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1156     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1157     physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed\r
1158     formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
1159     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
1160     formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
1161     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
1162     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1163     physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
1164     physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
1165 \r
1166     bool setPhysicalFormat = false;\r
1167     for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
1168       testDescription = description;\r
1169       testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
1170       testDescription.mFormatFlags = physicalFormats[i].second;\r
1171       if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
1172         testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;\r
1173       else\r
1174         testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
1175       testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
1176       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
1177       if ( result == noErr ) {\r
1178         setPhysicalFormat = true;\r
1179         //std::cout << "Updated physical stream format:" << std::endl;\r
1180         //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
1181         //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1182         //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
1183         //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;\r
1184         break;\r
1185       }\r
1186     }\r
1187 \r
1188     if ( !setPhysicalFormat ) {\r
1189       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
1190       errorText_ = errorStream_.str();\r
1191       return FAILURE;\r
1192     }\r
1193   } // done setting virtual/physical formats.\r
1194 \r
1195   // Get the stream / device latency.\r
1196   UInt32 latency;\r
1197   dataSize = sizeof( UInt32 );\r
1198   property.mSelector = kAudioDevicePropertyLatency;\r
1199   if ( AudioObjectHasProperty( id, &property ) == true ) {\r
1200     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );\r
1201     if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;\r
1202     else {\r
1203       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";\r
1204       errorText_ = errorStream_.str();\r
1205       error( RtAudioError::WARNING );\r
1206     }\r
1207   }\r
1208 \r
1209   // Byte-swapping: According to AudioHardware.h, the stream data will\r
1210   // always be presented in native-endian format, so we should never\r
1211   // need to byte swap.\r
1212   stream_.doByteSwap[mode] = false;\r
1213 \r
1214   // From the CoreAudio documentation, PCM data must be supplied as\r
1215   // 32-bit floats.\r
1216   stream_.userFormat = format;\r
1217   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
1218 \r
1219   if ( streamCount == 1 )\r
1220     stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;\r
1221   else // multiple streams\r
1222     stream_.nDeviceChannels[mode] = channels;\r
1223   stream_.nUserChannels[mode] = channels;\r
1224   stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream\r
1225   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
1226   else stream_.userInterleaved = true;\r
1227   stream_.deviceInterleaved[mode] = true;\r
1228   if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;\r
1229 \r
1230   // Set flags for buffer conversion.\r
1231   stream_.doConvertBuffer[mode] = false;\r
1232   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
1233     stream_.doConvertBuffer[mode] = true;\r
1234   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
1235     stream_.doConvertBuffer[mode] = true;\r
1236   if ( streamCount == 1 ) {\r
1237     if ( stream_.nUserChannels[mode] > 1 &&\r
1238          stream_.userInterleaved != stream_.deviceInterleaved[mode] )\r
1239       stream_.doConvertBuffer[mode] = true;\r
1240   }\r
1241   else if ( monoMode && stream_.userInterleaved )\r
1242     stream_.doConvertBuffer[mode] = true;\r
1243 \r
1244   // Allocate our CoreHandle structure for the stream.\r
1245   CoreHandle *handle = 0;\r
1246   if ( stream_.apiHandle == 0 ) {\r
1247     try {\r
1248       handle = new CoreHandle;\r
1249     }\r
1250     catch ( std::bad_alloc& ) {\r
1251       errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";\r
1252       goto error;\r
1253     }\r
1254 \r
1255     if ( pthread_cond_init( &handle->condition, NULL ) ) {\r
1256       errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";\r
1257       goto error;\r
1258     }\r
1259     stream_.apiHandle = (void *) handle;\r
1260   }\r
1261   else\r
1262     handle = (CoreHandle *) stream_.apiHandle;\r
1263   handle->iStream[mode] = firstStream;\r
1264   handle->nStreams[mode] = streamCount;\r
1265   handle->id[mode] = id;\r
1266 \r
1267   // Allocate necessary internal buffers.\r
1268   unsigned long bufferBytes;\r
1269   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
1270   //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
1271   stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
1272   memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
1273   if ( stream_.userBuffer[mode] == NULL ) {\r
1274     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
1275     goto error;\r
1276   }\r
1277 \r
1278   // If possible, we will make use of the CoreAudio stream buffers as\r
1279   // "device buffers".  However, we can't do this if using multiple\r
1280   // streams.\r
1281   if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {\r
1282 \r
1283     bool makeBuffer = true;\r
1284     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
1285     if ( mode == INPUT ) {\r
1286       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
1287         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
1288         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
1289       }\r
1290     }\r
1291 \r
1292     if ( makeBuffer ) {\r
1293       bufferBytes *= *bufferSize;\r
1294       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
1295       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
1296       if ( stream_.deviceBuffer == NULL ) {\r
1297         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";\r
1298         goto error;\r
1299       }\r
1300     }\r
1301   }\r
1302 \r
1303   stream_.sampleRate = sampleRate;\r
1304   stream_.device[mode] = device;\r
1305   stream_.state = STREAM_STOPPED;\r
1306   stream_.callbackInfo.object = (void *) this;\r
1307 \r
1308   // Setup the buffer conversion information structure.\r
1309   if ( stream_.doConvertBuffer[mode] ) {\r
1310     if ( streamCount > 1 ) setConvertInfo( mode, 0 );\r
1311     else setConvertInfo( mode, channelOffset );\r
1312   }\r
1313 \r
1314   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )\r
1315     // Only one callback procedure per device.\r
1316     stream_.mode = DUPLEX;\r
1317   else {\r
1318 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1319     result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );\r
1320 #else\r
1321     // deprecated in favor of AudioDeviceCreateIOProcID()\r
1322     result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );\r
1323 #endif\r
1324     if ( result != noErr ) {\r
1325       errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";\r
1326       errorText_ = errorStream_.str();\r
1327       goto error;\r
1328     }\r
1329     if ( stream_.mode == OUTPUT && mode == INPUT )\r
1330       stream_.mode = DUPLEX;\r
1331     else\r
1332       stream_.mode = mode;\r
1333   }\r
1334 \r
1335   // Setup the device property listener for over/underload.\r
1336   property.mSelector = kAudioDeviceProcessorOverload;\r
1337   property.mScope = kAudioObjectPropertyScopeGlobal;\r
1338   result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
1339 \r
1340   return SUCCESS;\r
1341 \r
1342  error:\r
1343   if ( handle ) {\r
1344     pthread_cond_destroy( &handle->condition );\r
1345     delete handle;\r
1346     stream_.apiHandle = 0;\r
1347   }\r
1348 \r
1349   for ( int i=0; i<2; i++ ) {\r
1350     if ( stream_.userBuffer[i] ) {\r
1351       free( stream_.userBuffer[i] );\r
1352       stream_.userBuffer[i] = 0;\r
1353     }\r
1354   }\r
1355 \r
1356   if ( stream_.deviceBuffer ) {\r
1357     free( stream_.deviceBuffer );\r
1358     stream_.deviceBuffer = 0;\r
1359   }\r
1360 \r
1361   stream_.state = STREAM_CLOSED;\r
1362   return FAILURE;\r
1363 }\r
1364 \r
1365 void RtApiCore :: closeStream( void )\r
1366 {\r
1367   if ( stream_.state == STREAM_CLOSED ) {\r
1368     errorText_ = "RtApiCore::closeStream(): no open stream to close!";\r
1369     error( RtAudioError::WARNING );\r
1370     return;\r
1371   }\r
1372 \r
1373   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1374   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1375     if ( stream_.state == STREAM_RUNNING )\r
1376       AudioDeviceStop( handle->id[0], callbackHandler );\r
1377 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1378     AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );\r
1379 #else\r
1380     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1381     AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );\r
1382 #endif\r
1383   }\r
1384 \r
1385   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1386     if ( stream_.state == STREAM_RUNNING )\r
1387       AudioDeviceStop( handle->id[1], callbackHandler );\r
1388 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1389     AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );\r
1390 #else\r
1391     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1392     AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );\r
1393 #endif\r
1394   }\r
1395 \r
1396   for ( int i=0; i<2; i++ ) {\r
1397     if ( stream_.userBuffer[i] ) {\r
1398       free( stream_.userBuffer[i] );\r
1399       stream_.userBuffer[i] = 0;\r
1400     }\r
1401   }\r
1402 \r
1403   if ( stream_.deviceBuffer ) {\r
1404     free( stream_.deviceBuffer );\r
1405     stream_.deviceBuffer = 0;\r
1406   }\r
1407 \r
1408   // Destroy pthread condition variable.\r
1409   pthread_cond_destroy( &handle->condition );\r
1410   delete handle;\r
1411   stream_.apiHandle = 0;\r
1412 \r
1413   stream_.mode = UNINITIALIZED;\r
1414   stream_.state = STREAM_CLOSED;\r
1415 }\r
1416 \r
1417 void RtApiCore :: startStream( void )\r
1418 {\r
1419   verifyStream();\r
1420   if ( stream_.state == STREAM_RUNNING ) {\r
1421     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
1422     error( RtAudioError::WARNING );\r
1423     return;\r
1424   }\r
1425 \r
1426   OSStatus result = noErr;\r
1427   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1428   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1429 \r
1430     result = AudioDeviceStart( handle->id[0], callbackHandler );\r
1431     if ( result != noErr ) {\r
1432       errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";\r
1433       errorText_ = errorStream_.str();\r
1434       goto unlock;\r
1435     }\r
1436   }\r
1437 \r
1438   if ( stream_.mode == INPUT ||\r
1439        ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1440 \r
1441     result = AudioDeviceStart( handle->id[1], callbackHandler );\r
1442     if ( result != noErr ) {\r
1443       errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";\r
1444       errorText_ = errorStream_.str();\r
1445       goto unlock;\r
1446     }\r
1447   }\r
1448 \r
1449   handle->drainCounter = 0;\r
1450   handle->internalDrain = false;\r
1451   stream_.state = STREAM_RUNNING;\r
1452 \r
1453  unlock:\r
1454   if ( result == noErr ) return;\r
1455   error( RtAudioError::SYSTEM_ERROR );\r
1456 }\r
1457 \r
1458 void RtApiCore :: stopStream( void )\r
1459 {\r
1460   verifyStream();\r
1461   if ( stream_.state == STREAM_STOPPED ) {\r
1462     errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";\r
1463     error( RtAudioError::WARNING );\r
1464     return;\r
1465   }\r
1466 \r
1467   OSStatus result = noErr;\r
1468   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1469   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1470 \r
1471     if ( handle->drainCounter == 0 ) {\r
1472       handle->drainCounter = 2;\r
1473       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
1474     }\r
1475 \r
1476     result = AudioDeviceStop( handle->id[0], callbackHandler );\r
1477     if ( result != noErr ) {\r
1478       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
1479       errorText_ = errorStream_.str();\r
1480       goto unlock;\r
1481     }\r
1482   }\r
1483 \r
1484   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1485 \r
1486     result = AudioDeviceStop( handle->id[1], callbackHandler );\r
1487     if ( result != noErr ) {\r
1488       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
1489       errorText_ = errorStream_.str();\r
1490       goto unlock;\r
1491     }\r
1492   }\r
1493 \r
1494   stream_.state = STREAM_STOPPED;\r
1495 \r
1496  unlock:\r
1497   if ( result == noErr ) return;\r
1498   error( RtAudioError::SYSTEM_ERROR );\r
1499 }\r
1500 \r
1501 void RtApiCore :: abortStream( void )\r
1502 {\r
1503   verifyStream();\r
1504   if ( stream_.state == STREAM_STOPPED ) {\r
1505     errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";\r
1506     error( RtAudioError::WARNING );\r
1507     return;\r
1508   }\r
1509 \r
1510   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1511   handle->drainCounter = 2;\r
1512 \r
1513   stopStream();\r
1514 }\r
1515 \r
1516 // This function will be called by a spawned thread when the user\r
1517 // callback function signals that the stream should be stopped or\r
1518 // aborted.  It is better to handle it this way because the\r
1519 // callbackEvent() function probably should return before the AudioDeviceStop()\r
1520 // function is called.\r
1521 static void *coreStopStream( void *ptr )\r
1522 {\r
1523   CallbackInfo *info = (CallbackInfo *) ptr;\r
1524   RtApiCore *object = (RtApiCore *) info->object;\r
1525 \r
1526   object->stopStream();\r
1527   pthread_exit( NULL );\r
1528 }\r
1529 \r
1530 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,\r
1531                                  const AudioBufferList *inBufferList,\r
1532                                  const AudioBufferList *outBufferList )\r
1533 {\r
1534   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
1535   if ( stream_.state == STREAM_CLOSED ) {\r
1536     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
1537     error( RtAudioError::WARNING );\r
1538     return FAILURE;\r
1539   }\r
1540 \r
1541   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
1542   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1543 \r
1544   // Check if we were draining the stream and signal is finished.\r
1545   if ( handle->drainCounter > 3 ) {\r
1546     ThreadHandle threadId;\r
1547 \r
1548     stream_.state = STREAM_STOPPING;\r
1549     if ( handle->internalDrain == true )\r
1550       pthread_create( &threadId, NULL, coreStopStream, info );\r
1551     else // external call to stopStream()\r
1552       pthread_cond_signal( &handle->condition );\r
1553     return SUCCESS;\r
1554   }\r
1555 \r
1556   AudioDeviceID outputDevice = handle->id[0];\r
1557 \r
1558   // Invoke user callback to get fresh output data UNLESS we are\r
1559   // draining stream or duplex mode AND the input/output devices are\r
1560   // different AND this function is called for the input device.\r
1561   if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {\r
1562     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
1563     double streamTime = getStreamTime();\r
1564     RtAudioStreamStatus status = 0;\r
1565     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
1566       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
1567       handle->xrun[0] = false;\r
1568     }\r
1569     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
1570       status |= RTAUDIO_INPUT_OVERFLOW;\r
1571       handle->xrun[1] = false;\r
1572     }\r
1573 \r
1574     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
1575                                   stream_.bufferSize, streamTime, status, info->userData );\r
1576     if ( cbReturnValue == 2 ) {\r
1577       stream_.state = STREAM_STOPPING;\r
1578       handle->drainCounter = 2;\r
1579       abortStream();\r
1580       return SUCCESS;\r
1581     }\r
1582     else if ( cbReturnValue == 1 ) {\r
1583       handle->drainCounter = 1;\r
1584       handle->internalDrain = true;\r
1585     }\r
1586   }\r
1587 \r
1588   if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {\r
1589 \r
1590     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
1591 \r
1592       if ( handle->nStreams[0] == 1 ) {\r
1593         memset( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1594                 0,\r
1595                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1596       }\r
1597       else { // fill multiple streams with zeros\r
1598         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1599           memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1600                   0,\r
1601                   outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );\r
1602         }\r
1603       }\r
1604     }\r
1605     else if ( handle->nStreams[0] == 1 ) {\r
1606       if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer\r
1607         convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,\r
1608                        stream_.userBuffer[0], stream_.convertInfo[0] );\r
1609       }\r
1610       else { // copy from user buffer\r
1611         memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1612                 stream_.userBuffer[0],\r
1613                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1614       }\r
1615     }\r
1616     else { // fill multiple streams\r
1617       Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];\r
1618       if ( stream_.doConvertBuffer[0] ) {\r
1619         convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
1620         inBuffer = (Float32 *) stream_.deviceBuffer;\r
1621       }\r
1622 \r
1623       if ( stream_.deviceInterleaved[0] == false ) { // mono mode\r
1624         UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;\r
1625         for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
1626           memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1627                   (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );\r
1628         }\r
1629       }\r
1630       else { // fill multiple multi-channel streams with interleaved data\r
1631         UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;\r
1632         Float32 *out, *in;\r
1633 \r
1634         bool inInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1635         UInt32 inChannels = stream_.nUserChannels[0];\r
1636         if ( stream_.doConvertBuffer[0] ) {\r
1637           inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1638           inChannels = stream_.nDeviceChannels[0];\r
1639         }\r
1640 \r
1641         if ( inInterleaved ) inOffset = 1;\r
1642         else inOffset = stream_.bufferSize;\r
1643 \r
1644         channelsLeft = inChannels;\r
1645         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1646           in = inBuffer;\r
1647           out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;\r
1648           streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;\r
1649 \r
1650           outJump = 0;\r
1651           // Account for possible channel offset in first stream\r
1652           if ( i == 0 && stream_.channelOffset[0] > 0 ) {\r
1653             streamChannels -= stream_.channelOffset[0];\r
1654             outJump = stream_.channelOffset[0];\r
1655             out += outJump;\r
1656           }\r
1657 \r
1658           // Account for possible unfilled channels at end of the last stream\r
1659           if ( streamChannels > channelsLeft ) {\r
1660             outJump = streamChannels - channelsLeft;\r
1661             streamChannels = channelsLeft;\r
1662           }\r
1663 \r
1664           // Determine input buffer offsets and skips\r
1665           if ( inInterleaved ) {\r
1666             inJump = inChannels;\r
1667             in += inChannels - channelsLeft;\r
1668           }\r
1669           else {\r
1670             inJump = 1;\r
1671             in += (inChannels - channelsLeft) * inOffset;\r
1672           }\r
1673 \r
1674           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1675             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1676               *out++ = in[j*inOffset];\r
1677             }\r
1678             out += outJump;\r
1679             in += inJump;\r
1680           }\r
1681           channelsLeft -= streamChannels;\r
1682         }\r
1683       }\r
1684     }\r
1685 \r
1686     if ( handle->drainCounter ) {\r
1687       handle->drainCounter++;\r
1688       goto unlock;\r
1689     }\r
1690   }\r
1691 \r
1692   AudioDeviceID inputDevice;\r
1693   inputDevice = handle->id[1];\r
1694   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {\r
1695 \r
1696     if ( handle->nStreams[1] == 1 ) {\r
1697       if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer\r
1698         convertBuffer( stream_.userBuffer[1],\r
1699                        (char *) inBufferList->mBuffers[handle->iStream[1]].mData,\r
1700                        stream_.convertInfo[1] );\r
1701       }\r
1702       else { // copy to user buffer\r
1703         memcpy( stream_.userBuffer[1],\r
1704                 inBufferList->mBuffers[handle->iStream[1]].mData,\r
1705                 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );\r
1706       }\r
1707     }\r
1708     else { // read from multiple streams\r
1709       Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];\r
1710       if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;\r
1711 \r
1712       if ( stream_.deviceInterleaved[1] == false ) { // mono mode\r
1713         UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;\r
1714         for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
1715           memcpy( (void *)&outBuffer[i*stream_.bufferSize],\r
1716                   inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );\r
1717         }\r
1718       }\r
1719       else { // read from multiple multi-channel streams\r
1720         UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;\r
1721         Float32 *out, *in;\r
1722 \r
1723         bool outInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1724         UInt32 outChannels = stream_.nUserChannels[1];\r
1725         if ( stream_.doConvertBuffer[1] ) {\r
1726           outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1727           outChannels = stream_.nDeviceChannels[1];\r
1728         }\r
1729 \r
1730         if ( outInterleaved ) outOffset = 1;\r
1731         else outOffset = stream_.bufferSize;\r
1732 \r
1733         channelsLeft = outChannels;\r
1734         for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {\r
1735           out = outBuffer;\r
1736           in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;\r
1737           streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;\r
1738 \r
1739           inJump = 0;\r
1740           // Account for possible channel offset in first stream\r
1741           if ( i == 0 && stream_.channelOffset[1] > 0 ) {\r
1742             streamChannels -= stream_.channelOffset[1];\r
1743             inJump = stream_.channelOffset[1];\r
1744             in += inJump;\r
1745           }\r
1746 \r
1747           // Account for possible unread channels at end of the last stream\r
1748           if ( streamChannels > channelsLeft ) {\r
1749             inJump = streamChannels - channelsLeft;\r
1750             streamChannels = channelsLeft;\r
1751           }\r
1752 \r
1753           // Determine output buffer offsets and skips\r
1754           if ( outInterleaved ) {\r
1755             outJump = outChannels;\r
1756             out += outChannels - channelsLeft;\r
1757           }\r
1758           else {\r
1759             outJump = 1;\r
1760             out += (outChannels - channelsLeft) * outOffset;\r
1761           }\r
1762 \r
1763           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1764             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1765               out[j*outOffset] = *in++;\r
1766             }\r
1767             out += outJump;\r
1768             in += inJump;\r
1769           }\r
1770           channelsLeft -= streamChannels;\r
1771         }\r
1772       }\r
1773       \r
1774       if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
1775         convertBuffer( stream_.userBuffer[1],\r
1776                        stream_.deviceBuffer,\r
1777                        stream_.convertInfo[1] );\r
1778       }\r
1779     }\r
1780   }\r
1781 \r
1782  unlock:\r
1783   //MUTEX_UNLOCK( &stream_.mutex );\r
1784 \r
1785   RtApi::tickStreamTime();\r
1786   return SUCCESS;\r
1787 }\r
1788 \r
1789 const char* RtApiCore :: getErrorCode( OSStatus code )\r
1790 {\r
1791   switch( code ) {\r
1792 \r
1793   case kAudioHardwareNotRunningError:\r
1794     return "kAudioHardwareNotRunningError";\r
1795 \r
1796   case kAudioHardwareUnspecifiedError:\r
1797     return "kAudioHardwareUnspecifiedError";\r
1798 \r
1799   case kAudioHardwareUnknownPropertyError:\r
1800     return "kAudioHardwareUnknownPropertyError";\r
1801 \r
1802   case kAudioHardwareBadPropertySizeError:\r
1803     return "kAudioHardwareBadPropertySizeError";\r
1804 \r
1805   case kAudioHardwareIllegalOperationError:\r
1806     return "kAudioHardwareIllegalOperationError";\r
1807 \r
1808   case kAudioHardwareBadObjectError:\r
1809     return "kAudioHardwareBadObjectError";\r
1810 \r
1811   case kAudioHardwareBadDeviceError:\r
1812     return "kAudioHardwareBadDeviceError";\r
1813 \r
1814   case kAudioHardwareBadStreamError:\r
1815     return "kAudioHardwareBadStreamError";\r
1816 \r
1817   case kAudioHardwareUnsupportedOperationError:\r
1818     return "kAudioHardwareUnsupportedOperationError";\r
1819 \r
1820   case kAudioDeviceUnsupportedFormatError:\r
1821     return "kAudioDeviceUnsupportedFormatError";\r
1822 \r
1823   case kAudioDevicePermissionsError:\r
1824     return "kAudioDevicePermissionsError";\r
1825 \r
1826   default:\r
1827     return "CoreAudio unknown error";\r
1828   }\r
1829 }\r
1830 \r
1831   //******************** End of __MACOSX_CORE__ *********************//\r
1832 #endif\r
1833 \r
1834 #if defined(__UNIX_JACK__)\r
1835 \r
1836 // JACK is a low-latency audio server, originally written for the\r
1837 // GNU/Linux operating system and now also ported to OS-X. It can\r
1838 // connect a number of different applications to an audio device, as\r
1839 // well as allowing them to share audio between themselves.\r
1840 //\r
1841 // When using JACK with RtAudio, "devices" refer to JACK clients that\r
1842 // have ports connected to the server.  The JACK server is typically\r
1843 // started in a terminal as follows:\r
1844 //\r
1845 // .jackd -d alsa -d hw:0\r
1846 //\r
1847 // or through an interface program such as qjackctl.  Many of the\r
1848 // parameters normally set for a stream are fixed by the JACK server\r
1849 // and can be specified when the JACK server is started.  In\r
1850 // particular,\r
1851 //\r
1852 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4\r
1853 //\r
1854 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample\r
1855 // frames, and number of buffers = 4.  Once the server is running, it\r
1856 // is not possible to override these values.  If the values are not\r
1857 // specified in the command-line, the JACK server uses default values.\r
1858 //\r
1859 // The JACK server does not have to be running when an instance of\r
1860 // RtApiJack is created, though the function getDeviceCount() will\r
1861 // report 0 devices found until JACK has been started.  When no\r
1862 // devices are available (i.e., the JACK server is not running), a\r
1863 // stream cannot be opened.\r
1864 \r
1865 #include <jack/jack.h>\r
1866 #include <unistd.h>\r
1867 #include <cstdio>\r
1868 \r
1869 // A structure to hold various information related to the Jack API\r
1870 // implementation.\r
1871 struct JackHandle {\r
1872   jack_client_t *client;\r
1873   jack_port_t **ports[2];\r
1874   std::string deviceName[2];\r
1875   bool xrun[2];\r
1876   pthread_cond_t condition;\r
1877   int drainCounter;       // Tracks callback counts when draining\r
1878   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
1879 \r
1880   JackHandle()\r
1881     :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
1882 };\r
1883 \r
1884 static void jackSilentError( const char * ) {};\r
1885 \r
1886 RtApiJack :: RtApiJack()\r
1887 {\r
1888   // Nothing to do here.\r
1889 #if !defined(__RTAUDIO_DEBUG__)\r
1890   // Turn off Jack's internal error reporting.\r
1891   jack_set_error_function( &jackSilentError );\r
1892 #endif\r
1893 }\r
1894 \r
1895 RtApiJack :: ~RtApiJack()\r
1896 {\r
1897   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
1898 }\r
1899 \r
1900 unsigned int RtApiJack :: getDeviceCount( void )\r
1901 {\r
1902   // See if we can become a jack client.\r
1903   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
1904   jack_status_t *status = NULL;\r
1905   jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );\r
1906   if ( client == 0 ) return 0;\r
1907 \r
1908   const char **ports;\r
1909   std::string port, previousPort;\r
1910   unsigned int nChannels = 0, nDevices = 0;\r
1911   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1912   if ( ports ) {\r
1913     // Parse the port names up to the first colon (:).\r
1914     size_t iColon = 0;\r
1915     do {\r
1916       port = (char *) ports[ nChannels ];\r
1917       iColon = port.find(":");\r
1918       if ( iColon != std::string::npos ) {\r
1919         port = port.substr( 0, iColon + 1 );\r
1920         if ( port != previousPort ) {\r
1921           nDevices++;\r
1922           previousPort = port;\r
1923         }\r
1924       }\r
1925     } while ( ports[++nChannels] );\r
1926     free( ports );\r
1927   }\r
1928 \r
1929   jack_client_close( client );\r
1930   return nDevices;\r
1931 }\r
1932 \r
1933 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )\r
1934 {\r
1935   RtAudio::DeviceInfo info;\r
1936   info.probed = false;\r
1937 \r
1938   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption\r
1939   jack_status_t *status = NULL;\r
1940   jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );\r
1941   if ( client == 0 ) {\r
1942     errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";\r
1943     error( RtAudioError::WARNING );\r
1944     return info;\r
1945   }\r
1946 \r
1947   const char **ports;\r
1948   std::string port, previousPort;\r
1949   unsigned int nPorts = 0, nDevices = 0;\r
1950   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1951   if ( ports ) {\r
1952     // Parse the port names up to the first colon (:).\r
1953     size_t iColon = 0;\r
1954     do {\r
1955       port = (char *) ports[ nPorts ];\r
1956       iColon = port.find(":");\r
1957       if ( iColon != std::string::npos ) {\r
1958         port = port.substr( 0, iColon );\r
1959         if ( port != previousPort ) {\r
1960           if ( nDevices == device ) info.name = port;\r
1961           nDevices++;\r
1962           previousPort = port;\r
1963         }\r
1964       }\r
1965     } while ( ports[++nPorts] );\r
1966     free( ports );\r
1967   }\r
1968 \r
1969   if ( device >= nDevices ) {\r
1970     jack_client_close( client );\r
1971     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
1972     error( RtAudioError::INVALID_USE );\r
1973     return info;\r
1974   }\r
1975 \r
1976   // Get the current jack server sample rate.\r
1977   info.sampleRates.clear();\r
1978   info.sampleRates.push_back( jack_get_sample_rate( client ) );\r
1979 \r
1980   // Count the available ports containing the client name as device\r
1981   // channels.  Jack "input ports" equal RtAudio output channels.\r
1982   unsigned int nChannels = 0;\r
1983   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );\r
1984   if ( ports ) {\r
1985     while ( ports[ nChannels ] ) nChannels++;\r
1986     free( ports );\r
1987     info.outputChannels = nChannels;\r
1988   }\r
1989 \r
1990   // Jack "output ports" equal RtAudio input channels.\r
1991   nChannels = 0;\r
1992   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );\r
1993   if ( ports ) {\r
1994     while ( ports[ nChannels ] ) nChannels++;\r
1995     free( ports );\r
1996     info.inputChannels = nChannels;\r
1997   }\r
1998 \r
1999   if ( info.outputChannels == 0 && info.inputChannels == 0 ) {\r
2000     jack_client_close(client);\r
2001     errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";\r
2002     error( RtAudioError::WARNING );\r
2003     return info;\r
2004   }\r
2005 \r
2006   // If device opens for both playback and capture, we determine the channels.\r
2007   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2008     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2009 \r
2010   // Jack always uses 32-bit floats.\r
2011   info.nativeFormats = RTAUDIO_FLOAT32;\r
2012 \r
2013   // Jack doesn't provide default devices so we'll use the first available one.\r
2014   if ( device == 0 && info.outputChannels > 0 )\r
2015     info.isDefaultOutput = true;\r
2016   if ( device == 0 && info.inputChannels > 0 )\r
2017     info.isDefaultInput = true;\r
2018 \r
2019   jack_client_close(client);\r
2020   info.probed = true;\r
2021   return info;\r
2022 }\r
2023 \r
2024 static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
2025 {\r
2026   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2027 \r
2028   RtApiJack *object = (RtApiJack *) info->object;\r
2029   if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;\r
2030 \r
2031   return 0;\r
2032 }\r
2033 \r
2034 // This function will be called by a spawned thread when the Jack\r
2035 // server signals that it is shutting down.  It is necessary to handle\r
2036 // it this way because the jackShutdown() function must return before\r
2037 // the jack_deactivate() function (in closeStream()) will return.\r
2038 static void *jackCloseStream( void *ptr )\r
2039 {\r
2040   CallbackInfo *info = (CallbackInfo *) ptr;\r
2041   RtApiJack *object = (RtApiJack *) info->object;\r
2042 \r
2043   object->closeStream();\r
2044 \r
2045   pthread_exit( NULL );\r
2046 }\r
2047 static void jackShutdown( void *infoPointer )\r
2048 {\r
2049   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2050   RtApiJack *object = (RtApiJack *) info->object;\r
2051 \r
2052   // Check current stream state.  If stopped, then we'll assume this\r
2053   // was called as a result of a call to RtApiJack::stopStream (the\r
2054   // deactivation of a client handle causes this function to be called).\r
2055   // If not, we'll assume the Jack server is shutting down or some\r
2056   // other problem occurred and we should close the stream.\r
2057   if ( object->isStreamRunning() == false ) return;\r
2058 \r
2059   ThreadHandle threadId;\r
2060   pthread_create( &threadId, NULL, jackCloseStream, info );\r
2061   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
2062 }\r
2063 \r
2064 static int jackXrun( void *infoPointer )\r
2065 {\r
2066   JackHandle *handle = (JackHandle *) infoPointer;\r
2067 \r
2068   if ( handle->ports[0] ) handle->xrun[0] = true;\r
2069   if ( handle->ports[1] ) handle->xrun[1] = true;\r
2070 \r
2071   return 0;\r
2072 }\r
2073 \r
2074 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2075                                    unsigned int firstChannel, unsigned int sampleRate,\r
2076                                    RtAudioFormat format, unsigned int *bufferSize,\r
2077                                    RtAudio::StreamOptions *options )\r
2078 {\r
2079   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2080 \r
2081   // Look for jack server and try to become a client (only do once per stream).\r
2082   jack_client_t *client = 0;\r
2083   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {\r
2084     jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
2085     jack_status_t *status = NULL;\r
2086     if ( options && !options->streamName.empty() )\r
2087       client = jack_client_open( options->streamName.c_str(), jackoptions, status );\r
2088     else\r
2089       client = jack_client_open( "RtApiJack", jackoptions, status );\r
2090     if ( client == 0 ) {\r
2091       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";\r
2092       error( RtAudioError::WARNING );\r
2093       return FAILURE;\r
2094     }\r
2095   }\r
2096   else {\r
2097     // The handle must have been created on an earlier pass.\r
2098     client = handle->client;\r
2099   }\r
2100 \r
2101   const char **ports;\r
2102   std::string port, previousPort, deviceName;\r
2103   unsigned int nPorts = 0, nDevices = 0;\r
2104   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2105   if ( ports ) {\r
2106     // Parse the port names up to the first colon (:).\r
2107     size_t iColon = 0;\r
2108     do {\r
2109       port = (char *) ports[ nPorts ];\r
2110       iColon = port.find(":");\r
2111       if ( iColon != std::string::npos ) {\r
2112         port = port.substr( 0, iColon );\r
2113         if ( port != previousPort ) {\r
2114           if ( nDevices == device ) deviceName = port;\r
2115           nDevices++;\r
2116           previousPort = port;\r
2117         }\r
2118       }\r
2119     } while ( ports[++nPorts] );\r
2120     free( ports );\r
2121   }\r
2122 \r
2123   if ( device >= nDevices ) {\r
2124     errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";\r
2125     return FAILURE;\r
2126   }\r
2127 \r
2128   // Count the available ports containing the client name as device\r
2129   // channels.  Jack "input ports" equal RtAudio output channels.\r
2130   unsigned int nChannels = 0;\r
2131   unsigned long flag = JackPortIsInput;\r
2132   if ( mode == INPUT ) flag = JackPortIsOutput;\r
2133   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2134   if ( ports ) {\r
2135     while ( ports[ nChannels ] ) nChannels++;\r
2136     free( ports );\r
2137   }\r
2138 \r
2139   // Compare the jack ports for specified client to the requested number of channels.\r
2140   if ( nChannels < (channels + firstChannel) ) {\r
2141     errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";\r
2142     errorText_ = errorStream_.str();\r
2143     return FAILURE;\r
2144   }\r
2145 \r
2146   // Check the jack server sample rate.\r
2147   unsigned int jackRate = jack_get_sample_rate( client );\r
2148   if ( sampleRate != jackRate ) {\r
2149     jack_client_close( client );\r
2150     errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";\r
2151     errorText_ = errorStream_.str();\r
2152     return FAILURE;\r
2153   }\r
2154   stream_.sampleRate = jackRate;\r
2155 \r
2156   // Get the latency of the JACK port.\r
2157   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2158   if ( ports[ firstChannel ] ) {\r
2159     // Added by Ge Wang\r
2160     jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
2161     // the range (usually the min and max are equal)\r
2162     jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
2163     // get the latency range\r
2164     jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
2165     // be optimistic, use the min!\r
2166     stream_.latency[mode] = latrange.min;\r
2167     //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
2168   }\r
2169   free( ports );\r
2170 \r
2171   // The jack server always uses 32-bit floating-point data.\r
2172   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2173   stream_.userFormat = format;\r
2174 \r
2175   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
2176   else stream_.userInterleaved = true;\r
2177 \r
2178   // Jack always uses non-interleaved buffers.\r
2179   stream_.deviceInterleaved[mode] = false;\r
2180 \r
2181   // Jack always provides host byte-ordered data.\r
2182   stream_.doByteSwap[mode] = false;\r
2183 \r
2184   // Get the buffer size.  The buffer size and number of buffers\r
2185   // (periods) is set when the jack server is started.\r
2186   stream_.bufferSize = (int) jack_get_buffer_size( client );\r
2187   *bufferSize = stream_.bufferSize;\r
2188 \r
2189   stream_.nDeviceChannels[mode] = channels;\r
2190   stream_.nUserChannels[mode] = channels;\r
2191 \r
2192   // Set flags for buffer conversion.\r
2193   stream_.doConvertBuffer[mode] = false;\r
2194   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
2195     stream_.doConvertBuffer[mode] = true;\r
2196   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
2197        stream_.nUserChannels[mode] > 1 )\r
2198     stream_.doConvertBuffer[mode] = true;\r
2199 \r
2200   // Allocate our JackHandle structure for the stream.\r
2201   if ( handle == 0 ) {\r
2202     try {\r
2203       handle = new JackHandle;\r
2204     }\r
2205     catch ( std::bad_alloc& ) {\r
2206       errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";\r
2207       goto error;\r
2208     }\r
2209 \r
2210     if ( pthread_cond_init(&handle->condition, NULL) ) {\r
2211       errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";\r
2212       goto error;\r
2213     }\r
2214     stream_.apiHandle = (void *) handle;\r
2215     handle->client = client;\r
2216   }\r
2217   handle->deviceName[mode] = deviceName;\r
2218 \r
2219   // Allocate necessary internal buffers.\r
2220   unsigned long bufferBytes;\r
2221   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
2222   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
2223   if ( stream_.userBuffer[mode] == NULL ) {\r
2224     errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";\r
2225     goto error;\r
2226   }\r
2227 \r
2228   if ( stream_.doConvertBuffer[mode] ) {\r
2229 \r
2230     bool makeBuffer = true;\r
2231     if ( mode == OUTPUT )\r
2232       bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
2233     else { // mode == INPUT\r
2234       bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );\r
2235       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
2236         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);\r
2237         if ( bufferBytes < bytesOut ) makeBuffer = false;\r
2238       }\r
2239     }\r
2240 \r
2241     if ( makeBuffer ) {\r
2242       bufferBytes *= *bufferSize;\r
2243       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
2244       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
2245       if ( stream_.deviceBuffer == NULL ) {\r
2246         errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";\r
2247         goto error;\r
2248       }\r
2249     }\r
2250   }\r
2251 \r
2252   // Allocate memory for the Jack ports (channels) identifiers.\r
2253   handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );\r
2254   if ( handle->ports[mode] == NULL )  {\r
2255     errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";\r
2256     goto error;\r
2257   }\r
2258 \r
2259   stream_.device[mode] = device;\r
2260   stream_.channelOffset[mode] = firstChannel;\r
2261   stream_.state = STREAM_STOPPED;\r
2262   stream_.callbackInfo.object = (void *) this;\r
2263 \r
2264   if ( stream_.mode == OUTPUT && mode == INPUT )\r
2265     // We had already set up the stream for output.\r
2266     stream_.mode = DUPLEX;\r
2267   else {\r
2268     stream_.mode = mode;\r
2269     jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );\r
2270     jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );\r
2271     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );\r
2272   }\r
2273 \r
2274   // Register our ports.\r
2275   char label[64];\r
2276   if ( mode == OUTPUT ) {\r
2277     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2278       snprintf( label, 64, "outport %d", i );\r
2279       handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,\r
2280                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );\r
2281     }\r
2282   }\r
2283   else {\r
2284     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2285       snprintf( label, 64, "inport %d", i );\r
2286       handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,\r
2287                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );\r
2288     }\r
2289   }\r
2290 \r
2291   // Setup the buffer conversion information structure.  We don't use\r
2292   // buffers to do channel offsets, so we override that parameter\r
2293   // here.\r
2294   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
2295 \r
2296   return SUCCESS;\r
2297 \r
2298  error:\r
2299   if ( handle ) {\r
2300     pthread_cond_destroy( &handle->condition );\r
2301     jack_client_close( handle->client );\r
2302 \r
2303     if ( handle->ports[0] ) free( handle->ports[0] );\r
2304     if ( handle->ports[1] ) free( handle->ports[1] );\r
2305 \r
2306     delete handle;\r
2307     stream_.apiHandle = 0;\r
2308   }\r
2309 \r
2310   for ( int i=0; i<2; i++ ) {\r
2311     if ( stream_.userBuffer[i] ) {\r
2312       free( stream_.userBuffer[i] );\r
2313       stream_.userBuffer[i] = 0;\r
2314     }\r
2315   }\r
2316 \r
2317   if ( stream_.deviceBuffer ) {\r
2318     free( stream_.deviceBuffer );\r
2319     stream_.deviceBuffer = 0;\r
2320   }\r
2321 \r
2322   return FAILURE;\r
2323 }\r
2324 \r
2325 void RtApiJack :: closeStream( void )\r
2326 {\r
2327   if ( stream_.state == STREAM_CLOSED ) {\r
2328     errorText_ = "RtApiJack::closeStream(): no open stream to close!";\r
2329     error( RtAudioError::WARNING );\r
2330     return;\r
2331   }\r
2332 \r
2333   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2334   if ( handle ) {\r
2335 \r
2336     if ( stream_.state == STREAM_RUNNING )\r
2337       jack_deactivate( handle->client );\r
2338 \r
2339     jack_client_close( handle->client );\r
2340   }\r
2341 \r
2342   if ( handle ) {\r
2343     if ( handle->ports[0] ) free( handle->ports[0] );\r
2344     if ( handle->ports[1] ) free( handle->ports[1] );\r
2345     pthread_cond_destroy( &handle->condition );\r
2346     delete handle;\r
2347     stream_.apiHandle = 0;\r
2348   }\r
2349 \r
2350   for ( int i=0; i<2; i++ ) {\r
2351     if ( stream_.userBuffer[i] ) {\r
2352       free( stream_.userBuffer[i] );\r
2353       stream_.userBuffer[i] = 0;\r
2354     }\r
2355   }\r
2356 \r
2357   if ( stream_.deviceBuffer ) {\r
2358     free( stream_.deviceBuffer );\r
2359     stream_.deviceBuffer = 0;\r
2360   }\r
2361 \r
2362   stream_.mode = UNINITIALIZED;\r
2363   stream_.state = STREAM_CLOSED;\r
2364 }\r
2365 \r
2366 void RtApiJack :: startStream( void )\r
2367 {\r
2368   verifyStream();\r
2369   if ( stream_.state == STREAM_RUNNING ) {\r
2370     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
2371     error( RtAudioError::WARNING );\r
2372     return;\r
2373   }\r
2374 \r
2375   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2376   int result = jack_activate( handle->client );\r
2377   if ( result ) {\r
2378     errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";\r
2379     goto unlock;\r
2380   }\r
2381 \r
2382   const char **ports;\r
2383 \r
2384   // Get the list of available ports.\r
2385   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2386     result = 1;\r
2387     ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);\r
2388     if ( ports == NULL) {\r
2389       errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";\r
2390       goto unlock;\r
2391     }\r
2392 \r
2393     // Now make the port connections.  Since RtAudio wasn't designed to\r
2394     // allow the user to select particular channels of a device, we'll\r
2395     // just open the first "nChannels" ports with offset.\r
2396     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2397       result = 1;\r
2398       if ( ports[ stream_.channelOffset[0] + i ] )\r
2399         result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );\r
2400       if ( result ) {\r
2401         free( ports );\r
2402         errorText_ = "RtApiJack::startStream(): error connecting output ports!";\r
2403         goto unlock;\r
2404       }\r
2405     }\r
2406     free(ports);\r
2407   }\r
2408 \r
2409   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2410     result = 1;\r
2411     ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );\r
2412     if ( ports == NULL) {\r
2413       errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";\r
2414       goto unlock;\r
2415     }\r
2416 \r
2417     // Now make the port connections.  See note above.\r
2418     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2419       result = 1;\r
2420       if ( ports[ stream_.channelOffset[1] + i ] )\r
2421         result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );\r
2422       if ( result ) {\r
2423         free( ports );\r
2424         errorText_ = "RtApiJack::startStream(): error connecting input ports!";\r
2425         goto unlock;\r
2426       }\r
2427     }\r
2428     free(ports);\r
2429   }\r
2430 \r
2431   handle->drainCounter = 0;\r
2432   handle->internalDrain = false;\r
2433   stream_.state = STREAM_RUNNING;\r
2434 \r
2435  unlock:\r
2436   if ( result == 0 ) return;\r
2437   error( RtAudioError::SYSTEM_ERROR );\r
2438 }\r
2439 \r
2440 void RtApiJack :: stopStream( void )\r
2441 {\r
2442   verifyStream();\r
2443   if ( stream_.state == STREAM_STOPPED ) {\r
2444     errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";\r
2445     error( RtAudioError::WARNING );\r
2446     return;\r
2447   }\r
2448 \r
2449   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2450   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2451 \r
2452     if ( handle->drainCounter == 0 ) {\r
2453       handle->drainCounter = 2;\r
2454       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
2455     }\r
2456   }\r
2457 \r
2458   jack_deactivate( handle->client );\r
2459   stream_.state = STREAM_STOPPED;\r
2460 }\r
2461 \r
2462 void RtApiJack :: abortStream( void )\r
2463 {\r
2464   verifyStream();\r
2465   if ( stream_.state == STREAM_STOPPED ) {\r
2466     errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";\r
2467     error( RtAudioError::WARNING );\r
2468     return;\r
2469   }\r
2470 \r
2471   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2472   handle->drainCounter = 2;\r
2473 \r
2474   stopStream();\r
2475 }\r
2476 \r
2477 // This function will be called by a spawned thread when the user\r
2478 // callback function signals that the stream should be stopped or\r
2479 // aborted.  It is necessary to handle it this way because the\r
2480 // callbackEvent() function must return before the jack_deactivate()\r
2481 // function will return.\r
2482 static void *jackStopStream( void *ptr )\r
2483 {\r
2484   CallbackInfo *info = (CallbackInfo *) ptr;\r
2485   RtApiJack *object = (RtApiJack *) info->object;\r
2486 \r
2487   object->stopStream();\r
2488   pthread_exit( NULL );\r
2489 }\r
2490 \r
2491 bool RtApiJack :: callbackEvent( unsigned long nframes )\r
2492 {\r
2493   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
2494   if ( stream_.state == STREAM_CLOSED ) {\r
2495     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
2496     error( RtAudioError::WARNING );\r
2497     return FAILURE;\r
2498   }\r
2499   if ( stream_.bufferSize != nframes ) {\r
2500     errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";\r
2501     error( RtAudioError::WARNING );\r
2502     return FAILURE;\r
2503   }\r
2504 \r
2505   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
2506   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2507 \r
2508   // Check if we were draining the stream and signal is finished.\r
2509   if ( handle->drainCounter > 3 ) {\r
2510     ThreadHandle threadId;\r
2511 \r
2512     stream_.state = STREAM_STOPPING;\r
2513     if ( handle->internalDrain == true )\r
2514       pthread_create( &threadId, NULL, jackStopStream, info );\r
2515     else\r
2516       pthread_cond_signal( &handle->condition );\r
2517     return SUCCESS;\r
2518   }\r
2519 \r
2520   // Invoke user callback first, to get fresh output data.\r
2521   if ( handle->drainCounter == 0 ) {\r
2522     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
2523     double streamTime = getStreamTime();\r
2524     RtAudioStreamStatus status = 0;\r
2525     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
2526       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
2527       handle->xrun[0] = false;\r
2528     }\r
2529     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
2530       status |= RTAUDIO_INPUT_OVERFLOW;\r
2531       handle->xrun[1] = false;\r
2532     }\r
2533     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
2534                                   stream_.bufferSize, streamTime, status, info->userData );\r
2535     if ( cbReturnValue == 2 ) {\r
2536       stream_.state = STREAM_STOPPING;\r
2537       handle->drainCounter = 2;\r
2538       ThreadHandle id;\r
2539       pthread_create( &id, NULL, jackStopStream, info );\r
2540       return SUCCESS;\r
2541     }\r
2542     else if ( cbReturnValue == 1 ) {\r
2543       handle->drainCounter = 1;\r
2544       handle->internalDrain = true;\r
2545     }\r
2546   }\r
2547 \r
2548   jack_default_audio_sample_t *jackbuffer;\r
2549   unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );\r
2550   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2551 \r
2552     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
2553 \r
2554       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2555         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2556         memset( jackbuffer, 0, bufferBytes );\r
2557       }\r
2558 \r
2559     }\r
2560     else if ( stream_.doConvertBuffer[0] ) {\r
2561 \r
2562       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
2563 \r
2564       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2565         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2566         memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );\r
2567       }\r
2568     }\r
2569     else { // no buffer conversion\r
2570       for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2571         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2572         memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
2573       }\r
2574     }\r
2575 \r
2576     if ( handle->drainCounter ) {\r
2577       handle->drainCounter++;\r
2578       goto unlock;\r
2579     }\r
2580   }\r
2581 \r
2582   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2583 \r
2584     if ( stream_.doConvertBuffer[1] ) {\r
2585       for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {\r
2586         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2587         memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );\r
2588       }\r
2589       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
2590     }\r
2591     else { // no buffer conversion\r
2592       for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2593         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2594         memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );\r
2595       }\r
2596     }\r
2597   }\r
2598 \r
2599  unlock:\r
2600   RtApi::tickStreamTime();\r
2601   return SUCCESS;\r
2602 }\r
2603   //******************** End of __UNIX_JACK__ *********************//\r
2604 #endif\r
2605 \r
2606 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows\r
2607 \r
2608 // The ASIO API is designed around a callback scheme, so this\r
2609 // implementation is similar to that used for OS-X CoreAudio and Linux\r
2610 // Jack.  The primary constraint with ASIO is that it only allows\r
2611 // access to a single driver at a time.  Thus, it is not possible to\r
2612 // have more than one simultaneous RtAudio stream.\r
2613 //\r
2614 // This implementation also requires a number of external ASIO files\r
2615 // and a few global variables.  The ASIO callback scheme does not\r
2616 // allow for the passing of user data, so we must create a global\r
2617 // pointer to our callbackInfo structure.\r
2618 //\r
2619 // On unix systems, we make use of a pthread condition variable.\r
2620 // Since there is no equivalent in Windows, I hacked something based\r
2621 // on information found in\r
2622 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r
2623 \r
2624 #include "asiosys.h"\r
2625 #include "asio.h"\r
2626 #include "iasiothiscallresolver.h"\r
2627 #include "asiodrivers.h"\r
2628 #include <cmath>\r
2629 \r
2630 static AsioDrivers drivers;\r
2631 static ASIOCallbacks asioCallbacks;\r
2632 static ASIODriverInfo driverInfo;\r
2633 static CallbackInfo *asioCallbackInfo;\r
2634 static bool asioXRun;\r
2635 \r
2636 struct AsioHandle {\r
2637   int drainCounter;       // Tracks callback counts when draining\r
2638   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
2639   ASIOBufferInfo *bufferInfos;\r
2640   HANDLE condition;\r
2641 \r
2642   AsioHandle()\r
2643     :drainCounter(0), internalDrain(false), bufferInfos(0) {}\r
2644 };\r
2645 \r
2646 // Function declarations (definitions at end of section)\r
2647 static const char* getAsioErrorString( ASIOError result );\r
2648 static void sampleRateChanged( ASIOSampleRate sRate );\r
2649 static long asioMessages( long selector, long value, void* message, double* opt );\r
2650 \r
2651 RtApiAsio :: RtApiAsio()\r
2652 {\r
2653   // ASIO cannot run on a multi-threaded appartment. You can call\r
2654   // CoInitialize beforehand, but it must be for appartment threading\r
2655   // (in which case, CoInitilialize will return S_FALSE here).\r
2656   coInitialized_ = false;\r
2657   HRESULT hr = CoInitialize( NULL ); \r
2658   if ( FAILED(hr) ) {\r
2659     errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
2660     error( RtAudioError::WARNING );\r
2661   }\r
2662   coInitialized_ = true;\r
2663 \r
2664   drivers.removeCurrentDriver();\r
2665   driverInfo.asioVersion = 2;\r
2666 \r
2667   // See note in DirectSound implementation about GetDesktopWindow().\r
2668   driverInfo.sysRef = GetForegroundWindow();\r
2669 }\r
2670 \r
2671 RtApiAsio :: ~RtApiAsio()\r
2672 {\r
2673   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
2674   if ( coInitialized_ ) CoUninitialize();\r
2675 }\r
2676 \r
2677 unsigned int RtApiAsio :: getDeviceCount( void )\r
2678 {\r
2679   return (unsigned int) drivers.asioGetNumDev();\r
2680 }\r
2681 \r
2682 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )\r
2683 {\r
2684   RtAudio::DeviceInfo info;\r
2685   info.probed = false;\r
2686 \r
2687   // Get device ID\r
2688   unsigned int nDevices = getDeviceCount();\r
2689   if ( nDevices == 0 ) {\r
2690     errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
2691     error( RtAudioError::INVALID_USE );\r
2692     return info;\r
2693   }\r
2694 \r
2695   if ( device >= nDevices ) {\r
2696     errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
2697     error( RtAudioError::INVALID_USE );\r
2698     return info;\r
2699   }\r
2700 \r
2701   // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.\r
2702   if ( stream_.state != STREAM_CLOSED ) {\r
2703     if ( device >= devices_.size() ) {\r
2704       errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";\r
2705       error( RtAudioError::WARNING );\r
2706       return info;\r
2707     }\r
2708     return devices_[ device ];\r
2709   }\r
2710 \r
2711   char driverName[32];\r
2712   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2713   if ( result != ASE_OK ) {\r
2714     errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2715     errorText_ = errorStream_.str();\r
2716     error( RtAudioError::WARNING );\r
2717     return info;\r
2718   }\r
2719 \r
2720   info.name = driverName;\r
2721 \r
2722   if ( !drivers.loadDriver( driverName ) ) {\r
2723     errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";\r
2724     errorText_ = errorStream_.str();\r
2725     error( RtAudioError::WARNING );\r
2726     return info;\r
2727   }\r
2728 \r
2729   result = ASIOInit( &driverInfo );\r
2730   if ( result != ASE_OK ) {\r
2731     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2732     errorText_ = errorStream_.str();\r
2733     error( RtAudioError::WARNING );\r
2734     return info;\r
2735   }\r
2736 \r
2737   // Determine the device channel information.\r
2738   long inputChannels, outputChannels;\r
2739   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2740   if ( result != ASE_OK ) {\r
2741     drivers.removeCurrentDriver();\r
2742     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2743     errorText_ = errorStream_.str();\r
2744     error( RtAudioError::WARNING );\r
2745     return info;\r
2746   }\r
2747 \r
2748   info.outputChannels = outputChannels;\r
2749   info.inputChannels = inputChannels;\r
2750   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2751     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2752 \r
2753   // Determine the supported sample rates.\r
2754   info.sampleRates.clear();\r
2755   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
2756     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
2757     if ( result == ASE_OK )\r
2758       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
2759   }\r
2760 \r
2761   // Determine supported data types ... just check first channel and assume rest are the same.\r
2762   ASIOChannelInfo channelInfo;\r
2763   channelInfo.channel = 0;\r
2764   channelInfo.isInput = true;\r
2765   if ( info.inputChannels <= 0 ) channelInfo.isInput = false;\r
2766   result = ASIOGetChannelInfo( &channelInfo );\r
2767   if ( result != ASE_OK ) {\r
2768     drivers.removeCurrentDriver();\r
2769     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";\r
2770     errorText_ = errorStream_.str();\r
2771     error( RtAudioError::WARNING );\r
2772     return info;\r
2773   }\r
2774 \r
2775   info.nativeFormats = 0;\r
2776   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )\r
2777     info.nativeFormats |= RTAUDIO_SINT16;\r
2778   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )\r
2779     info.nativeFormats |= RTAUDIO_SINT32;\r
2780   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )\r
2781     info.nativeFormats |= RTAUDIO_FLOAT32;\r
2782   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
2783     info.nativeFormats |= RTAUDIO_FLOAT64;\r
2784   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
2785     info.nativeFormats |= RTAUDIO_SINT24;\r
2786 \r
2787   if ( info.outputChannels > 0 )\r
2788     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
2789   if ( info.inputChannels > 0 )\r
2790     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
2791 \r
2792   info.probed = true;\r
2793   drivers.removeCurrentDriver();\r
2794   return info;\r
2795 }\r
2796 \r
2797 static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
2798 {\r
2799   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
2800   object->callbackEvent( index );\r
2801 }\r
2802 \r
2803 void RtApiAsio :: saveDeviceInfo( void )\r
2804 {\r
2805   devices_.clear();\r
2806 \r
2807   unsigned int nDevices = getDeviceCount();\r
2808   devices_.resize( nDevices );\r
2809   for ( unsigned int i=0; i<nDevices; i++ )\r
2810     devices_[i] = getDeviceInfo( i );\r
2811 }\r
2812 \r
2813 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2814                                    unsigned int firstChannel, unsigned int sampleRate,\r
2815                                    RtAudioFormat format, unsigned int *bufferSize,\r
2816                                    RtAudio::StreamOptions *options )\r
2817 {\r
2818   // For ASIO, a duplex stream MUST use the same driver.\r
2819   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {\r
2820     errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
2821     return FAILURE;\r
2822   }\r
2823 \r
2824   char driverName[32];\r
2825   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2826   if ( result != ASE_OK ) {\r
2827     errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2828     errorText_ = errorStream_.str();\r
2829     return FAILURE;\r
2830   }\r
2831 \r
2832   // Only load the driver once for duplex stream.\r
2833   if ( mode != INPUT || stream_.mode != OUTPUT ) {\r
2834     // The getDeviceInfo() function will not work when a stream is open\r
2835     // because ASIO does not allow multiple devices to run at the same\r
2836     // time.  Thus, we'll probe the system before opening a stream and\r
2837     // save the results for use by getDeviceInfo().\r
2838     this->saveDeviceInfo();\r
2839 \r
2840     if ( !drivers.loadDriver( driverName ) ) {\r
2841       errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
2842       errorText_ = errorStream_.str();\r
2843       return FAILURE;\r
2844     }\r
2845 \r
2846     result = ASIOInit( &driverInfo );\r
2847     if ( result != ASE_OK ) {\r
2848       errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2849       errorText_ = errorStream_.str();\r
2850       return FAILURE;\r
2851     }\r
2852   }\r
2853 \r
2854   // Check the device channel count.\r
2855   long inputChannels, outputChannels;\r
2856   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2857   if ( result != ASE_OK ) {\r
2858     drivers.removeCurrentDriver();\r
2859     errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2860     errorText_ = errorStream_.str();\r
2861     return FAILURE;\r
2862   }\r
2863 \r
2864   if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
2865        ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
2866     drivers.removeCurrentDriver();\r
2867     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
2868     errorText_ = errorStream_.str();\r
2869     return FAILURE;\r
2870   }\r
2871   stream_.nDeviceChannels[mode] = channels;\r
2872   stream_.nUserChannels[mode] = channels;\r
2873   stream_.channelOffset[mode] = firstChannel;\r
2874 \r
2875   // Verify the sample rate is supported.\r
2876   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
2877   if ( result != ASE_OK ) {\r
2878     drivers.removeCurrentDriver();\r
2879     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
2880     errorText_ = errorStream_.str();\r
2881     return FAILURE;\r
2882   }\r
2883 \r
2884   // Get the current sample rate\r
2885   ASIOSampleRate currentRate;\r
2886   result = ASIOGetSampleRate( &currentRate );\r
2887   if ( result != ASE_OK ) {\r
2888     drivers.removeCurrentDriver();\r
2889     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
2890     errorText_ = errorStream_.str();\r
2891     return FAILURE;\r
2892   }\r
2893 \r
2894   // Set the sample rate only if necessary\r
2895   if ( currentRate != sampleRate ) {\r
2896     result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
2897     if ( result != ASE_OK ) {\r
2898       drivers.removeCurrentDriver();\r
2899       errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
2900       errorText_ = errorStream_.str();\r
2901       return FAILURE;\r
2902     }\r
2903   }\r
2904 \r
2905   // Determine the driver data type.\r
2906   ASIOChannelInfo channelInfo;\r
2907   channelInfo.channel = 0;\r
2908   if ( mode == OUTPUT ) channelInfo.isInput = false;\r
2909   else channelInfo.isInput = true;\r
2910   result = ASIOGetChannelInfo( &channelInfo );\r
2911   if ( result != ASE_OK ) {\r
2912     drivers.removeCurrentDriver();\r
2913     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
2914     errorText_ = errorStream_.str();\r
2915     return FAILURE;\r
2916   }\r
2917 \r
2918   // Assuming WINDOWS host is always little-endian.\r
2919   stream_.doByteSwap[mode] = false;\r
2920   stream_.userFormat = format;\r
2921   stream_.deviceFormat[mode] = 0;\r
2922   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {\r
2923     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
2924     if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;\r
2925   }\r
2926   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {\r
2927     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
2928     if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;\r
2929   }\r
2930   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {\r
2931     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2932     if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;\r
2933   }\r
2934   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {\r
2935     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
2936     if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
2937   }\r
2938   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
2939     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
2940     if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
2941   }\r
2942 \r
2943   if ( stream_.deviceFormat[mode] == 0 ) {\r
2944     drivers.removeCurrentDriver();\r
2945     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
2946     errorText_ = errorStream_.str();\r
2947     return FAILURE;\r
2948   }\r
2949 \r
2950   // Set the buffer size.  For a duplex stream, this will end up\r
2951   // setting the buffer size based on the input constraints, which\r
2952   // should be ok.\r
2953   long minSize, maxSize, preferSize, granularity;\r
2954   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
2955   if ( result != ASE_OK ) {\r
2956     drivers.removeCurrentDriver();\r
2957     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
2958     errorText_ = errorStream_.str();\r
2959     return FAILURE;\r
2960   }\r
2961 \r
2962   if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
2963   else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
2964   else if ( granularity == -1 ) {\r
2965     // Make sure bufferSize is a power of two.\r
2966     int log2_of_min_size = 0;\r
2967     int log2_of_max_size = 0;\r
2968 \r
2969     for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
2970       if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
2971       if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
2972     }\r
2973 \r
2974     long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
2975     int min_delta_num = log2_of_min_size;\r
2976 \r
2977     for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
2978       long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
2979       if (current_delta < min_delta) {\r
2980         min_delta = current_delta;\r
2981         min_delta_num = i;\r
2982       }\r
2983     }\r
2984 \r
2985     *bufferSize = ( (unsigned int)1 << min_delta_num );\r
2986     if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
2987     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
2988   }\r
2989   else if ( granularity != 0 ) {\r
2990     // Set to an even multiple of granularity, rounding up.\r
2991     *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
2992   }\r
2993 \r
2994   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {\r
2995     drivers.removeCurrentDriver();\r
2996     errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
2997     return FAILURE;\r
2998   }\r
2999 \r
3000   stream_.bufferSize = *bufferSize;\r
3001   stream_.nBuffers = 2;\r
3002 \r
3003   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
3004   else stream_.userInterleaved = true;\r
3005 \r
3006   // ASIO always uses non-interleaved buffers.\r
3007   stream_.deviceInterleaved[mode] = false;\r
3008 \r
3009   // Allocate, if necessary, our AsioHandle structure for the stream.\r
3010   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3011   if ( handle == 0 ) {\r
3012     try {\r
3013       handle = new AsioHandle;\r
3014     }\r
3015     catch ( std::bad_alloc& ) {\r
3016       //if ( handle == NULL ) {    \r
3017       drivers.removeCurrentDriver();\r
3018       errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
3019       return FAILURE;\r
3020     }\r
3021     handle->bufferInfos = 0;\r
3022 \r
3023     // Create a manual-reset event.\r
3024     handle->condition = CreateEvent( NULL,   // no security\r
3025                                      TRUE,   // manual-reset\r
3026                                      FALSE,  // non-signaled initially\r
3027                                      NULL ); // unnamed\r
3028     stream_.apiHandle = (void *) handle;\r
3029   }\r
3030 \r
3031   // Create the ASIO internal buffers.  Since RtAudio sets up input\r
3032   // and output separately, we'll have to dispose of previously\r
3033   // created output buffers for a duplex stream.\r
3034   long inputLatency, outputLatency;\r
3035   if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
3036     ASIODisposeBuffers();\r
3037     if ( handle->bufferInfos ) free( handle->bufferInfos );\r
3038   }\r
3039 \r
3040   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
3041   bool buffersAllocated = false;\r
3042   unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3043   handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
3044   if ( handle->bufferInfos == NULL ) {\r
3045     errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
3046     errorText_ = errorStream_.str();\r
3047     goto error;\r
3048   }\r
3049 \r
3050   ASIOBufferInfo *infos;\r
3051   infos = handle->bufferInfos;\r
3052   for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {\r
3053     infos->isInput = ASIOFalse;\r
3054     infos->channelNum = i + stream_.channelOffset[0];\r
3055     infos->buffers[0] = infos->buffers[1] = 0;\r
3056   }\r
3057   for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {\r
3058     infos->isInput = ASIOTrue;\r
3059     infos->channelNum = i + stream_.channelOffset[1];\r
3060     infos->buffers[0] = infos->buffers[1] = 0;\r
3061   }\r
3062 \r
3063   // Set up the ASIO callback structure and create the ASIO data buffers.\r
3064   asioCallbacks.bufferSwitch = &bufferSwitch;\r
3065   asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
3066   asioCallbacks.asioMessage = &asioMessages;\r
3067   asioCallbacks.bufferSwitchTimeInfo = NULL;\r
3068   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3069   if ( result != ASE_OK ) {\r
3070     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
3071     errorText_ = errorStream_.str();\r
3072     goto error;\r
3073   }\r
3074   buffersAllocated = true;\r
3075 \r
3076   // Set flags for buffer conversion.\r
3077   stream_.doConvertBuffer[mode] = false;\r
3078   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
3079     stream_.doConvertBuffer[mode] = true;\r
3080   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
3081        stream_.nUserChannels[mode] > 1 )\r
3082     stream_.doConvertBuffer[mode] = true;\r
3083 \r
3084   // Allocate necessary internal buffers\r
3085   unsigned long bufferBytes;\r
3086   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
3087   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
3088   if ( stream_.userBuffer[mode] == NULL ) {\r
3089     errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";\r
3090     goto error;\r
3091   }\r
3092 \r
3093   if ( stream_.doConvertBuffer[mode] ) {\r
3094 \r
3095     bool makeBuffer = true;\r
3096     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
3097     if ( mode == INPUT ) {\r
3098       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
3099         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
3100         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
3101       }\r
3102     }\r
3103 \r
3104     if ( makeBuffer ) {\r
3105       bufferBytes *= *bufferSize;\r
3106       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
3107       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
3108       if ( stream_.deviceBuffer == NULL ) {\r
3109         errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";\r
3110         goto error;\r
3111       }\r
3112     }\r
3113   }\r
3114 \r
3115   stream_.sampleRate = sampleRate;\r
3116   stream_.device[mode] = device;\r
3117   stream_.state = STREAM_STOPPED;\r
3118   asioCallbackInfo = &stream_.callbackInfo;\r
3119   stream_.callbackInfo.object = (void *) this;\r
3120   if ( stream_.mode == OUTPUT && mode == INPUT )\r
3121     // We had already set up an output stream.\r
3122     stream_.mode = DUPLEX;\r
3123   else\r
3124     stream_.mode = mode;\r
3125 \r
3126   // Determine device latencies\r
3127   result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
3128   if ( result != ASE_OK ) {\r
3129     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
3130     errorText_ = errorStream_.str();\r
3131     error( RtAudioError::WARNING); // warn but don't fail\r
3132   }\r
3133   else {\r
3134     stream_.latency[0] = outputLatency;\r
3135     stream_.latency[1] = inputLatency;\r
3136   }\r
3137 \r
3138   // Setup the buffer conversion information structure.  We don't use\r
3139   // buffers to do channel offsets, so we override that parameter\r
3140   // here.\r
3141   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
3142 \r
3143   return SUCCESS;\r
3144 \r
3145  error:\r
3146   if ( buffersAllocated )\r
3147     ASIODisposeBuffers();\r
3148   drivers.removeCurrentDriver();\r
3149 \r
3150   if ( handle ) {\r
3151     CloseHandle( handle->condition );\r
3152     if ( handle->bufferInfos )\r
3153       free( handle->bufferInfos );\r
3154     delete handle;\r
3155     stream_.apiHandle = 0;\r
3156   }\r
3157 \r
3158   for ( int i=0; i<2; i++ ) {\r
3159     if ( stream_.userBuffer[i] ) {\r
3160       free( stream_.userBuffer[i] );\r
3161       stream_.userBuffer[i] = 0;\r
3162     }\r
3163   }\r
3164 \r
3165   if ( stream_.deviceBuffer ) {\r
3166     free( stream_.deviceBuffer );\r
3167     stream_.deviceBuffer = 0;\r
3168   }\r
3169 \r
3170   return FAILURE;\r
3171 }\r
3172 \r
3173 void RtApiAsio :: closeStream()\r
3174 {\r
3175   if ( stream_.state == STREAM_CLOSED ) {\r
3176     errorText_ = "RtApiAsio::closeStream(): no open stream to close!";\r
3177     error( RtAudioError::WARNING );\r
3178     return;\r
3179   }\r
3180 \r
3181   if ( stream_.state == STREAM_RUNNING ) {\r
3182     stream_.state = STREAM_STOPPED;\r
3183     ASIOStop();\r
3184   }\r
3185   ASIODisposeBuffers();\r
3186   drivers.removeCurrentDriver();\r
3187 \r
3188   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3189   if ( handle ) {\r
3190     CloseHandle( handle->condition );\r
3191     if ( handle->bufferInfos )\r
3192       free( handle->bufferInfos );\r
3193     delete handle;\r
3194     stream_.apiHandle = 0;\r
3195   }\r
3196 \r
3197   for ( int i=0; i<2; i++ ) {\r
3198     if ( stream_.userBuffer[i] ) {\r
3199       free( stream_.userBuffer[i] );\r
3200       stream_.userBuffer[i] = 0;\r
3201     }\r
3202   }\r
3203 \r
3204   if ( stream_.deviceBuffer ) {\r
3205     free( stream_.deviceBuffer );\r
3206     stream_.deviceBuffer = 0;\r
3207   }\r
3208 \r
3209   stream_.mode = UNINITIALIZED;\r
3210   stream_.state = STREAM_CLOSED;\r
3211 }\r
3212 \r
3213 bool stopThreadCalled = false;\r
3214 \r
3215 void RtApiAsio :: startStream()\r
3216 {\r
3217   verifyStream();\r
3218   if ( stream_.state == STREAM_RUNNING ) {\r
3219     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
3220     error( RtAudioError::WARNING );\r
3221     return;\r
3222   }\r
3223 \r
3224   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3225   ASIOError result = ASIOStart();\r
3226   if ( result != ASE_OK ) {\r
3227     errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";\r
3228     errorText_ = errorStream_.str();\r
3229     goto unlock;\r
3230   }\r
3231 \r
3232   handle->drainCounter = 0;\r
3233   handle->internalDrain = false;\r
3234   ResetEvent( handle->condition );\r
3235   stream_.state = STREAM_RUNNING;\r
3236   asioXRun = false;\r
3237 \r
3238  unlock:\r
3239   stopThreadCalled = false;\r
3240 \r
3241   if ( result == ASE_OK ) return;\r
3242   error( RtAudioError::SYSTEM_ERROR );\r
3243 }\r
3244 \r
3245 void RtApiAsio :: stopStream()\r
3246 {\r
3247   verifyStream();\r
3248   if ( stream_.state == STREAM_STOPPED ) {\r
3249     errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";\r
3250     error( RtAudioError::WARNING );\r
3251     return;\r
3252   }\r
3253 \r
3254   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3255   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3256     if ( handle->drainCounter == 0 ) {\r
3257       handle->drainCounter = 2;\r
3258       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
3259     }\r
3260   }\r
3261 \r
3262   stream_.state = STREAM_STOPPED;\r
3263 \r
3264   ASIOError result = ASIOStop();\r
3265   if ( result != ASE_OK ) {\r
3266     errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";\r
3267     errorText_ = errorStream_.str();\r
3268   }\r
3269 \r
3270   if ( result == ASE_OK ) return;\r
3271   error( RtAudioError::SYSTEM_ERROR );\r
3272 }\r
3273 \r
3274 void RtApiAsio :: abortStream()\r
3275 {\r
3276   verifyStream();\r
3277   if ( stream_.state == STREAM_STOPPED ) {\r
3278     errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";\r
3279     error( RtAudioError::WARNING );\r
3280     return;\r
3281   }\r
3282 \r
3283   // The following lines were commented-out because some behavior was\r
3284   // noted where the device buffers need to be zeroed to avoid\r
3285   // continuing sound, even when the device buffers are completely\r
3286   // disposed.  So now, calling abort is the same as calling stop.\r
3287   // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3288   // handle->drainCounter = 2;\r
3289   stopStream();\r
3290 }\r
3291 \r
3292 // This function will be called by a spawned thread when the user\r
3293 // callback function signals that the stream should be stopped or\r
3294 // aborted.  It is necessary to handle it this way because the\r
3295 // callbackEvent() function must return before the ASIOStop()\r
3296 // function will return.\r
3297 static unsigned __stdcall asioStopStream( void *ptr )\r
3298 {\r
3299   CallbackInfo *info = (CallbackInfo *) ptr;\r
3300   RtApiAsio *object = (RtApiAsio *) info->object;\r
3301 \r
3302   object->stopStream();\r
3303   _endthreadex( 0 );\r
3304   return 0;\r
3305 }\r
3306 \r
3307 bool RtApiAsio :: callbackEvent( long bufferIndex )\r
3308 {\r
3309   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
3310   if ( stream_.state == STREAM_CLOSED ) {\r
3311     errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
3312     error( RtAudioError::WARNING );\r
3313     return FAILURE;\r
3314   }\r
3315 \r
3316   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
3317   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3318 \r
3319   // Check if we were draining the stream and signal if finished.\r
3320   if ( handle->drainCounter > 3 ) {\r
3321 \r
3322     stream_.state = STREAM_STOPPING;\r
3323     if ( handle->internalDrain == false )\r
3324       SetEvent( handle->condition );\r
3325     else { // spawn a thread to stop the stream\r
3326       unsigned threadId;\r
3327       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3328                                                     &stream_.callbackInfo, 0, &threadId );\r
3329     }\r
3330     return SUCCESS;\r
3331   }\r
3332 \r
3333   // Invoke user callback to get fresh output data UNLESS we are\r
3334   // draining stream.\r
3335   if ( handle->drainCounter == 0 ) {\r
3336     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
3337     double streamTime = getStreamTime();\r
3338     RtAudioStreamStatus status = 0;\r
3339     if ( stream_.mode != INPUT && asioXRun == true ) {\r
3340       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
3341       asioXRun = false;\r
3342     }\r
3343     if ( stream_.mode != OUTPUT && asioXRun == true ) {\r
3344       status |= RTAUDIO_INPUT_OVERFLOW;\r
3345       asioXRun = false;\r
3346     }\r
3347     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
3348                                      stream_.bufferSize, streamTime, status, info->userData );\r
3349     if ( cbReturnValue == 2 ) {\r
3350       stream_.state = STREAM_STOPPING;\r
3351       handle->drainCounter = 2;\r
3352       unsigned threadId;\r
3353       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3354                                                     &stream_.callbackInfo, 0, &threadId );\r
3355       return SUCCESS;\r
3356     }\r
3357     else if ( cbReturnValue == 1 ) {\r
3358       handle->drainCounter = 1;\r
3359       handle->internalDrain = true;\r
3360     }\r
3361   }\r
3362 \r
3363   unsigned int nChannels, bufferBytes, i, j;\r
3364   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3365   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3366 \r
3367     bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );\r
3368 \r
3369     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
3370 \r
3371       for ( i=0, j=0; i<nChannels; i++ ) {\r
3372         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3373           memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );\r
3374       }\r
3375 \r
3376     }\r
3377     else if ( stream_.doConvertBuffer[0] ) {\r
3378 \r
3379       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
3380       if ( stream_.doByteSwap[0] )\r
3381         byteSwapBuffer( stream_.deviceBuffer,\r
3382                         stream_.bufferSize * stream_.nDeviceChannels[0],\r
3383                         stream_.deviceFormat[0] );\r
3384 \r
3385       for ( i=0, j=0; i<nChannels; i++ ) {\r
3386         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3387           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3388                   &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );\r
3389       }\r
3390 \r
3391     }\r
3392     else {\r
3393 \r
3394       if ( stream_.doByteSwap[0] )\r
3395         byteSwapBuffer( stream_.userBuffer[0],\r
3396                         stream_.bufferSize * stream_.nUserChannels[0],\r
3397                         stream_.userFormat );\r
3398 \r
3399       for ( i=0, j=0; i<nChannels; i++ ) {\r
3400         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3401           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3402                   &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );\r
3403       }\r
3404 \r
3405     }\r
3406 \r
3407     if ( handle->drainCounter ) {\r
3408       handle->drainCounter++;\r
3409       goto unlock;\r
3410     }\r
3411   }\r
3412 \r
3413   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
3414 \r
3415     bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);\r
3416 \r
3417     if (stream_.doConvertBuffer[1]) {\r
3418 \r
3419       // Always interleave ASIO input data.\r
3420       for ( i=0, j=0; i<nChannels; i++ ) {\r
3421         if ( handle->bufferInfos[i].isInput == ASIOTrue )\r
3422           memcpy( &stream_.deviceBuffer[j++*bufferBytes],\r
3423                   handle->bufferInfos[i].buffers[bufferIndex],\r
3424                   bufferBytes );\r
3425       }\r
3426 \r
3427       if ( stream_.doByteSwap[1] )\r
3428         byteSwapBuffer( stream_.deviceBuffer,\r
3429                         stream_.bufferSize * stream_.nDeviceChannels[1],\r
3430                         stream_.deviceFormat[1] );\r
3431       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
3432 \r
3433     }\r
3434     else {\r
3435       for ( i=0, j=0; i<nChannels; i++ ) {\r
3436         if ( handle->bufferInfos[i].isInput == ASIOTrue ) {\r
3437           memcpy( &stream_.userBuffer[1][bufferBytes*j++],\r
3438                   handle->bufferInfos[i].buffers[bufferIndex],\r
3439                   bufferBytes );\r
3440         }\r
3441       }\r
3442 \r
3443       if ( stream_.doByteSwap[1] )\r
3444         byteSwapBuffer( stream_.userBuffer[1],\r
3445                         stream_.bufferSize * stream_.nUserChannels[1],\r
3446                         stream_.userFormat );\r
3447     }\r
3448   }\r
3449 \r
3450  unlock:\r
3451   // The following call was suggested by Malte Clasen.  While the API\r
3452   // documentation indicates it should not be required, some device\r
3453   // drivers apparently do not function correctly without it.\r
3454   ASIOOutputReady();\r
3455 \r
3456   RtApi::tickStreamTime();\r
3457   return SUCCESS;\r
3458 }\r
3459 \r
3460 static void sampleRateChanged( ASIOSampleRate sRate )\r
3461 {\r
3462   // The ASIO documentation says that this usually only happens during\r
3463   // external sync.  Audio processing is not stopped by the driver,\r
3464   // actual sample rate might not have even changed, maybe only the\r
3465   // sample rate status of an AES/EBU or S/PDIF digital input at the\r
3466   // audio device.\r
3467 \r
3468   RtApi *object = (RtApi *) asioCallbackInfo->object;\r
3469   try {\r
3470     object->stopStream();\r
3471   }\r
3472   catch ( RtAudioError &exception ) {\r
3473     std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;\r
3474     return;\r
3475   }\r
3476 \r
3477   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
3478 }\r
3479 \r
3480 static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
3481 {\r
3482   long ret = 0;\r
3483 \r
3484   switch( selector ) {\r
3485   case kAsioSelectorSupported:\r
3486     if ( value == kAsioResetRequest\r
3487          || value == kAsioEngineVersion\r
3488          || value == kAsioResyncRequest\r
3489          || value == kAsioLatenciesChanged\r
3490          // The following three were added for ASIO 2.0, you don't\r
3491          // necessarily have to support them.\r
3492          || value == kAsioSupportsTimeInfo\r
3493          || value == kAsioSupportsTimeCode\r
3494          || value == kAsioSupportsInputMonitor)\r
3495       ret = 1L;\r
3496     break;\r
3497   case kAsioResetRequest:\r
3498     // Defer the task and perform the reset of the driver during the\r
3499     // next "safe" situation.  You cannot reset the driver right now,\r
3500     // as this code is called from the driver.  Reset the driver is\r
3501     // done by completely destruct is. I.e. ASIOStop(),\r
3502     // ASIODisposeBuffers(), Destruction Afterwards you initialize the\r
3503     // driver again.\r
3504     std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;\r
3505     ret = 1L;\r
3506     break;\r
3507   case kAsioResyncRequest:\r
3508     // This informs the application that the driver encountered some\r
3509     // non-fatal data loss.  It is used for synchronization purposes\r
3510     // of different media.  Added mainly to work around the Win16Mutex\r
3511     // problems in Windows 95/98 with the Windows Multimedia system,\r
3512     // which could lose data because the Mutex was held too long by\r
3513     // another thread.  However a driver can issue it in other\r
3514     // situations, too.\r
3515     // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;\r
3516     asioXRun = true;\r
3517     ret = 1L;\r
3518     break;\r
3519   case kAsioLatenciesChanged:\r
3520     // This will inform the host application that the drivers were\r
3521     // latencies changed.  Beware, it this does not mean that the\r
3522     // buffer sizes have changed!  You might need to update internal\r
3523     // delay data.\r
3524     std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;\r
3525     ret = 1L;\r
3526     break;\r
3527   case kAsioEngineVersion:\r
3528     // Return the supported ASIO version of the host application.  If\r
3529     // a host application does not implement this selector, ASIO 1.0\r
3530     // is assumed by the driver.\r
3531     ret = 2L;\r
3532     break;\r
3533   case kAsioSupportsTimeInfo:\r
3534     // Informs the driver whether the\r
3535     // asioCallbacks.bufferSwitchTimeInfo() callback is supported.\r
3536     // For compatibility with ASIO 1.0 drivers the host application\r
3537     // should always support the "old" bufferSwitch method, too.\r
3538     ret = 0;\r
3539     break;\r
3540   case kAsioSupportsTimeCode:\r
3541     // Informs the driver whether application is interested in time\r
3542     // code info.  If an application does not need to know about time\r
3543     // code, the driver has less work to do.\r
3544     ret = 0;\r
3545     break;\r
3546   }\r
3547   return ret;\r
3548 }\r
3549 \r
3550 static const char* getAsioErrorString( ASIOError result )\r
3551 {\r
3552   struct Messages \r
3553   {\r
3554     ASIOError value;\r
3555     const char*message;\r
3556   };\r
3557 \r
3558   static const Messages m[] = \r
3559     {\r
3560       {   ASE_NotPresent,    "Hardware input or output is not present or available." },\r
3561       {   ASE_HWMalfunction,  "Hardware is malfunctioning." },\r
3562       {   ASE_InvalidParameter, "Invalid input parameter." },\r
3563       {   ASE_InvalidMode,      "Invalid mode." },\r
3564       {   ASE_SPNotAdvancing,     "Sample position not advancing." },\r
3565       {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },\r
3566       {   ASE_NoMemory,           "Not enough memory to complete the request." }\r
3567     };\r
3568 \r
3569   for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )\r
3570     if ( m[i].value == result ) return m[i].message;\r
3571 \r
3572   return "Unknown error.";\r
3573 }\r
3574 \r
3575 //******************** End of __WINDOWS_ASIO__ *********************//\r
3576 #endif\r
3577 \r
3578 \r
3579 #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
3580 \r
3581 // Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
3582 // - Introduces support for the Windows WASAPI API\r
3583 // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
3584 // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
3585 // - Includes automatic internal conversion of sample rate, buffer size and channel count\r
3586 \r
3587 #ifndef INITGUID\r
3588   #define INITGUID\r
3589 #endif\r
3590 #include <audioclient.h>\r
3591 #include <avrt.h>\r
3592 #include <mmdeviceapi.h>\r
3593 #include <functiondiscoverykeys_devpkey.h>\r
3594 \r
3595 //=============================================================================\r
3596 \r
3597 #define SAFE_RELEASE( objectPtr )\\r
3598 if ( objectPtr )\\r
3599 {\\r
3600   objectPtr->Release();\\r
3601   objectPtr = NULL;\\r
3602 }\r
3603 \r
3604 typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
3605 \r
3606 //-----------------------------------------------------------------------------\r
3607 \r
3608 // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
3609 // Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
3610 // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
3611 // provide intermediate storage for read / write synchronization.\r
3612 class WasapiBuffer\r
3613 {\r
3614 public:\r
3615   WasapiBuffer()\r
3616     : buffer_( NULL ),\r
3617       bufferSize_( 0 ),\r
3618       inIndex_( 0 ),\r
3619       outIndex_( 0 ) {}\r
3620 \r
3621   ~WasapiBuffer() {\r
3622     delete buffer_;\r
3623   }\r
3624 \r
3625   // sets the length of the internal ring buffer\r
3626   void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
3627     delete buffer_;\r
3628 \r
3629     buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
3630 \r
3631     bufferSize_ = bufferSize;\r
3632     inIndex_ = 0;\r
3633     outIndex_ = 0;\r
3634   }\r
3635 \r
3636   // attempt to push a buffer into the ring buffer at the current "in" index\r
3637   bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3638   {\r
3639     if ( !buffer ||                 // incoming buffer is NULL\r
3640          bufferSize == 0 ||         // incoming buffer has no data\r
3641          bufferSize > bufferSize_ ) // incoming buffer too large\r
3642     {\r
3643       return false;\r
3644     }\r
3645 \r
3646     unsigned int relOutIndex = outIndex_;\r
3647     unsigned int inIndexEnd = inIndex_ + bufferSize;\r
3648     if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
3649       relOutIndex += bufferSize_;\r
3650     }\r
3651 \r
3652     // "in" index can end on the "out" index but cannot begin at it\r
3653     if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
3654       return false; // not enough space between "in" index and "out" index\r
3655     }\r
3656 \r
3657     // copy buffer from external to internal\r
3658     int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
3659     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3660     int fromInSize = bufferSize - fromZeroSize;\r
3661 \r
3662     switch( format )\r
3663       {\r
3664       case RTAUDIO_SINT8:\r
3665         memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
3666         memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
3667         break;\r
3668       case RTAUDIO_SINT16:\r
3669         memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
3670         memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
3671         break;\r
3672       case RTAUDIO_SINT24:\r
3673         memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
3674         memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
3675         break;\r
3676       case RTAUDIO_SINT32:\r
3677         memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
3678         memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
3679         break;\r
3680       case RTAUDIO_FLOAT32:\r
3681         memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
3682         memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
3683         break;\r
3684       case RTAUDIO_FLOAT64:\r
3685         memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
3686         memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
3687         break;\r
3688     }\r
3689 \r
3690     // update "in" index\r
3691     inIndex_ += bufferSize;\r
3692     inIndex_ %= bufferSize_;\r
3693 \r
3694     return true;\r
3695   }\r
3696 \r
3697   // attempt to pull a buffer from the ring buffer from the current "out" index\r
3698   bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3699   {\r
3700     if ( !buffer ||                 // incoming buffer is NULL\r
3701          bufferSize == 0 ||         // incoming buffer has no data\r
3702          bufferSize > bufferSize_ ) // incoming buffer too large\r
3703     {\r
3704       return false;\r
3705     }\r
3706 \r
3707     unsigned int relInIndex = inIndex_;\r
3708     unsigned int outIndexEnd = outIndex_ + bufferSize;\r
3709     if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
3710       relInIndex += bufferSize_;\r
3711     }\r
3712 \r
3713     // "out" index can begin at and end on the "in" index\r
3714     if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
3715       return false; // not enough space between "out" index and "in" index\r
3716     }\r
3717 \r
3718     // copy buffer from internal to external\r
3719     int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
3720     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3721     int fromOutSize = bufferSize - fromZeroSize;\r
3722 \r
3723     switch( format )\r
3724     {\r
3725       case RTAUDIO_SINT8:\r
3726         memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
3727         memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
3728         break;\r
3729       case RTAUDIO_SINT16:\r
3730         memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
3731         memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
3732         break;\r
3733       case RTAUDIO_SINT24:\r
3734         memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
3735         memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
3736         break;\r
3737       case RTAUDIO_SINT32:\r
3738         memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
3739         memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
3740         break;\r
3741       case RTAUDIO_FLOAT32:\r
3742         memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
3743         memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
3744         break;\r
3745       case RTAUDIO_FLOAT64:\r
3746         memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
3747         memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
3748         break;\r
3749     }\r
3750 \r
3751     // update "out" index\r
3752     outIndex_ += bufferSize;\r
3753     outIndex_ %= bufferSize_;\r
3754 \r
3755     return true;\r
3756   }\r
3757 \r
3758 private:\r
3759   char* buffer_;\r
3760   unsigned int bufferSize_;\r
3761   unsigned int inIndex_;\r
3762   unsigned int outIndex_;\r
3763 };\r
3764 \r
3765 //-----------------------------------------------------------------------------\r
3766 \r
3767 // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate and\r
3768 // channel counts between HW and the user. The convertBufferWasapi function is used to perform\r
3769 // these conversions between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
3770 // This sample rate converter favors speed over quality, and works best with conversions between\r
3771 // one rate and its multiple. RtApiWasapi will not populate a device's sample rate list with rates\r
3772 // that may cause artifacts via this conversion.\r
3773 void convertBufferWasapi( char* outBuffer,\r
3774                           const char* inBuffer,\r
3775                           const unsigned int& inChannelCount,\r
3776                           const unsigned int& outChannelCount,\r
3777                           const unsigned int& inSampleRate,\r
3778                           const unsigned int& outSampleRate,\r
3779                           const unsigned int& inSampleCount,\r
3780                           unsigned int& outSampleCount,\r
3781                           const RtAudioFormat& format )\r
3782 {\r
3783   // calculate the new outSampleCount and relative sampleStep\r
3784   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
3785   float sampleStep = 1.0f / sampleRatio;\r
3786   float inSampleFraction = 0.0f;\r
3787   unsigned int commonChannelCount = std::min( inChannelCount, outChannelCount );\r
3788 \r
3789   outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio );\r
3790 \r
3791   // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3792   for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3793   {\r
3794     unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3795 \r
3796     switch ( format )\r
3797     {\r
3798       case RTAUDIO_SINT8:\r
3799         memcpy( &( ( char* ) outBuffer )[ outSample * outChannelCount ], &( ( char* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( char ) );\r
3800         break;\r
3801       case RTAUDIO_SINT16:\r
3802         memcpy( &( ( short* ) outBuffer )[ outSample * outChannelCount ], &( ( short* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( short ) );\r
3803         break;\r
3804       case RTAUDIO_SINT24:\r
3805         memcpy( &( ( S24* ) outBuffer )[ outSample * outChannelCount ], &( ( S24* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( S24 ) );\r
3806         break;\r
3807       case RTAUDIO_SINT32:\r
3808         memcpy( &( ( int* ) outBuffer )[ outSample * outChannelCount ], &( ( int* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( int ) );\r
3809         break;\r
3810       case RTAUDIO_FLOAT32:\r
3811         memcpy( &( ( float* ) outBuffer )[ outSample * outChannelCount ], &( ( float* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( float ) );\r
3812         break;\r
3813       case RTAUDIO_FLOAT64:\r
3814         memcpy( &( ( double* ) outBuffer )[ outSample * outChannelCount ], &( ( double* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( double ) );\r
3815         break;\r
3816     }\r
3817 \r
3818     // jump to next in sample\r
3819     inSampleFraction += sampleStep;\r
3820   }\r
3821 }\r
3822 \r
3823 //-----------------------------------------------------------------------------\r
3824 \r
3825 // A structure to hold various information related to the WASAPI implementation.\r
3826 struct WasapiHandle\r
3827 {\r
3828   IAudioClient* captureAudioClient;\r
3829   IAudioClient* renderAudioClient;\r
3830   IAudioCaptureClient* captureClient;\r
3831   IAudioRenderClient* renderClient;\r
3832   HANDLE captureEvent;\r
3833   HANDLE renderEvent;\r
3834 \r
3835   WasapiHandle()\r
3836   : captureAudioClient( NULL ),\r
3837     renderAudioClient( NULL ),\r
3838     captureClient( NULL ),\r
3839     renderClient( NULL ),\r
3840     captureEvent( NULL ),\r
3841     renderEvent( NULL ) {}\r
3842 };\r
3843 \r
3844 //=============================================================================\r
3845 \r
3846 RtApiWasapi::RtApiWasapi()\r
3847   : coInitialized_( false ), deviceEnumerator_( NULL )\r
3848 {\r
3849   // WASAPI can run either apartment or multi-threaded\r
3850   HRESULT hr = CoInitialize( NULL );\r
3851 \r
3852   if ( !FAILED( hr ) )\r
3853     coInitialized_ = true;\r
3854 \r
3855   // Instantiate device enumerator\r
3856   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
3857                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
3858                          ( void** ) &deviceEnumerator_ );\r
3859 \r
3860   if ( FAILED( hr ) ) {\r
3861     errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
3862     error( RtAudioError::DRIVER_ERROR );\r
3863   }\r
3864 }\r
3865 \r
3866 //-----------------------------------------------------------------------------\r
3867 \r
3868 RtApiWasapi::~RtApiWasapi()\r
3869 {\r
3870   // if this object previously called CoInitialize()\r
3871   if ( coInitialized_ ) {\r
3872     CoUninitialize();\r
3873   }\r
3874 \r
3875   if ( stream_.state != STREAM_CLOSED ) {\r
3876     closeStream();\r
3877   }\r
3878 \r
3879   SAFE_RELEASE( deviceEnumerator_ );\r
3880 }\r
3881 \r
3882 //=============================================================================\r
3883 \r
3884 unsigned int RtApiWasapi::getDeviceCount( void )\r
3885 {\r
3886   unsigned int captureDeviceCount = 0;\r
3887   unsigned int renderDeviceCount = 0;\r
3888 \r
3889   IMMDeviceCollection* captureDevices = NULL;\r
3890   IMMDeviceCollection* renderDevices = NULL;\r
3891 \r
3892   // Count capture devices\r
3893   errorText_.clear();\r
3894   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3895   if ( FAILED( hr ) ) {\r
3896     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
3897     goto Exit;\r
3898   }\r
3899 \r
3900   hr = captureDevices->GetCount( &captureDeviceCount );\r
3901   if ( FAILED( hr ) ) {\r
3902     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
3903     goto Exit;\r
3904   }\r
3905 \r
3906   // Count render devices\r
3907   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3908   if ( FAILED( hr ) ) {\r
3909     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
3910     goto Exit;\r
3911   }\r
3912 \r
3913   hr = renderDevices->GetCount( &renderDeviceCount );\r
3914   if ( FAILED( hr ) ) {\r
3915     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
3916     goto Exit;\r
3917   }\r
3918 \r
3919 Exit:\r
3920   // release all references\r
3921   SAFE_RELEASE( captureDevices );\r
3922   SAFE_RELEASE( renderDevices );\r
3923 \r
3924   if ( errorText_.empty() )\r
3925     return captureDeviceCount + renderDeviceCount;\r
3926 \r
3927   error( RtAudioError::DRIVER_ERROR );\r
3928   return 0;\r
3929 }\r
3930 \r
3931 //-----------------------------------------------------------------------------\r
3932 \r
3933 RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
3934 {\r
3935   RtAudio::DeviceInfo info;\r
3936   unsigned int captureDeviceCount = 0;\r
3937   unsigned int renderDeviceCount = 0;\r
3938   std::wstring deviceName;\r
3939   std::string defaultDeviceName;\r
3940   bool isCaptureDevice = false;\r
3941 \r
3942   PROPVARIANT deviceNameProp;\r
3943   PROPVARIANT defaultDeviceNameProp;\r
3944 \r
3945   IMMDeviceCollection* captureDevices = NULL;\r
3946   IMMDeviceCollection* renderDevices = NULL;\r
3947   IMMDevice* devicePtr = NULL;\r
3948   IMMDevice* defaultDevicePtr = NULL;\r
3949   IAudioClient* audioClient = NULL;\r
3950   IPropertyStore* devicePropStore = NULL;\r
3951   IPropertyStore* defaultDevicePropStore = NULL;\r
3952 \r
3953   WAVEFORMATEX* deviceFormat = NULL;\r
3954   WAVEFORMATEX* closestMatchFormat = NULL;\r
3955 \r
3956   // probed\r
3957   info.probed = false;\r
3958 \r
3959   // Count capture devices\r
3960   errorText_.clear();\r
3961   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
3962   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3963   if ( FAILED( hr ) ) {\r
3964     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
3965     goto Exit;\r
3966   }\r
3967 \r
3968   hr = captureDevices->GetCount( &captureDeviceCount );\r
3969   if ( FAILED( hr ) ) {\r
3970     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
3971     goto Exit;\r
3972   }\r
3973 \r
3974   // Count render devices\r
3975   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3976   if ( FAILED( hr ) ) {\r
3977     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
3978     goto Exit;\r
3979   }\r
3980 \r
3981   hr = renderDevices->GetCount( &renderDeviceCount );\r
3982   if ( FAILED( hr ) ) {\r
3983     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
3984     goto Exit;\r
3985   }\r
3986 \r
3987   // validate device index\r
3988   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
3989     errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
3990     errorType = RtAudioError::INVALID_USE;\r
3991     goto Exit;\r
3992   }\r
3993 \r
3994   // determine whether index falls within capture or render devices\r
3995   if ( device >= renderDeviceCount ) {\r
3996     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
3997     if ( FAILED( hr ) ) {\r
3998       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
3999       goto Exit;\r
4000     }\r
4001     isCaptureDevice = true;\r
4002   }\r
4003   else {\r
4004     hr = renderDevices->Item( device, &devicePtr );\r
4005     if ( FAILED( hr ) ) {\r
4006       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
4007       goto Exit;\r
4008     }\r
4009     isCaptureDevice = false;\r
4010   }\r
4011 \r
4012   // get default device name\r
4013   if ( isCaptureDevice ) {\r
4014     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
4015     if ( FAILED( hr ) ) {\r
4016       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
4017       goto Exit;\r
4018     }\r
4019   }\r
4020   else {\r
4021     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
4022     if ( FAILED( hr ) ) {\r
4023       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
4024       goto Exit;\r
4025     }\r
4026   }\r
4027 \r
4028   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
4029   if ( FAILED( hr ) ) {\r
4030     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
4031     goto Exit;\r
4032   }\r
4033   PropVariantInit( &defaultDeviceNameProp );\r
4034 \r
4035   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
4036   if ( FAILED( hr ) ) {\r
4037     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
4038     goto Exit;\r
4039   }\r
4040 \r
4041   deviceName = defaultDeviceNameProp.pwszVal;\r
4042   defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );\r
4043 \r
4044   // name\r
4045   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
4046   if ( FAILED( hr ) ) {\r
4047     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
4048     goto Exit;\r
4049   }\r
4050 \r
4051   PropVariantInit( &deviceNameProp );\r
4052 \r
4053   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
4054   if ( FAILED( hr ) ) {\r
4055     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
4056     goto Exit;\r
4057   }\r
4058 \r
4059   deviceName = deviceNameProp.pwszVal;\r
4060   info.name = std::string( deviceName.begin(), deviceName.end() );\r
4061 \r
4062   // is default\r
4063   if ( isCaptureDevice ) {\r
4064     info.isDefaultInput = info.name == defaultDeviceName;\r
4065     info.isDefaultOutput = false;\r
4066   }\r
4067   else {\r
4068     info.isDefaultInput = false;\r
4069     info.isDefaultOutput = info.name == defaultDeviceName;\r
4070   }\r
4071 \r
4072   // channel count\r
4073   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
4074   if ( FAILED( hr ) ) {\r
4075     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
4076     goto Exit;\r
4077   }\r
4078 \r
4079   hr = audioClient->GetMixFormat( &deviceFormat );\r
4080   if ( FAILED( hr ) ) {\r
4081     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
4082     goto Exit;\r
4083   }\r
4084 \r
4085   if ( isCaptureDevice ) {\r
4086     info.inputChannels = deviceFormat->nChannels;\r
4087     info.outputChannels = 0;\r
4088     info.duplexChannels = 0;\r
4089   }\r
4090   else {\r
4091     info.inputChannels = 0;\r
4092     info.outputChannels = deviceFormat->nChannels;\r
4093     info.duplexChannels = 0;\r
4094   }\r
4095 \r
4096   // sample rates\r
4097   info.sampleRates.clear();\r
4098 \r
4099   // allow support for sample rates that are multiples of the base rate\r
4100   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
4101     if ( SAMPLE_RATES[i] < deviceFormat->nSamplesPerSec ) {\r
4102       if ( deviceFormat->nSamplesPerSec % SAMPLE_RATES[i] == 0 ) {\r
4103         info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4104       }\r
4105     }\r
4106     else {\r
4107       if ( SAMPLE_RATES[i] % deviceFormat->nSamplesPerSec == 0 ) {\r
4108         info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4109       }\r
4110     }\r
4111   }\r
4112 \r
4113   // native format\r
4114   info.nativeFormats = 0;\r
4115 \r
4116   if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
4117        ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4118          ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
4119   {\r
4120     if ( deviceFormat->wBitsPerSample == 32 ) {\r
4121       info.nativeFormats |= RTAUDIO_FLOAT32;\r
4122     }\r
4123     else if ( deviceFormat->wBitsPerSample == 64 ) {\r
4124       info.nativeFormats |= RTAUDIO_FLOAT64;\r
4125     }\r
4126   }\r
4127   else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
4128            ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4129              ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
4130   {\r
4131     if ( deviceFormat->wBitsPerSample == 8 ) {\r
4132       info.nativeFormats |= RTAUDIO_SINT8;\r
4133     }\r
4134     else if ( deviceFormat->wBitsPerSample == 16 ) {\r
4135       info.nativeFormats |= RTAUDIO_SINT16;\r
4136     }\r
4137     else if ( deviceFormat->wBitsPerSample == 24 ) {\r
4138       info.nativeFormats |= RTAUDIO_SINT24;\r
4139     }\r
4140     else if ( deviceFormat->wBitsPerSample == 32 ) {\r
4141       info.nativeFormats |= RTAUDIO_SINT32;\r
4142     }\r
4143   }\r
4144 \r
4145   // probed\r
4146   info.probed = true;\r
4147 \r
4148 Exit:\r
4149   // release all references\r
4150   PropVariantClear( &deviceNameProp );\r
4151   PropVariantClear( &defaultDeviceNameProp );\r
4152 \r
4153   SAFE_RELEASE( captureDevices );\r
4154   SAFE_RELEASE( renderDevices );\r
4155   SAFE_RELEASE( devicePtr );\r
4156   SAFE_RELEASE( defaultDevicePtr );\r
4157   SAFE_RELEASE( audioClient );\r
4158   SAFE_RELEASE( devicePropStore );\r
4159   SAFE_RELEASE( defaultDevicePropStore );\r
4160 \r
4161   CoTaskMemFree( deviceFormat );\r
4162   CoTaskMemFree( closestMatchFormat );\r
4163 \r
4164   if ( !errorText_.empty() )\r
4165     error( errorType );\r
4166   return info;\r
4167 }\r
4168 \r
4169 //-----------------------------------------------------------------------------\r
4170 \r
4171 unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
4172 {\r
4173   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4174     if ( getDeviceInfo( i ).isDefaultOutput ) {\r
4175       return i;\r
4176     }\r
4177   }\r
4178 \r
4179   return 0;\r
4180 }\r
4181 \r
4182 //-----------------------------------------------------------------------------\r
4183 \r
4184 unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
4185 {\r
4186   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4187     if ( getDeviceInfo( i ).isDefaultInput ) {\r
4188       return i;\r
4189     }\r
4190   }\r
4191 \r
4192   return 0;\r
4193 }\r
4194 \r
4195 //-----------------------------------------------------------------------------\r
4196 \r
4197 void RtApiWasapi::closeStream( void )\r
4198 {\r
4199   if ( stream_.state == STREAM_CLOSED ) {\r
4200     errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
4201     error( RtAudioError::WARNING );\r
4202     return;\r
4203   }\r
4204 \r
4205   if ( stream_.state != STREAM_STOPPED )\r
4206     stopStream();\r
4207 \r
4208   // clean up stream memory\r
4209   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
4210   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
4211 \r
4212   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
4213   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
4214 \r
4215   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
4216     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
4217 \r
4218   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
4219     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
4220 \r
4221   delete ( WasapiHandle* ) stream_.apiHandle;\r
4222   stream_.apiHandle = NULL;\r
4223 \r
4224   for ( int i = 0; i < 2; i++ ) {\r
4225     if ( stream_.userBuffer[i] ) {\r
4226       free( stream_.userBuffer[i] );\r
4227       stream_.userBuffer[i] = 0;\r
4228     }\r
4229   }\r
4230 \r
4231   if ( stream_.deviceBuffer ) {\r
4232     free( stream_.deviceBuffer );\r
4233     stream_.deviceBuffer = 0;\r
4234   }\r
4235 \r
4236   // update stream state\r
4237   stream_.state = STREAM_CLOSED;\r
4238 }\r
4239 \r
4240 //-----------------------------------------------------------------------------\r
4241 \r
4242 void RtApiWasapi::startStream( void )\r
4243 {\r
4244   verifyStream();\r
4245 \r
4246   if ( stream_.state == STREAM_RUNNING ) {\r
4247     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
4248     error( RtAudioError::WARNING );\r
4249     return;\r
4250   }\r
4251 \r
4252   // update stream state\r
4253   stream_.state = STREAM_RUNNING;\r
4254 \r
4255   // create WASAPI stream thread\r
4256   stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
4257 \r
4258   if ( !stream_.callbackInfo.thread ) {\r
4259     errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
4260     error( RtAudioError::THREAD_ERROR );\r
4261   }\r
4262   else {\r
4263     SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
4264     ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
4265   }\r
4266 }\r
4267 \r
4268 //-----------------------------------------------------------------------------\r
4269 \r
4270 void RtApiWasapi::stopStream( void )\r
4271 {\r
4272   verifyStream();\r
4273 \r
4274   if ( stream_.state == STREAM_STOPPED ) {\r
4275     errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
4276     error( RtAudioError::WARNING );\r
4277     return;\r
4278   }\r
4279 \r
4280   // inform stream thread by setting stream state to STREAM_STOPPING\r
4281   stream_.state = STREAM_STOPPING;\r
4282 \r
4283   // wait until stream thread is stopped\r
4284   while( stream_.state != STREAM_STOPPED ) {\r
4285     Sleep( 1 );\r
4286   }\r
4287 \r
4288   // Wait for the last buffer to play before stopping.\r
4289   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
4290 \r
4291   // stop capture client if applicable\r
4292   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4293     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4294     if ( FAILED( hr ) ) {\r
4295       errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
4296       error( RtAudioError::DRIVER_ERROR );\r
4297       return;\r
4298     }\r
4299   }\r
4300 \r
4301   // stop render client if applicable\r
4302   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4303     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4304     if ( FAILED( hr ) ) {\r
4305       errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
4306       error( RtAudioError::DRIVER_ERROR );\r
4307       return;\r
4308     }\r
4309   }\r
4310 \r
4311   // close thread handle\r
4312   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4313     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
4314     error( RtAudioError::THREAD_ERROR );\r
4315     return;\r
4316   }\r
4317 \r
4318   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4319 }\r
4320 \r
4321 //-----------------------------------------------------------------------------\r
4322 \r
4323 void RtApiWasapi::abortStream( void )\r
4324 {\r
4325   verifyStream();\r
4326 \r
4327   if ( stream_.state == STREAM_STOPPED ) {\r
4328     errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
4329     error( RtAudioError::WARNING );\r
4330     return;\r
4331   }\r
4332 \r
4333   // inform stream thread by setting stream state to STREAM_STOPPING\r
4334   stream_.state = STREAM_STOPPING;\r
4335 \r
4336   // wait until stream thread is stopped\r
4337   while ( stream_.state != STREAM_STOPPED ) {\r
4338     Sleep( 1 );\r
4339   }\r
4340 \r
4341   // stop capture client if applicable\r
4342   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4343     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4344     if ( FAILED( hr ) ) {\r
4345       errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
4346       error( RtAudioError::DRIVER_ERROR );\r
4347       return;\r
4348     }\r
4349   }\r
4350 \r
4351   // stop render client if applicable\r
4352   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4353     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4354     if ( FAILED( hr ) ) {\r
4355       errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
4356       error( RtAudioError::DRIVER_ERROR );\r
4357       return;\r
4358     }\r
4359   }\r
4360 \r
4361   // close thread handle\r
4362   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4363     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
4364     error( RtAudioError::THREAD_ERROR );\r
4365     return;\r
4366   }\r
4367 \r
4368   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4369 }\r
4370 \r
4371 //-----------------------------------------------------------------------------\r
4372 \r
4373 bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
4374                                    unsigned int firstChannel, unsigned int sampleRate,\r
4375                                    RtAudioFormat format, unsigned int* bufferSize,\r
4376                                    RtAudio::StreamOptions* options )\r
4377 {\r
4378   bool methodResult = FAILURE;\r
4379   unsigned int captureDeviceCount = 0;\r
4380   unsigned int renderDeviceCount = 0;\r
4381 \r
4382   IMMDeviceCollection* captureDevices = NULL;\r
4383   IMMDeviceCollection* renderDevices = NULL;\r
4384   IMMDevice* devicePtr = NULL;\r
4385   WAVEFORMATEX* deviceFormat = NULL;\r
4386   unsigned int bufferBytes;\r
4387   stream_.state = STREAM_STOPPED;\r
4388 \r
4389   // create API Handle if not already created\r
4390   if ( !stream_.apiHandle )\r
4391     stream_.apiHandle = ( void* ) new WasapiHandle();\r
4392 \r
4393   // Count capture devices\r
4394   errorText_.clear();\r
4395   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4396   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4397   if ( FAILED( hr ) ) {\r
4398     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
4399     goto Exit;\r
4400   }\r
4401 \r
4402   hr = captureDevices->GetCount( &captureDeviceCount );\r
4403   if ( FAILED( hr ) ) {\r
4404     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
4405     goto Exit;\r
4406   }\r
4407 \r
4408   // Count render devices\r
4409   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4410   if ( FAILED( hr ) ) {\r
4411     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
4412     goto Exit;\r
4413   }\r
4414 \r
4415   hr = renderDevices->GetCount( &renderDeviceCount );\r
4416   if ( FAILED( hr ) ) {\r
4417     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
4418     goto Exit;\r
4419   }\r
4420 \r
4421   // validate device index\r
4422   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4423     errorType = RtAudioError::INVALID_USE;\r
4424     errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
4425     goto Exit;\r
4426   }\r
4427 \r
4428   // determine whether index falls within capture or render devices\r
4429   if ( device >= renderDeviceCount ) {\r
4430     if ( mode != INPUT ) {\r
4431       errorType = RtAudioError::INVALID_USE;\r
4432       errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
4433       goto Exit;\r
4434     }\r
4435 \r
4436     // retrieve captureAudioClient from devicePtr\r
4437     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4438 \r
4439     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4440     if ( FAILED( hr ) ) {\r
4441       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
4442       goto Exit;\r
4443     }\r
4444 \r
4445     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4446                               NULL, ( void** ) &captureAudioClient );\r
4447     if ( FAILED( hr ) ) {\r
4448       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4449       goto Exit;\r
4450     }\r
4451 \r
4452     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
4453     if ( FAILED( hr ) ) {\r
4454       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4455       goto Exit;\r
4456     }\r
4457 \r
4458     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4459     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4460   }\r
4461   else {\r
4462     if ( mode != OUTPUT ) {\r
4463       errorType = RtAudioError::INVALID_USE;\r
4464       errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
4465       goto Exit;\r
4466     }\r
4467 \r
4468     // retrieve renderAudioClient from devicePtr\r
4469     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4470 \r
4471     hr = renderDevices->Item( device, &devicePtr );\r
4472     if ( FAILED( hr ) ) {\r
4473       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
4474       goto Exit;\r
4475     }\r
4476 \r
4477     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4478                               NULL, ( void** ) &renderAudioClient );\r
4479     if ( FAILED( hr ) ) {\r
4480       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4481       goto Exit;\r
4482     }\r
4483 \r
4484     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
4485     if ( FAILED( hr ) ) {\r
4486       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4487       goto Exit;\r
4488     }\r
4489 \r
4490     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4491     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4492   }\r
4493 \r
4494   // fill stream data\r
4495   if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
4496        ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
4497     stream_.mode = DUPLEX;\r
4498   }\r
4499   else {\r
4500     stream_.mode = mode;\r
4501   }\r
4502 \r
4503   stream_.device[mode] = device;\r
4504   stream_.doByteSwap[mode] = false;\r
4505   stream_.sampleRate = sampleRate;\r
4506   stream_.bufferSize = *bufferSize;\r
4507   stream_.nBuffers = 1;\r
4508   stream_.nUserChannels[mode] = channels;\r
4509   stream_.channelOffset[mode] = firstChannel;\r
4510   stream_.userFormat = format;\r
4511   stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
4512 \r
4513   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
4514     stream_.userInterleaved = false;\r
4515   else\r
4516     stream_.userInterleaved = true;\r
4517   stream_.deviceInterleaved[mode] = true;\r
4518 \r
4519   // Set flags for buffer conversion.\r
4520   stream_.doConvertBuffer[mode] = false;\r
4521   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
4522     stream_.doConvertBuffer[mode] = true;\r
4523   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
4524        stream_.nUserChannels[mode] > 1 )\r
4525     stream_.doConvertBuffer[mode] = true;\r
4526 \r
4527   if ( stream_.doConvertBuffer[mode] )\r
4528     setConvertInfo( mode, 0 );\r
4529 \r
4530   // Allocate necessary internal buffers\r
4531   bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
4532 \r
4533   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
4534   if ( !stream_.userBuffer[mode] ) {\r
4535     errorType = RtAudioError::MEMORY_ERROR;\r
4536     errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
4537     goto Exit;\r
4538   }\r
4539 \r
4540   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
4541     stream_.callbackInfo.priority = 15;\r
4542   else\r
4543     stream_.callbackInfo.priority = 0;\r
4544 \r
4545   ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
4546   ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
4547 \r
4548   methodResult = SUCCESS;\r
4549 \r
4550 Exit:\r
4551   //clean up\r
4552   SAFE_RELEASE( captureDevices );\r
4553   SAFE_RELEASE( renderDevices );\r
4554   SAFE_RELEASE( devicePtr );\r
4555   CoTaskMemFree( deviceFormat );\r
4556 \r
4557   // if method failed, close the stream\r
4558   if ( methodResult == FAILURE )\r
4559     closeStream();\r
4560 \r
4561   if ( !errorText_.empty() )\r
4562     error( errorType );\r
4563   return methodResult;\r
4564 }\r
4565 \r
4566 //=============================================================================\r
4567 \r
4568 DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
4569 {\r
4570   if ( wasapiPtr )\r
4571     ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
4572 \r
4573   return 0;\r
4574 }\r
4575 \r
4576 DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
4577 {\r
4578   if ( wasapiPtr )\r
4579     ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
4580 \r
4581   return 0;\r
4582 }\r
4583 \r
4584 DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
4585 {\r
4586   if ( wasapiPtr )\r
4587     ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
4588 \r
4589   return 0;\r
4590 }\r
4591 \r
4592 //-----------------------------------------------------------------------------\r
4593 \r
4594 void RtApiWasapi::wasapiThread()\r
4595 {\r
4596   // as this is a new thread, we must CoInitialize it\r
4597   CoInitialize( NULL );\r
4598 \r
4599   HRESULT hr;\r
4600 \r
4601   IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4602   IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4603   IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
4604   IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
4605   HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
4606   HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
4607 \r
4608   WAVEFORMATEX* captureFormat = NULL;\r
4609   WAVEFORMATEX* renderFormat = NULL;\r
4610   float captureSrRatio = 0.0f;\r
4611   float renderSrRatio = 0.0f;\r
4612   WasapiBuffer captureBuffer;\r
4613   WasapiBuffer renderBuffer;\r
4614 \r
4615   // declare local stream variables\r
4616   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
4617   BYTE* streamBuffer = NULL;\r
4618   unsigned long captureFlags = 0;\r
4619   unsigned int bufferFrameCount = 0;\r
4620   unsigned int numFramesPadding = 0;\r
4621   unsigned int convBufferSize = 0;\r
4622   bool callbackPushed = false;\r
4623   bool callbackPulled = false;\r
4624   bool callbackStopped = false;\r
4625   int callbackResult = 0;\r
4626 \r
4627   // convBuffer is used to store converted buffers between WASAPI and the user\r
4628   char* convBuffer = NULL;\r
4629   unsigned int convBuffSize = 0;\r
4630   unsigned int deviceBuffSize = 0;\r
4631 \r
4632   errorText_.clear();\r
4633   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4634 \r
4635   // Attempt to assign "Pro Audio" characteristic to thread\r
4636   HMODULE AvrtDll = LoadLibrary( "AVRT.dll" );\r
4637   if ( AvrtDll ) {\r
4638     DWORD taskIndex = 0;\r
4639     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
4640     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
4641     FreeLibrary( AvrtDll );\r
4642   }\r
4643 \r
4644   // start capture stream if applicable\r
4645   if ( captureAudioClient ) {\r
4646     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
4647     if ( FAILED( hr ) ) {\r
4648       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4649       goto Exit;\r
4650     }\r
4651 \r
4652     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
4653 \r
4654     // initialize capture stream according to desire buffer size\r
4655     float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
4656     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
4657 \r
4658     if ( !captureClient ) {\r
4659       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4660                                            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4661                                            desiredBufferPeriod,\r
4662                                            desiredBufferPeriod,\r
4663                                            captureFormat,\r
4664                                            NULL );\r
4665       if ( FAILED( hr ) ) {\r
4666         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
4667         goto Exit;\r
4668       }\r
4669 \r
4670       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
4671                                            ( void** ) &captureClient );\r
4672       if ( FAILED( hr ) ) {\r
4673         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
4674         goto Exit;\r
4675       }\r
4676 \r
4677       // configure captureEvent to trigger on every available capture buffer\r
4678       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4679       if ( !captureEvent ) {\r
4680         errorType = RtAudioError::SYSTEM_ERROR;\r
4681         errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
4682         goto Exit;\r
4683       }\r
4684 \r
4685       hr = captureAudioClient->SetEventHandle( captureEvent );\r
4686       if ( FAILED( hr ) ) {\r
4687         errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
4688         goto Exit;\r
4689       }\r
4690 \r
4691       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
4692       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
4693     }\r
4694 \r
4695     unsigned int inBufferSize = 0;\r
4696     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
4697     if ( FAILED( hr ) ) {\r
4698       errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
4699       goto Exit;\r
4700     }\r
4701 \r
4702     // scale outBufferSize according to stream->user sample rate ratio\r
4703     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
4704     inBufferSize *= stream_.nDeviceChannels[INPUT];\r
4705 \r
4706     // set captureBuffer size\r
4707     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
4708 \r
4709     // reset the capture stream\r
4710     hr = captureAudioClient->Reset();\r
4711     if ( FAILED( hr ) ) {\r
4712       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
4713       goto Exit;\r
4714     }\r
4715 \r
4716     // start the capture stream\r
4717     hr = captureAudioClient->Start();\r
4718     if ( FAILED( hr ) ) {\r
4719       errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
4720       goto Exit;\r
4721     }\r
4722   }\r
4723 \r
4724   // start render stream if applicable\r
4725   if ( renderAudioClient ) {\r
4726     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
4727     if ( FAILED( hr ) ) {\r
4728       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4729       goto Exit;\r
4730     }\r
4731 \r
4732     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
4733 \r
4734     // initialize render stream according to desire buffer size\r
4735     float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
4736     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
4737 \r
4738     if ( !renderClient ) {\r
4739       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4740                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4741                                           desiredBufferPeriod,\r
4742                                           desiredBufferPeriod,\r
4743                                           renderFormat,\r
4744                                           NULL );\r
4745       if ( FAILED( hr ) ) {\r
4746         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
4747         goto Exit;\r
4748       }\r
4749 \r
4750       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
4751                                           ( void** ) &renderClient );\r
4752       if ( FAILED( hr ) ) {\r
4753         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
4754         goto Exit;\r
4755       }\r
4756 \r
4757       // configure renderEvent to trigger on every available render buffer\r
4758       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4759       if ( !renderEvent ) {\r
4760         errorType = RtAudioError::SYSTEM_ERROR;\r
4761         errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
4762         goto Exit;\r
4763       }\r
4764 \r
4765       hr = renderAudioClient->SetEventHandle( renderEvent );\r
4766       if ( FAILED( hr ) ) {\r
4767         errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
4768         goto Exit;\r
4769       }\r
4770 \r
4771       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
4772       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
4773     }\r
4774 \r
4775     unsigned int outBufferSize = 0;\r
4776     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
4777     if ( FAILED( hr ) ) {\r
4778       errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
4779       goto Exit;\r
4780     }\r
4781 \r
4782     // scale inBufferSize according to user->stream sample rate ratio\r
4783     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
4784     outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
4785 \r
4786     // set renderBuffer size\r
4787     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4788 \r
4789     // reset the render stream\r
4790     hr = renderAudioClient->Reset();\r
4791     if ( FAILED( hr ) ) {\r
4792       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
4793       goto Exit;\r
4794     }\r
4795 \r
4796     // start the render stream\r
4797     hr = renderAudioClient->Start();\r
4798     if ( FAILED( hr ) ) {\r
4799       errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
4800       goto Exit;\r
4801     }\r
4802   }\r
4803 \r
4804   if ( stream_.mode == INPUT ) {\r
4805     convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4806     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4807   }\r
4808   else if ( stream_.mode == OUTPUT ) {\r
4809     convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4810     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4811   }\r
4812   else if ( stream_.mode == DUPLEX ) {\r
4813     convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4814                              ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4815     deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4816                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4817   }\r
4818 \r
4819   convBuffer = ( char* ) malloc( convBuffSize );\r
4820   stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
4821   if ( !convBuffer || !stream_.deviceBuffer ) {\r
4822     errorType = RtAudioError::MEMORY_ERROR;\r
4823     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
4824     goto Exit;\r
4825   }\r
4826 \r
4827   // stream process loop\r
4828   while ( stream_.state != STREAM_STOPPING ) {\r
4829     if ( !callbackPulled ) {\r
4830       // Callback Input\r
4831       // ==============\r
4832       // 1. Pull callback buffer from inputBuffer\r
4833       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
4834       //                          Convert callback buffer to user format\r
4835 \r
4836       if ( captureAudioClient ) {\r
4837         // Pull callback buffer from inputBuffer\r
4838         callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
4839                                                    ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
4840                                                    stream_.deviceFormat[INPUT] );\r
4841 \r
4842         if ( callbackPulled ) {\r
4843           // Convert callback buffer to user sample rate and channel count\r
4844           convertBufferWasapi( stream_.deviceBuffer,\r
4845                                convBuffer,\r
4846                                stream_.nDeviceChannels[INPUT],\r
4847                                stream_.nUserChannels[INPUT],\r
4848                                captureFormat->nSamplesPerSec,\r
4849                                stream_.sampleRate,\r
4850                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
4851                                convBufferSize,\r
4852                                stream_.deviceFormat[INPUT] );\r
4853 \r
4854           if ( stream_.doConvertBuffer[INPUT] ) {\r
4855             // Convert callback buffer to user format\r
4856             convertBuffer( stream_.userBuffer[INPUT],\r
4857                            stream_.deviceBuffer,\r
4858                            stream_.convertInfo[INPUT] );\r
4859           }\r
4860           else {\r
4861             // no conversion, simple copy deviceBuffer to userBuffer\r
4862             memcpy( stream_.userBuffer[INPUT],\r
4863                     stream_.deviceBuffer,\r
4864                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
4865           }\r
4866         }\r
4867       }\r
4868       else {\r
4869         // if there is no capture stream, set callbackPulled flag\r
4870         callbackPulled = true;\r
4871       }\r
4872 \r
4873       // Execute Callback\r
4874       // ================\r
4875       // 1. Execute user callback method\r
4876       // 2. Handle return value from callback\r
4877 \r
4878       // if callback has not requested the stream to stop\r
4879       if ( callbackPulled && !callbackStopped ) {\r
4880         // Execute user callback method\r
4881         callbackResult = callback( stream_.userBuffer[OUTPUT],\r
4882                                    stream_.userBuffer[INPUT],\r
4883                                    stream_.bufferSize,\r
4884                                    getStreamTime(),\r
4885                                    captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
4886                                    stream_.callbackInfo.userData );\r
4887 \r
4888         // Handle return value from callback\r
4889         if ( callbackResult == 1 ) {\r
4890           // instantiate a thread to stop this thread\r
4891           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
4892           if ( !threadHandle ) {\r
4893             errorType = RtAudioError::THREAD_ERROR;\r
4894             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
4895             goto Exit;\r
4896           }\r
4897           else if ( !CloseHandle( threadHandle ) ) {\r
4898             errorType = RtAudioError::THREAD_ERROR;\r
4899             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
4900             goto Exit;\r
4901           }\r
4902 \r
4903           callbackStopped = true;\r
4904         }\r
4905         else if ( callbackResult == 2 ) {\r
4906           // instantiate a thread to stop this thread\r
4907           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
4908           if ( !threadHandle ) {\r
4909             errorType = RtAudioError::THREAD_ERROR;\r
4910             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
4911             goto Exit;\r
4912           }\r
4913           else if ( !CloseHandle( threadHandle ) ) {\r
4914             errorType = RtAudioError::THREAD_ERROR;\r
4915             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
4916             goto Exit;\r
4917           }\r
4918 \r
4919           callbackStopped = true;\r
4920         }\r
4921       }\r
4922     }\r
4923 \r
4924     // Callback Output\r
4925     // ===============\r
4926     // 1. Convert callback buffer to stream format\r
4927     // 2. Convert callback buffer to stream sample rate and channel count\r
4928     // 3. Push callback buffer into outputBuffer\r
4929 \r
4930     if ( renderAudioClient && callbackPulled ) {\r
4931       if ( stream_.doConvertBuffer[OUTPUT] ) {\r
4932         // Convert callback buffer to stream format\r
4933         convertBuffer( stream_.deviceBuffer,\r
4934                        stream_.userBuffer[OUTPUT],\r
4935                        stream_.convertInfo[OUTPUT] );\r
4936 \r
4937         // Convert callback buffer to stream sample rate and channel count\r
4938         convertBufferWasapi( convBuffer,\r
4939                              stream_.deviceBuffer,\r
4940                              stream_.nUserChannels[OUTPUT],\r
4941                              stream_.nDeviceChannels[OUTPUT],\r
4942                              stream_.sampleRate,\r
4943                              renderFormat->nSamplesPerSec,\r
4944                              stream_.bufferSize,\r
4945                              convBufferSize,\r
4946                              stream_.deviceFormat[OUTPUT] );\r
4947       }\r
4948       else {\r
4949         // Convert callback buffer to stream sample rate and channel count\r
4950         convertBufferWasapi( convBuffer,\r
4951                              stream_.userBuffer[OUTPUT],\r
4952                              stream_.nUserChannels[OUTPUT],\r
4953                              stream_.nDeviceChannels[OUTPUT],\r
4954                              stream_.sampleRate,\r
4955                              renderFormat->nSamplesPerSec,\r
4956                              stream_.bufferSize,\r
4957                              convBufferSize,\r
4958                              stream_.deviceFormat[OUTPUT] );\r
4959       }\r
4960 \r
4961       // Push callback buffer into outputBuffer\r
4962       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
4963                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
4964                                                 stream_.deviceFormat[OUTPUT] );\r
4965     }\r
4966 \r
4967     // Stream Capture\r
4968     // ==============\r
4969     // 1. Get capture buffer from stream\r
4970     // 2. Push capture buffer into inputBuffer\r
4971     // 3. If 2. was successful: Release capture buffer\r
4972 \r
4973     if ( captureAudioClient ) {\r
4974       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
4975       if ( !callbackPulled ) {\r
4976         WaitForSingleObject( captureEvent, INFINITE );\r
4977       }\r
4978 \r
4979       // Get capture buffer from stream\r
4980       hr = captureClient->GetBuffer( &streamBuffer,\r
4981                                      &bufferFrameCount,\r
4982                                      &captureFlags, NULL, NULL );\r
4983       if ( FAILED( hr ) ) {\r
4984         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
4985         goto Exit;\r
4986       }\r
4987 \r
4988       if ( bufferFrameCount != 0 ) {\r
4989         // Push capture buffer into inputBuffer\r
4990         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
4991                                       bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
4992                                       stream_.deviceFormat[INPUT] ) )\r
4993         {\r
4994           // Release capture buffer\r
4995           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
4996           if ( FAILED( hr ) ) {\r
4997             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
4998             goto Exit;\r
4999           }\r
5000         }\r
5001         else\r
5002         {\r
5003           // Inform WASAPI that capture was unsuccessful\r
5004           hr = captureClient->ReleaseBuffer( 0 );\r
5005           if ( FAILED( hr ) ) {\r
5006             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5007             goto Exit;\r
5008           }\r
5009         }\r
5010       }\r
5011       else\r
5012       {\r
5013         // Inform WASAPI that capture was unsuccessful\r
5014         hr = captureClient->ReleaseBuffer( 0 );\r
5015         if ( FAILED( hr ) ) {\r
5016           errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5017           goto Exit;\r
5018         }\r
5019       }\r
5020     }\r
5021 \r
5022     // Stream Render\r
5023     // =============\r
5024     // 1. Get render buffer from stream\r
5025     // 2. Pull next buffer from outputBuffer\r
5026     // 3. If 2. was successful: Fill render buffer with next buffer\r
5027     //                          Release render buffer\r
5028 \r
5029     if ( renderAudioClient ) {\r
5030       // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
5031       if ( callbackPulled && !callbackPushed ) {\r
5032         WaitForSingleObject( renderEvent, INFINITE );\r
5033       }\r
5034 \r
5035       // Get render buffer from stream\r
5036       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
5037       if ( FAILED( hr ) ) {\r
5038         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
5039         goto Exit;\r
5040       }\r
5041 \r
5042       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
5043       if ( FAILED( hr ) ) {\r
5044         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
5045         goto Exit;\r
5046       }\r
5047 \r
5048       bufferFrameCount -= numFramesPadding;\r
5049 \r
5050       if ( bufferFrameCount != 0 ) {\r
5051         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
5052         if ( FAILED( hr ) ) {\r
5053           errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
5054           goto Exit;\r
5055         }\r
5056 \r
5057         // Pull next buffer from outputBuffer\r
5058         // Fill render buffer with next buffer\r
5059         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
5060                                      bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
5061                                      stream_.deviceFormat[OUTPUT] ) )\r
5062         {\r
5063           // Release render buffer\r
5064           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
5065           if ( FAILED( hr ) ) {\r
5066             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5067             goto Exit;\r
5068           }\r
5069         }\r
5070         else\r
5071         {\r
5072           // Inform WASAPI that render was unsuccessful\r
5073           hr = renderClient->ReleaseBuffer( 0, 0 );\r
5074           if ( FAILED( hr ) ) {\r
5075             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5076             goto Exit;\r
5077           }\r
5078         }\r
5079       }\r
5080       else\r
5081       {\r
5082         // Inform WASAPI that render was unsuccessful\r
5083         hr = renderClient->ReleaseBuffer( 0, 0 );\r
5084         if ( FAILED( hr ) ) {\r
5085           errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5086           goto Exit;\r
5087         }\r
5088       }\r
5089     }\r
5090 \r
5091     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
5092     if ( callbackPushed ) {\r
5093       callbackPulled = false;\r
5094     }\r
5095 \r
5096     // tick stream time\r
5097     RtApi::tickStreamTime();\r
5098   }\r
5099 \r
5100 Exit:\r
5101   // clean up\r
5102   CoTaskMemFree( captureFormat );\r
5103   CoTaskMemFree( renderFormat );\r
5104 \r
5105   //delete convBuffer;\r
5106   free ( convBuffer );\r
5107 \r
5108   CoUninitialize();\r
5109 \r
5110   // update stream state\r
5111   stream_.state = STREAM_STOPPED;\r
5112 \r
5113   if ( errorText_.empty() )\r
5114     return;\r
5115   else\r
5116     error( errorType );\r
5117 }\r
5118 \r
5119 //******************** End of __WINDOWS_WASAPI__ *********************//\r
5120 #endif\r
5121 \r
5122 \r
5123 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
5124 \r
5125 // Modified by Robin Davies, October 2005\r
5126 // - Improvements to DirectX pointer chasing. \r
5127 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
5128 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
5129 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
5130 // Changed device query structure for RtAudio 4.0.7, January 2010\r
5131 \r
5132 #include <dsound.h>\r
5133 #include <assert.h>\r
5134 #include <algorithm>\r
5135 \r
5136 #if defined(__MINGW32__)\r
5137   // missing from latest mingw winapi\r
5138 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
5139 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
5140 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
5141 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
5142 #endif\r
5143 \r
5144 #define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
5145 \r
5146 #ifdef _MSC_VER // if Microsoft Visual C++\r
5147 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
5148 #endif\r
5149 \r
5150 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
5151 {\r
5152   if ( pointer > bufferSize ) pointer -= bufferSize;\r
5153   if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
5154   if ( pointer < earlierPointer ) pointer += bufferSize;\r
5155   return pointer >= earlierPointer && pointer < laterPointer;\r
5156 }\r
5157 \r
5158 // A structure to hold various information related to the DirectSound\r
5159 // API implementation.\r
5160 struct DsHandle {\r
5161   unsigned int drainCounter; // Tracks callback counts when draining\r
5162   bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
5163   void *id[2];\r
5164   void *buffer[2];\r
5165   bool xrun[2];\r
5166   UINT bufferPointer[2];  \r
5167   DWORD dsBufferSize[2];\r
5168   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
5169   HANDLE condition;\r
5170 \r
5171   DsHandle()\r
5172     :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
5173 };\r
5174 \r
5175 // Declarations for utility functions, callbacks, and structures\r
5176 // specific to the DirectSound implementation.\r
5177 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
5178                                           LPCTSTR description,\r
5179                                           LPCTSTR module,\r
5180                                           LPVOID lpContext );\r
5181 \r
5182 static const char* getErrorString( int code );\r
5183 \r
5184 static unsigned __stdcall callbackHandler( void *ptr );\r
5185 \r
5186 struct DsDevice {\r
5187   LPGUID id[2];\r
5188   bool validId[2];\r
5189   bool found;\r
5190   std::string name;\r
5191 \r
5192   DsDevice()\r
5193   : found(false) { validId[0] = false; validId[1] = false; }\r
5194 };\r
5195 \r
5196 struct DsProbeData {\r
5197   bool isInput;\r
5198   std::vector<struct DsDevice>* dsDevices;\r
5199 };\r
5200 \r
5201 RtApiDs :: RtApiDs()\r
5202 {\r
5203   // Dsound will run both-threaded. If CoInitialize fails, then just\r
5204   // accept whatever the mainline chose for a threading model.\r
5205   coInitialized_ = false;\r
5206   HRESULT hr = CoInitialize( NULL );\r
5207   if ( !FAILED( hr ) ) coInitialized_ = true;\r
5208 }\r
5209 \r
5210 RtApiDs :: ~RtApiDs()\r
5211 {\r
5212   if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
5213   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
5214 }\r
5215 \r
5216 // The DirectSound default output is always the first device.\r
5217 unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
5218 {\r
5219   return 0;\r
5220 }\r
5221 \r
5222 // The DirectSound default input is always the first input device,\r
5223 // which is the first capture device enumerated.\r
5224 unsigned int RtApiDs :: getDefaultInputDevice( void )\r
5225 {\r
5226   return 0;\r
5227 }\r
5228 \r
5229 unsigned int RtApiDs :: getDeviceCount( void )\r
5230 {\r
5231   // Set query flag for previously found devices to false, so that we\r
5232   // can check for any devices that have disappeared.\r
5233   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5234     dsDevices[i].found = false;\r
5235 \r
5236   // Query DirectSound devices.\r
5237   struct DsProbeData probeInfo;\r
5238   probeInfo.isInput = false;\r
5239   probeInfo.dsDevices = &dsDevices;\r
5240   HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5241   if ( FAILED( result ) ) {\r
5242     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
5243     errorText_ = errorStream_.str();\r
5244     error( RtAudioError::WARNING );\r
5245   }\r
5246 \r
5247   // Query DirectSoundCapture devices.\r
5248   probeInfo.isInput = true;\r
5249   result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5250   if ( FAILED( result ) ) {\r
5251     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
5252     errorText_ = errorStream_.str();\r
5253     error( RtAudioError::WARNING );\r
5254   }\r
5255 \r
5256   // Clean out any devices that may have disappeared.\r
5257   std::vector< int > indices;\r
5258   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5259     if ( dsDevices[i].found == false ) indices.push_back( i );\r
5260   //unsigned int nErased = 0;\r
5261   for ( unsigned int i=0; i<indices.size(); i++ )\r
5262     dsDevices.erase( dsDevices.begin()+indices[i] );\r
5263   //dsDevices.erase( dsDevices.begin()-nErased++ );\r
5264 \r
5265   return static_cast<unsigned int>(dsDevices.size());\r
5266 }\r
5267 \r
5268 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
5269 {\r
5270   RtAudio::DeviceInfo info;\r
5271   info.probed = false;\r
5272 \r
5273   if ( dsDevices.size() == 0 ) {\r
5274     // Force a query of all devices\r
5275     getDeviceCount();\r
5276     if ( dsDevices.size() == 0 ) {\r
5277       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
5278       error( RtAudioError::INVALID_USE );\r
5279       return info;\r
5280     }\r
5281   }\r
5282 \r
5283   if ( device >= dsDevices.size() ) {\r
5284     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
5285     error( RtAudioError::INVALID_USE );\r
5286     return info;\r
5287   }\r
5288 \r
5289   HRESULT result;\r
5290   if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
5291 \r
5292   LPDIRECTSOUND output;\r
5293   DSCAPS outCaps;\r
5294   result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5295   if ( FAILED( result ) ) {\r
5296     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5297     errorText_ = errorStream_.str();\r
5298     error( RtAudioError::WARNING );\r
5299     goto probeInput;\r
5300   }\r
5301 \r
5302   outCaps.dwSize = sizeof( outCaps );\r
5303   result = output->GetCaps( &outCaps );\r
5304   if ( FAILED( result ) ) {\r
5305     output->Release();\r
5306     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
5307     errorText_ = errorStream_.str();\r
5308     error( RtAudioError::WARNING );\r
5309     goto probeInput;\r
5310   }\r
5311 \r
5312   // Get output channel information.\r
5313   info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
5314 \r
5315   // Get sample rate information.\r
5316   info.sampleRates.clear();\r
5317   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
5318     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
5319          SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )\r
5320       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
5321   }\r
5322 \r
5323   // Get format information.\r
5324   if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
5325   if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
5326 \r
5327   output->Release();\r
5328 \r
5329   if ( getDefaultOutputDevice() == device )\r
5330     info.isDefaultOutput = true;\r
5331 \r
5332   if ( dsDevices[ device ].validId[1] == false ) {\r
5333     info.name = dsDevices[ device ].name;\r
5334     info.probed = true;\r
5335     return info;\r
5336   }\r
5337 \r
5338  probeInput:\r
5339 \r
5340   LPDIRECTSOUNDCAPTURE input;\r
5341   result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5342   if ( FAILED( result ) ) {\r
5343     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5344     errorText_ = errorStream_.str();\r
5345     error( RtAudioError::WARNING );\r
5346     return info;\r
5347   }\r
5348 \r
5349   DSCCAPS inCaps;\r
5350   inCaps.dwSize = sizeof( inCaps );\r
5351   result = input->GetCaps( &inCaps );\r
5352   if ( FAILED( result ) ) {\r
5353     input->Release();\r
5354     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
5355     errorText_ = errorStream_.str();\r
5356     error( RtAudioError::WARNING );\r
5357     return info;\r
5358   }\r
5359 \r
5360   // Get input channel information.\r
5361   info.inputChannels = inCaps.dwChannels;\r
5362 \r
5363   // Get sample rate and format information.\r
5364   std::vector<unsigned int> rates;\r
5365   if ( inCaps.dwChannels >= 2 ) {\r
5366     if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5367     if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5368     if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5369     if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5370     if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5371     if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5372     if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5373     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5374 \r
5375     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5376       if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
5377       if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
5378       if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
5379       if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
5380     }\r
5381     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5382       if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
5383       if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
5384       if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
5385       if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
5386     }\r
5387   }\r
5388   else if ( inCaps.dwChannels == 1 ) {\r
5389     if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5390     if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5391     if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5392     if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5393     if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5394     if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5395     if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5396     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5397 \r
5398     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5399       if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
5400       if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
5401       if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
5402       if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
5403     }\r
5404     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5405       if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
5406       if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
5407       if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
5408       if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
5409     }\r
5410   }\r
5411   else info.inputChannels = 0; // technically, this would be an error\r
5412 \r
5413   input->Release();\r
5414 \r
5415   if ( info.inputChannels == 0 ) return info;\r
5416 \r
5417   // Copy the supported rates to the info structure but avoid duplication.\r
5418   bool found;\r
5419   for ( unsigned int i=0; i<rates.size(); i++ ) {\r
5420     found = false;\r
5421     for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
5422       if ( rates[i] == info.sampleRates[j] ) {\r
5423         found = true;\r
5424         break;\r
5425       }\r
5426     }\r
5427     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
5428   }\r
5429   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
5430 \r
5431   // If device opens for both playback and capture, we determine the channels.\r
5432   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
5433     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
5434 \r
5435   if ( device == 0 ) info.isDefaultInput = true;\r
5436 \r
5437   // Copy name and return.\r
5438   info.name = dsDevices[ device ].name;\r
5439   info.probed = true;\r
5440   return info;\r
5441 }\r
5442 \r
5443 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
5444                                  unsigned int firstChannel, unsigned int sampleRate,\r
5445                                  RtAudioFormat format, unsigned int *bufferSize,\r
5446                                  RtAudio::StreamOptions *options )\r
5447 {\r
5448   if ( channels + firstChannel > 2 ) {\r
5449     errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
5450     return FAILURE;\r
5451   }\r
5452 \r
5453   size_t nDevices = dsDevices.size();\r
5454   if ( nDevices == 0 ) {\r
5455     // This should not happen because a check is made before this function is called.\r
5456     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
5457     return FAILURE;\r
5458   }\r
5459 \r
5460   if ( device >= nDevices ) {\r
5461     // This should not happen because a check is made before this function is called.\r
5462     errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
5463     return FAILURE;\r
5464   }\r
5465 \r
5466   if ( mode == OUTPUT ) {\r
5467     if ( dsDevices[ device ].validId[0] == false ) {\r
5468       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
5469       errorText_ = errorStream_.str();\r
5470       return FAILURE;\r
5471     }\r
5472   }\r
5473   else { // mode == INPUT\r
5474     if ( dsDevices[ device ].validId[1] == false ) {\r
5475       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
5476       errorText_ = errorStream_.str();\r
5477       return FAILURE;\r
5478     }\r
5479   }\r
5480 \r
5481   // According to a note in PortAudio, using GetDesktopWindow()\r
5482   // instead of GetForegroundWindow() is supposed to avoid problems\r
5483   // that occur when the application's window is not the foreground\r
5484   // window.  Also, if the application window closes before the\r
5485   // DirectSound buffer, DirectSound can crash.  In the past, I had\r
5486   // problems when using GetDesktopWindow() but it seems fine now\r
5487   // (January 2010).  I'll leave it commented here.\r
5488   // HWND hWnd = GetForegroundWindow();\r
5489   HWND hWnd = GetDesktopWindow();\r
5490 \r
5491   // Check the numberOfBuffers parameter and limit the lowest value to\r
5492   // two.  This is a judgement call and a value of two is probably too\r
5493   // low for capture, but it should work for playback.\r
5494   int nBuffers = 0;\r
5495   if ( options ) nBuffers = options->numberOfBuffers;\r
5496   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
5497   if ( nBuffers < 2 ) nBuffers = 3;\r
5498 \r
5499   // Check the lower range of the user-specified buffer size and set\r
5500   // (arbitrarily) to a lower bound of 32.\r
5501   if ( *bufferSize < 32 ) *bufferSize = 32;\r
5502 \r
5503   // Create the wave format structure.  The data format setting will\r
5504   // be determined later.\r
5505   WAVEFORMATEX waveFormat;\r
5506   ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
5507   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
5508   waveFormat.nChannels = channels + firstChannel;\r
5509   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
5510 \r
5511   // Determine the device buffer size. By default, we'll use the value\r
5512   // defined above (32K), but we will grow it to make allowances for\r
5513   // very large software buffer sizes.\r
5514   DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
5515   DWORD dsPointerLeadTime = 0;\r
5516 \r
5517   void *ohandle = 0, *bhandle = 0;\r
5518   HRESULT result;\r
5519   if ( mode == OUTPUT ) {\r
5520 \r
5521     LPDIRECTSOUND output;\r
5522     result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5523     if ( FAILED( result ) ) {\r
5524       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5525       errorText_ = errorStream_.str();\r
5526       return FAILURE;\r
5527     }\r
5528 \r
5529     DSCAPS outCaps;\r
5530     outCaps.dwSize = sizeof( outCaps );\r
5531     result = output->GetCaps( &outCaps );\r
5532     if ( FAILED( result ) ) {\r
5533       output->Release();\r
5534       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
5535       errorText_ = errorStream_.str();\r
5536       return FAILURE;\r
5537     }\r
5538 \r
5539     // Check channel information.\r
5540     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
5541       errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
5542       errorText_ = errorStream_.str();\r
5543       return FAILURE;\r
5544     }\r
5545 \r
5546     // Check format information.  Use 16-bit format unless not\r
5547     // supported or user requests 8-bit.\r
5548     if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
5549          !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
5550       waveFormat.wBitsPerSample = 16;\r
5551       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5552     }\r
5553     else {\r
5554       waveFormat.wBitsPerSample = 8;\r
5555       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5556     }\r
5557     stream_.userFormat = format;\r
5558 \r
5559     // Update wave format structure and buffer information.\r
5560     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5561     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5562     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5563 \r
5564     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5565     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5566       dsBufferSize *= 2;\r
5567 \r
5568     // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
5569     // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
5570     // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
5571     result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
5572     if ( FAILED( result ) ) {\r
5573       output->Release();\r
5574       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
5575       errorText_ = errorStream_.str();\r
5576       return FAILURE;\r
5577     }\r
5578 \r
5579     // Even though we will write to the secondary buffer, we need to\r
5580     // access the primary buffer to set the correct output format\r
5581     // (since the default is 8-bit, 22 kHz!).  Setup the DS primary\r
5582     // buffer description.\r
5583     DSBUFFERDESC bufferDescription;\r
5584     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5585     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5586     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
5587 \r
5588     // Obtain the primary buffer\r
5589     LPDIRECTSOUNDBUFFER buffer;\r
5590     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5591     if ( FAILED( result ) ) {\r
5592       output->Release();\r
5593       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
5594       errorText_ = errorStream_.str();\r
5595       return FAILURE;\r
5596     }\r
5597 \r
5598     // Set the primary DS buffer sound format.\r
5599     result = buffer->SetFormat( &waveFormat );\r
5600     if ( FAILED( result ) ) {\r
5601       output->Release();\r
5602       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
5603       errorText_ = errorStream_.str();\r
5604       return FAILURE;\r
5605     }\r
5606 \r
5607     // Setup the secondary DS buffer description.\r
5608     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5609     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5610     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5611                                   DSBCAPS_GLOBALFOCUS |\r
5612                                   DSBCAPS_GETCURRENTPOSITION2 |\r
5613                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
5614     bufferDescription.dwBufferBytes = dsBufferSize;\r
5615     bufferDescription.lpwfxFormat = &waveFormat;\r
5616 \r
5617     // Try to create the secondary DS buffer.  If that doesn't work,\r
5618     // try to use software mixing.  Otherwise, there's a problem.\r
5619     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5620     if ( FAILED( result ) ) {\r
5621       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5622                                     DSBCAPS_GLOBALFOCUS |\r
5623                                     DSBCAPS_GETCURRENTPOSITION2 |\r
5624                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
5625       result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5626       if ( FAILED( result ) ) {\r
5627         output->Release();\r
5628         errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
5629         errorText_ = errorStream_.str();\r
5630         return FAILURE;\r
5631       }\r
5632     }\r
5633 \r
5634     // Get the buffer size ... might be different from what we specified.\r
5635     DSBCAPS dsbcaps;\r
5636     dsbcaps.dwSize = sizeof( DSBCAPS );\r
5637     result = buffer->GetCaps( &dsbcaps );\r
5638     if ( FAILED( result ) ) {\r
5639       output->Release();\r
5640       buffer->Release();\r
5641       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5642       errorText_ = errorStream_.str();\r
5643       return FAILURE;\r
5644     }\r
5645 \r
5646     dsBufferSize = dsbcaps.dwBufferBytes;\r
5647 \r
5648     // Lock the DS buffer\r
5649     LPVOID audioPtr;\r
5650     DWORD dataLen;\r
5651     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5652     if ( FAILED( result ) ) {\r
5653       output->Release();\r
5654       buffer->Release();\r
5655       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
5656       errorText_ = errorStream_.str();\r
5657       return FAILURE;\r
5658     }\r
5659 \r
5660     // Zero the DS buffer\r
5661     ZeroMemory( audioPtr, dataLen );\r
5662 \r
5663     // Unlock the DS buffer\r
5664     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5665     if ( FAILED( result ) ) {\r
5666       output->Release();\r
5667       buffer->Release();\r
5668       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
5669       errorText_ = errorStream_.str();\r
5670       return FAILURE;\r
5671     }\r
5672 \r
5673     ohandle = (void *) output;\r
5674     bhandle = (void *) buffer;\r
5675   }\r
5676 \r
5677   if ( mode == INPUT ) {\r
5678 \r
5679     LPDIRECTSOUNDCAPTURE input;\r
5680     result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5681     if ( FAILED( result ) ) {\r
5682       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5683       errorText_ = errorStream_.str();\r
5684       return FAILURE;\r
5685     }\r
5686 \r
5687     DSCCAPS inCaps;\r
5688     inCaps.dwSize = sizeof( inCaps );\r
5689     result = input->GetCaps( &inCaps );\r
5690     if ( FAILED( result ) ) {\r
5691       input->Release();\r
5692       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
5693       errorText_ = errorStream_.str();\r
5694       return FAILURE;\r
5695     }\r
5696 \r
5697     // Check channel information.\r
5698     if ( inCaps.dwChannels < channels + firstChannel ) {\r
5699       errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
5700       return FAILURE;\r
5701     }\r
5702 \r
5703     // Check format information.  Use 16-bit format unless user\r
5704     // requests 8-bit.\r
5705     DWORD deviceFormats;\r
5706     if ( channels + firstChannel == 2 ) {\r
5707       deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
5708       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5709         waveFormat.wBitsPerSample = 8;\r
5710         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5711       }\r
5712       else { // assume 16-bit is supported\r
5713         waveFormat.wBitsPerSample = 16;\r
5714         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5715       }\r
5716     }\r
5717     else { // channel == 1\r
5718       deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
5719       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5720         waveFormat.wBitsPerSample = 8;\r
5721         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5722       }\r
5723       else { // assume 16-bit is supported\r
5724         waveFormat.wBitsPerSample = 16;\r
5725         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5726       }\r
5727     }\r
5728     stream_.userFormat = format;\r
5729 \r
5730     // Update wave format structure and buffer information.\r
5731     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5732     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5733     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5734 \r
5735     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5736     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5737       dsBufferSize *= 2;\r
5738 \r
5739     // Setup the secondary DS buffer description.\r
5740     DSCBUFFERDESC bufferDescription;\r
5741     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
5742     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
5743     bufferDescription.dwFlags = 0;\r
5744     bufferDescription.dwReserved = 0;\r
5745     bufferDescription.dwBufferBytes = dsBufferSize;\r
5746     bufferDescription.lpwfxFormat = &waveFormat;\r
5747 \r
5748     // Create the capture buffer.\r
5749     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
5750     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
5751     if ( FAILED( result ) ) {\r
5752       input->Release();\r
5753       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
5754       errorText_ = errorStream_.str();\r
5755       return FAILURE;\r
5756     }\r
5757 \r
5758     // Get the buffer size ... might be different from what we specified.\r
5759     DSCBCAPS dscbcaps;\r
5760     dscbcaps.dwSize = sizeof( DSCBCAPS );\r
5761     result = buffer->GetCaps( &dscbcaps );\r
5762     if ( FAILED( result ) ) {\r
5763       input->Release();\r
5764       buffer->Release();\r
5765       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5766       errorText_ = errorStream_.str();\r
5767       return FAILURE;\r
5768     }\r
5769 \r
5770     dsBufferSize = dscbcaps.dwBufferBytes;\r
5771 \r
5772     // NOTE: We could have a problem here if this is a duplex stream\r
5773     // and the play and capture hardware buffer sizes are different\r
5774     // (I'm actually not sure if that is a problem or not).\r
5775     // Currently, we are not verifying that.\r
5776 \r
5777     // Lock the capture buffer\r
5778     LPVOID audioPtr;\r
5779     DWORD dataLen;\r
5780     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5781     if ( FAILED( result ) ) {\r
5782       input->Release();\r
5783       buffer->Release();\r
5784       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
5785       errorText_ = errorStream_.str();\r
5786       return FAILURE;\r
5787     }\r
5788 \r
5789     // Zero the buffer\r
5790     ZeroMemory( audioPtr, dataLen );\r
5791 \r
5792     // Unlock the buffer\r
5793     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5794     if ( FAILED( result ) ) {\r
5795       input->Release();\r
5796       buffer->Release();\r
5797       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
5798       errorText_ = errorStream_.str();\r
5799       return FAILURE;\r
5800     }\r
5801 \r
5802     ohandle = (void *) input;\r
5803     bhandle = (void *) buffer;\r
5804   }\r
5805 \r
5806   // Set various stream parameters\r
5807   DsHandle *handle = 0;\r
5808   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
5809   stream_.nUserChannels[mode] = channels;\r
5810   stream_.bufferSize = *bufferSize;\r
5811   stream_.channelOffset[mode] = firstChannel;\r
5812   stream_.deviceInterleaved[mode] = true;\r
5813   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
5814   else stream_.userInterleaved = true;\r
5815 \r
5816   // Set flag for buffer conversion\r
5817   stream_.doConvertBuffer[mode] = false;\r
5818   if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
5819     stream_.doConvertBuffer[mode] = true;\r
5820   if (stream_.userFormat != stream_.deviceFormat[mode])\r
5821     stream_.doConvertBuffer[mode] = true;\r
5822   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
5823        stream_.nUserChannels[mode] > 1 )\r
5824     stream_.doConvertBuffer[mode] = true;\r
5825 \r
5826   // Allocate necessary internal buffers\r
5827   long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
5828   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
5829   if ( stream_.userBuffer[mode] == NULL ) {\r
5830     errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
5831     goto error;\r
5832   }\r
5833 \r
5834   if ( stream_.doConvertBuffer[mode] ) {\r
5835 \r
5836     bool makeBuffer = true;\r
5837     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
5838     if ( mode == INPUT ) {\r
5839       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
5840         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
5841         if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
5842       }\r
5843     }\r
5844 \r
5845     if ( makeBuffer ) {\r
5846       bufferBytes *= *bufferSize;\r
5847       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
5848       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
5849       if ( stream_.deviceBuffer == NULL ) {\r
5850         errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
5851         goto error;\r
5852       }\r
5853     }\r
5854   }\r
5855 \r
5856   // Allocate our DsHandle structures for the stream.\r
5857   if ( stream_.apiHandle == 0 ) {\r
5858     try {\r
5859       handle = new DsHandle;\r
5860     }\r
5861     catch ( std::bad_alloc& ) {\r
5862       errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
5863       goto error;\r
5864     }\r
5865 \r
5866     // Create a manual-reset event.\r
5867     handle->condition = CreateEvent( NULL,   // no security\r
5868                                      TRUE,   // manual-reset\r
5869                                      FALSE,  // non-signaled initially\r
5870                                      NULL ); // unnamed\r
5871     stream_.apiHandle = (void *) handle;\r
5872   }\r
5873   else\r
5874     handle = (DsHandle *) stream_.apiHandle;\r
5875   handle->id[mode] = ohandle;\r
5876   handle->buffer[mode] = bhandle;\r
5877   handle->dsBufferSize[mode] = dsBufferSize;\r
5878   handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
5879 \r
5880   stream_.device[mode] = device;\r
5881   stream_.state = STREAM_STOPPED;\r
5882   if ( stream_.mode == OUTPUT && mode == INPUT )\r
5883     // We had already set up an output stream.\r
5884     stream_.mode = DUPLEX;\r
5885   else\r
5886     stream_.mode = mode;\r
5887   stream_.nBuffers = nBuffers;\r
5888   stream_.sampleRate = sampleRate;\r
5889 \r
5890   // Setup the buffer conversion information structure.\r
5891   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
5892 \r
5893   // Setup the callback thread.\r
5894   if ( stream_.callbackInfo.isRunning == false ) {\r
5895     unsigned threadId;\r
5896     stream_.callbackInfo.isRunning = true;\r
5897     stream_.callbackInfo.object = (void *) this;\r
5898     stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
5899                                                   &stream_.callbackInfo, 0, &threadId );\r
5900     if ( stream_.callbackInfo.thread == 0 ) {\r
5901       errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
5902       goto error;\r
5903     }\r
5904 \r
5905     // Boost DS thread priority\r
5906     SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
5907   }\r
5908   return SUCCESS;\r
5909 \r
5910  error:\r
5911   if ( handle ) {\r
5912     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5913       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5914       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5915       if ( buffer ) buffer->Release();\r
5916       object->Release();\r
5917     }\r
5918     if ( handle->buffer[1] ) {\r
5919       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5920       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5921       if ( buffer ) buffer->Release();\r
5922       object->Release();\r
5923     }\r
5924     CloseHandle( handle->condition );\r
5925     delete handle;\r
5926     stream_.apiHandle = 0;\r
5927   }\r
5928 \r
5929   for ( int i=0; i<2; i++ ) {\r
5930     if ( stream_.userBuffer[i] ) {\r
5931       free( stream_.userBuffer[i] );\r
5932       stream_.userBuffer[i] = 0;\r
5933     }\r
5934   }\r
5935 \r
5936   if ( stream_.deviceBuffer ) {\r
5937     free( stream_.deviceBuffer );\r
5938     stream_.deviceBuffer = 0;\r
5939   }\r
5940 \r
5941   stream_.state = STREAM_CLOSED;\r
5942   return FAILURE;\r
5943 }\r
5944 \r
5945 void RtApiDs :: closeStream()\r
5946 {\r
5947   if ( stream_.state == STREAM_CLOSED ) {\r
5948     errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
5949     error( RtAudioError::WARNING );\r
5950     return;\r
5951   }\r
5952 \r
5953   // Stop the callback thread.\r
5954   stream_.callbackInfo.isRunning = false;\r
5955   WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
5956   CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
5957 \r
5958   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
5959   if ( handle ) {\r
5960     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5961       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5962       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5963       if ( buffer ) {\r
5964         buffer->Stop();\r
5965         buffer->Release();\r
5966       }\r
5967       object->Release();\r
5968     }\r
5969     if ( handle->buffer[1] ) {\r
5970       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5971       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5972       if ( buffer ) {\r
5973         buffer->Stop();\r
5974         buffer->Release();\r
5975       }\r
5976       object->Release();\r
5977     }\r
5978     CloseHandle( handle->condition );\r
5979     delete handle;\r
5980     stream_.apiHandle = 0;\r
5981   }\r
5982 \r
5983   for ( int i=0; i<2; i++ ) {\r
5984     if ( stream_.userBuffer[i] ) {\r
5985       free( stream_.userBuffer[i] );\r
5986       stream_.userBuffer[i] = 0;\r
5987     }\r
5988   }\r
5989 \r
5990   if ( stream_.deviceBuffer ) {\r
5991     free( stream_.deviceBuffer );\r
5992     stream_.deviceBuffer = 0;\r
5993   }\r
5994 \r
5995   stream_.mode = UNINITIALIZED;\r
5996   stream_.state = STREAM_CLOSED;\r
5997 }\r
5998 \r
5999 void RtApiDs :: startStream()\r
6000 {\r
6001   verifyStream();\r
6002   if ( stream_.state == STREAM_RUNNING ) {\r
6003     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
6004     error( RtAudioError::WARNING );\r
6005     return;\r
6006   }\r
6007 \r
6008   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6009 \r
6010   // Increase scheduler frequency on lesser windows (a side-effect of\r
6011   // increasing timer accuracy).  On greater windows (Win2K or later),\r
6012   // this is already in effect.\r
6013   timeBeginPeriod( 1 ); \r
6014 \r
6015   buffersRolling = false;\r
6016   duplexPrerollBytes = 0;\r
6017 \r
6018   if ( stream_.mode == DUPLEX ) {\r
6019     // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
6020     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
6021   }\r
6022 \r
6023   HRESULT result = 0;\r
6024   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6025 \r
6026     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6027     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
6028     if ( FAILED( result ) ) {\r
6029       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
6030       errorText_ = errorStream_.str();\r
6031       goto unlock;\r
6032     }\r
6033   }\r
6034 \r
6035   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6036 \r
6037     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6038     result = buffer->Start( DSCBSTART_LOOPING );\r
6039     if ( FAILED( result ) ) {\r
6040       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
6041       errorText_ = errorStream_.str();\r
6042       goto unlock;\r
6043     }\r
6044   }\r
6045 \r
6046   handle->drainCounter = 0;\r
6047   handle->internalDrain = false;\r
6048   ResetEvent( handle->condition );\r
6049   stream_.state = STREAM_RUNNING;\r
6050 \r
6051  unlock:\r
6052   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6053 }\r
6054 \r
6055 void RtApiDs :: stopStream()\r
6056 {\r
6057   verifyStream();\r
6058   if ( stream_.state == STREAM_STOPPED ) {\r
6059     errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
6060     error( RtAudioError::WARNING );\r
6061     return;\r
6062   }\r
6063 \r
6064   HRESULT result = 0;\r
6065   LPVOID audioPtr;\r
6066   DWORD dataLen;\r
6067   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6068   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6069     if ( handle->drainCounter == 0 ) {\r
6070       handle->drainCounter = 2;\r
6071       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
6072     }\r
6073 \r
6074     stream_.state = STREAM_STOPPED;\r
6075 \r
6076     // Stop the buffer and clear memory\r
6077     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6078     result = buffer->Stop();\r
6079     if ( FAILED( result ) ) {\r
6080       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
6081       errorText_ = errorStream_.str();\r
6082       goto unlock;\r
6083     }\r
6084 \r
6085     // Lock the buffer and clear it so that if we start to play again,\r
6086     // we won't have old data playing.\r
6087     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6088     if ( FAILED( result ) ) {\r
6089       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
6090       errorText_ = errorStream_.str();\r
6091       goto unlock;\r
6092     }\r
6093 \r
6094     // Zero the DS buffer\r
6095     ZeroMemory( audioPtr, dataLen );\r
6096 \r
6097     // Unlock the DS buffer\r
6098     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6099     if ( FAILED( result ) ) {\r
6100       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
6101       errorText_ = errorStream_.str();\r
6102       goto unlock;\r
6103     }\r
6104 \r
6105     // If we start playing again, we must begin at beginning of buffer.\r
6106     handle->bufferPointer[0] = 0;\r
6107   }\r
6108 \r
6109   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6110     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6111     audioPtr = NULL;\r
6112     dataLen = 0;\r
6113 \r
6114     stream_.state = STREAM_STOPPED;\r
6115 \r
6116     result = buffer->Stop();\r
6117     if ( FAILED( result ) ) {\r
6118       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
6119       errorText_ = errorStream_.str();\r
6120       goto unlock;\r
6121     }\r
6122 \r
6123     // Lock the buffer and clear it so that if we start to play again,\r
6124     // we won't have old data playing.\r
6125     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6126     if ( FAILED( result ) ) {\r
6127       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
6128       errorText_ = errorStream_.str();\r
6129       goto unlock;\r
6130     }\r
6131 \r
6132     // Zero the DS buffer\r
6133     ZeroMemory( audioPtr, dataLen );\r
6134 \r
6135     // Unlock the DS buffer\r
6136     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6137     if ( FAILED( result ) ) {\r
6138       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
6139       errorText_ = errorStream_.str();\r
6140       goto unlock;\r
6141     }\r
6142 \r
6143     // If we start recording again, we must begin at beginning of buffer.\r
6144     handle->bufferPointer[1] = 0;\r
6145   }\r
6146 \r
6147  unlock:\r
6148   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
6149   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6150 }\r
6151 \r
6152 void RtApiDs :: abortStream()\r
6153 {\r
6154   verifyStream();\r
6155   if ( stream_.state == STREAM_STOPPED ) {\r
6156     errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
6157     error( RtAudioError::WARNING );\r
6158     return;\r
6159   }\r
6160 \r
6161   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6162   handle->drainCounter = 2;\r
6163 \r
6164   stopStream();\r
6165 }\r
6166 \r
6167 void RtApiDs :: callbackEvent()\r
6168 {\r
6169   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
6170     Sleep( 50 ); // sleep 50 milliseconds\r
6171     return;\r
6172   }\r
6173 \r
6174   if ( stream_.state == STREAM_CLOSED ) {\r
6175     errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
6176     error( RtAudioError::WARNING );\r
6177     return;\r
6178   }\r
6179 \r
6180   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
6181   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6182 \r
6183   // Check if we were draining the stream and signal is finished.\r
6184   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
6185 \r
6186     stream_.state = STREAM_STOPPING;\r
6187     if ( handle->internalDrain == false )\r
6188       SetEvent( handle->condition );\r
6189     else\r
6190       stopStream();\r
6191     return;\r
6192   }\r
6193 \r
6194   // Invoke user callback to get fresh output data UNLESS we are\r
6195   // draining stream.\r
6196   if ( handle->drainCounter == 0 ) {\r
6197     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
6198     double streamTime = getStreamTime();\r
6199     RtAudioStreamStatus status = 0;\r
6200     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
6201       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
6202       handle->xrun[0] = false;\r
6203     }\r
6204     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
6205       status |= RTAUDIO_INPUT_OVERFLOW;\r
6206       handle->xrun[1] = false;\r
6207     }\r
6208     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
6209                                   stream_.bufferSize, streamTime, status, info->userData );\r
6210     if ( cbReturnValue == 2 ) {\r
6211       stream_.state = STREAM_STOPPING;\r
6212       handle->drainCounter = 2;\r
6213       abortStream();\r
6214       return;\r
6215     }\r
6216     else if ( cbReturnValue == 1 ) {\r
6217       handle->drainCounter = 1;\r
6218       handle->internalDrain = true;\r
6219     }\r
6220   }\r
6221 \r
6222   HRESULT result;\r
6223   DWORD currentWritePointer, safeWritePointer;\r
6224   DWORD currentReadPointer, safeReadPointer;\r
6225   UINT nextWritePointer;\r
6226 \r
6227   LPVOID buffer1 = NULL;\r
6228   LPVOID buffer2 = NULL;\r
6229   DWORD bufferSize1 = 0;\r
6230   DWORD bufferSize2 = 0;\r
6231 \r
6232   char *buffer;\r
6233   long bufferBytes;\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     if ( handle->drainCounter ) {\r
6417       handle->drainCounter++;\r
6418       goto unlock;\r
6419     }\r
6420   }\r
6421 \r
6422   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6423 \r
6424     // Setup parameters.\r
6425     if ( stream_.doConvertBuffer[1] ) {\r
6426       buffer = stream_.deviceBuffer;\r
6427       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
6428       bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
6429     }\r
6430     else {\r
6431       buffer = stream_.userBuffer[1];\r
6432       bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
6433       bufferBytes *= formatBytes( stream_.userFormat );\r
6434     }\r
6435 \r
6436     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6437     long nextReadPointer = handle->bufferPointer[1];\r
6438     DWORD dsBufferSize = handle->dsBufferSize[1];\r
6439 \r
6440     // Find out where the write and "safe read" pointers are.\r
6441     result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6442     if ( FAILED( result ) ) {\r
6443       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6444       errorText_ = errorStream_.str();\r
6445       error( RtAudioError::SYSTEM_ERROR );\r
6446       return;\r
6447     }\r
6448 \r
6449     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6450     DWORD endRead = nextReadPointer + bufferBytes;\r
6451 \r
6452     // Handling depends on whether we are INPUT or DUPLEX. \r
6453     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
6454     // then a wait here will drag the write pointers into the forbidden zone.\r
6455     // \r
6456     // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
6457     // it's in a safe position. This causes dropouts, but it seems to be the only \r
6458     // practical way to sync up the read and write pointers reliably, given the \r
6459     // the very complex relationship between phase and increment of the read and write \r
6460     // pointers.\r
6461     //\r
6462     // In order to minimize audible dropouts in DUPLEX mode, we will\r
6463     // provide a pre-roll period of 0.5 seconds in which we return\r
6464     // zeros from the read buffer while the pointers sync up.\r
6465 \r
6466     if ( stream_.mode == DUPLEX ) {\r
6467       if ( safeReadPointer < endRead ) {\r
6468         if ( duplexPrerollBytes <= 0 ) {\r
6469           // Pre-roll time over. Be more agressive.\r
6470           int adjustment = endRead-safeReadPointer;\r
6471 \r
6472           handle->xrun[1] = true;\r
6473           // Two cases:\r
6474           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
6475           //     and perform fine adjustments later.\r
6476           //   - small adjustments: back off by twice as much.\r
6477           if ( adjustment >= 2*bufferBytes )\r
6478             nextReadPointer = safeReadPointer-2*bufferBytes;\r
6479           else\r
6480             nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
6481 \r
6482           if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6483 \r
6484         }\r
6485         else {\r
6486           // In pre=roll time. Just do it.\r
6487           nextReadPointer = safeReadPointer - bufferBytes;\r
6488           while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6489         }\r
6490         endRead = nextReadPointer + bufferBytes;\r
6491       }\r
6492     }\r
6493     else { // mode == INPUT\r
6494       while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
6495         // See comments for playback.\r
6496         double millis = (endRead - safeReadPointer) * 1000.0;\r
6497         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
6498         if ( millis < 1.0 ) millis = 1.0;\r
6499         Sleep( (DWORD) millis );\r
6500 \r
6501         // Wake up and find out where we are now.\r
6502         result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6503         if ( FAILED( result ) ) {\r
6504           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6505           errorText_ = errorStream_.str();\r
6506           error( RtAudioError::SYSTEM_ERROR );\r
6507           return;\r
6508         }\r
6509       \r
6510         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6511       }\r
6512     }\r
6513 \r
6514     // Lock free space in the buffer\r
6515     result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
6516                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6517     if ( FAILED( result ) ) {\r
6518       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
6519       errorText_ = errorStream_.str();\r
6520       error( RtAudioError::SYSTEM_ERROR );\r
6521       return;\r
6522     }\r
6523 \r
6524     if ( duplexPrerollBytes <= 0 ) {\r
6525       // Copy our buffer into the DS buffer\r
6526       CopyMemory( buffer, buffer1, bufferSize1 );\r
6527       if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
6528     }\r
6529     else {\r
6530       memset( buffer, 0, bufferSize1 );\r
6531       if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
6532       duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
6533     }\r
6534 \r
6535     // Update our buffer offset and unlock sound buffer\r
6536     nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6537     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6538     if ( FAILED( result ) ) {\r
6539       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
6540       errorText_ = errorStream_.str();\r
6541       error( RtAudioError::SYSTEM_ERROR );\r
6542       return;\r
6543     }\r
6544     handle->bufferPointer[1] = nextReadPointer;\r
6545 \r
6546     // No byte swapping necessary in DirectSound implementation.\r
6547 \r
6548     // If necessary, convert 8-bit data from unsigned to signed.\r
6549     if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
6550       for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
6551 \r
6552     // Do buffer conversion if necessary.\r
6553     if ( stream_.doConvertBuffer[1] )\r
6554       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
6555   }\r
6556 \r
6557  unlock:\r
6558   RtApi::tickStreamTime();\r
6559 }\r
6560 \r
6561 // Definitions for utility functions and callbacks\r
6562 // specific to the DirectSound implementation.\r
6563 \r
6564 static unsigned __stdcall callbackHandler( void *ptr )\r
6565 {\r
6566   CallbackInfo *info = (CallbackInfo *) ptr;\r
6567   RtApiDs *object = (RtApiDs *) info->object;\r
6568   bool* isRunning = &info->isRunning;\r
6569 \r
6570   while ( *isRunning == true ) {\r
6571     object->callbackEvent();\r
6572   }\r
6573 \r
6574   _endthreadex( 0 );\r
6575   return 0;\r
6576 }\r
6577 \r
6578 #include "tchar.h"\r
6579 \r
6580 static std::string convertTChar( LPCTSTR name )\r
6581 {\r
6582 #if defined( UNICODE ) || defined( _UNICODE )\r
6583   int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);\r
6584   std::string s( length-1, '\0' );\r
6585   WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);\r
6586 #else\r
6587   std::string s( name );\r
6588 #endif\r
6589 \r
6590   return s;\r
6591 }\r
6592 \r
6593 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
6594                                           LPCTSTR description,\r
6595                                           LPCTSTR /*module*/,\r
6596                                           LPVOID lpContext )\r
6597 {\r
6598   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
6599   std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
6600 \r
6601   HRESULT hr;\r
6602   bool validDevice = false;\r
6603   if ( probeInfo.isInput == true ) {\r
6604     DSCCAPS caps;\r
6605     LPDIRECTSOUNDCAPTURE object;\r
6606 \r
6607     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
6608     if ( hr != DS_OK ) return TRUE;\r
6609 \r
6610     caps.dwSize = sizeof(caps);\r
6611     hr = object->GetCaps( &caps );\r
6612     if ( hr == DS_OK ) {\r
6613       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
6614         validDevice = true;\r
6615     }\r
6616     object->Release();\r
6617   }\r
6618   else {\r
6619     DSCAPS caps;\r
6620     LPDIRECTSOUND object;\r
6621     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
6622     if ( hr != DS_OK ) return TRUE;\r
6623 \r
6624     caps.dwSize = sizeof(caps);\r
6625     hr = object->GetCaps( &caps );\r
6626     if ( hr == DS_OK ) {\r
6627       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
6628         validDevice = true;\r
6629     }\r
6630     object->Release();\r
6631   }\r
6632 \r
6633   // If good device, then save its name and guid.\r
6634   std::string name = convertTChar( description );\r
6635   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
6636   if ( lpguid == NULL )\r
6637     name = "Default Device";\r
6638   if ( validDevice ) {\r
6639     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
6640       if ( dsDevices[i].name == name ) {\r
6641         dsDevices[i].found = true;\r
6642         if ( probeInfo.isInput ) {\r
6643           dsDevices[i].id[1] = lpguid;\r
6644           dsDevices[i].validId[1] = true;\r
6645         }\r
6646         else {\r
6647           dsDevices[i].id[0] = lpguid;\r
6648           dsDevices[i].validId[0] = true;\r
6649         }\r
6650         return TRUE;\r
6651       }\r
6652     }\r
6653 \r
6654     DsDevice device;\r
6655     device.name = name;\r
6656     device.found = true;\r
6657     if ( probeInfo.isInput ) {\r
6658       device.id[1] = lpguid;\r
6659       device.validId[1] = true;\r
6660     }\r
6661     else {\r
6662       device.id[0] = lpguid;\r
6663       device.validId[0] = true;\r
6664     }\r
6665     dsDevices.push_back( device );\r
6666   }\r
6667 \r
6668   return TRUE;\r
6669 }\r
6670 \r
6671 static const char* getErrorString( int code )\r
6672 {\r
6673   switch ( code ) {\r
6674 \r
6675   case DSERR_ALLOCATED:\r
6676     return "Already allocated";\r
6677 \r
6678   case DSERR_CONTROLUNAVAIL:\r
6679     return "Control unavailable";\r
6680 \r
6681   case DSERR_INVALIDPARAM:\r
6682     return "Invalid parameter";\r
6683 \r
6684   case DSERR_INVALIDCALL:\r
6685     return "Invalid call";\r
6686 \r
6687   case DSERR_GENERIC:\r
6688     return "Generic error";\r
6689 \r
6690   case DSERR_PRIOLEVELNEEDED:\r
6691     return "Priority level needed";\r
6692 \r
6693   case DSERR_OUTOFMEMORY:\r
6694     return "Out of memory";\r
6695 \r
6696   case DSERR_BADFORMAT:\r
6697     return "The sample rate or the channel format is not supported";\r
6698 \r
6699   case DSERR_UNSUPPORTED:\r
6700     return "Not supported";\r
6701 \r
6702   case DSERR_NODRIVER:\r
6703     return "No driver";\r
6704 \r
6705   case DSERR_ALREADYINITIALIZED:\r
6706     return "Already initialized";\r
6707 \r
6708   case DSERR_NOAGGREGATION:\r
6709     return "No aggregation";\r
6710 \r
6711   case DSERR_BUFFERLOST:\r
6712     return "Buffer lost";\r
6713 \r
6714   case DSERR_OTHERAPPHASPRIO:\r
6715     return "Another application already has priority";\r
6716 \r
6717   case DSERR_UNINITIALIZED:\r
6718     return "Uninitialized";\r
6719 \r
6720   default:\r
6721     return "DirectSound unknown error";\r
6722   }\r
6723 }\r
6724 //******************** End of __WINDOWS_DS__ *********************//\r
6725 #endif\r
6726 \r
6727 \r
6728 #if defined(__LINUX_ALSA__)\r
6729 \r
6730 #include <alsa/asoundlib.h>\r
6731 #include <unistd.h>\r
6732 \r
6733   // A structure to hold various information related to the ALSA API\r
6734   // implementation.\r
6735 struct AlsaHandle {\r
6736   snd_pcm_t *handles[2];\r
6737   bool synchronized;\r
6738   bool xrun[2];\r
6739   pthread_cond_t runnable_cv;\r
6740   bool runnable;\r
6741 \r
6742   AlsaHandle()\r
6743     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
6744 };\r
6745 \r
6746 static void *alsaCallbackHandler( void * ptr );\r
6747 \r
6748 RtApiAlsa :: RtApiAlsa()\r
6749 {\r
6750   // Nothing to do here.\r
6751 }\r
6752 \r
6753 RtApiAlsa :: ~RtApiAlsa()\r
6754 {\r
6755   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
6756 }\r
6757 \r
6758 unsigned int RtApiAlsa :: getDeviceCount( void )\r
6759 {\r
6760   unsigned nDevices = 0;\r
6761   int result, subdevice, card;\r
6762   char name[64];\r
6763   snd_ctl_t *handle;\r
6764 \r
6765   // Count cards and devices\r
6766   card = -1;\r
6767   snd_card_next( &card );\r
6768   while ( card >= 0 ) {\r
6769     sprintf( name, "hw:%d", card );\r
6770     result = snd_ctl_open( &handle, name, 0 );\r
6771     if ( result < 0 ) {\r
6772       errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6773       errorText_ = errorStream_.str();\r
6774       error( RtAudioError::WARNING );\r
6775       goto nextcard;\r
6776     }\r
6777     subdevice = -1;\r
6778     while( 1 ) {\r
6779       result = snd_ctl_pcm_next_device( handle, &subdevice );\r
6780       if ( result < 0 ) {\r
6781         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6782         errorText_ = errorStream_.str();\r
6783         error( RtAudioError::WARNING );\r
6784         break;\r
6785       }\r
6786       if ( subdevice < 0 )\r
6787         break;\r
6788       nDevices++;\r
6789     }\r
6790   nextcard:\r
6791     snd_ctl_close( handle );\r
6792     snd_card_next( &card );\r
6793   }\r
6794 \r
6795   result = snd_ctl_open( &handle, "default", 0 );\r
6796   if (result == 0) {\r
6797     nDevices++;\r
6798     snd_ctl_close( handle );\r
6799   }\r
6800 \r
6801   return nDevices;\r
6802 }\r
6803 \r
6804 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
6805 {\r
6806   RtAudio::DeviceInfo info;\r
6807   info.probed = false;\r
6808 \r
6809   unsigned nDevices = 0;\r
6810   int result, subdevice, card;\r
6811   char name[64];\r
6812   snd_ctl_t *chandle;\r
6813 \r
6814   // Count cards and devices\r
6815   card = -1;\r
6816   snd_card_next( &card );\r
6817   while ( card >= 0 ) {\r
6818     sprintf( name, "hw:%d", card );\r
6819     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6820     if ( result < 0 ) {\r
6821       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6822       errorText_ = errorStream_.str();\r
6823       error( RtAudioError::WARNING );\r
6824       goto nextcard;\r
6825     }\r
6826     subdevice = -1;\r
6827     while( 1 ) {\r
6828       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
6829       if ( result < 0 ) {\r
6830         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6831         errorText_ = errorStream_.str();\r
6832         error( RtAudioError::WARNING );\r
6833         break;\r
6834       }\r
6835       if ( subdevice < 0 ) break;\r
6836       if ( nDevices == device ) {\r
6837         sprintf( name, "hw:%d,%d", card, subdevice );\r
6838         goto foundDevice;\r
6839       }\r
6840       nDevices++;\r
6841     }\r
6842   nextcard:\r
6843     snd_ctl_close( chandle );\r
6844     snd_card_next( &card );\r
6845   }\r
6846 \r
6847   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
6848   if ( result == 0 ) {\r
6849     if ( nDevices == device ) {\r
6850       strcpy( name, "default" );\r
6851       goto foundDevice;\r
6852     }\r
6853     nDevices++;\r
6854   }\r
6855 \r
6856   if ( nDevices == 0 ) {\r
6857     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
6858     error( RtAudioError::INVALID_USE );\r
6859     return info;\r
6860   }\r
6861 \r
6862   if ( device >= nDevices ) {\r
6863     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
6864     error( RtAudioError::INVALID_USE );\r
6865     return info;\r
6866   }\r
6867 \r
6868  foundDevice:\r
6869 \r
6870   // If a stream is already open, we cannot probe the stream devices.\r
6871   // Thus, use the saved results.\r
6872   if ( stream_.state != STREAM_CLOSED &&\r
6873        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
6874     snd_ctl_close( chandle );\r
6875     if ( device >= devices_.size() ) {\r
6876       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
6877       error( RtAudioError::WARNING );\r
6878       return info;\r
6879     }\r
6880     return devices_[ device ];\r
6881   }\r
6882 \r
6883   int openMode = SND_PCM_ASYNC;\r
6884   snd_pcm_stream_t stream;\r
6885   snd_pcm_info_t *pcminfo;\r
6886   snd_pcm_info_alloca( &pcminfo );\r
6887   snd_pcm_t *phandle;\r
6888   snd_pcm_hw_params_t *params;\r
6889   snd_pcm_hw_params_alloca( &params );\r
6890 \r
6891   // First try for playback unless default device (which has subdev -1)\r
6892   stream = SND_PCM_STREAM_PLAYBACK;\r
6893   snd_pcm_info_set_stream( pcminfo, stream );\r
6894   if ( subdevice != -1 ) {\r
6895     snd_pcm_info_set_device( pcminfo, subdevice );\r
6896     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
6897 \r
6898     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6899     if ( result < 0 ) {\r
6900       // Device probably doesn't support playback.\r
6901       goto captureProbe;\r
6902     }\r
6903   }\r
6904 \r
6905   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
6906   if ( result < 0 ) {\r
6907     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6908     errorText_ = errorStream_.str();\r
6909     error( RtAudioError::WARNING );\r
6910     goto captureProbe;\r
6911   }\r
6912 \r
6913   // The device is open ... fill the parameter structure.\r
6914   result = snd_pcm_hw_params_any( phandle, params );\r
6915   if ( result < 0 ) {\r
6916     snd_pcm_close( phandle );\r
6917     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6918     errorText_ = errorStream_.str();\r
6919     error( RtAudioError::WARNING );\r
6920     goto captureProbe;\r
6921   }\r
6922 \r
6923   // Get output channel information.\r
6924   unsigned int value;\r
6925   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6926   if ( result < 0 ) {\r
6927     snd_pcm_close( phandle );\r
6928     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
6929     errorText_ = errorStream_.str();\r
6930     error( RtAudioError::WARNING );\r
6931     goto captureProbe;\r
6932   }\r
6933   info.outputChannels = value;\r
6934   snd_pcm_close( phandle );\r
6935 \r
6936  captureProbe:\r
6937   stream = SND_PCM_STREAM_CAPTURE;\r
6938   snd_pcm_info_set_stream( pcminfo, stream );\r
6939 \r
6940   // Now try for capture unless default device (with subdev = -1)\r
6941   if ( subdevice != -1 ) {\r
6942     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6943     snd_ctl_close( chandle );\r
6944     if ( result < 0 ) {\r
6945       // Device probably doesn't support capture.\r
6946       if ( info.outputChannels == 0 ) return info;\r
6947       goto probeParameters;\r
6948     }\r
6949   }\r
6950   else\r
6951     snd_ctl_close( chandle );\r
6952 \r
6953   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
6954   if ( result < 0 ) {\r
6955     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6956     errorText_ = errorStream_.str();\r
6957     error( RtAudioError::WARNING );\r
6958     if ( info.outputChannels == 0 ) return info;\r
6959     goto probeParameters;\r
6960   }\r
6961 \r
6962   // The device is open ... fill the parameter structure.\r
6963   result = snd_pcm_hw_params_any( phandle, params );\r
6964   if ( result < 0 ) {\r
6965     snd_pcm_close( phandle );\r
6966     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6967     errorText_ = errorStream_.str();\r
6968     error( RtAudioError::WARNING );\r
6969     if ( info.outputChannels == 0 ) return info;\r
6970     goto probeParameters;\r
6971   }\r
6972 \r
6973   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
6974   if ( result < 0 ) {\r
6975     snd_pcm_close( phandle );\r
6976     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
6977     errorText_ = errorStream_.str();\r
6978     error( RtAudioError::WARNING );\r
6979     if ( info.outputChannels == 0 ) return info;\r
6980     goto probeParameters;\r
6981   }\r
6982   info.inputChannels = value;\r
6983   snd_pcm_close( phandle );\r
6984 \r
6985   // If device opens for both playback and capture, we determine the channels.\r
6986   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
6987     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
6988 \r
6989   // ALSA doesn't provide default devices so we'll use the first available one.\r
6990   if ( device == 0 && info.outputChannels > 0 )\r
6991     info.isDefaultOutput = true;\r
6992   if ( device == 0 && info.inputChannels > 0 )\r
6993     info.isDefaultInput = true;\r
6994 \r
6995  probeParameters:\r
6996   // At this point, we just need to figure out the supported data\r
6997   // formats and sample rates.  We'll proceed by opening the device in\r
6998   // the direction with the maximum number of channels, or playback if\r
6999   // they are equal.  This might limit our sample rate options, but so\r
7000   // be it.\r
7001 \r
7002   if ( info.outputChannels >= info.inputChannels )\r
7003     stream = SND_PCM_STREAM_PLAYBACK;\r
7004   else\r
7005     stream = SND_PCM_STREAM_CAPTURE;\r
7006   snd_pcm_info_set_stream( pcminfo, stream );\r
7007 \r
7008   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7009   if ( result < 0 ) {\r
7010     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7011     errorText_ = errorStream_.str();\r
7012     error( RtAudioError::WARNING );\r
7013     return info;\r
7014   }\r
7015 \r
7016   // The device is open ... fill the parameter structure.\r
7017   result = snd_pcm_hw_params_any( phandle, params );\r
7018   if ( result < 0 ) {\r
7019     snd_pcm_close( phandle );\r
7020     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7021     errorText_ = errorStream_.str();\r
7022     error( RtAudioError::WARNING );\r
7023     return info;\r
7024   }\r
7025 \r
7026   // Test our discrete set of sample rate values.\r
7027   info.sampleRates.clear();\r
7028   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7029     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )\r
7030       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7031   }\r
7032   if ( info.sampleRates.size() == 0 ) {\r
7033     snd_pcm_close( phandle );\r
7034     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7035     errorText_ = errorStream_.str();\r
7036     error( RtAudioError::WARNING );\r
7037     return info;\r
7038   }\r
7039 \r
7040   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7041   snd_pcm_format_t format;\r
7042   info.nativeFormats = 0;\r
7043   format = SND_PCM_FORMAT_S8;\r
7044   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7045     info.nativeFormats |= RTAUDIO_SINT8;\r
7046   format = SND_PCM_FORMAT_S16;\r
7047   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7048     info.nativeFormats |= RTAUDIO_SINT16;\r
7049   format = SND_PCM_FORMAT_S24;\r
7050   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7051     info.nativeFormats |= RTAUDIO_SINT24;\r
7052   format = SND_PCM_FORMAT_S32;\r
7053   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7054     info.nativeFormats |= RTAUDIO_SINT32;\r
7055   format = SND_PCM_FORMAT_FLOAT;\r
7056   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7057     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7058   format = SND_PCM_FORMAT_FLOAT64;\r
7059   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7060     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7061 \r
7062   // Check that we have at least one supported format\r
7063   if ( info.nativeFormats == 0 ) {\r
7064     snd_pcm_close( phandle );\r
7065     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7066     errorText_ = errorStream_.str();\r
7067     error( RtAudioError::WARNING );\r
7068     return info;\r
7069   }\r
7070 \r
7071   // Get the device name\r
7072   char *cardname;\r
7073   result = snd_card_get_name( card, &cardname );\r
7074   if ( result >= 0 ) {\r
7075     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7076     free( cardname );\r
7077   }\r
7078   info.name = name;\r
7079 \r
7080   // That's all ... close the device and return\r
7081   snd_pcm_close( phandle );\r
7082   info.probed = true;\r
7083   return info;\r
7084 }\r
7085 \r
7086 void RtApiAlsa :: saveDeviceInfo( void )\r
7087 {\r
7088   devices_.clear();\r
7089 \r
7090   unsigned int nDevices = getDeviceCount();\r
7091   devices_.resize( nDevices );\r
7092   for ( unsigned int i=0; i<nDevices; i++ )\r
7093     devices_[i] = getDeviceInfo( i );\r
7094 }\r
7095 \r
7096 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7097                                    unsigned int firstChannel, unsigned int sampleRate,\r
7098                                    RtAudioFormat format, unsigned int *bufferSize,\r
7099                                    RtAudio::StreamOptions *options )\r
7100 \r
7101 {\r
7102 #if defined(__RTAUDIO_DEBUG__)\r
7103   snd_output_t *out;\r
7104   snd_output_stdio_attach(&out, stderr, 0);\r
7105 #endif\r
7106 \r
7107   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7108 \r
7109   unsigned nDevices = 0;\r
7110   int result, subdevice, card;\r
7111   char name[64];\r
7112   snd_ctl_t *chandle;\r
7113 \r
7114   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7115     snprintf(name, sizeof(name), "%s", "default");\r
7116   else {\r
7117     // Count cards and devices\r
7118     card = -1;\r
7119     snd_card_next( &card );\r
7120     while ( card >= 0 ) {\r
7121       sprintf( name, "hw:%d", card );\r
7122       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7123       if ( result < 0 ) {\r
7124         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7125         errorText_ = errorStream_.str();\r
7126         return FAILURE;\r
7127       }\r
7128       subdevice = -1;\r
7129       while( 1 ) {\r
7130         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7131         if ( result < 0 ) break;\r
7132         if ( subdevice < 0 ) break;\r
7133         if ( nDevices == device ) {\r
7134           sprintf( name, "hw:%d,%d", card, subdevice );\r
7135           snd_ctl_close( chandle );\r
7136           goto foundDevice;\r
7137         }\r
7138         nDevices++;\r
7139       }\r
7140       snd_ctl_close( chandle );\r
7141       snd_card_next( &card );\r
7142     }\r
7143 \r
7144     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7145     if ( result == 0 ) {\r
7146       if ( nDevices == device ) {\r
7147         strcpy( name, "default" );\r
7148         goto foundDevice;\r
7149       }\r
7150       nDevices++;\r
7151     }\r
7152 \r
7153     if ( nDevices == 0 ) {\r
7154       // This should not happen because a check is made before this function is called.\r
7155       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7156       return FAILURE;\r
7157     }\r
7158 \r
7159     if ( device >= nDevices ) {\r
7160       // This should not happen because a check is made before this function is called.\r
7161       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7162       return FAILURE;\r
7163     }\r
7164   }\r
7165 \r
7166  foundDevice:\r
7167 \r
7168   // The getDeviceInfo() function will not work for a device that is\r
7169   // already open.  Thus, we'll probe the system before opening a\r
7170   // stream and save the results for use by getDeviceInfo().\r
7171   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7172     this->saveDeviceInfo();\r
7173 \r
7174   snd_pcm_stream_t stream;\r
7175   if ( mode == OUTPUT )\r
7176     stream = SND_PCM_STREAM_PLAYBACK;\r
7177   else\r
7178     stream = SND_PCM_STREAM_CAPTURE;\r
7179 \r
7180   snd_pcm_t *phandle;\r
7181   int openMode = SND_PCM_ASYNC;\r
7182   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7183   if ( result < 0 ) {\r
7184     if ( mode == OUTPUT )\r
7185       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7186     else\r
7187       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7188     errorText_ = errorStream_.str();\r
7189     return FAILURE;\r
7190   }\r
7191 \r
7192   // Fill the parameter structure.\r
7193   snd_pcm_hw_params_t *hw_params;\r
7194   snd_pcm_hw_params_alloca( &hw_params );\r
7195   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7196   if ( result < 0 ) {\r
7197     snd_pcm_close( phandle );\r
7198     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7199     errorText_ = errorStream_.str();\r
7200     return FAILURE;\r
7201   }\r
7202 \r
7203 #if defined(__RTAUDIO_DEBUG__)\r
7204   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7205   snd_pcm_hw_params_dump( hw_params, out );\r
7206 #endif\r
7207 \r
7208   // Set access ... check user preference.\r
7209   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7210     stream_.userInterleaved = false;\r
7211     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7212     if ( result < 0 ) {\r
7213       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7214       stream_.deviceInterleaved[mode] =  true;\r
7215     }\r
7216     else\r
7217       stream_.deviceInterleaved[mode] = false;\r
7218   }\r
7219   else {\r
7220     stream_.userInterleaved = true;\r
7221     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7222     if ( result < 0 ) {\r
7223       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7224       stream_.deviceInterleaved[mode] =  false;\r
7225     }\r
7226     else\r
7227       stream_.deviceInterleaved[mode] =  true;\r
7228   }\r
7229 \r
7230   if ( result < 0 ) {\r
7231     snd_pcm_close( phandle );\r
7232     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7233     errorText_ = errorStream_.str();\r
7234     return FAILURE;\r
7235   }\r
7236 \r
7237   // Determine how to set the device format.\r
7238   stream_.userFormat = format;\r
7239   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7240 \r
7241   if ( format == RTAUDIO_SINT8 )\r
7242     deviceFormat = SND_PCM_FORMAT_S8;\r
7243   else if ( format == RTAUDIO_SINT16 )\r
7244     deviceFormat = SND_PCM_FORMAT_S16;\r
7245   else if ( format == RTAUDIO_SINT24 )\r
7246     deviceFormat = SND_PCM_FORMAT_S24;\r
7247   else if ( format == RTAUDIO_SINT32 )\r
7248     deviceFormat = SND_PCM_FORMAT_S32;\r
7249   else if ( format == RTAUDIO_FLOAT32 )\r
7250     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7251   else if ( format == RTAUDIO_FLOAT64 )\r
7252     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7253 \r
7254   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7255     stream_.deviceFormat[mode] = format;\r
7256     goto setFormat;\r
7257   }\r
7258 \r
7259   // The user requested format is not natively supported by the device.\r
7260   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7261   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7262     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7263     goto setFormat;\r
7264   }\r
7265 \r
7266   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7267   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7268     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7269     goto setFormat;\r
7270   }\r
7271 \r
7272   deviceFormat = SND_PCM_FORMAT_S32;\r
7273   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7274     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7275     goto setFormat;\r
7276   }\r
7277 \r
7278   deviceFormat = SND_PCM_FORMAT_S24;\r
7279   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7280     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7281     goto setFormat;\r
7282   }\r
7283 \r
7284   deviceFormat = SND_PCM_FORMAT_S16;\r
7285   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7286     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7287     goto setFormat;\r
7288   }\r
7289 \r
7290   deviceFormat = SND_PCM_FORMAT_S8;\r
7291   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7292     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7293     goto setFormat;\r
7294   }\r
7295 \r
7296   // If we get here, no supported format was found.\r
7297   snd_pcm_close( phandle );\r
7298   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7299   errorText_ = errorStream_.str();\r
7300   return FAILURE;\r
7301 \r
7302  setFormat:\r
7303   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7304   if ( result < 0 ) {\r
7305     snd_pcm_close( phandle );\r
7306     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7307     errorText_ = errorStream_.str();\r
7308     return FAILURE;\r
7309   }\r
7310 \r
7311   // Determine whether byte-swaping is necessary.\r
7312   stream_.doByteSwap[mode] = false;\r
7313   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7314     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7315     if ( result == 0 )\r
7316       stream_.doByteSwap[mode] = true;\r
7317     else if (result < 0) {\r
7318       snd_pcm_close( phandle );\r
7319       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7320       errorText_ = errorStream_.str();\r
7321       return FAILURE;\r
7322     }\r
7323   }\r
7324 \r
7325   // Set the sample rate.\r
7326   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7327   if ( result < 0 ) {\r
7328     snd_pcm_close( phandle );\r
7329     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7330     errorText_ = errorStream_.str();\r
7331     return FAILURE;\r
7332   }\r
7333 \r
7334   // Determine the number of channels for this device.  We support a possible\r
7335   // minimum device channel number > than the value requested by the user.\r
7336   stream_.nUserChannels[mode] = channels;\r
7337   unsigned int value;\r
7338   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7339   unsigned int deviceChannels = value;\r
7340   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7341     snd_pcm_close( phandle );\r
7342     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7343     errorText_ = errorStream_.str();\r
7344     return FAILURE;\r
7345   }\r
7346 \r
7347   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7348   if ( result < 0 ) {\r
7349     snd_pcm_close( phandle );\r
7350     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7351     errorText_ = errorStream_.str();\r
7352     return FAILURE;\r
7353   }\r
7354   deviceChannels = value;\r
7355   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7356   stream_.nDeviceChannels[mode] = deviceChannels;\r
7357 \r
7358   // Set the device channels.\r
7359   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7360   if ( result < 0 ) {\r
7361     snd_pcm_close( phandle );\r
7362     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7363     errorText_ = errorStream_.str();\r
7364     return FAILURE;\r
7365   }\r
7366 \r
7367   // Set the buffer (or period) size.\r
7368   int dir = 0;\r
7369   snd_pcm_uframes_t periodSize = *bufferSize;\r
7370   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7371   if ( result < 0 ) {\r
7372     snd_pcm_close( phandle );\r
7373     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7374     errorText_ = errorStream_.str();\r
7375     return FAILURE;\r
7376   }\r
7377   *bufferSize = periodSize;\r
7378 \r
7379   // Set the buffer number, which in ALSA is referred to as the "period".\r
7380   unsigned int periods = 0;\r
7381   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7382   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7383   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7384   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7385   if ( result < 0 ) {\r
7386     snd_pcm_close( phandle );\r
7387     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7388     errorText_ = errorStream_.str();\r
7389     return FAILURE;\r
7390   }\r
7391 \r
7392   // If attempting to setup a duplex stream, the bufferSize parameter\r
7393   // MUST be the same in both directions!\r
7394   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7395     snd_pcm_close( phandle );\r
7396     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7397     errorText_ = errorStream_.str();\r
7398     return FAILURE;\r
7399   }\r
7400 \r
7401   stream_.bufferSize = *bufferSize;\r
7402 \r
7403   // Install the hardware configuration\r
7404   result = snd_pcm_hw_params( phandle, hw_params );\r
7405   if ( result < 0 ) {\r
7406     snd_pcm_close( phandle );\r
7407     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7408     errorText_ = errorStream_.str();\r
7409     return FAILURE;\r
7410   }\r
7411 \r
7412 #if defined(__RTAUDIO_DEBUG__)\r
7413   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7414   snd_pcm_hw_params_dump( hw_params, out );\r
7415 #endif\r
7416 \r
7417   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7418   snd_pcm_sw_params_t *sw_params = NULL;\r
7419   snd_pcm_sw_params_alloca( &sw_params );\r
7420   snd_pcm_sw_params_current( phandle, sw_params );\r
7421   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7422   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7423   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7424 \r
7425   // The following two settings were suggested by Theo Veenker\r
7426   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7427   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7428 \r
7429   // here are two options for a fix\r
7430   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7431   snd_pcm_uframes_t val;\r
7432   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7433   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7434 \r
7435   result = snd_pcm_sw_params( phandle, sw_params );\r
7436   if ( result < 0 ) {\r
7437     snd_pcm_close( phandle );\r
7438     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7439     errorText_ = errorStream_.str();\r
7440     return FAILURE;\r
7441   }\r
7442 \r
7443 #if defined(__RTAUDIO_DEBUG__)\r
7444   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7445   snd_pcm_sw_params_dump( sw_params, out );\r
7446 #endif\r
7447 \r
7448   // Set flags for buffer conversion\r
7449   stream_.doConvertBuffer[mode] = false;\r
7450   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7451     stream_.doConvertBuffer[mode] = true;\r
7452   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7453     stream_.doConvertBuffer[mode] = true;\r
7454   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7455        stream_.nUserChannels[mode] > 1 )\r
7456     stream_.doConvertBuffer[mode] = true;\r
7457 \r
7458   // Allocate the ApiHandle if necessary and then save.\r
7459   AlsaHandle *apiInfo = 0;\r
7460   if ( stream_.apiHandle == 0 ) {\r
7461     try {\r
7462       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7463     }\r
7464     catch ( std::bad_alloc& ) {\r
7465       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7466       goto error;\r
7467     }\r
7468 \r
7469     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7470       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7471       goto error;\r
7472     }\r
7473 \r
7474     stream_.apiHandle = (void *) apiInfo;\r
7475     apiInfo->handles[0] = 0;\r
7476     apiInfo->handles[1] = 0;\r
7477   }\r
7478   else {\r
7479     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7480   }\r
7481   apiInfo->handles[mode] = phandle;\r
7482   phandle = 0;\r
7483 \r
7484   // Allocate necessary internal buffers.\r
7485   unsigned long bufferBytes;\r
7486   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7487   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7488   if ( stream_.userBuffer[mode] == NULL ) {\r
7489     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7490     goto error;\r
7491   }\r
7492 \r
7493   if ( stream_.doConvertBuffer[mode] ) {\r
7494 \r
7495     bool makeBuffer = true;\r
7496     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7497     if ( mode == INPUT ) {\r
7498       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7499         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7500         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7501       }\r
7502     }\r
7503 \r
7504     if ( makeBuffer ) {\r
7505       bufferBytes *= *bufferSize;\r
7506       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7507       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7508       if ( stream_.deviceBuffer == NULL ) {\r
7509         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7510         goto error;\r
7511       }\r
7512     }\r
7513   }\r
7514 \r
7515   stream_.sampleRate = sampleRate;\r
7516   stream_.nBuffers = periods;\r
7517   stream_.device[mode] = device;\r
7518   stream_.state = STREAM_STOPPED;\r
7519 \r
7520   // Setup the buffer conversion information structure.\r
7521   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7522 \r
7523   // Setup thread if necessary.\r
7524   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7525     // We had already set up an output stream.\r
7526     stream_.mode = DUPLEX;\r
7527     // Link the streams if possible.\r
7528     apiInfo->synchronized = false;\r
7529     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7530       apiInfo->synchronized = true;\r
7531     else {\r
7532       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7533       error( RtAudioError::WARNING );\r
7534     }\r
7535   }\r
7536   else {\r
7537     stream_.mode = mode;\r
7538 \r
7539     // Setup callback thread.\r
7540     stream_.callbackInfo.object = (void *) this;\r
7541 \r
7542     // Set the thread attributes for joinable and realtime scheduling\r
7543     // priority (optional).  The higher priority will only take affect\r
7544     // if the program is run as root or suid. Note, under Linux\r
7545     // processes with CAP_SYS_NICE privilege, a user can change\r
7546     // scheduling policy and priority (thus need not be root). See\r
7547     // POSIX "capabilities".\r
7548     pthread_attr_t attr;\r
7549     pthread_attr_init( &attr );\r
7550     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7551 \r
7552 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7553     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7554       // We previously attempted to increase the audio callback priority\r
7555       // to SCHED_RR here via the attributes.  However, while no errors\r
7556       // were reported in doing so, it did not work.  So, now this is\r
7557       // done in the alsaCallbackHandler function.\r
7558       stream_.callbackInfo.doRealtime = true;\r
7559       int priority = options->priority;\r
7560       int min = sched_get_priority_min( SCHED_RR );\r
7561       int max = sched_get_priority_max( SCHED_RR );\r
7562       if ( priority < min ) priority = min;\r
7563       else if ( priority > max ) priority = max;\r
7564       stream_.callbackInfo.priority = priority;\r
7565     }\r
7566 #endif\r
7567 \r
7568     stream_.callbackInfo.isRunning = true;\r
7569     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7570     pthread_attr_destroy( &attr );\r
7571     if ( result ) {\r
7572       stream_.callbackInfo.isRunning = false;\r
7573       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7574       goto error;\r
7575     }\r
7576   }\r
7577 \r
7578   return SUCCESS;\r
7579 \r
7580  error:\r
7581   if ( apiInfo ) {\r
7582     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7583     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7584     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7585     delete apiInfo;\r
7586     stream_.apiHandle = 0;\r
7587   }\r
7588 \r
7589   if ( phandle) snd_pcm_close( phandle );\r
7590 \r
7591   for ( int i=0; i<2; i++ ) {\r
7592     if ( stream_.userBuffer[i] ) {\r
7593       free( stream_.userBuffer[i] );\r
7594       stream_.userBuffer[i] = 0;\r
7595     }\r
7596   }\r
7597 \r
7598   if ( stream_.deviceBuffer ) {\r
7599     free( stream_.deviceBuffer );\r
7600     stream_.deviceBuffer = 0;\r
7601   }\r
7602 \r
7603   stream_.state = STREAM_CLOSED;\r
7604   return FAILURE;\r
7605 }\r
7606 \r
7607 void RtApiAlsa :: closeStream()\r
7608 {\r
7609   if ( stream_.state == STREAM_CLOSED ) {\r
7610     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7611     error( RtAudioError::WARNING );\r
7612     return;\r
7613   }\r
7614 \r
7615   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7616   stream_.callbackInfo.isRunning = false;\r
7617   MUTEX_LOCK( &stream_.mutex );\r
7618   if ( stream_.state == STREAM_STOPPED ) {\r
7619     apiInfo->runnable = true;\r
7620     pthread_cond_signal( &apiInfo->runnable_cv );\r
7621   }\r
7622   MUTEX_UNLOCK( &stream_.mutex );\r
7623   pthread_join( stream_.callbackInfo.thread, NULL );\r
7624 \r
7625   if ( stream_.state == STREAM_RUNNING ) {\r
7626     stream_.state = STREAM_STOPPED;\r
7627     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7628       snd_pcm_drop( apiInfo->handles[0] );\r
7629     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7630       snd_pcm_drop( apiInfo->handles[1] );\r
7631   }\r
7632 \r
7633   if ( apiInfo ) {\r
7634     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7635     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7636     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7637     delete apiInfo;\r
7638     stream_.apiHandle = 0;\r
7639   }\r
7640 \r
7641   for ( int i=0; i<2; i++ ) {\r
7642     if ( stream_.userBuffer[i] ) {\r
7643       free( stream_.userBuffer[i] );\r
7644       stream_.userBuffer[i] = 0;\r
7645     }\r
7646   }\r
7647 \r
7648   if ( stream_.deviceBuffer ) {\r
7649     free( stream_.deviceBuffer );\r
7650     stream_.deviceBuffer = 0;\r
7651   }\r
7652 \r
7653   stream_.mode = UNINITIALIZED;\r
7654   stream_.state = STREAM_CLOSED;\r
7655 }\r
7656 \r
7657 void RtApiAlsa :: startStream()\r
7658 {\r
7659   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7660 \r
7661   verifyStream();\r
7662   if ( stream_.state == STREAM_RUNNING ) {\r
7663     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7664     error( RtAudioError::WARNING );\r
7665     return;\r
7666   }\r
7667 \r
7668   MUTEX_LOCK( &stream_.mutex );\r
7669 \r
7670   int result = 0;\r
7671   snd_pcm_state_t state;\r
7672   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7673   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7674   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7675     state = snd_pcm_state( handle[0] );\r
7676     if ( state != SND_PCM_STATE_PREPARED ) {\r
7677       result = snd_pcm_prepare( handle[0] );\r
7678       if ( result < 0 ) {\r
7679         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7680         errorText_ = errorStream_.str();\r
7681         goto unlock;\r
7682       }\r
7683     }\r
7684   }\r
7685 \r
7686   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7687     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7688     state = snd_pcm_state( handle[1] );\r
7689     if ( state != SND_PCM_STATE_PREPARED ) {\r
7690       result = snd_pcm_prepare( handle[1] );\r
7691       if ( result < 0 ) {\r
7692         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7693         errorText_ = errorStream_.str();\r
7694         goto unlock;\r
7695       }\r
7696     }\r
7697   }\r
7698 \r
7699   stream_.state = STREAM_RUNNING;\r
7700 \r
7701  unlock:\r
7702   apiInfo->runnable = true;\r
7703   pthread_cond_signal( &apiInfo->runnable_cv );\r
7704   MUTEX_UNLOCK( &stream_.mutex );\r
7705 \r
7706   if ( result >= 0 ) return;\r
7707   error( RtAudioError::SYSTEM_ERROR );\r
7708 }\r
7709 \r
7710 void RtApiAlsa :: stopStream()\r
7711 {\r
7712   verifyStream();\r
7713   if ( stream_.state == STREAM_STOPPED ) {\r
7714     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7715     error( RtAudioError::WARNING );\r
7716     return;\r
7717   }\r
7718 \r
7719   stream_.state = STREAM_STOPPED;\r
7720   MUTEX_LOCK( &stream_.mutex );\r
7721 \r
7722   int result = 0;\r
7723   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7724   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7725   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7726     if ( apiInfo->synchronized ) \r
7727       result = snd_pcm_drop( handle[0] );\r
7728     else\r
7729       result = snd_pcm_drain( handle[0] );\r
7730     if ( result < 0 ) {\r
7731       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7732       errorText_ = errorStream_.str();\r
7733       goto unlock;\r
7734     }\r
7735   }\r
7736 \r
7737   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7738     result = snd_pcm_drop( handle[1] );\r
7739     if ( result < 0 ) {\r
7740       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7741       errorText_ = errorStream_.str();\r
7742       goto unlock;\r
7743     }\r
7744   }\r
7745 \r
7746  unlock:\r
7747   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7748   MUTEX_UNLOCK( &stream_.mutex );\r
7749 \r
7750   if ( result >= 0 ) return;\r
7751   error( RtAudioError::SYSTEM_ERROR );\r
7752 }\r
7753 \r
7754 void RtApiAlsa :: abortStream()\r
7755 {\r
7756   verifyStream();\r
7757   if ( stream_.state == STREAM_STOPPED ) {\r
7758     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7759     error( RtAudioError::WARNING );\r
7760     return;\r
7761   }\r
7762 \r
7763   stream_.state = STREAM_STOPPED;\r
7764   MUTEX_LOCK( &stream_.mutex );\r
7765 \r
7766   int result = 0;\r
7767   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7768   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7769   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7770     result = snd_pcm_drop( handle[0] );\r
7771     if ( result < 0 ) {\r
7772       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7773       errorText_ = errorStream_.str();\r
7774       goto unlock;\r
7775     }\r
7776   }\r
7777 \r
7778   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7779     result = snd_pcm_drop( handle[1] );\r
7780     if ( result < 0 ) {\r
7781       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7782       errorText_ = errorStream_.str();\r
7783       goto unlock;\r
7784     }\r
7785   }\r
7786 \r
7787  unlock:\r
7788   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7789   MUTEX_UNLOCK( &stream_.mutex );\r
7790 \r
7791   if ( result >= 0 ) return;\r
7792   error( RtAudioError::SYSTEM_ERROR );\r
7793 }\r
7794 \r
7795 void RtApiAlsa :: callbackEvent()\r
7796 {\r
7797   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7798   if ( stream_.state == STREAM_STOPPED ) {\r
7799     MUTEX_LOCK( &stream_.mutex );\r
7800     while ( !apiInfo->runnable )\r
7801       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7802 \r
7803     if ( stream_.state != STREAM_RUNNING ) {\r
7804       MUTEX_UNLOCK( &stream_.mutex );\r
7805       return;\r
7806     }\r
7807     MUTEX_UNLOCK( &stream_.mutex );\r
7808   }\r
7809 \r
7810   if ( stream_.state == STREAM_CLOSED ) {\r
7811     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7812     error( RtAudioError::WARNING );\r
7813     return;\r
7814   }\r
7815 \r
7816   int doStopStream = 0;\r
7817   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7818   double streamTime = getStreamTime();\r
7819   RtAudioStreamStatus status = 0;\r
7820   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7821     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
7822     apiInfo->xrun[0] = false;\r
7823   }\r
7824   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
7825     status |= RTAUDIO_INPUT_OVERFLOW;\r
7826     apiInfo->xrun[1] = false;\r
7827   }\r
7828   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
7829                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
7830 \r
7831   if ( doStopStream == 2 ) {\r
7832     abortStream();\r
7833     return;\r
7834   }\r
7835 \r
7836   MUTEX_LOCK( &stream_.mutex );\r
7837 \r
7838   // The state might change while waiting on a mutex.\r
7839   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
7840 \r
7841   int result;\r
7842   char *buffer;\r
7843   int channels;\r
7844   snd_pcm_t **handle;\r
7845   snd_pcm_sframes_t frames;\r
7846   RtAudioFormat format;\r
7847   handle = (snd_pcm_t **) apiInfo->handles;\r
7848 \r
7849   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
7850 \r
7851     // Setup parameters.\r
7852     if ( stream_.doConvertBuffer[1] ) {\r
7853       buffer = stream_.deviceBuffer;\r
7854       channels = stream_.nDeviceChannels[1];\r
7855       format = stream_.deviceFormat[1];\r
7856     }\r
7857     else {\r
7858       buffer = stream_.userBuffer[1];\r
7859       channels = stream_.nUserChannels[1];\r
7860       format = stream_.userFormat;\r
7861     }\r
7862 \r
7863     // Read samples from device in interleaved/non-interleaved format.\r
7864     if ( stream_.deviceInterleaved[1] )\r
7865       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
7866     else {\r
7867       void *bufs[channels];\r
7868       size_t offset = stream_.bufferSize * formatBytes( format );\r
7869       for ( int i=0; i<channels; i++ )\r
7870         bufs[i] = (void *) (buffer + (i * offset));\r
7871       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
7872     }\r
7873 \r
7874     if ( result < (int) stream_.bufferSize ) {\r
7875       // Either an error or overrun occured.\r
7876       if ( result == -EPIPE ) {\r
7877         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
7878         if ( state == SND_PCM_STATE_XRUN ) {\r
7879           apiInfo->xrun[1] = true;\r
7880           result = snd_pcm_prepare( handle[1] );\r
7881           if ( result < 0 ) {\r
7882             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
7883             errorText_ = errorStream_.str();\r
7884           }\r
7885         }\r
7886         else {\r
7887           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7888           errorText_ = errorStream_.str();\r
7889         }\r
7890       }\r
7891       else {\r
7892         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
7893         errorText_ = errorStream_.str();\r
7894       }\r
7895       error( RtAudioError::WARNING );\r
7896       goto tryOutput;\r
7897     }\r
7898 \r
7899     // Do byte swapping if necessary.\r
7900     if ( stream_.doByteSwap[1] )\r
7901       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
7902 \r
7903     // Do buffer conversion if necessary.\r
7904     if ( stream_.doConvertBuffer[1] )\r
7905       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
7906 \r
7907     // Check stream latency\r
7908     result = snd_pcm_delay( handle[1], &frames );\r
7909     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
7910   }\r
7911 \r
7912  tryOutput:\r
7913 \r
7914   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7915 \r
7916     // Setup parameters and do buffer conversion if necessary.\r
7917     if ( stream_.doConvertBuffer[0] ) {\r
7918       buffer = stream_.deviceBuffer;\r
7919       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
7920       channels = stream_.nDeviceChannels[0];\r
7921       format = stream_.deviceFormat[0];\r
7922     }\r
7923     else {\r
7924       buffer = stream_.userBuffer[0];\r
7925       channels = stream_.nUserChannels[0];\r
7926       format = stream_.userFormat;\r
7927     }\r
7928 \r
7929     // Do byte swapping if necessary.\r
7930     if ( stream_.doByteSwap[0] )\r
7931       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
7932 \r
7933     // Write samples to device in interleaved/non-interleaved format.\r
7934     if ( stream_.deviceInterleaved[0] )\r
7935       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
7936     else {\r
7937       void *bufs[channels];\r
7938       size_t offset = stream_.bufferSize * formatBytes( format );\r
7939       for ( int i=0; i<channels; i++ )\r
7940         bufs[i] = (void *) (buffer + (i * offset));\r
7941       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
7942     }\r
7943 \r
7944     if ( result < (int) stream_.bufferSize ) {\r
7945       // Either an error or underrun occured.\r
7946       if ( result == -EPIPE ) {\r
7947         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
7948         if ( state == SND_PCM_STATE_XRUN ) {\r
7949           apiInfo->xrun[0] = true;\r
7950           result = snd_pcm_prepare( handle[0] );\r
7951           if ( result < 0 ) {\r
7952             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
7953             errorText_ = errorStream_.str();\r
7954           }\r
7955         }\r
7956         else {\r
7957           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7958           errorText_ = errorStream_.str();\r
7959         }\r
7960       }\r
7961       else {\r
7962         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
7963         errorText_ = errorStream_.str();\r
7964       }\r
7965       error( RtAudioError::WARNING );\r
7966       goto unlock;\r
7967     }\r
7968 \r
7969     // Check stream latency\r
7970     result = snd_pcm_delay( handle[0], &frames );\r
7971     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
7972   }\r
7973 \r
7974  unlock:\r
7975   MUTEX_UNLOCK( &stream_.mutex );\r
7976 \r
7977   RtApi::tickStreamTime();\r
7978   if ( doStopStream == 1 ) this->stopStream();\r
7979 }\r
7980 \r
7981 static void *alsaCallbackHandler( void *ptr )\r
7982 {\r
7983   CallbackInfo *info = (CallbackInfo *) ptr;\r
7984   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
7985   bool *isRunning = &info->isRunning;\r
7986 \r
7987 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7988   if ( &info->doRealtime ) {\r
7989     pthread_t tID = pthread_self();      // ID of this thread\r
7990     sched_param prio = { info->priority }; // scheduling priority of thread\r
7991     pthread_setschedparam( tID, SCHED_RR, &prio );\r
7992   }\r
7993 #endif\r
7994 \r
7995   while ( *isRunning == true ) {\r
7996     pthread_testcancel();\r
7997     object->callbackEvent();\r
7998   }\r
7999 \r
8000   pthread_exit( NULL );\r
8001 }\r
8002 \r
8003 //******************** End of __LINUX_ALSA__ *********************//\r
8004 #endif\r
8005 \r
8006 #if defined(__LINUX_PULSE__)\r
8007 \r
8008 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8009 // and Tristan Matthews.\r
8010 \r
8011 #include <pulse/error.h>\r
8012 #include <pulse/simple.h>\r
8013 #include <cstdio>\r
8014 \r
8015 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8016                                                       44100, 48000, 96000, 0};\r
8017 \r
8018 struct rtaudio_pa_format_mapping_t {\r
8019   RtAudioFormat rtaudio_format;\r
8020   pa_sample_format_t pa_format;\r
8021 };\r
8022 \r
8023 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8024   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8025   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8026   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8027   {0, PA_SAMPLE_INVALID}};\r
8028 \r
8029 struct PulseAudioHandle {\r
8030   pa_simple *s_play;\r
8031   pa_simple *s_rec;\r
8032   pthread_t thread;\r
8033   pthread_cond_t runnable_cv;\r
8034   bool runnable;\r
8035   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8036 };\r
8037 \r
8038 RtApiPulse::~RtApiPulse()\r
8039 {\r
8040   if ( stream_.state != STREAM_CLOSED )\r
8041     closeStream();\r
8042 }\r
8043 \r
8044 unsigned int RtApiPulse::getDeviceCount( void )\r
8045 {\r
8046   return 1;\r
8047 }\r
8048 \r
8049 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8050 {\r
8051   RtAudio::DeviceInfo info;\r
8052   info.probed = true;\r
8053   info.name = "PulseAudio";\r
8054   info.outputChannels = 2;\r
8055   info.inputChannels = 2;\r
8056   info.duplexChannels = 2;\r
8057   info.isDefaultOutput = true;\r
8058   info.isDefaultInput = true;\r
8059 \r
8060   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8061     info.sampleRates.push_back( *sr );\r
8062 \r
8063   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8064 \r
8065   return info;\r
8066 }\r
8067 \r
8068 static void *pulseaudio_callback( void * user )\r
8069 {\r
8070   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8071   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8072   volatile bool *isRunning = &cbi->isRunning;\r
8073 \r
8074   while ( *isRunning ) {\r
8075     pthread_testcancel();\r
8076     context->callbackEvent();\r
8077   }\r
8078 \r
8079   pthread_exit( NULL );\r
8080 }\r
8081 \r
8082 void RtApiPulse::closeStream( void )\r
8083 {\r
8084   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8085 \r
8086   stream_.callbackInfo.isRunning = false;\r
8087   if ( pah ) {\r
8088     MUTEX_LOCK( &stream_.mutex );\r
8089     if ( stream_.state == STREAM_STOPPED ) {\r
8090       pah->runnable = true;\r
8091       pthread_cond_signal( &pah->runnable_cv );\r
8092     }\r
8093     MUTEX_UNLOCK( &stream_.mutex );\r
8094 \r
8095     pthread_join( pah->thread, 0 );\r
8096     if ( pah->s_play ) {\r
8097       pa_simple_flush( pah->s_play, NULL );\r
8098       pa_simple_free( pah->s_play );\r
8099     }\r
8100     if ( pah->s_rec )\r
8101       pa_simple_free( pah->s_rec );\r
8102 \r
8103     pthread_cond_destroy( &pah->runnable_cv );\r
8104     delete pah;\r
8105     stream_.apiHandle = 0;\r
8106   }\r
8107 \r
8108   if ( stream_.userBuffer[0] ) {\r
8109     free( stream_.userBuffer[0] );\r
8110     stream_.userBuffer[0] = 0;\r
8111   }\r
8112   if ( stream_.userBuffer[1] ) {\r
8113     free( stream_.userBuffer[1] );\r
8114     stream_.userBuffer[1] = 0;\r
8115   }\r
8116 \r
8117   stream_.state = STREAM_CLOSED;\r
8118   stream_.mode = UNINITIALIZED;\r
8119 }\r
8120 \r
8121 void RtApiPulse::callbackEvent( void )\r
8122 {\r
8123   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8124 \r
8125   if ( stream_.state == STREAM_STOPPED ) {\r
8126     MUTEX_LOCK( &stream_.mutex );\r
8127     while ( !pah->runnable )\r
8128       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8129 \r
8130     if ( stream_.state != STREAM_RUNNING ) {\r
8131       MUTEX_UNLOCK( &stream_.mutex );\r
8132       return;\r
8133     }\r
8134     MUTEX_UNLOCK( &stream_.mutex );\r
8135   }\r
8136 \r
8137   if ( stream_.state == STREAM_CLOSED ) {\r
8138     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8139       "this shouldn't happen!";\r
8140     error( RtAudioError::WARNING );\r
8141     return;\r
8142   }\r
8143 \r
8144   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8145   double streamTime = getStreamTime();\r
8146   RtAudioStreamStatus status = 0;\r
8147   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8148                                stream_.bufferSize, streamTime, status,\r
8149                                stream_.callbackInfo.userData );\r
8150 \r
8151   if ( doStopStream == 2 ) {\r
8152     abortStream();\r
8153     return;\r
8154   }\r
8155 \r
8156   MUTEX_LOCK( &stream_.mutex );\r
8157   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8158   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8159 \r
8160   if ( stream_.state != STREAM_RUNNING )\r
8161     goto unlock;\r
8162 \r
8163   int pa_error;\r
8164   size_t bytes;\r
8165   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8166     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8167         convertBuffer( stream_.deviceBuffer,\r
8168                        stream_.userBuffer[OUTPUT],\r
8169                        stream_.convertInfo[OUTPUT] );\r
8170         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8171                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8172     } else\r
8173         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8174                 formatBytes( stream_.userFormat );\r
8175 \r
8176     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8177       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8178         pa_strerror( pa_error ) << ".";\r
8179       errorText_ = errorStream_.str();\r
8180       error( RtAudioError::WARNING );\r
8181     }\r
8182   }\r
8183 \r
8184   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8185     if ( stream_.doConvertBuffer[INPUT] )\r
8186       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8187         formatBytes( stream_.deviceFormat[INPUT] );\r
8188     else\r
8189       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8190         formatBytes( stream_.userFormat );\r
8191             \r
8192     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8193       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8194         pa_strerror( pa_error ) << ".";\r
8195       errorText_ = errorStream_.str();\r
8196       error( RtAudioError::WARNING );\r
8197     }\r
8198     if ( stream_.doConvertBuffer[INPUT] ) {\r
8199       convertBuffer( stream_.userBuffer[INPUT],\r
8200                      stream_.deviceBuffer,\r
8201                      stream_.convertInfo[INPUT] );\r
8202     }\r
8203   }\r
8204 \r
8205  unlock:\r
8206   MUTEX_UNLOCK( &stream_.mutex );\r
8207   RtApi::tickStreamTime();\r
8208 \r
8209   if ( doStopStream == 1 )\r
8210     stopStream();\r
8211 }\r
8212 \r
8213 void RtApiPulse::startStream( void )\r
8214 {\r
8215   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8216 \r
8217   if ( stream_.state == STREAM_CLOSED ) {\r
8218     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8219     error( RtAudioError::INVALID_USE );\r
8220     return;\r
8221   }\r
8222   if ( stream_.state == STREAM_RUNNING ) {\r
8223     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8224     error( RtAudioError::WARNING );\r
8225     return;\r
8226   }\r
8227 \r
8228   MUTEX_LOCK( &stream_.mutex );\r
8229 \r
8230   stream_.state = STREAM_RUNNING;\r
8231 \r
8232   pah->runnable = true;\r
8233   pthread_cond_signal( &pah->runnable_cv );\r
8234   MUTEX_UNLOCK( &stream_.mutex );\r
8235 }\r
8236 \r
8237 void RtApiPulse::stopStream( void )\r
8238 {\r
8239   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8240 \r
8241   if ( stream_.state == STREAM_CLOSED ) {\r
8242     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8243     error( RtAudioError::INVALID_USE );\r
8244     return;\r
8245   }\r
8246   if ( stream_.state == STREAM_STOPPED ) {\r
8247     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8248     error( RtAudioError::WARNING );\r
8249     return;\r
8250   }\r
8251 \r
8252   stream_.state = STREAM_STOPPED;\r
8253   MUTEX_LOCK( &stream_.mutex );\r
8254 \r
8255   if ( pah && pah->s_play ) {\r
8256     int pa_error;\r
8257     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8258       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8259         pa_strerror( pa_error ) << ".";\r
8260       errorText_ = errorStream_.str();\r
8261       MUTEX_UNLOCK( &stream_.mutex );\r
8262       error( RtAudioError::SYSTEM_ERROR );\r
8263       return;\r
8264     }\r
8265   }\r
8266 \r
8267   stream_.state = STREAM_STOPPED;\r
8268   MUTEX_UNLOCK( &stream_.mutex );\r
8269 }\r
8270 \r
8271 void RtApiPulse::abortStream( void )\r
8272 {\r
8273   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8274 \r
8275   if ( stream_.state == STREAM_CLOSED ) {\r
8276     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8277     error( RtAudioError::INVALID_USE );\r
8278     return;\r
8279   }\r
8280   if ( stream_.state == STREAM_STOPPED ) {\r
8281     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8282     error( RtAudioError::WARNING );\r
8283     return;\r
8284   }\r
8285 \r
8286   stream_.state = STREAM_STOPPED;\r
8287   MUTEX_LOCK( &stream_.mutex );\r
8288 \r
8289   if ( pah && pah->s_play ) {\r
8290     int pa_error;\r
8291     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8292       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8293         pa_strerror( pa_error ) << ".";\r
8294       errorText_ = errorStream_.str();\r
8295       MUTEX_UNLOCK( &stream_.mutex );\r
8296       error( RtAudioError::SYSTEM_ERROR );\r
8297       return;\r
8298     }\r
8299   }\r
8300 \r
8301   stream_.state = STREAM_STOPPED;\r
8302   MUTEX_UNLOCK( &stream_.mutex );\r
8303 }\r
8304 \r
8305 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8306                                   unsigned int channels, unsigned int firstChannel,\r
8307                                   unsigned int sampleRate, RtAudioFormat format,\r
8308                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8309 {\r
8310   PulseAudioHandle *pah = 0;\r
8311   unsigned long bufferBytes = 0;\r
8312   pa_sample_spec ss;\r
8313 \r
8314   if ( device != 0 ) return false;\r
8315   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8316   if ( channels != 1 && channels != 2 ) {\r
8317     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
8318     return false;\r
8319   }\r
8320   ss.channels = channels;\r
8321 \r
8322   if ( firstChannel != 0 ) return false;\r
8323 \r
8324   bool sr_found = false;\r
8325   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8326     if ( sampleRate == *sr ) {\r
8327       sr_found = true;\r
8328       stream_.sampleRate = sampleRate;\r
8329       ss.rate = sampleRate;\r
8330       break;\r
8331     }\r
8332   }\r
8333   if ( !sr_found ) {\r
8334     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8335     return false;\r
8336   }\r
8337 \r
8338   bool sf_found = 0;\r
8339   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8340         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8341     if ( format == sf->rtaudio_format ) {\r
8342       sf_found = true;\r
8343       stream_.userFormat = sf->rtaudio_format;\r
8344       stream_.deviceFormat[mode] = stream_.userFormat;\r
8345       ss.format = sf->pa_format;\r
8346       break;\r
8347     }\r
8348   }\r
8349   if ( !sf_found ) { // Use internal data format conversion.\r
8350     stream_.userFormat = format;\r
8351     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8352     ss.format = PA_SAMPLE_FLOAT32LE;\r
8353   }\r
8354 \r
8355   // Set other stream parameters.\r
8356   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8357   else stream_.userInterleaved = true;\r
8358   stream_.deviceInterleaved[mode] = true;\r
8359   stream_.nBuffers = 1;\r
8360   stream_.doByteSwap[mode] = false;\r
8361   stream_.nUserChannels[mode] = channels;\r
8362   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8363   stream_.channelOffset[mode] = 0;\r
8364   std::string streamName = "RtAudio";\r
8365 \r
8366   // Set flags for buffer conversion.\r
8367   stream_.doConvertBuffer[mode] = false;\r
8368   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8369     stream_.doConvertBuffer[mode] = true;\r
8370   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8371     stream_.doConvertBuffer[mode] = true;\r
8372 \r
8373   // Allocate necessary internal buffers.\r
8374   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8375   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8376   if ( stream_.userBuffer[mode] == NULL ) {\r
8377     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8378     goto error;\r
8379   }\r
8380   stream_.bufferSize = *bufferSize;\r
8381 \r
8382   if ( stream_.doConvertBuffer[mode] ) {\r
8383 \r
8384     bool makeBuffer = true;\r
8385     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8386     if ( mode == INPUT ) {\r
8387       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8388         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8389         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8390       }\r
8391     }\r
8392 \r
8393     if ( makeBuffer ) {\r
8394       bufferBytes *= *bufferSize;\r
8395       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8396       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8397       if ( stream_.deviceBuffer == NULL ) {\r
8398         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8399         goto error;\r
8400       }\r
8401     }\r
8402   }\r
8403 \r
8404   stream_.device[mode] = device;\r
8405 \r
8406   // Setup the buffer conversion information structure.\r
8407   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8408 \r
8409   if ( !stream_.apiHandle ) {\r
8410     PulseAudioHandle *pah = new PulseAudioHandle;\r
8411     if ( !pah ) {\r
8412       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8413       goto error;\r
8414     }\r
8415 \r
8416     stream_.apiHandle = pah;\r
8417     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8418       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8419       goto error;\r
8420     }\r
8421   }\r
8422   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8423 \r
8424   int error;\r
8425   if ( !options->streamName.empty() ) streamName = options->streamName;\r
8426   switch ( mode ) {\r
8427   case INPUT:\r
8428     pa_buffer_attr buffer_attr;\r
8429     buffer_attr.fragsize = bufferBytes;\r
8430     buffer_attr.maxlength = -1;\r
8431 \r
8432     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8433     if ( !pah->s_rec ) {\r
8434       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8435       goto error;\r
8436     }\r
8437     break;\r
8438   case OUTPUT:\r
8439     pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
8440     if ( !pah->s_play ) {\r
8441       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8442       goto error;\r
8443     }\r
8444     break;\r
8445   default:\r
8446     goto error;\r
8447   }\r
8448 \r
8449   if ( stream_.mode == UNINITIALIZED )\r
8450     stream_.mode = mode;\r
8451   else if ( stream_.mode == mode )\r
8452     goto error;\r
8453   else\r
8454     stream_.mode = DUPLEX;\r
8455 \r
8456   if ( !stream_.callbackInfo.isRunning ) {\r
8457     stream_.callbackInfo.object = this;\r
8458     stream_.callbackInfo.isRunning = true;\r
8459     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8460       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8461       goto error;\r
8462     }\r
8463   }\r
8464 \r
8465   stream_.state = STREAM_STOPPED;\r
8466   return true;\r
8467  \r
8468  error:\r
8469   if ( pah && stream_.callbackInfo.isRunning ) {\r
8470     pthread_cond_destroy( &pah->runnable_cv );\r
8471     delete pah;\r
8472     stream_.apiHandle = 0;\r
8473   }\r
8474 \r
8475   for ( int i=0; i<2; i++ ) {\r
8476     if ( stream_.userBuffer[i] ) {\r
8477       free( stream_.userBuffer[i] );\r
8478       stream_.userBuffer[i] = 0;\r
8479     }\r
8480   }\r
8481 \r
8482   if ( stream_.deviceBuffer ) {\r
8483     free( stream_.deviceBuffer );\r
8484     stream_.deviceBuffer = 0;\r
8485   }\r
8486 \r
8487   return FAILURE;\r
8488 }\r
8489 \r
8490 //******************** End of __LINUX_PULSE__ *********************//\r
8491 #endif\r
8492 \r
8493 #if defined(__LINUX_OSS__)\r
8494 \r
8495 #include <unistd.h>\r
8496 #include <sys/ioctl.h>\r
8497 #include <unistd.h>\r
8498 #include <fcntl.h>\r
8499 #include <sys/soundcard.h>\r
8500 #include <errno.h>\r
8501 #include <math.h>\r
8502 \r
8503 static void *ossCallbackHandler(void * ptr);\r
8504 \r
8505 // A structure to hold various information related to the OSS API\r
8506 // implementation.\r
8507 struct OssHandle {\r
8508   int id[2];    // device ids\r
8509   bool xrun[2];\r
8510   bool triggered;\r
8511   pthread_cond_t runnable;\r
8512 \r
8513   OssHandle()\r
8514     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8515 };\r
8516 \r
8517 RtApiOss :: RtApiOss()\r
8518 {\r
8519   // Nothing to do here.\r
8520 }\r
8521 \r
8522 RtApiOss :: ~RtApiOss()\r
8523 {\r
8524   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8525 }\r
8526 \r
8527 unsigned int RtApiOss :: getDeviceCount( void )\r
8528 {\r
8529   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8530   if ( mixerfd == -1 ) {\r
8531     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8532     error( RtAudioError::WARNING );\r
8533     return 0;\r
8534   }\r
8535 \r
8536   oss_sysinfo sysinfo;\r
8537   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8538     close( mixerfd );\r
8539     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8540     error( RtAudioError::WARNING );\r
8541     return 0;\r
8542   }\r
8543 \r
8544   close( mixerfd );\r
8545   return sysinfo.numaudios;\r
8546 }\r
8547 \r
8548 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8549 {\r
8550   RtAudio::DeviceInfo info;\r
8551   info.probed = false;\r
8552 \r
8553   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8554   if ( mixerfd == -1 ) {\r
8555     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8556     error( RtAudioError::WARNING );\r
8557     return info;\r
8558   }\r
8559 \r
8560   oss_sysinfo sysinfo;\r
8561   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8562   if ( result == -1 ) {\r
8563     close( mixerfd );\r
8564     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8565     error( RtAudioError::WARNING );\r
8566     return info;\r
8567   }\r
8568 \r
8569   unsigned nDevices = sysinfo.numaudios;\r
8570   if ( nDevices == 0 ) {\r
8571     close( mixerfd );\r
8572     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8573     error( RtAudioError::INVALID_USE );\r
8574     return info;\r
8575   }\r
8576 \r
8577   if ( device >= nDevices ) {\r
8578     close( mixerfd );\r
8579     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8580     error( RtAudioError::INVALID_USE );\r
8581     return info;\r
8582   }\r
8583 \r
8584   oss_audioinfo ainfo;\r
8585   ainfo.dev = device;\r
8586   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8587   close( mixerfd );\r
8588   if ( result == -1 ) {\r
8589     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8590     errorText_ = errorStream_.str();\r
8591     error( RtAudioError::WARNING );\r
8592     return info;\r
8593   }\r
8594 \r
8595   // Probe channels\r
8596   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8597   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8598   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8599     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8600       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8601   }\r
8602 \r
8603   // Probe data formats ... do for input\r
8604   unsigned long mask = ainfo.iformats;\r
8605   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8606     info.nativeFormats |= RTAUDIO_SINT16;\r
8607   if ( mask & AFMT_S8 )\r
8608     info.nativeFormats |= RTAUDIO_SINT8;\r
8609   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8610     info.nativeFormats |= RTAUDIO_SINT32;\r
8611   if ( mask & AFMT_FLOAT )\r
8612     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8613   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8614     info.nativeFormats |= RTAUDIO_SINT24;\r
8615 \r
8616   // Check that we have at least one supported format\r
8617   if ( info.nativeFormats == 0 ) {\r
8618     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8619     errorText_ = errorStream_.str();\r
8620     error( RtAudioError::WARNING );\r
8621     return info;\r
8622   }\r
8623 \r
8624   // Probe the supported sample rates.\r
8625   info.sampleRates.clear();\r
8626   if ( ainfo.nrates ) {\r
8627     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8628       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8629         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8630           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8631           break;\r
8632         }\r
8633       }\r
8634     }\r
8635   }\r
8636   else {\r
8637     // Check min and max rate values;\r
8638     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8639       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )\r
8640         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8641     }\r
8642   }\r
8643 \r
8644   if ( info.sampleRates.size() == 0 ) {\r
8645     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8646     errorText_ = errorStream_.str();\r
8647     error( RtAudioError::WARNING );\r
8648   }\r
8649   else {\r
8650     info.probed = true;\r
8651     info.name = ainfo.name;\r
8652   }\r
8653 \r
8654   return info;\r
8655 }\r
8656 \r
8657 \r
8658 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8659                                   unsigned int firstChannel, unsigned int sampleRate,\r
8660                                   RtAudioFormat format, unsigned int *bufferSize,\r
8661                                   RtAudio::StreamOptions *options )\r
8662 {\r
8663   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8664   if ( mixerfd == -1 ) {\r
8665     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8666     return FAILURE;\r
8667   }\r
8668 \r
8669   oss_sysinfo sysinfo;\r
8670   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8671   if ( result == -1 ) {\r
8672     close( mixerfd );\r
8673     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8674     return FAILURE;\r
8675   }\r
8676 \r
8677   unsigned nDevices = sysinfo.numaudios;\r
8678   if ( nDevices == 0 ) {\r
8679     // This should not happen because a check is made before this function is called.\r
8680     close( mixerfd );\r
8681     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8682     return FAILURE;\r
8683   }\r
8684 \r
8685   if ( device >= nDevices ) {\r
8686     // This should not happen because a check is made before this function is called.\r
8687     close( mixerfd );\r
8688     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8689     return FAILURE;\r
8690   }\r
8691 \r
8692   oss_audioinfo ainfo;\r
8693   ainfo.dev = device;\r
8694   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8695   close( mixerfd );\r
8696   if ( result == -1 ) {\r
8697     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8698     errorText_ = errorStream_.str();\r
8699     return FAILURE;\r
8700   }\r
8701 \r
8702   // Check if device supports input or output\r
8703   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8704        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8705     if ( mode == OUTPUT )\r
8706       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8707     else\r
8708       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8709     errorText_ = errorStream_.str();\r
8710     return FAILURE;\r
8711   }\r
8712 \r
8713   int flags = 0;\r
8714   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8715   if ( mode == OUTPUT )\r
8716     flags |= O_WRONLY;\r
8717   else { // mode == INPUT\r
8718     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8719       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8720       close( handle->id[0] );\r
8721       handle->id[0] = 0;\r
8722       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8723         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8724         errorText_ = errorStream_.str();\r
8725         return FAILURE;\r
8726       }\r
8727       // Check that the number previously set channels is the same.\r
8728       if ( stream_.nUserChannels[0] != channels ) {\r
8729         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8730         errorText_ = errorStream_.str();\r
8731         return FAILURE;\r
8732       }\r
8733       flags |= O_RDWR;\r
8734     }\r
8735     else\r
8736       flags |= O_RDONLY;\r
8737   }\r
8738 \r
8739   // Set exclusive access if specified.\r
8740   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8741 \r
8742   // Try to open the device.\r
8743   int fd;\r
8744   fd = open( ainfo.devnode, flags, 0 );\r
8745   if ( fd == -1 ) {\r
8746     if ( errno == EBUSY )\r
8747       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8748     else\r
8749       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8750     errorText_ = errorStream_.str();\r
8751     return FAILURE;\r
8752   }\r
8753 \r
8754   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8755   /*\r
8756     if ( flags | O_RDWR ) {\r
8757     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
8758     if ( result == -1) {\r
8759     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
8760     errorText_ = errorStream_.str();\r
8761     return FAILURE;\r
8762     }\r
8763     }\r
8764   */\r
8765 \r
8766   // Check the device channel support.\r
8767   stream_.nUserChannels[mode] = channels;\r
8768   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
8769     close( fd );\r
8770     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
8771     errorText_ = errorStream_.str();\r
8772     return FAILURE;\r
8773   }\r
8774 \r
8775   // Set the number of channels.\r
8776   int deviceChannels = channels + firstChannel;\r
8777   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
8778   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
8779     close( fd );\r
8780     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
8781     errorText_ = errorStream_.str();\r
8782     return FAILURE;\r
8783   }\r
8784   stream_.nDeviceChannels[mode] = deviceChannels;\r
8785 \r
8786   // Get the data format mask\r
8787   int mask;\r
8788   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
8789   if ( result == -1 ) {\r
8790     close( fd );\r
8791     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
8792     errorText_ = errorStream_.str();\r
8793     return FAILURE;\r
8794   }\r
8795 \r
8796   // Determine how to set the device format.\r
8797   stream_.userFormat = format;\r
8798   int deviceFormat = -1;\r
8799   stream_.doByteSwap[mode] = false;\r
8800   if ( format == RTAUDIO_SINT8 ) {\r
8801     if ( mask & AFMT_S8 ) {\r
8802       deviceFormat = AFMT_S8;\r
8803       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8804     }\r
8805   }\r
8806   else if ( format == RTAUDIO_SINT16 ) {\r
8807     if ( mask & AFMT_S16_NE ) {\r
8808       deviceFormat = AFMT_S16_NE;\r
8809       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8810     }\r
8811     else if ( mask & AFMT_S16_OE ) {\r
8812       deviceFormat = AFMT_S16_OE;\r
8813       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8814       stream_.doByteSwap[mode] = true;\r
8815     }\r
8816   }\r
8817   else if ( format == RTAUDIO_SINT24 ) {\r
8818     if ( mask & AFMT_S24_NE ) {\r
8819       deviceFormat = AFMT_S24_NE;\r
8820       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8821     }\r
8822     else if ( mask & AFMT_S24_OE ) {\r
8823       deviceFormat = AFMT_S24_OE;\r
8824       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8825       stream_.doByteSwap[mode] = true;\r
8826     }\r
8827   }\r
8828   else if ( format == RTAUDIO_SINT32 ) {\r
8829     if ( mask & AFMT_S32_NE ) {\r
8830       deviceFormat = AFMT_S32_NE;\r
8831       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8832     }\r
8833     else if ( mask & AFMT_S32_OE ) {\r
8834       deviceFormat = AFMT_S32_OE;\r
8835       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8836       stream_.doByteSwap[mode] = true;\r
8837     }\r
8838   }\r
8839 \r
8840   if ( deviceFormat == -1 ) {\r
8841     // The user requested format is not natively supported by the device.\r
8842     if ( mask & AFMT_S16_NE ) {\r
8843       deviceFormat = AFMT_S16_NE;\r
8844       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8845     }\r
8846     else if ( mask & AFMT_S32_NE ) {\r
8847       deviceFormat = AFMT_S32_NE;\r
8848       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8849     }\r
8850     else if ( mask & AFMT_S24_NE ) {\r
8851       deviceFormat = AFMT_S24_NE;\r
8852       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8853     }\r
8854     else if ( mask & AFMT_S16_OE ) {\r
8855       deviceFormat = AFMT_S16_OE;\r
8856       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8857       stream_.doByteSwap[mode] = true;\r
8858     }\r
8859     else if ( mask & AFMT_S32_OE ) {\r
8860       deviceFormat = AFMT_S32_OE;\r
8861       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8862       stream_.doByteSwap[mode] = true;\r
8863     }\r
8864     else if ( mask & AFMT_S24_OE ) {\r
8865       deviceFormat = AFMT_S24_OE;\r
8866       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8867       stream_.doByteSwap[mode] = true;\r
8868     }\r
8869     else if ( mask & AFMT_S8) {\r
8870       deviceFormat = AFMT_S8;\r
8871       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8872     }\r
8873   }\r
8874 \r
8875   if ( stream_.deviceFormat[mode] == 0 ) {\r
8876     // This really shouldn't happen ...\r
8877     close( fd );\r
8878     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8879     errorText_ = errorStream_.str();\r
8880     return FAILURE;\r
8881   }\r
8882 \r
8883   // Set the data format.\r
8884   int temp = deviceFormat;\r
8885   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
8886   if ( result == -1 || deviceFormat != temp ) {\r
8887     close( fd );\r
8888     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
8889     errorText_ = errorStream_.str();\r
8890     return FAILURE;\r
8891   }\r
8892 \r
8893   // Attempt to set the buffer size.  According to OSS, the minimum\r
8894   // number of buffers is two.  The supposed minimum buffer size is 16\r
8895   // bytes, so that will be our lower bound.  The argument to this\r
8896   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
8897   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
8898   // We'll check the actual value used near the end of the setup\r
8899   // procedure.\r
8900   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
8901   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
8902   int buffers = 0;\r
8903   if ( options ) buffers = options->numberOfBuffers;\r
8904   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
8905   if ( buffers < 2 ) buffers = 3;\r
8906   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
8907   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
8908   if ( result == -1 ) {\r
8909     close( fd );\r
8910     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
8911     errorText_ = errorStream_.str();\r
8912     return FAILURE;\r
8913   }\r
8914   stream_.nBuffers = buffers;\r
8915 \r
8916   // Save buffer size (in sample frames).\r
8917   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
8918   stream_.bufferSize = *bufferSize;\r
8919 \r
8920   // Set the sample rate.\r
8921   int srate = sampleRate;\r
8922   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
8923   if ( result == -1 ) {\r
8924     close( fd );\r
8925     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
8926     errorText_ = errorStream_.str();\r
8927     return FAILURE;\r
8928   }\r
8929 \r
8930   // Verify the sample rate setup worked.\r
8931   if ( abs( srate - sampleRate ) > 100 ) {\r
8932     close( fd );\r
8933     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
8934     errorText_ = errorStream_.str();\r
8935     return FAILURE;\r
8936   }\r
8937   stream_.sampleRate = sampleRate;\r
8938 \r
8939   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8940     // We're doing duplex setup here.\r
8941     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
8942     stream_.nDeviceChannels[0] = deviceChannels;\r
8943   }\r
8944 \r
8945   // Set interleaving parameters.\r
8946   stream_.userInterleaved = true;\r
8947   stream_.deviceInterleaved[mode] =  true;\r
8948   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
8949     stream_.userInterleaved = false;\r
8950 \r
8951   // Set flags for buffer conversion\r
8952   stream_.doConvertBuffer[mode] = false;\r
8953   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8954     stream_.doConvertBuffer[mode] = true;\r
8955   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8956     stream_.doConvertBuffer[mode] = true;\r
8957   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
8958        stream_.nUserChannels[mode] > 1 )\r
8959     stream_.doConvertBuffer[mode] = true;\r
8960 \r
8961   // Allocate the stream handles if necessary and then save.\r
8962   if ( stream_.apiHandle == 0 ) {\r
8963     try {\r
8964       handle = new OssHandle;\r
8965     }\r
8966     catch ( std::bad_alloc& ) {\r
8967       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
8968       goto error;\r
8969     }\r
8970 \r
8971     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
8972       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
8973       goto error;\r
8974     }\r
8975 \r
8976     stream_.apiHandle = (void *) handle;\r
8977   }\r
8978   else {\r
8979     handle = (OssHandle *) stream_.apiHandle;\r
8980   }\r
8981   handle->id[mode] = fd;\r
8982 \r
8983   // Allocate necessary internal buffers.\r
8984   unsigned long bufferBytes;\r
8985   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8986   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8987   if ( stream_.userBuffer[mode] == NULL ) {\r
8988     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
8989     goto error;\r
8990   }\r
8991 \r
8992   if ( stream_.doConvertBuffer[mode] ) {\r
8993 \r
8994     bool makeBuffer = true;\r
8995     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8996     if ( mode == INPUT ) {\r
8997       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8998         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8999         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
9000       }\r
9001     }\r
9002 \r
9003     if ( makeBuffer ) {\r
9004       bufferBytes *= *bufferSize;\r
9005       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9006       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9007       if ( stream_.deviceBuffer == NULL ) {\r
9008         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9009         goto error;\r
9010       }\r
9011     }\r
9012   }\r
9013 \r
9014   stream_.device[mode] = device;\r
9015   stream_.state = STREAM_STOPPED;\r
9016 \r
9017   // Setup the buffer conversion information structure.\r
9018   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9019 \r
9020   // Setup thread if necessary.\r
9021   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9022     // We had already set up an output stream.\r
9023     stream_.mode = DUPLEX;\r
9024     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9025   }\r
9026   else {\r
9027     stream_.mode = mode;\r
9028 \r
9029     // Setup callback thread.\r
9030     stream_.callbackInfo.object = (void *) this;\r
9031 \r
9032     // Set the thread attributes for joinable and realtime scheduling\r
9033     // priority.  The higher priority will only take affect if the\r
9034     // program is run as root or suid.\r
9035     pthread_attr_t attr;\r
9036     pthread_attr_init( &attr );\r
9037     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9038 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9039     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9040       struct sched_param param;\r
9041       int priority = options->priority;\r
9042       int min = sched_get_priority_min( SCHED_RR );\r
9043       int max = sched_get_priority_max( SCHED_RR );\r
9044       if ( priority < min ) priority = min;\r
9045       else if ( priority > max ) priority = max;\r
9046       param.sched_priority = priority;\r
9047       pthread_attr_setschedparam( &attr, &param );\r
9048       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9049     }\r
9050     else\r
9051       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9052 #else\r
9053     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9054 #endif\r
9055 \r
9056     stream_.callbackInfo.isRunning = true;\r
9057     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9058     pthread_attr_destroy( &attr );\r
9059     if ( result ) {\r
9060       stream_.callbackInfo.isRunning = false;\r
9061       errorText_ = "RtApiOss::error creating callback thread!";\r
9062       goto error;\r
9063     }\r
9064   }\r
9065 \r
9066   return SUCCESS;\r
9067 \r
9068  error:\r
9069   if ( handle ) {\r
9070     pthread_cond_destroy( &handle->runnable );\r
9071     if ( handle->id[0] ) close( handle->id[0] );\r
9072     if ( handle->id[1] ) close( handle->id[1] );\r
9073     delete handle;\r
9074     stream_.apiHandle = 0;\r
9075   }\r
9076 \r
9077   for ( int i=0; i<2; i++ ) {\r
9078     if ( stream_.userBuffer[i] ) {\r
9079       free( stream_.userBuffer[i] );\r
9080       stream_.userBuffer[i] = 0;\r
9081     }\r
9082   }\r
9083 \r
9084   if ( stream_.deviceBuffer ) {\r
9085     free( stream_.deviceBuffer );\r
9086     stream_.deviceBuffer = 0;\r
9087   }\r
9088 \r
9089   return FAILURE;\r
9090 }\r
9091 \r
9092 void RtApiOss :: closeStream()\r
9093 {\r
9094   if ( stream_.state == STREAM_CLOSED ) {\r
9095     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9096     error( RtAudioError::WARNING );\r
9097     return;\r
9098   }\r
9099 \r
9100   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9101   stream_.callbackInfo.isRunning = false;\r
9102   MUTEX_LOCK( &stream_.mutex );\r
9103   if ( stream_.state == STREAM_STOPPED )\r
9104     pthread_cond_signal( &handle->runnable );\r
9105   MUTEX_UNLOCK( &stream_.mutex );\r
9106   pthread_join( stream_.callbackInfo.thread, NULL );\r
9107 \r
9108   if ( stream_.state == STREAM_RUNNING ) {\r
9109     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9110       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9111     else\r
9112       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9113     stream_.state = STREAM_STOPPED;\r
9114   }\r
9115 \r
9116   if ( handle ) {\r
9117     pthread_cond_destroy( &handle->runnable );\r
9118     if ( handle->id[0] ) close( handle->id[0] );\r
9119     if ( handle->id[1] ) close( handle->id[1] );\r
9120     delete handle;\r
9121     stream_.apiHandle = 0;\r
9122   }\r
9123 \r
9124   for ( int i=0; i<2; i++ ) {\r
9125     if ( stream_.userBuffer[i] ) {\r
9126       free( stream_.userBuffer[i] );\r
9127       stream_.userBuffer[i] = 0;\r
9128     }\r
9129   }\r
9130 \r
9131   if ( stream_.deviceBuffer ) {\r
9132     free( stream_.deviceBuffer );\r
9133     stream_.deviceBuffer = 0;\r
9134   }\r
9135 \r
9136   stream_.mode = UNINITIALIZED;\r
9137   stream_.state = STREAM_CLOSED;\r
9138 }\r
9139 \r
9140 void RtApiOss :: startStream()\r
9141 {\r
9142   verifyStream();\r
9143   if ( stream_.state == STREAM_RUNNING ) {\r
9144     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9145     error( RtAudioError::WARNING );\r
9146     return;\r
9147   }\r
9148 \r
9149   MUTEX_LOCK( &stream_.mutex );\r
9150 \r
9151   stream_.state = STREAM_RUNNING;\r
9152 \r
9153   // No need to do anything else here ... OSS automatically starts\r
9154   // when fed samples.\r
9155 \r
9156   MUTEX_UNLOCK( &stream_.mutex );\r
9157 \r
9158   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9159   pthread_cond_signal( &handle->runnable );\r
9160 }\r
9161 \r
9162 void RtApiOss :: stopStream()\r
9163 {\r
9164   verifyStream();\r
9165   if ( stream_.state == STREAM_STOPPED ) {\r
9166     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9167     error( RtAudioError::WARNING );\r
9168     return;\r
9169   }\r
9170 \r
9171   MUTEX_LOCK( &stream_.mutex );\r
9172 \r
9173   // The state might change while waiting on a mutex.\r
9174   if ( stream_.state == STREAM_STOPPED ) {\r
9175     MUTEX_UNLOCK( &stream_.mutex );\r
9176     return;\r
9177   }\r
9178 \r
9179   int result = 0;\r
9180   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9181   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9182 \r
9183     // Flush the output with zeros a few times.\r
9184     char *buffer;\r
9185     int samples;\r
9186     RtAudioFormat format;\r
9187 \r
9188     if ( stream_.doConvertBuffer[0] ) {\r
9189       buffer = stream_.deviceBuffer;\r
9190       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9191       format = stream_.deviceFormat[0];\r
9192     }\r
9193     else {\r
9194       buffer = stream_.userBuffer[0];\r
9195       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9196       format = stream_.userFormat;\r
9197     }\r
9198 \r
9199     memset( buffer, 0, samples * formatBytes(format) );\r
9200     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9201       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9202       if ( result == -1 ) {\r
9203         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9204         error( RtAudioError::WARNING );\r
9205       }\r
9206     }\r
9207 \r
9208     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9209     if ( result == -1 ) {\r
9210       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9211       errorText_ = errorStream_.str();\r
9212       goto unlock;\r
9213     }\r
9214     handle->triggered = false;\r
9215   }\r
9216 \r
9217   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9218     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9219     if ( result == -1 ) {\r
9220       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9221       errorText_ = errorStream_.str();\r
9222       goto unlock;\r
9223     }\r
9224   }\r
9225 \r
9226  unlock:\r
9227   stream_.state = STREAM_STOPPED;\r
9228   MUTEX_UNLOCK( &stream_.mutex );\r
9229 \r
9230   if ( result != -1 ) return;\r
9231   error( RtAudioError::SYSTEM_ERROR );\r
9232 }\r
9233 \r
9234 void RtApiOss :: abortStream()\r
9235 {\r
9236   verifyStream();\r
9237   if ( stream_.state == STREAM_STOPPED ) {\r
9238     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9239     error( RtAudioError::WARNING );\r
9240     return;\r
9241   }\r
9242 \r
9243   MUTEX_LOCK( &stream_.mutex );\r
9244 \r
9245   // The state might change while waiting on a mutex.\r
9246   if ( stream_.state == STREAM_STOPPED ) {\r
9247     MUTEX_UNLOCK( &stream_.mutex );\r
9248     return;\r
9249   }\r
9250 \r
9251   int result = 0;\r
9252   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9253   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9254     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9255     if ( result == -1 ) {\r
9256       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9257       errorText_ = errorStream_.str();\r
9258       goto unlock;\r
9259     }\r
9260     handle->triggered = false;\r
9261   }\r
9262 \r
9263   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9264     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9265     if ( result == -1 ) {\r
9266       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9267       errorText_ = errorStream_.str();\r
9268       goto unlock;\r
9269     }\r
9270   }\r
9271 \r
9272  unlock:\r
9273   stream_.state = STREAM_STOPPED;\r
9274   MUTEX_UNLOCK( &stream_.mutex );\r
9275 \r
9276   if ( result != -1 ) return;\r
9277   error( RtAudioError::SYSTEM_ERROR );\r
9278 }\r
9279 \r
9280 void RtApiOss :: callbackEvent()\r
9281 {\r
9282   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9283   if ( stream_.state == STREAM_STOPPED ) {\r
9284     MUTEX_LOCK( &stream_.mutex );\r
9285     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9286     if ( stream_.state != STREAM_RUNNING ) {\r
9287       MUTEX_UNLOCK( &stream_.mutex );\r
9288       return;\r
9289     }\r
9290     MUTEX_UNLOCK( &stream_.mutex );\r
9291   }\r
9292 \r
9293   if ( stream_.state == STREAM_CLOSED ) {\r
9294     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9295     error( RtAudioError::WARNING );\r
9296     return;\r
9297   }\r
9298 \r
9299   // Invoke user callback to get fresh output data.\r
9300   int doStopStream = 0;\r
9301   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9302   double streamTime = getStreamTime();\r
9303   RtAudioStreamStatus status = 0;\r
9304   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9305     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9306     handle->xrun[0] = false;\r
9307   }\r
9308   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9309     status |= RTAUDIO_INPUT_OVERFLOW;\r
9310     handle->xrun[1] = false;\r
9311   }\r
9312   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9313                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9314   if ( doStopStream == 2 ) {\r
9315     this->abortStream();\r
9316     return;\r
9317   }\r
9318 \r
9319   MUTEX_LOCK( &stream_.mutex );\r
9320 \r
9321   // The state might change while waiting on a mutex.\r
9322   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9323 \r
9324   int result;\r
9325   char *buffer;\r
9326   int samples;\r
9327   RtAudioFormat format;\r
9328 \r
9329   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9330 \r
9331     // Setup parameters and do buffer conversion if necessary.\r
9332     if ( stream_.doConvertBuffer[0] ) {\r
9333       buffer = stream_.deviceBuffer;\r
9334       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9335       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9336       format = stream_.deviceFormat[0];\r
9337     }\r
9338     else {\r
9339       buffer = stream_.userBuffer[0];\r
9340       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9341       format = stream_.userFormat;\r
9342     }\r
9343 \r
9344     // Do byte swapping if necessary.\r
9345     if ( stream_.doByteSwap[0] )\r
9346       byteSwapBuffer( buffer, samples, format );\r
9347 \r
9348     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9349       int trig = 0;\r
9350       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9351       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9352       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9353       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9354       handle->triggered = true;\r
9355     }\r
9356     else\r
9357       // Write samples to device.\r
9358       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9359 \r
9360     if ( result == -1 ) {\r
9361       // We'll assume this is an underrun, though there isn't a\r
9362       // specific means for determining that.\r
9363       handle->xrun[0] = true;\r
9364       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9365       error( RtAudioError::WARNING );\r
9366       // Continue on to input section.\r
9367     }\r
9368   }\r
9369 \r
9370   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9371 \r
9372     // Setup parameters.\r
9373     if ( stream_.doConvertBuffer[1] ) {\r
9374       buffer = stream_.deviceBuffer;\r
9375       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9376       format = stream_.deviceFormat[1];\r
9377     }\r
9378     else {\r
9379       buffer = stream_.userBuffer[1];\r
9380       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9381       format = stream_.userFormat;\r
9382     }\r
9383 \r
9384     // Read samples from device.\r
9385     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9386 \r
9387     if ( result == -1 ) {\r
9388       // We'll assume this is an overrun, though there isn't a\r
9389       // specific means for determining that.\r
9390       handle->xrun[1] = true;\r
9391       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9392       error( RtAudioError::WARNING );\r
9393       goto unlock;\r
9394     }\r
9395 \r
9396     // Do byte swapping if necessary.\r
9397     if ( stream_.doByteSwap[1] )\r
9398       byteSwapBuffer( buffer, samples, format );\r
9399 \r
9400     // Do buffer conversion if necessary.\r
9401     if ( stream_.doConvertBuffer[1] )\r
9402       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9403   }\r
9404 \r
9405  unlock:\r
9406   MUTEX_UNLOCK( &stream_.mutex );\r
9407 \r
9408   RtApi::tickStreamTime();\r
9409   if ( doStopStream == 1 ) this->stopStream();\r
9410 }\r
9411 \r
9412 static void *ossCallbackHandler( void *ptr )\r
9413 {\r
9414   CallbackInfo *info = (CallbackInfo *) ptr;\r
9415   RtApiOss *object = (RtApiOss *) info->object;\r
9416   bool *isRunning = &info->isRunning;\r
9417 \r
9418   while ( *isRunning == true ) {\r
9419     pthread_testcancel();\r
9420     object->callbackEvent();\r
9421   }\r
9422 \r
9423   pthread_exit( NULL );\r
9424 }\r
9425 \r
9426 //******************** End of __LINUX_OSS__ *********************//\r
9427 #endif\r
9428 \r
9429 \r
9430 // *************************************************** //\r
9431 //\r
9432 // Protected common (OS-independent) RtAudio methods.\r
9433 //\r
9434 // *************************************************** //\r
9435 \r
9436 // This method can be modified to control the behavior of error\r
9437 // message printing.\r
9438 void RtApi :: error( RtAudioError::Type type )\r
9439 {\r
9440   errorStream_.str(""); // clear the ostringstream\r
9441 \r
9442   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9443   if ( errorCallback ) {\r
9444     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9445 \r
9446     if ( firstErrorOccurred_ )\r
9447       return;\r
9448 \r
9449     firstErrorOccurred_ = true;\r
9450     const std::string errorMessage = errorText_;\r
9451 \r
9452     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9453       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9454       abortStream();\r
9455     }\r
9456 \r
9457     errorCallback( type, errorMessage );\r
9458     firstErrorOccurred_ = false;\r
9459     return;\r
9460   }\r
9461 \r
9462   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9463     std::cerr << '\n' << errorText_ << "\n\n";\r
9464   else if ( type != RtAudioError::WARNING )\r
9465     throw( RtAudioError( errorText_, type ) );\r
9466 }\r
9467 \r
9468 void RtApi :: verifyStream()\r
9469 {\r
9470   if ( stream_.state == STREAM_CLOSED ) {\r
9471     errorText_ = "RtApi:: a stream is not open!";\r
9472     error( RtAudioError::INVALID_USE );\r
9473   }\r
9474 }\r
9475 \r
9476 void RtApi :: clearStreamInfo()\r
9477 {\r
9478   stream_.mode = UNINITIALIZED;\r
9479   stream_.state = STREAM_CLOSED;\r
9480   stream_.sampleRate = 0;\r
9481   stream_.bufferSize = 0;\r
9482   stream_.nBuffers = 0;\r
9483   stream_.userFormat = 0;\r
9484   stream_.userInterleaved = true;\r
9485   stream_.streamTime = 0.0;\r
9486   stream_.apiHandle = 0;\r
9487   stream_.deviceBuffer = 0;\r
9488   stream_.callbackInfo.callback = 0;\r
9489   stream_.callbackInfo.userData = 0;\r
9490   stream_.callbackInfo.isRunning = false;\r
9491   stream_.callbackInfo.errorCallback = 0;\r
9492   for ( int i=0; i<2; i++ ) {\r
9493     stream_.device[i] = 11111;\r
9494     stream_.doConvertBuffer[i] = false;\r
9495     stream_.deviceInterleaved[i] = true;\r
9496     stream_.doByteSwap[i] = false;\r
9497     stream_.nUserChannels[i] = 0;\r
9498     stream_.nDeviceChannels[i] = 0;\r
9499     stream_.channelOffset[i] = 0;\r
9500     stream_.deviceFormat[i] = 0;\r
9501     stream_.latency[i] = 0;\r
9502     stream_.userBuffer[i] = 0;\r
9503     stream_.convertInfo[i].channels = 0;\r
9504     stream_.convertInfo[i].inJump = 0;\r
9505     stream_.convertInfo[i].outJump = 0;\r
9506     stream_.convertInfo[i].inFormat = 0;\r
9507     stream_.convertInfo[i].outFormat = 0;\r
9508     stream_.convertInfo[i].inOffset.clear();\r
9509     stream_.convertInfo[i].outOffset.clear();\r
9510   }\r
9511 }\r
9512 \r
9513 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9514 {\r
9515   if ( format == RTAUDIO_SINT16 )\r
9516     return 2;\r
9517   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9518     return 4;\r
9519   else if ( format == RTAUDIO_FLOAT64 )\r
9520     return 8;\r
9521   else if ( format == RTAUDIO_SINT24 )\r
9522     return 3;\r
9523   else if ( format == RTAUDIO_SINT8 )\r
9524     return 1;\r
9525 \r
9526   errorText_ = "RtApi::formatBytes: undefined format.";\r
9527   error( RtAudioError::WARNING );\r
9528 \r
9529   return 0;\r
9530 }\r
9531 \r
9532 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9533 {\r
9534   if ( mode == INPUT ) { // convert device to user buffer\r
9535     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9536     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9537     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9538     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9539   }\r
9540   else { // convert user to device buffer\r
9541     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9542     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9543     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9544     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9545   }\r
9546 \r
9547   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9548     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9549   else\r
9550     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9551 \r
9552   // Set up the interleave/deinterleave offsets.\r
9553   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9554     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9555          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9556       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9557         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9558         stream_.convertInfo[mode].outOffset.push_back( k );\r
9559         stream_.convertInfo[mode].inJump = 1;\r
9560       }\r
9561     }\r
9562     else {\r
9563       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9564         stream_.convertInfo[mode].inOffset.push_back( k );\r
9565         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9566         stream_.convertInfo[mode].outJump = 1;\r
9567       }\r
9568     }\r
9569   }\r
9570   else { // no (de)interleaving\r
9571     if ( stream_.userInterleaved ) {\r
9572       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9573         stream_.convertInfo[mode].inOffset.push_back( k );\r
9574         stream_.convertInfo[mode].outOffset.push_back( k );\r
9575       }\r
9576     }\r
9577     else {\r
9578       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9579         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9580         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9581         stream_.convertInfo[mode].inJump = 1;\r
9582         stream_.convertInfo[mode].outJump = 1;\r
9583       }\r
9584     }\r
9585   }\r
9586 \r
9587   // Add channel offset.\r
9588   if ( firstChannel > 0 ) {\r
9589     if ( stream_.deviceInterleaved[mode] ) {\r
9590       if ( mode == OUTPUT ) {\r
9591         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9592           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9593       }\r
9594       else {\r
9595         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9596           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9597       }\r
9598     }\r
9599     else {\r
9600       if ( mode == OUTPUT ) {\r
9601         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9602           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9603       }\r
9604       else {\r
9605         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9606           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9607       }\r
9608     }\r
9609   }\r
9610 }\r
9611 \r
9612 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9613 {\r
9614   // This function does format conversion, input/output channel compensation, and\r
9615   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9616   // the lower three bytes of a 32-bit integer.\r
9617 \r
9618   // Clear our device buffer when in/out duplex device channels are different\r
9619   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9620        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9621     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9622 \r
9623   int j;\r
9624   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9625     Float64 scale;\r
9626     Float64 *out = (Float64 *)outBuffer;\r
9627 \r
9628     if (info.inFormat == RTAUDIO_SINT8) {\r
9629       signed char *in = (signed char *)inBuffer;\r
9630       scale = 1.0 / 127.5;\r
9631       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9632         for (j=0; j<info.channels; j++) {\r
9633           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9634           out[info.outOffset[j]] += 0.5;\r
9635           out[info.outOffset[j]] *= scale;\r
9636         }\r
9637         in += info.inJump;\r
9638         out += info.outJump;\r
9639       }\r
9640     }\r
9641     else if (info.inFormat == RTAUDIO_SINT16) {\r
9642       Int16 *in = (Int16 *)inBuffer;\r
9643       scale = 1.0 / 32767.5;\r
9644       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9645         for (j=0; j<info.channels; j++) {\r
9646           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9647           out[info.outOffset[j]] += 0.5;\r
9648           out[info.outOffset[j]] *= scale;\r
9649         }\r
9650         in += info.inJump;\r
9651         out += info.outJump;\r
9652       }\r
9653     }\r
9654     else if (info.inFormat == RTAUDIO_SINT24) {\r
9655       Int24 *in = (Int24 *)inBuffer;\r
9656       scale = 1.0 / 8388607.5;\r
9657       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9658         for (j=0; j<info.channels; j++) {\r
9659           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9660           out[info.outOffset[j]] += 0.5;\r
9661           out[info.outOffset[j]] *= scale;\r
9662         }\r
9663         in += info.inJump;\r
9664         out += info.outJump;\r
9665       }\r
9666     }\r
9667     else if (info.inFormat == RTAUDIO_SINT32) {\r
9668       Int32 *in = (Int32 *)inBuffer;\r
9669       scale = 1.0 / 2147483647.5;\r
9670       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9671         for (j=0; j<info.channels; j++) {\r
9672           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9673           out[info.outOffset[j]] += 0.5;\r
9674           out[info.outOffset[j]] *= scale;\r
9675         }\r
9676         in += info.inJump;\r
9677         out += info.outJump;\r
9678       }\r
9679     }\r
9680     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9681       Float32 *in = (Float32 *)inBuffer;\r
9682       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9683         for (j=0; j<info.channels; j++) {\r
9684           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9685         }\r
9686         in += info.inJump;\r
9687         out += info.outJump;\r
9688       }\r
9689     }\r
9690     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9691       // Channel compensation and/or (de)interleaving only.\r
9692       Float64 *in = (Float64 *)inBuffer;\r
9693       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9694         for (j=0; j<info.channels; j++) {\r
9695           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9696         }\r
9697         in += info.inJump;\r
9698         out += info.outJump;\r
9699       }\r
9700     }\r
9701   }\r
9702   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9703     Float32 scale;\r
9704     Float32 *out = (Float32 *)outBuffer;\r
9705 \r
9706     if (info.inFormat == RTAUDIO_SINT8) {\r
9707       signed char *in = (signed char *)inBuffer;\r
9708       scale = (Float32) ( 1.0 / 127.5 );\r
9709       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9710         for (j=0; j<info.channels; j++) {\r
9711           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9712           out[info.outOffset[j]] += 0.5;\r
9713           out[info.outOffset[j]] *= scale;\r
9714         }\r
9715         in += info.inJump;\r
9716         out += info.outJump;\r
9717       }\r
9718     }\r
9719     else if (info.inFormat == RTAUDIO_SINT16) {\r
9720       Int16 *in = (Int16 *)inBuffer;\r
9721       scale = (Float32) ( 1.0 / 32767.5 );\r
9722       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9723         for (j=0; j<info.channels; j++) {\r
9724           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9725           out[info.outOffset[j]] += 0.5;\r
9726           out[info.outOffset[j]] *= scale;\r
9727         }\r
9728         in += info.inJump;\r
9729         out += info.outJump;\r
9730       }\r
9731     }\r
9732     else if (info.inFormat == RTAUDIO_SINT24) {\r
9733       Int24 *in = (Int24 *)inBuffer;\r
9734       scale = (Float32) ( 1.0 / 8388607.5 );\r
9735       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9736         for (j=0; j<info.channels; j++) {\r
9737           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9738           out[info.outOffset[j]] += 0.5;\r
9739           out[info.outOffset[j]] *= scale;\r
9740         }\r
9741         in += info.inJump;\r
9742         out += info.outJump;\r
9743       }\r
9744     }\r
9745     else if (info.inFormat == RTAUDIO_SINT32) {\r
9746       Int32 *in = (Int32 *)inBuffer;\r
9747       scale = (Float32) ( 1.0 / 2147483647.5 );\r
9748       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9749         for (j=0; j<info.channels; j++) {\r
9750           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9751           out[info.outOffset[j]] += 0.5;\r
9752           out[info.outOffset[j]] *= scale;\r
9753         }\r
9754         in += info.inJump;\r
9755         out += info.outJump;\r
9756       }\r
9757     }\r
9758     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9759       // Channel compensation and/or (de)interleaving only.\r
9760       Float32 *in = (Float32 *)inBuffer;\r
9761       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9762         for (j=0; j<info.channels; j++) {\r
9763           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9764         }\r
9765         in += info.inJump;\r
9766         out += info.outJump;\r
9767       }\r
9768     }\r
9769     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9770       Float64 *in = (Float64 *)inBuffer;\r
9771       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9772         for (j=0; j<info.channels; j++) {\r
9773           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9774         }\r
9775         in += info.inJump;\r
9776         out += info.outJump;\r
9777       }\r
9778     }\r
9779   }\r
9780   else if (info.outFormat == RTAUDIO_SINT32) {\r
9781     Int32 *out = (Int32 *)outBuffer;\r
9782     if (info.inFormat == RTAUDIO_SINT8) {\r
9783       signed char *in = (signed char *)inBuffer;\r
9784       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9785         for (j=0; j<info.channels; j++) {\r
9786           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9787           out[info.outOffset[j]] <<= 24;\r
9788         }\r
9789         in += info.inJump;\r
9790         out += info.outJump;\r
9791       }\r
9792     }\r
9793     else if (info.inFormat == RTAUDIO_SINT16) {\r
9794       Int16 *in = (Int16 *)inBuffer;\r
9795       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9796         for (j=0; j<info.channels; j++) {\r
9797           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9798           out[info.outOffset[j]] <<= 16;\r
9799         }\r
9800         in += info.inJump;\r
9801         out += info.outJump;\r
9802       }\r
9803     }\r
9804     else if (info.inFormat == RTAUDIO_SINT24) {\r
9805       Int24 *in = (Int24 *)inBuffer;\r
9806       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9807         for (j=0; j<info.channels; j++) {\r
9808           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
9809           out[info.outOffset[j]] <<= 8;\r
9810         }\r
9811         in += info.inJump;\r
9812         out += info.outJump;\r
9813       }\r
9814     }\r
9815     else if (info.inFormat == RTAUDIO_SINT32) {\r
9816       // Channel compensation and/or (de)interleaving only.\r
9817       Int32 *in = (Int32 *)inBuffer;\r
9818       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9819         for (j=0; j<info.channels; j++) {\r
9820           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9821         }\r
9822         in += info.inJump;\r
9823         out += info.outJump;\r
9824       }\r
9825     }\r
9826     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9827       Float32 *in = (Float32 *)inBuffer;\r
9828       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9829         for (j=0; j<info.channels; j++) {\r
9830           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9831         }\r
9832         in += info.inJump;\r
9833         out += info.outJump;\r
9834       }\r
9835     }\r
9836     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9837       Float64 *in = (Float64 *)inBuffer;\r
9838       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9839         for (j=0; j<info.channels; j++) {\r
9840           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9841         }\r
9842         in += info.inJump;\r
9843         out += info.outJump;\r
9844       }\r
9845     }\r
9846   }\r
9847   else if (info.outFormat == RTAUDIO_SINT24) {\r
9848     Int24 *out = (Int24 *)outBuffer;\r
9849     if (info.inFormat == RTAUDIO_SINT8) {\r
9850       signed char *in = (signed char *)inBuffer;\r
9851       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9852         for (j=0; j<info.channels; j++) {\r
9853           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
9854           //out[info.outOffset[j]] <<= 16;\r
9855         }\r
9856         in += info.inJump;\r
9857         out += info.outJump;\r
9858       }\r
9859     }\r
9860     else if (info.inFormat == RTAUDIO_SINT16) {\r
9861       Int16 *in = (Int16 *)inBuffer;\r
9862       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9863         for (j=0; j<info.channels; j++) {\r
9864           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
9865           //out[info.outOffset[j]] <<= 8;\r
9866         }\r
9867         in += info.inJump;\r
9868         out += info.outJump;\r
9869       }\r
9870     }\r
9871     else if (info.inFormat == RTAUDIO_SINT24) {\r
9872       // Channel compensation and/or (de)interleaving only.\r
9873       Int24 *in = (Int24 *)inBuffer;\r
9874       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9875         for (j=0; j<info.channels; j++) {\r
9876           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9877         }\r
9878         in += info.inJump;\r
9879         out += info.outJump;\r
9880       }\r
9881     }\r
9882     else if (info.inFormat == RTAUDIO_SINT32) {\r
9883       Int32 *in = (Int32 *)inBuffer;\r
9884       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9885         for (j=0; j<info.channels; j++) {\r
9886           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
9887           //out[info.outOffset[j]] >>= 8;\r
9888         }\r
9889         in += info.inJump;\r
9890         out += info.outJump;\r
9891       }\r
9892     }\r
9893     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9894       Float32 *in = (Float32 *)inBuffer;\r
9895       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9896         for (j=0; j<info.channels; j++) {\r
9897           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9898         }\r
9899         in += info.inJump;\r
9900         out += info.outJump;\r
9901       }\r
9902     }\r
9903     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9904       Float64 *in = (Float64 *)inBuffer;\r
9905       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9906         for (j=0; j<info.channels; j++) {\r
9907           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9908         }\r
9909         in += info.inJump;\r
9910         out += info.outJump;\r
9911       }\r
9912     }\r
9913   }\r
9914   else if (info.outFormat == RTAUDIO_SINT16) {\r
9915     Int16 *out = (Int16 *)outBuffer;\r
9916     if (info.inFormat == RTAUDIO_SINT8) {\r
9917       signed char *in = (signed char *)inBuffer;\r
9918       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9919         for (j=0; j<info.channels; j++) {\r
9920           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
9921           out[info.outOffset[j]] <<= 8;\r
9922         }\r
9923         in += info.inJump;\r
9924         out += info.outJump;\r
9925       }\r
9926     }\r
9927     else if (info.inFormat == RTAUDIO_SINT16) {\r
9928       // Channel compensation and/or (de)interleaving only.\r
9929       Int16 *in = (Int16 *)inBuffer;\r
9930       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9931         for (j=0; j<info.channels; j++) {\r
9932           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9933         }\r
9934         in += info.inJump;\r
9935         out += info.outJump;\r
9936       }\r
9937     }\r
9938     else if (info.inFormat == RTAUDIO_SINT24) {\r
9939       Int24 *in = (Int24 *)inBuffer;\r
9940       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9941         for (j=0; j<info.channels; j++) {\r
9942           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
9943         }\r
9944         in += info.inJump;\r
9945         out += info.outJump;\r
9946       }\r
9947     }\r
9948     else if (info.inFormat == RTAUDIO_SINT32) {\r
9949       Int32 *in = (Int32 *)inBuffer;\r
9950       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9951         for (j=0; j<info.channels; j++) {\r
9952           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
9953         }\r
9954         in += info.inJump;\r
9955         out += info.outJump;\r
9956       }\r
9957     }\r
9958     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9959       Float32 *in = (Float32 *)inBuffer;\r
9960       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9961         for (j=0; j<info.channels; j++) {\r
9962           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9963         }\r
9964         in += info.inJump;\r
9965         out += info.outJump;\r
9966       }\r
9967     }\r
9968     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9969       Float64 *in = (Float64 *)inBuffer;\r
9970       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9971         for (j=0; j<info.channels; j++) {\r
9972           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
9973         }\r
9974         in += info.inJump;\r
9975         out += info.outJump;\r
9976       }\r
9977     }\r
9978   }\r
9979   else if (info.outFormat == RTAUDIO_SINT8) {\r
9980     signed char *out = (signed char *)outBuffer;\r
9981     if (info.inFormat == RTAUDIO_SINT8) {\r
9982       // Channel compensation and/or (de)interleaving only.\r
9983       signed char *in = (signed char *)inBuffer;\r
9984       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9985         for (j=0; j<info.channels; j++) {\r
9986           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9987         }\r
9988         in += info.inJump;\r
9989         out += info.outJump;\r
9990       }\r
9991     }\r
9992     if (info.inFormat == RTAUDIO_SINT16) {\r
9993       Int16 *in = (Int16 *)inBuffer;\r
9994       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9995         for (j=0; j<info.channels; j++) {\r
9996           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
9997         }\r
9998         in += info.inJump;\r
9999         out += info.outJump;\r
10000       }\r
10001     }\r
10002     else if (info.inFormat == RTAUDIO_SINT24) {\r
10003       Int24 *in = (Int24 *)inBuffer;\r
10004       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10005         for (j=0; j<info.channels; j++) {\r
10006           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10007         }\r
10008         in += info.inJump;\r
10009         out += info.outJump;\r
10010       }\r
10011     }\r
10012     else if (info.inFormat == RTAUDIO_SINT32) {\r
10013       Int32 *in = (Int32 *)inBuffer;\r
10014       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10015         for (j=0; j<info.channels; j++) {\r
10016           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10017         }\r
10018         in += info.inJump;\r
10019         out += info.outJump;\r
10020       }\r
10021     }\r
10022     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10023       Float32 *in = (Float32 *)inBuffer;\r
10024       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10025         for (j=0; j<info.channels; j++) {\r
10026           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10027         }\r
10028         in += info.inJump;\r
10029         out += info.outJump;\r
10030       }\r
10031     }\r
10032     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10033       Float64 *in = (Float64 *)inBuffer;\r
10034       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10035         for (j=0; j<info.channels; j++) {\r
10036           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10037         }\r
10038         in += info.inJump;\r
10039         out += info.outJump;\r
10040       }\r
10041     }\r
10042   }\r
10043 }\r
10044 \r
10045 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10046 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10047 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10048 \r
10049 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10050 {\r
10051   register char val;\r
10052   register char *ptr;\r
10053 \r
10054   ptr = buffer;\r
10055   if ( format == RTAUDIO_SINT16 ) {\r
10056     for ( unsigned int i=0; i<samples; i++ ) {\r
10057       // Swap 1st and 2nd bytes.\r
10058       val = *(ptr);\r
10059       *(ptr) = *(ptr+1);\r
10060       *(ptr+1) = val;\r
10061 \r
10062       // Increment 2 bytes.\r
10063       ptr += 2;\r
10064     }\r
10065   }\r
10066   else if ( format == RTAUDIO_SINT32 ||\r
10067             format == RTAUDIO_FLOAT32 ) {\r
10068     for ( unsigned int i=0; i<samples; i++ ) {\r
10069       // Swap 1st and 4th bytes.\r
10070       val = *(ptr);\r
10071       *(ptr) = *(ptr+3);\r
10072       *(ptr+3) = val;\r
10073 \r
10074       // Swap 2nd and 3rd bytes.\r
10075       ptr += 1;\r
10076       val = *(ptr);\r
10077       *(ptr) = *(ptr+1);\r
10078       *(ptr+1) = val;\r
10079 \r
10080       // Increment 3 more bytes.\r
10081       ptr += 3;\r
10082     }\r
10083   }\r
10084   else if ( format == RTAUDIO_SINT24 ) {\r
10085     for ( unsigned int i=0; i<samples; i++ ) {\r
10086       // Swap 1st and 3rd bytes.\r
10087       val = *(ptr);\r
10088       *(ptr) = *(ptr+2);\r
10089       *(ptr+2) = val;\r
10090 \r
10091       // Increment 2 more bytes.\r
10092       ptr += 2;\r
10093     }\r
10094   }\r
10095   else if ( format == RTAUDIO_FLOAT64 ) {\r
10096     for ( unsigned int i=0; i<samples; i++ ) {\r
10097       // Swap 1st and 8th bytes\r
10098       val = *(ptr);\r
10099       *(ptr) = *(ptr+7);\r
10100       *(ptr+7) = val;\r
10101 \r
10102       // Swap 2nd and 7th bytes\r
10103       ptr += 1;\r
10104       val = *(ptr);\r
10105       *(ptr) = *(ptr+5);\r
10106       *(ptr+5) = val;\r
10107 \r
10108       // Swap 3rd and 6th bytes\r
10109       ptr += 1;\r
10110       val = *(ptr);\r
10111       *(ptr) = *(ptr+3);\r
10112       *(ptr+3) = val;\r
10113 \r
10114       // Swap 4th and 5th bytes\r
10115       ptr += 1;\r
10116       val = *(ptr);\r
10117       *(ptr) = *(ptr+1);\r
10118       *(ptr+1) = val;\r
10119 \r
10120       // Increment 5 more bytes.\r
10121       ptr += 5;\r
10122     }\r
10123   }\r
10124 }\r
10125 \r
10126   // Indentation settings for Vim and Emacs\r
10127   //\r
10128   // Local Variables:\r
10129   // c-basic-offset: 2\r
10130   // indent-tabs-mode: nil\r
10131   // End:\r
10132   //\r
10133   // vim: et sts=2 sw=2\r
10134 \r