Some optimisation
[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-2016 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.2\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 #include <algorithm>\r
49 \r
50 // Static variable definitions.\r
51 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
52 const unsigned int RtApi::SAMPLE_RATES[] = {\r
53   4000, 5512, 8000, 9600, 11025, 16000, 22050,\r
54   32000, 44100, 48000, 88200, 96000, 176400, 192000\r
55 };\r
56 \r
57 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)\r
58   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
59   #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)\r
60   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
61   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)\r
62 \r
63   #include "tchar.h"\r
64 \r
65   static std::string convertCharPointerToStdString(const char *text)\r
66   {\r
67     return std::string(text);\r
68   }\r
69 \r
70   static std::string convertCharPointerToStdString(const wchar_t *text)\r
71   {\r
72     int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);\r
73     std::string s( length-1, '\0' );\r
74     WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);\r
75     return s;\r
76   }\r
77 \r
78 #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
79   // pthread API\r
80   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
81   #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)\r
82   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)\r
83   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)\r
84 #else\r
85   #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions\r
86   #define MUTEX_DESTROY(A)    abs(*A) // dummy definitions\r
87 #endif\r
88 \r
89 // *************************************************** //\r
90 //\r
91 // RtAudio definitions.\r
92 //\r
93 // *************************************************** //\r
94 \r
95 std::string RtAudio :: getVersion( void ) throw()\r
96 {\r
97   return RTAUDIO_VERSION;\r
98 }\r
99 \r
100 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()\r
101 {\r
102   apis.clear();\r
103 \r
104   // The order here will control the order of RtAudio's API search in\r
105   // the constructor.\r
106 #if defined(__UNIX_JACK__)\r
107   apis.push_back( UNIX_JACK );\r
108 #endif\r
109 #if defined(__LINUX_ALSA__)\r
110   apis.push_back( LINUX_ALSA );\r
111 #endif\r
112 #if defined(__LINUX_PULSE__)\r
113   apis.push_back( LINUX_PULSE );\r
114 #endif\r
115 #if defined(__LINUX_OSS__)\r
116   apis.push_back( LINUX_OSS );\r
117 #endif\r
118 #if defined(__WINDOWS_ASIO__)\r
119   apis.push_back( WINDOWS_ASIO );\r
120 #endif\r
121 #if defined(__WINDOWS_WASAPI__)\r
122   apis.push_back( WINDOWS_WASAPI );\r
123 #endif\r
124 #if defined(__WINDOWS_DS__)\r
125   apis.push_back( WINDOWS_DS );\r
126 #endif\r
127 #if defined(__MACOSX_CORE__)\r
128   apis.push_back( MACOSX_CORE );\r
129 #endif\r
130 #if defined(__RTAUDIO_DUMMY__)\r
131   apis.push_back( RTAUDIO_DUMMY );\r
132 #endif\r
133 }\r
134 \r
135 void RtAudio :: openRtApi( RtAudio::Api api )\r
136 {\r
137   if ( rtapi_ )\r
138     delete rtapi_;\r
139   rtapi_ = 0;\r
140 \r
141 #if defined(__UNIX_JACK__)\r
142   if ( api == UNIX_JACK )\r
143     rtapi_ = new RtApiJack();\r
144 #endif\r
145 #if defined(__LINUX_ALSA__)\r
146   if ( api == LINUX_ALSA )\r
147     rtapi_ = new RtApiAlsa();\r
148 #endif\r
149 #if defined(__LINUX_PULSE__)\r
150   if ( api == LINUX_PULSE )\r
151     rtapi_ = new RtApiPulse();\r
152 #endif\r
153 #if defined(__LINUX_OSS__)\r
154   if ( api == LINUX_OSS )\r
155     rtapi_ = new RtApiOss();\r
156 #endif\r
157 #if defined(__WINDOWS_ASIO__)\r
158   if ( api == WINDOWS_ASIO )\r
159     rtapi_ = new RtApiAsio();\r
160 #endif\r
161 #if defined(__WINDOWS_WASAPI__)\r
162   if ( api == WINDOWS_WASAPI )\r
163     rtapi_ = new RtApiWasapi();\r
164 #endif\r
165 #if defined(__WINDOWS_DS__)\r
166   if ( api == WINDOWS_DS )\r
167     rtapi_ = new RtApiDs();\r
168 #endif\r
169 #if defined(__MACOSX_CORE__)\r
170   if ( api == MACOSX_CORE )\r
171     rtapi_ = new RtApiCore();\r
172 #endif\r
173 #if defined(__RTAUDIO_DUMMY__)\r
174   if ( api == RTAUDIO_DUMMY )\r
175     rtapi_ = new RtApiDummy();\r
176 #endif\r
177 }\r
178 \r
179 RtAudio :: RtAudio( RtAudio::Api api )\r
180 {\r
181   rtapi_ = 0;\r
182 \r
183   if ( api != UNSPECIFIED ) {\r
184     // Attempt to open the specified API.\r
185     openRtApi( api );\r
186     if ( rtapi_ ) return;\r
187 \r
188     // No compiled support for specified API value.  Issue a debug\r
189     // warning and continue as if no API was specified.\r
190     std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;\r
191   }\r
192 \r
193   // Iterate through the compiled APIs and return as soon as we find\r
194   // one with at least one device or we reach the end of the list.\r
195   std::vector< RtAudio::Api > apis;\r
196   getCompiledApi( apis );\r
197   for ( unsigned int i=0; i<apis.size(); i++ ) {\r
198     openRtApi( apis[i] );\r
199     if ( rtapi_ && rtapi_->getDeviceCount() ) break;\r
200   }\r
201 \r
202   if ( rtapi_ ) return;\r
203 \r
204   // It should not be possible to get here because the preprocessor\r
205   // definition __RTAUDIO_DUMMY__ is automatically defined if no\r
206   // API-specific definitions are passed to the compiler. But just in\r
207   // case something weird happens, we'll thow an error.\r
208   std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
209   throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );\r
210 }\r
211 \r
212 RtAudio :: ~RtAudio() throw()\r
213 {\r
214   if ( rtapi_ )\r
215     delete rtapi_;\r
216 }\r
217 \r
218 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,\r
219                             RtAudio::StreamParameters *inputParameters,\r
220                             RtAudioFormat format, unsigned int sampleRate,\r
221                             unsigned int *bufferFrames,\r
222                             RtAudioCallback callback, void *userData,\r
223                             RtAudio::StreamOptions *options,\r
224                             RtAudioErrorCallback errorCallback )\r
225 {\r
226   return rtapi_->openStream( outputParameters, inputParameters, format,\r
227                              sampleRate, bufferFrames, callback,\r
228                              userData, options, errorCallback );\r
229 }\r
230 \r
231 // *************************************************** //\r
232 //\r
233 // Public RtApi definitions (see end of file for\r
234 // private or protected utility functions).\r
235 //\r
236 // *************************************************** //\r
237 \r
238 RtApi :: RtApi()\r
239 {\r
240   stream_.state = STREAM_CLOSED;\r
241   stream_.mode = UNINITIALIZED;\r
242   stream_.apiHandle = 0;\r
243   stream_.userBuffer[0] = 0;\r
244   stream_.userBuffer[1] = 0;\r
245   MUTEX_INITIALIZE( &stream_.mutex );\r
246   showWarnings_ = true;\r
247   firstErrorOccurred_ = false;\r
248 }\r
249 \r
250 RtApi :: ~RtApi()\r
251 {\r
252   MUTEX_DESTROY( &stream_.mutex );\r
253 }\r
254 \r
255 void RtApi :: openStream( RtAudio::StreamParameters *oParams,\r
256                           RtAudio::StreamParameters *iParams,\r
257                           RtAudioFormat format, unsigned int sampleRate,\r
258                           unsigned int *bufferFrames,\r
259                           RtAudioCallback callback, void *userData,\r
260                           RtAudio::StreamOptions *options,\r
261                           RtAudioErrorCallback errorCallback )\r
262 {\r
263   if ( stream_.state != STREAM_CLOSED ) {\r
264     errorText_ = "RtApi::openStream: a stream is already open!";\r
265     error( RtAudioError::INVALID_USE );\r
266     return;\r
267   }\r
268 \r
269   // Clear stream information potentially left from a previously open stream.\r
270   clearStreamInfo();\r
271 \r
272   if ( oParams && oParams->nChannels < 1 ) {\r
273     errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";\r
274     error( RtAudioError::INVALID_USE );\r
275     return;\r
276   }\r
277 \r
278   if ( iParams && iParams->nChannels < 1 ) {\r
279     errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";\r
280     error( RtAudioError::INVALID_USE );\r
281     return;\r
282   }\r
283 \r
284   if ( oParams == NULL && iParams == NULL ) {\r
285     errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";\r
286     error( RtAudioError::INVALID_USE );\r
287     return;\r
288   }\r
289 \r
290   if ( formatBytes(format) == 0 ) {\r
291     errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";\r
292     error( RtAudioError::INVALID_USE );\r
293     return;\r
294   }\r
295 \r
296   unsigned int nDevices = getDeviceCount();\r
297   unsigned int oChannels = 0;\r
298   if ( oParams ) {\r
299     oChannels = oParams->nChannels;\r
300     if ( oParams->deviceId >= nDevices ) {\r
301       errorText_ = "RtApi::openStream: output device parameter value is invalid.";\r
302       error( RtAudioError::INVALID_USE );\r
303       return;\r
304     }\r
305   }\r
306 \r
307   unsigned int iChannels = 0;\r
308   if ( iParams ) {\r
309     iChannels = iParams->nChannels;\r
310     if ( iParams->deviceId >= nDevices ) {\r
311       errorText_ = "RtApi::openStream: input device parameter value is invalid.";\r
312       error( RtAudioError::INVALID_USE );\r
313       return;\r
314     }\r
315   }\r
316 \r
317   bool result;\r
318 \r
319   if ( oChannels > 0 ) {\r
320 \r
321     result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,\r
322                               sampleRate, format, bufferFrames, options );\r
323     if ( result == false ) {\r
324       error( RtAudioError::SYSTEM_ERROR );\r
325       return;\r
326     }\r
327   }\r
328 \r
329   if ( iChannels > 0 ) {\r
330 \r
331     result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,\r
332                               sampleRate, format, bufferFrames, options );\r
333     if ( result == false ) {\r
334       if ( oChannels > 0 ) closeStream();\r
335       error( RtAudioError::SYSTEM_ERROR );\r
336       return;\r
337     }\r
338   }\r
339 \r
340   stream_.callbackInfo.callback = (void *) callback;\r
341   stream_.callbackInfo.userData = userData;\r
342   stream_.callbackInfo.errorCallback = (void *) errorCallback;\r
343 \r
344   if ( options ) options->numberOfBuffers = stream_.nBuffers;\r
345   stream_.state = STREAM_STOPPED;\r
346 }\r
347 \r
348 unsigned int RtApi :: getDefaultInputDevice( void )\r
349 {\r
350   // Should be implemented in subclasses if possible.\r
351   return 0;\r
352 }\r
353 \r
354 unsigned int RtApi :: getDefaultOutputDevice( void )\r
355 {\r
356   // Should be implemented in subclasses if possible.\r
357   return 0;\r
358 }\r
359 \r
360 void RtApi :: closeStream( void )\r
361 {\r
362   // MUST be implemented in subclasses!\r
363   return;\r
364 }\r
365 \r
366 bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,\r
367                                unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,\r
368                                RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,\r
369                                RtAudio::StreamOptions * /*options*/ )\r
370 {\r
371   // MUST be implemented in subclasses!\r
372   return FAILURE;\r
373 }\r
374 \r
375 void RtApi :: tickStreamTime( void )\r
376 {\r
377   // Subclasses that do not provide their own implementation of\r
378   // getStreamTime should call this function once per buffer I/O to\r
379   // provide basic stream time support.\r
380 \r
381   stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );\r
382 \r
383 #if defined( HAVE_GETTIMEOFDAY )\r
384   gettimeofday( &stream_.lastTickTimestamp, NULL );\r
385 #endif\r
386 }\r
387 \r
388 long RtApi :: getStreamLatency( void )\r
389 {\r
390   verifyStream();\r
391 \r
392   long totalLatency = 0;\r
393   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
394     totalLatency = stream_.latency[0];\r
395   if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
396     totalLatency += stream_.latency[1];\r
397 \r
398   return totalLatency;\r
399 }\r
400 \r
401 double RtApi :: getStreamTime( void )\r
402 {\r
403   verifyStream();\r
404 \r
405 #if defined( HAVE_GETTIMEOFDAY )\r
406   // Return a very accurate estimate of the stream time by\r
407   // adding in the elapsed time since the last tick.\r
408   struct timeval then;\r
409   struct timeval now;\r
410 \r
411   if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )\r
412     return stream_.streamTime;\r
413 \r
414   gettimeofday( &now, NULL );\r
415   then = stream_.lastTickTimestamp;\r
416   return stream_.streamTime +\r
417     ((now.tv_sec + 0.000001 * now.tv_usec) -\r
418      (then.tv_sec + 0.000001 * then.tv_usec));     \r
419 #else\r
420   return stream_.streamTime;\r
421 #endif\r
422 }\r
423 \r
424 void RtApi :: setStreamTime( double time )\r
425 {\r
426   verifyStream();\r
427 \r
428   if ( time >= 0.0 )\r
429     stream_.streamTime = time;\r
430 }\r
431 \r
432 unsigned int RtApi :: getStreamSampleRate( void )\r
433 {\r
434  verifyStream();\r
435 \r
436  return stream_.sampleRate;\r
437 }\r
438 \r
439 \r
440 // *************************************************** //\r
441 //\r
442 // OS/API-specific methods.\r
443 //\r
444 // *************************************************** //\r
445 \r
446 #if defined(__MACOSX_CORE__)\r
447 \r
448 // The OS X CoreAudio API is designed to use a separate callback\r
449 // procedure for each of its audio devices.  A single RtAudio duplex\r
450 // stream using two different devices is supported here, though it\r
451 // cannot be guaranteed to always behave correctly because we cannot\r
452 // synchronize these two callbacks.\r
453 //\r
454 // A property listener is installed for over/underrun information.\r
455 // However, no functionality is currently provided to allow property\r
456 // listeners to trigger user handlers because it is unclear what could\r
457 // be done if a critical stream parameter (buffer size, sample rate,\r
458 // device disconnect) notification arrived.  The listeners entail\r
459 // quite a bit of extra code and most likely, a user program wouldn't\r
460 // be prepared for the result anyway.  However, we do provide a flag\r
461 // to the client callback function to inform of an over/underrun.\r
462 \r
463 // A structure to hold various information related to the CoreAudio API\r
464 // implementation.\r
465 struct CoreHandle {\r
466   AudioDeviceID id[2];    // device ids\r
467 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
468   AudioDeviceIOProcID procId[2];\r
469 #endif\r
470   UInt32 iStream[2];      // device stream index (or first if using multiple)\r
471   UInt32 nStreams[2];     // number of streams to use\r
472   bool xrun[2];\r
473   char *deviceBuffer;\r
474   pthread_cond_t condition;\r
475   int drainCounter;       // Tracks callback counts when draining\r
476   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
477 \r
478   CoreHandle()\r
479     :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
480 };\r
481 \r
482 RtApiCore:: RtApiCore()\r
483 {\r
484 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
485   // This is a largely undocumented but absolutely necessary\r
486   // requirement starting with OS-X 10.6.  If not called, queries and\r
487   // updates to various audio device properties are not handled\r
488   // correctly.\r
489   CFRunLoopRef theRunLoop = NULL;\r
490   AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
491                                           kAudioObjectPropertyScopeGlobal,\r
492                                           kAudioObjectPropertyElementMaster };\r
493   OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
494   if ( result != noErr ) {\r
495     errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
496     error( RtAudioError::WARNING );\r
497   }\r
498 #endif\r
499 }\r
500 \r
501 RtApiCore :: ~RtApiCore()\r
502 {\r
503   // The subclass destructor gets called before the base class\r
504   // destructor, so close an existing stream before deallocating\r
505   // apiDeviceId memory.\r
506   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
507 }\r
508 \r
509 unsigned int RtApiCore :: getDeviceCount( void )\r
510 {\r
511   // Find out how many audio devices there are, if any.\r
512   UInt32 dataSize;\r
513   AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
514   OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );\r
515   if ( result != noErr ) {\r
516     errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";\r
517     error( RtAudioError::WARNING );\r
518     return 0;\r
519   }\r
520 \r
521   return dataSize / sizeof( AudioDeviceID );\r
522 }\r
523 \r
524 unsigned int RtApiCore :: getDefaultInputDevice( void )\r
525 {\r
526   unsigned int nDevices = getDeviceCount();\r
527   if ( nDevices <= 1 ) return 0;\r
528 \r
529   AudioDeviceID id;\r
530   UInt32 dataSize = sizeof( AudioDeviceID );\r
531   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
532   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
533   if ( result != noErr ) {\r
534     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";\r
535     error( RtAudioError::WARNING );\r
536     return 0;\r
537   }\r
538 \r
539   dataSize *= nDevices;\r
540   AudioDeviceID deviceList[ nDevices ];\r
541   property.mSelector = kAudioHardwarePropertyDevices;\r
542   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
543   if ( result != noErr ) {\r
544     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";\r
545     error( RtAudioError::WARNING );\r
546     return 0;\r
547   }\r
548 \r
549   for ( unsigned int i=0; i<nDevices; i++ )\r
550     if ( id == deviceList[i] ) return i;\r
551 \r
552   errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";\r
553   error( RtAudioError::WARNING );\r
554   return 0;\r
555 }\r
556 \r
557 unsigned int RtApiCore :: getDefaultOutputDevice( void )\r
558 {\r
559   unsigned int nDevices = getDeviceCount();\r
560   if ( nDevices <= 1 ) return 0;\r
561 \r
562   AudioDeviceID id;\r
563   UInt32 dataSize = sizeof( AudioDeviceID );\r
564   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
565   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
566   if ( result != noErr ) {\r
567     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";\r
568     error( RtAudioError::WARNING );\r
569     return 0;\r
570   }\r
571 \r
572   dataSize = sizeof( AudioDeviceID ) * nDevices;\r
573   AudioDeviceID deviceList[ nDevices ];\r
574   property.mSelector = kAudioHardwarePropertyDevices;\r
575   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
576   if ( result != noErr ) {\r
577     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";\r
578     error( RtAudioError::WARNING );\r
579     return 0;\r
580   }\r
581 \r
582   for ( unsigned int i=0; i<nDevices; i++ )\r
583     if ( id == deviceList[i] ) return i;\r
584 \r
585   errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";\r
586   error( RtAudioError::WARNING );\r
587   return 0;\r
588 }\r
589 \r
590 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )\r
591 {\r
592   RtAudio::DeviceInfo info;\r
593   info.probed = false;\r
594 \r
595   // Get device ID\r
596   unsigned int nDevices = getDeviceCount();\r
597   if ( nDevices == 0 ) {\r
598     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
599     error( RtAudioError::INVALID_USE );\r
600     return info;\r
601   }\r
602 \r
603   if ( device >= nDevices ) {\r
604     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
605     error( RtAudioError::INVALID_USE );\r
606     return info;\r
607   }\r
608 \r
609   AudioDeviceID deviceList[ nDevices ];\r
610   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
611   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
612                                           kAudioObjectPropertyScopeGlobal,\r
613                                           kAudioObjectPropertyElementMaster };\r
614   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
615                                                 0, NULL, &dataSize, (void *) &deviceList );\r
616   if ( result != noErr ) {\r
617     errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";\r
618     error( RtAudioError::WARNING );\r
619     return info;\r
620   }\r
621 \r
622   AudioDeviceID id = deviceList[ device ];\r
623 \r
624   // Get the device name.\r
625   info.name.erase();\r
626   CFStringRef cfname;\r
627   dataSize = sizeof( CFStringRef );\r
628   property.mSelector = kAudioObjectPropertyManufacturer;\r
629   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
630   if ( result != noErr ) {\r
631     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";\r
632     errorText_ = errorStream_.str();\r
633     error( RtAudioError::WARNING );\r
634     return info;\r
635   }\r
636 \r
637   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
638   int length = CFStringGetLength(cfname);\r
639   char *mname = (char *)malloc(length * 3 + 1);\r
640 #if defined( UNICODE ) || defined( _UNICODE )\r
641   CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
642 #else\r
643   CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
644 #endif\r
645   info.name.append( (const char *)mname, strlen(mname) );\r
646   info.name.append( ": " );\r
647   CFRelease( cfname );\r
648   free(mname);\r
649 \r
650   property.mSelector = kAudioObjectPropertyName;\r
651   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
652   if ( result != noErr ) {\r
653     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";\r
654     errorText_ = errorStream_.str();\r
655     error( RtAudioError::WARNING );\r
656     return info;\r
657   }\r
658 \r
659   //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
660   length = CFStringGetLength(cfname);\r
661   char *name = (char *)malloc(length * 3 + 1);\r
662 #if defined( UNICODE ) || defined( _UNICODE )\r
663   CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
664 #else\r
665   CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
666 #endif\r
667   info.name.append( (const char *)name, strlen(name) );\r
668   CFRelease( cfname );\r
669   free(name);\r
670 \r
671   // Get the output stream "configuration".\r
672   AudioBufferList       *bufferList = nil;\r
673   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
674   property.mScope = kAudioDevicePropertyScopeOutput;\r
675   //  property.mElement = kAudioObjectPropertyElementWildcard;\r
676   dataSize = 0;\r
677   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
678   if ( result != noErr || dataSize == 0 ) {\r
679     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";\r
680     errorText_ = errorStream_.str();\r
681     error( RtAudioError::WARNING );\r
682     return info;\r
683   }\r
684 \r
685   // Allocate the AudioBufferList.\r
686   bufferList = (AudioBufferList *) malloc( dataSize );\r
687   if ( bufferList == NULL ) {\r
688     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";\r
689     error( RtAudioError::WARNING );\r
690     return info;\r
691   }\r
692 \r
693   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
694   if ( result != noErr || dataSize == 0 ) {\r
695     free( bufferList );\r
696     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";\r
697     errorText_ = errorStream_.str();\r
698     error( RtAudioError::WARNING );\r
699     return info;\r
700   }\r
701 \r
702   // Get output channel information.\r
703   unsigned int i, nStreams = bufferList->mNumberBuffers;\r
704   for ( i=0; i<nStreams; i++ )\r
705     info.outputChannels += bufferList->mBuffers[i].mNumberChannels;\r
706   free( bufferList );\r
707 \r
708   // Get the input stream "configuration".\r
709   property.mScope = kAudioDevicePropertyScopeInput;\r
710   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
711   if ( result != noErr || dataSize == 0 ) {\r
712     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";\r
713     errorText_ = errorStream_.str();\r
714     error( RtAudioError::WARNING );\r
715     return info;\r
716   }\r
717 \r
718   // Allocate the AudioBufferList.\r
719   bufferList = (AudioBufferList *) malloc( dataSize );\r
720   if ( bufferList == NULL ) {\r
721     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";\r
722     error( RtAudioError::WARNING );\r
723     return info;\r
724   }\r
725 \r
726   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
727   if (result != noErr || dataSize == 0) {\r
728     free( bufferList );\r
729     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";\r
730     errorText_ = errorStream_.str();\r
731     error( RtAudioError::WARNING );\r
732     return info;\r
733   }\r
734 \r
735   // Get input channel information.\r
736   nStreams = bufferList->mNumberBuffers;\r
737   for ( i=0; i<nStreams; i++ )\r
738     info.inputChannels += bufferList->mBuffers[i].mNumberChannels;\r
739   free( bufferList );\r
740 \r
741   // If device opens for both playback and capture, we determine the channels.\r
742   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
743     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
744 \r
745   // Probe the device sample rates.\r
746   bool isInput = false;\r
747   if ( info.outputChannels == 0 ) isInput = true;\r
748 \r
749   // Determine the supported sample rates.\r
750   property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\r
751   if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;\r
752   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
753   if ( result != kAudioHardwareNoError || dataSize == 0 ) {\r
754     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";\r
755     errorText_ = errorStream_.str();\r
756     error( RtAudioError::WARNING );\r
757     return info;\r
758   }\r
759 \r
760   UInt32 nRanges = dataSize / sizeof( AudioValueRange );\r
761   AudioValueRange rangeList[ nRanges ];\r
762   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );\r
763   if ( result != kAudioHardwareNoError ) {\r
764     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";\r
765     errorText_ = errorStream_.str();\r
766     error( RtAudioError::WARNING );\r
767     return info;\r
768   }\r
769 \r
770   // The sample rate reporting mechanism is a bit of a mystery.  It\r
771   // seems that it can either return individual rates or a range of\r
772   // rates.  I assume that if the min / max range values are the same,\r
773   // then that represents a single supported rate and if the min / max\r
774   // range values are different, the device supports an arbitrary\r
775   // range of values (though there might be multiple ranges, so we'll\r
776   // use the most conservative range).\r
777   Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
778   bool haveValueRange = false;\r
779   info.sampleRates.clear();\r
780   for ( UInt32 i=0; i<nRanges; i++ ) {\r
781     if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {\r
782       unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;\r
783       info.sampleRates.push_back( tmpSr );\r
784 \r
785       if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )\r
786         info.preferredSampleRate = tmpSr;\r
787 \r
788     } else {\r
789       haveValueRange = true;\r
790       if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
791       if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
792     }\r
793   }\r
794 \r
795   if ( haveValueRange ) {\r
796     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
797       if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {\r
798         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
799 \r
800         if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
801           info.preferredSampleRate = SAMPLE_RATES[k];\r
802       }\r
803     }\r
804   }\r
805 \r
806   // Sort and remove any redundant values\r
807   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
808   info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
809 \r
810   if ( info.sampleRates.size() == 0 ) {\r
811     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
812     errorText_ = errorStream_.str();\r
813     error( RtAudioError::WARNING );\r
814     return info;\r
815   }\r
816 \r
817   // CoreAudio always uses 32-bit floating point data for PCM streams.\r
818   // Thus, any other "physical" formats supported by the device are of\r
819   // no interest to the client.\r
820   info.nativeFormats = RTAUDIO_FLOAT32;\r
821 \r
822   if ( info.outputChannels > 0 )\r
823     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
824   if ( info.inputChannels > 0 )\r
825     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
826 \r
827   info.probed = true;\r
828   return info;\r
829 }\r
830 \r
831 static OSStatus callbackHandler( AudioDeviceID inDevice,\r
832                                  const AudioTimeStamp* /*inNow*/,\r
833                                  const AudioBufferList* inInputData,\r
834                                  const AudioTimeStamp* /*inInputTime*/,\r
835                                  AudioBufferList* outOutputData,\r
836                                  const AudioTimeStamp* /*inOutputTime*/,\r
837                                  void* infoPointer )\r
838 {\r
839   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
840 \r
841   RtApiCore *object = (RtApiCore *) info->object;\r
842   if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )\r
843     return kAudioHardwareUnspecifiedError;\r
844   else\r
845     return kAudioHardwareNoError;\r
846 }\r
847 \r
848 static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
849                               UInt32 nAddresses,\r
850                               const AudioObjectPropertyAddress properties[],\r
851                               void* handlePointer )\r
852 {\r
853   CoreHandle *handle = (CoreHandle *) handlePointer;\r
854   for ( UInt32 i=0; i<nAddresses; i++ ) {\r
855     if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {\r
856       if ( properties[i].mScope == kAudioDevicePropertyScopeInput )\r
857         handle->xrun[1] = true;\r
858       else\r
859         handle->xrun[0] = true;\r
860     }\r
861   }\r
862 \r
863   return kAudioHardwareNoError;\r
864 }\r
865 \r
866 static OSStatus rateListener( AudioObjectID inDevice,\r
867                               UInt32 /*nAddresses*/,\r
868                               const AudioObjectPropertyAddress /*properties*/[],\r
869                               void* ratePointer )\r
870 {\r
871   Float64 *rate = (Float64 *) ratePointer;\r
872   UInt32 dataSize = sizeof( Float64 );\r
873   AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
874                                           kAudioObjectPropertyScopeGlobal,\r
875                                           kAudioObjectPropertyElementMaster };\r
876   AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
877   return kAudioHardwareNoError;\r
878 }\r
879 \r
880 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
881                                    unsigned int firstChannel, unsigned int sampleRate,\r
882                                    RtAudioFormat format, unsigned int *bufferSize,\r
883                                    RtAudio::StreamOptions *options )\r
884 {\r
885   // Get device ID\r
886   unsigned int nDevices = getDeviceCount();\r
887   if ( nDevices == 0 ) {\r
888     // This should not happen because a check is made before this function is called.\r
889     errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";\r
890     return FAILURE;\r
891   }\r
892 \r
893   if ( device >= nDevices ) {\r
894     // This should not happen because a check is made before this function is called.\r
895     errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";\r
896     return FAILURE;\r
897   }\r
898 \r
899   AudioDeviceID deviceList[ nDevices ];\r
900   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
901   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
902                                           kAudioObjectPropertyScopeGlobal,\r
903                                           kAudioObjectPropertyElementMaster };\r
904   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
905                                                 0, NULL, &dataSize, (void *) &deviceList );\r
906   if ( result != noErr ) {\r
907     errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";\r
908     return FAILURE;\r
909   }\r
910 \r
911   AudioDeviceID id = deviceList[ device ];\r
912 \r
913   // Setup for stream mode.\r
914   bool isInput = false;\r
915   if ( mode == INPUT ) {\r
916     isInput = true;\r
917     property.mScope = kAudioDevicePropertyScopeInput;\r
918   }\r
919   else\r
920     property.mScope = kAudioDevicePropertyScopeOutput;\r
921 \r
922   // Get the stream "configuration".\r
923   AudioBufferList       *bufferList = nil;\r
924   dataSize = 0;\r
925   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
926   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
927   if ( result != noErr || dataSize == 0 ) {\r
928     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";\r
929     errorText_ = errorStream_.str();\r
930     return FAILURE;\r
931   }\r
932 \r
933   // Allocate the AudioBufferList.\r
934   bufferList = (AudioBufferList *) malloc( dataSize );\r
935   if ( bufferList == NULL ) {\r
936     errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";\r
937     return FAILURE;\r
938   }\r
939 \r
940   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
941   if (result != noErr || dataSize == 0) {\r
942     free( bufferList );\r
943     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
944     errorText_ = errorStream_.str();\r
945     return FAILURE;\r
946   }\r
947 \r
948   // Search for one or more streams that contain the desired number of\r
949   // channels. CoreAudio devices can have an arbitrary number of\r
950   // streams and each stream can have an arbitrary number of channels.\r
951   // For each stream, a single buffer of interleaved samples is\r
952   // provided.  RtAudio prefers the use of one stream of interleaved\r
953   // data or multiple consecutive single-channel streams.  However, we\r
954   // now support multiple consecutive multi-channel streams of\r
955   // interleaved data as well.\r
956   UInt32 iStream, offsetCounter = firstChannel;\r
957   UInt32 nStreams = bufferList->mNumberBuffers;\r
958   bool monoMode = false;\r
959   bool foundStream = false;\r
960 \r
961   // First check that the device supports the requested number of\r
962   // channels.\r
963   UInt32 deviceChannels = 0;\r
964   for ( iStream=0; iStream<nStreams; iStream++ )\r
965     deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;\r
966 \r
967   if ( deviceChannels < ( channels + firstChannel ) ) {\r
968     free( bufferList );\r
969     errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";\r
970     errorText_ = errorStream_.str();\r
971     return FAILURE;\r
972   }\r
973 \r
974   // Look for a single stream meeting our needs.\r
975   UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;\r
976   for ( iStream=0; iStream<nStreams; iStream++ ) {\r
977     streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
978     if ( streamChannels >= channels + offsetCounter ) {\r
979       firstStream = iStream;\r
980       channelOffset = offsetCounter;\r
981       foundStream = true;\r
982       break;\r
983     }\r
984     if ( streamChannels > offsetCounter ) break;\r
985     offsetCounter -= streamChannels;\r
986   }\r
987 \r
988   // If we didn't find a single stream above, then we should be able\r
989   // to meet the channel specification with multiple streams.\r
990   if ( foundStream == false ) {\r
991     monoMode = true;\r
992     offsetCounter = firstChannel;\r
993     for ( iStream=0; iStream<nStreams; iStream++ ) {\r
994       streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
995       if ( streamChannels > offsetCounter ) break;\r
996       offsetCounter -= streamChannels;\r
997     }\r
998 \r
999     firstStream = iStream;\r
1000     channelOffset = offsetCounter;\r
1001     Int32 channelCounter = channels + offsetCounter - streamChannels;\r
1002 \r
1003     if ( streamChannels > 1 ) monoMode = false;\r
1004     while ( channelCounter > 0 ) {\r
1005       streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;\r
1006       if ( streamChannels > 1 ) monoMode = false;\r
1007       channelCounter -= streamChannels;\r
1008       streamCount++;\r
1009     }\r
1010   }\r
1011 \r
1012   free( bufferList );\r
1013 \r
1014   // Determine the buffer size.\r
1015   AudioValueRange       bufferRange;\r
1016   dataSize = sizeof( AudioValueRange );\r
1017   property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\r
1018   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );\r
1019 \r
1020   if ( result != noErr ) {\r
1021     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";\r
1022     errorText_ = errorStream_.str();\r
1023     return FAILURE;\r
1024   }\r
1025 \r
1026   if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
1027   else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;\r
1028   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
1029 \r
1030   // Set the buffer size.  For multiple streams, I'm assuming we only\r
1031   // need to make this setting for the master channel.\r
1032   UInt32 theSize = (UInt32) *bufferSize;\r
1033   dataSize = sizeof( UInt32 );\r
1034   property.mSelector = kAudioDevicePropertyBufferFrameSize;\r
1035   result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );\r
1036 \r
1037   if ( result != noErr ) {\r
1038     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";\r
1039     errorText_ = errorStream_.str();\r
1040     return FAILURE;\r
1041   }\r
1042 \r
1043   // If attempting to setup a duplex stream, the bufferSize parameter\r
1044   // MUST be the same in both directions!\r
1045   *bufferSize = theSize;\r
1046   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
1047     errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";\r
1048     errorText_ = errorStream_.str();\r
1049     return FAILURE;\r
1050   }\r
1051 \r
1052   stream_.bufferSize = *bufferSize;\r
1053   stream_.nBuffers = 1;\r
1054 \r
1055   // Try to set "hog" mode ... it's not clear to me this is working.\r
1056   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
1057     pid_t hog_pid;\r
1058     dataSize = sizeof( hog_pid );\r
1059     property.mSelector = kAudioDevicePropertyHogMode;\r
1060     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );\r
1061     if ( result != noErr ) {\r
1062       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";\r
1063       errorText_ = errorStream_.str();\r
1064       return FAILURE;\r
1065     }\r
1066 \r
1067     if ( hog_pid != getpid() ) {\r
1068       hog_pid = getpid();\r
1069       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );\r
1070       if ( result != noErr ) {\r
1071         errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";\r
1072         errorText_ = errorStream_.str();\r
1073         return FAILURE;\r
1074       }\r
1075     }\r
1076   }\r
1077 \r
1078   // Check and if necessary, change the sample rate for the device.\r
1079   Float64 nominalRate;\r
1080   dataSize = sizeof( Float64 );\r
1081   property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
1082   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
1083   if ( result != noErr ) {\r
1084     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
1085     errorText_ = errorStream_.str();\r
1086     return FAILURE;\r
1087   }\r
1088 \r
1089   // Only change the sample rate if off by more than 1 Hz.\r
1090   if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
1091 \r
1092     // Set a property listener for the sample rate change\r
1093     Float64 reportedRate = 0.0;\r
1094     AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
1095     result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1096     if ( result != noErr ) {\r
1097       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
1098       errorText_ = errorStream_.str();\r
1099       return FAILURE;\r
1100     }\r
1101 \r
1102     nominalRate = (Float64) sampleRate;\r
1103     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
1104     if ( result != noErr ) {\r
1105       AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1106       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
1107       errorText_ = errorStream_.str();\r
1108       return FAILURE;\r
1109     }\r
1110 \r
1111     // Now wait until the reported nominal rate is what we just set.\r
1112     UInt32 microCounter = 0;\r
1113     while ( reportedRate != nominalRate ) {\r
1114       microCounter += 5000;\r
1115       if ( microCounter > 5000000 ) break;\r
1116       usleep( 5000 );\r
1117     }\r
1118 \r
1119     // Remove the property listener.\r
1120     AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1121 \r
1122     if ( microCounter > 5000000 ) {\r
1123       errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
1124       errorText_ = errorStream_.str();\r
1125       return FAILURE;\r
1126     }\r
1127   }\r
1128 \r
1129   // Now set the stream format for all streams.  Also, check the\r
1130   // physical format of the device and change that if necessary.\r
1131   AudioStreamBasicDescription   description;\r
1132   dataSize = sizeof( AudioStreamBasicDescription );\r
1133   property.mSelector = kAudioStreamPropertyVirtualFormat;\r
1134   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
1135   if ( result != noErr ) {\r
1136     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
1137     errorText_ = errorStream_.str();\r
1138     return FAILURE;\r
1139   }\r
1140 \r
1141   // Set the sample rate and data format id.  However, only make the\r
1142   // change if the sample rate is not within 1.0 of the desired\r
1143   // rate and the format is not linear pcm.\r
1144   bool updateFormat = false;\r
1145   if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
1146     description.mSampleRate = (Float64) sampleRate;\r
1147     updateFormat = true;\r
1148   }\r
1149 \r
1150   if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
1151     description.mFormatID = kAudioFormatLinearPCM;\r
1152     updateFormat = true;\r
1153   }\r
1154 \r
1155   if ( updateFormat ) {\r
1156     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
1157     if ( result != noErr ) {\r
1158       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
1159       errorText_ = errorStream_.str();\r
1160       return FAILURE;\r
1161     }\r
1162   }\r
1163 \r
1164   // Now check the physical format.\r
1165   property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
1166   result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );\r
1167   if ( result != noErr ) {\r
1168     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
1169     errorText_ = errorStream_.str();\r
1170     return FAILURE;\r
1171   }\r
1172 \r
1173   //std::cout << "Current physical stream format:" << std::endl;\r
1174   //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
1175   //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1176   //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
1177   //std::cout << "   sample rate = " << description.mSampleRate << std::endl;\r
1178 \r
1179   if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
1180     description.mFormatID = kAudioFormatLinearPCM;\r
1181     //description.mSampleRate = (Float64) sampleRate;\r
1182     AudioStreamBasicDescription testDescription = description;\r
1183     UInt32 formatFlags;\r
1184 \r
1185     // We'll try higher bit rates first and then work our way down.\r
1186     std::vector< std::pair<UInt32, UInt32>  > physicalFormats;\r
1187     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;\r
1188     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1189     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1190     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1191     physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed\r
1192     formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
1193     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
1194     formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
1195     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
1196     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1197     physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
1198     physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
1199 \r
1200     bool setPhysicalFormat = false;\r
1201     for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
1202       testDescription = description;\r
1203       testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
1204       testDescription.mFormatFlags = physicalFormats[i].second;\r
1205       if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
1206         testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;\r
1207       else\r
1208         testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
1209       testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
1210       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
1211       if ( result == noErr ) {\r
1212         setPhysicalFormat = true;\r
1213         //std::cout << "Updated physical stream format:" << std::endl;\r
1214         //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
1215         //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1216         //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
1217         //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;\r
1218         break;\r
1219       }\r
1220     }\r
1221 \r
1222     if ( !setPhysicalFormat ) {\r
1223       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
1224       errorText_ = errorStream_.str();\r
1225       return FAILURE;\r
1226     }\r
1227   } // done setting virtual/physical formats.\r
1228 \r
1229   // Get the stream / device latency.\r
1230   UInt32 latency;\r
1231   dataSize = sizeof( UInt32 );\r
1232   property.mSelector = kAudioDevicePropertyLatency;\r
1233   if ( AudioObjectHasProperty( id, &property ) == true ) {\r
1234     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );\r
1235     if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;\r
1236     else {\r
1237       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";\r
1238       errorText_ = errorStream_.str();\r
1239       error( RtAudioError::WARNING );\r
1240     }\r
1241   }\r
1242 \r
1243   // Byte-swapping: According to AudioHardware.h, the stream data will\r
1244   // always be presented in native-endian format, so we should never\r
1245   // need to byte swap.\r
1246   stream_.doByteSwap[mode] = false;\r
1247 \r
1248   // From the CoreAudio documentation, PCM data must be supplied as\r
1249   // 32-bit floats.\r
1250   stream_.userFormat = format;\r
1251   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
1252 \r
1253   if ( streamCount == 1 )\r
1254     stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;\r
1255   else // multiple streams\r
1256     stream_.nDeviceChannels[mode] = channels;\r
1257   stream_.nUserChannels[mode] = channels;\r
1258   stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream\r
1259   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
1260   else stream_.userInterleaved = true;\r
1261   stream_.deviceInterleaved[mode] = true;\r
1262   if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;\r
1263 \r
1264   // Set flags for buffer conversion.\r
1265   stream_.doConvertBuffer[mode] = false;\r
1266   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
1267     stream_.doConvertBuffer[mode] = true;\r
1268   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
1269     stream_.doConvertBuffer[mode] = true;\r
1270   if ( streamCount == 1 ) {\r
1271     if ( stream_.nUserChannels[mode] > 1 &&\r
1272          stream_.userInterleaved != stream_.deviceInterleaved[mode] )\r
1273       stream_.doConvertBuffer[mode] = true;\r
1274   }\r
1275   else if ( monoMode && stream_.userInterleaved )\r
1276     stream_.doConvertBuffer[mode] = true;\r
1277 \r
1278   // Allocate our CoreHandle structure for the stream.\r
1279   CoreHandle *handle = 0;\r
1280   if ( stream_.apiHandle == 0 ) {\r
1281     try {\r
1282       handle = new CoreHandle;\r
1283     }\r
1284     catch ( std::bad_alloc& ) {\r
1285       errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";\r
1286       goto error;\r
1287     }\r
1288 \r
1289     if ( pthread_cond_init( &handle->condition, NULL ) ) {\r
1290       errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";\r
1291       goto error;\r
1292     }\r
1293     stream_.apiHandle = (void *) handle;\r
1294   }\r
1295   else\r
1296     handle = (CoreHandle *) stream_.apiHandle;\r
1297   handle->iStream[mode] = firstStream;\r
1298   handle->nStreams[mode] = streamCount;\r
1299   handle->id[mode] = id;\r
1300 \r
1301   // Allocate necessary internal buffers.\r
1302   unsigned long bufferBytes;\r
1303   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
1304   //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
1305   stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
1306   memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
1307   if ( stream_.userBuffer[mode] == NULL ) {\r
1308     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
1309     goto error;\r
1310   }\r
1311 \r
1312   // If possible, we will make use of the CoreAudio stream buffers as\r
1313   // "device buffers".  However, we can't do this if using multiple\r
1314   // streams.\r
1315   if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {\r
1316 \r
1317     bool makeBuffer = true;\r
1318     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
1319     if ( mode == INPUT ) {\r
1320       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
1321         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
1322         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
1323       }\r
1324     }\r
1325 \r
1326     if ( makeBuffer ) {\r
1327       bufferBytes *= *bufferSize;\r
1328       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
1329       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
1330       if ( stream_.deviceBuffer == NULL ) {\r
1331         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";\r
1332         goto error;\r
1333       }\r
1334     }\r
1335   }\r
1336 \r
1337   stream_.sampleRate = sampleRate;\r
1338   stream_.device[mode] = device;\r
1339   stream_.state = STREAM_STOPPED;\r
1340   stream_.callbackInfo.object = (void *) this;\r
1341 \r
1342   // Setup the buffer conversion information structure.\r
1343   if ( stream_.doConvertBuffer[mode] ) {\r
1344     if ( streamCount > 1 ) setConvertInfo( mode, 0 );\r
1345     else setConvertInfo( mode, channelOffset );\r
1346   }\r
1347 \r
1348   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )\r
1349     // Only one callback procedure per device.\r
1350     stream_.mode = DUPLEX;\r
1351   else {\r
1352 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1353     result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );\r
1354 #else\r
1355     // deprecated in favor of AudioDeviceCreateIOProcID()\r
1356     result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );\r
1357 #endif\r
1358     if ( result != noErr ) {\r
1359       errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";\r
1360       errorText_ = errorStream_.str();\r
1361       goto error;\r
1362     }\r
1363     if ( stream_.mode == OUTPUT && mode == INPUT )\r
1364       stream_.mode = DUPLEX;\r
1365     else\r
1366       stream_.mode = mode;\r
1367   }\r
1368 \r
1369   // Setup the device property listener for over/underload.\r
1370   property.mSelector = kAudioDeviceProcessorOverload;\r
1371   property.mScope = kAudioObjectPropertyScopeGlobal;\r
1372   result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
1373 \r
1374   return SUCCESS;\r
1375 \r
1376  error:\r
1377   if ( handle ) {\r
1378     pthread_cond_destroy( &handle->condition );\r
1379     delete handle;\r
1380     stream_.apiHandle = 0;\r
1381   }\r
1382 \r
1383   for ( int i=0; i<2; i++ ) {\r
1384     if ( stream_.userBuffer[i] ) {\r
1385       free( stream_.userBuffer[i] );\r
1386       stream_.userBuffer[i] = 0;\r
1387     }\r
1388   }\r
1389 \r
1390   if ( stream_.deviceBuffer ) {\r
1391     free( stream_.deviceBuffer );\r
1392     stream_.deviceBuffer = 0;\r
1393   }\r
1394 \r
1395   stream_.state = STREAM_CLOSED;\r
1396   return FAILURE;\r
1397 }\r
1398 \r
1399 void RtApiCore :: closeStream( void )\r
1400 {\r
1401   if ( stream_.state == STREAM_CLOSED ) {\r
1402     errorText_ = "RtApiCore::closeStream(): no open stream to close!";\r
1403     error( RtAudioError::WARNING );\r
1404     return;\r
1405   }\r
1406 \r
1407   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1408   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1409     if (handle) {\r
1410       AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
1411         kAudioObjectPropertyScopeGlobal,\r
1412         kAudioObjectPropertyElementMaster };\r
1413 \r
1414       property.mSelector = kAudioDeviceProcessorOverload;\r
1415       property.mScope = kAudioObjectPropertyScopeGlobal;\r
1416       if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {\r
1417         errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
1418         error( RtAudioError::WARNING );\r
1419       }\r
1420     }\r
1421     if ( stream_.state == STREAM_RUNNING )\r
1422       AudioDeviceStop( handle->id[0], callbackHandler );\r
1423 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1424     AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );\r
1425 #else\r
1426     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1427     AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );\r
1428 #endif\r
1429   }\r
1430 \r
1431   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1432     if (handle) {\r
1433       AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
1434         kAudioObjectPropertyScopeGlobal,\r
1435         kAudioObjectPropertyElementMaster };\r
1436 \r
1437       property.mSelector = kAudioDeviceProcessorOverload;\r
1438       property.mScope = kAudioObjectPropertyScopeGlobal;\r
1439       if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {\r
1440         errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
1441         error( RtAudioError::WARNING );\r
1442       }\r
1443     }\r
1444     if ( stream_.state == STREAM_RUNNING )\r
1445       AudioDeviceStop( handle->id[1], callbackHandler );\r
1446 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1447     AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );\r
1448 #else\r
1449     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1450     AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );\r
1451 #endif\r
1452   }\r
1453 \r
1454   for ( int i=0; i<2; i++ ) {\r
1455     if ( stream_.userBuffer[i] ) {\r
1456       free( stream_.userBuffer[i] );\r
1457       stream_.userBuffer[i] = 0;\r
1458     }\r
1459   }\r
1460 \r
1461   if ( stream_.deviceBuffer ) {\r
1462     free( stream_.deviceBuffer );\r
1463     stream_.deviceBuffer = 0;\r
1464   }\r
1465 \r
1466   // Destroy pthread condition variable.\r
1467   pthread_cond_destroy( &handle->condition );\r
1468   delete handle;\r
1469   stream_.apiHandle = 0;\r
1470 \r
1471   stream_.mode = UNINITIALIZED;\r
1472   stream_.state = STREAM_CLOSED;\r
1473 }\r
1474 \r
1475 void RtApiCore :: startStream( void )\r
1476 {\r
1477   verifyStream();\r
1478   if ( stream_.state == STREAM_RUNNING ) {\r
1479     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
1480     error( RtAudioError::WARNING );\r
1481     return;\r
1482   }\r
1483 \r
1484   OSStatus result = noErr;\r
1485   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1486   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1487 \r
1488     result = AudioDeviceStart( handle->id[0], callbackHandler );\r
1489     if ( result != noErr ) {\r
1490       errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";\r
1491       errorText_ = errorStream_.str();\r
1492       goto unlock;\r
1493     }\r
1494   }\r
1495 \r
1496   if ( stream_.mode == INPUT ||\r
1497        ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1498 \r
1499     result = AudioDeviceStart( handle->id[1], callbackHandler );\r
1500     if ( result != noErr ) {\r
1501       errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";\r
1502       errorText_ = errorStream_.str();\r
1503       goto unlock;\r
1504     }\r
1505   }\r
1506 \r
1507   handle->drainCounter = 0;\r
1508   handle->internalDrain = false;\r
1509   stream_.state = STREAM_RUNNING;\r
1510 \r
1511  unlock:\r
1512   if ( result == noErr ) return;\r
1513   error( RtAudioError::SYSTEM_ERROR );\r
1514 }\r
1515 \r
1516 void RtApiCore :: stopStream( void )\r
1517 {\r
1518   verifyStream();\r
1519   if ( stream_.state == STREAM_STOPPED ) {\r
1520     errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";\r
1521     error( RtAudioError::WARNING );\r
1522     return;\r
1523   }\r
1524 \r
1525   OSStatus result = noErr;\r
1526   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1527   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1528 \r
1529     if ( handle->drainCounter == 0 ) {\r
1530       handle->drainCounter = 2;\r
1531       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
1532     }\r
1533 \r
1534     result = AudioDeviceStop( handle->id[0], callbackHandler );\r
1535     if ( result != noErr ) {\r
1536       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
1537       errorText_ = errorStream_.str();\r
1538       goto unlock;\r
1539     }\r
1540   }\r
1541 \r
1542   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1543 \r
1544     result = AudioDeviceStop( handle->id[1], callbackHandler );\r
1545     if ( result != noErr ) {\r
1546       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
1547       errorText_ = errorStream_.str();\r
1548       goto unlock;\r
1549     }\r
1550   }\r
1551 \r
1552   stream_.state = STREAM_STOPPED;\r
1553 \r
1554  unlock:\r
1555   if ( result == noErr ) return;\r
1556   error( RtAudioError::SYSTEM_ERROR );\r
1557 }\r
1558 \r
1559 void RtApiCore :: abortStream( void )\r
1560 {\r
1561   verifyStream();\r
1562   if ( stream_.state == STREAM_STOPPED ) {\r
1563     errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";\r
1564     error( RtAudioError::WARNING );\r
1565     return;\r
1566   }\r
1567 \r
1568   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1569   handle->drainCounter = 2;\r
1570 \r
1571   stopStream();\r
1572 }\r
1573 \r
1574 // This function will be called by a spawned thread when the user\r
1575 // callback function signals that the stream should be stopped or\r
1576 // aborted.  It is better to handle it this way because the\r
1577 // callbackEvent() function probably should return before the AudioDeviceStop()\r
1578 // function is called.\r
1579 static void *coreStopStream( void *ptr )\r
1580 {\r
1581   CallbackInfo *info = (CallbackInfo *) ptr;\r
1582   RtApiCore *object = (RtApiCore *) info->object;\r
1583 \r
1584   object->stopStream();\r
1585   pthread_exit( NULL );\r
1586 }\r
1587 \r
1588 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,\r
1589                                  const AudioBufferList *inBufferList,\r
1590                                  const AudioBufferList *outBufferList )\r
1591 {\r
1592   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
1593   if ( stream_.state == STREAM_CLOSED ) {\r
1594     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
1595     error( RtAudioError::WARNING );\r
1596     return FAILURE;\r
1597   }\r
1598 \r
1599   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
1600   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1601 \r
1602   // Check if we were draining the stream and signal is finished.\r
1603   if ( handle->drainCounter > 3 ) {\r
1604     ThreadHandle threadId;\r
1605 \r
1606     stream_.state = STREAM_STOPPING;\r
1607     if ( handle->internalDrain == true )\r
1608       pthread_create( &threadId, NULL, coreStopStream, info );\r
1609     else // external call to stopStream()\r
1610       pthread_cond_signal( &handle->condition );\r
1611     return SUCCESS;\r
1612   }\r
1613 \r
1614   AudioDeviceID outputDevice = handle->id[0];\r
1615 \r
1616   // Invoke user callback to get fresh output data UNLESS we are\r
1617   // draining stream or duplex mode AND the input/output devices are\r
1618   // different AND this function is called for the input device.\r
1619   if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {\r
1620     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
1621     double streamTime = getStreamTime();\r
1622     RtAudioStreamStatus status = 0;\r
1623     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
1624       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
1625       handle->xrun[0] = false;\r
1626     }\r
1627     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
1628       status |= RTAUDIO_INPUT_OVERFLOW;\r
1629       handle->xrun[1] = false;\r
1630     }\r
1631 \r
1632     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
1633                                   stream_.bufferSize, streamTime, status, info->userData );\r
1634     if ( cbReturnValue == 2 ) {\r
1635       stream_.state = STREAM_STOPPING;\r
1636       handle->drainCounter = 2;\r
1637       abortStream();\r
1638       return SUCCESS;\r
1639     }\r
1640     else if ( cbReturnValue == 1 ) {\r
1641       handle->drainCounter = 1;\r
1642       handle->internalDrain = true;\r
1643     }\r
1644   }\r
1645 \r
1646   if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {\r
1647 \r
1648     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
1649 \r
1650       if ( handle->nStreams[0] == 1 ) {\r
1651         memset( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1652                 0,\r
1653                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1654       }\r
1655       else { // fill multiple streams with zeros\r
1656         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1657           memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1658                   0,\r
1659                   outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );\r
1660         }\r
1661       }\r
1662     }\r
1663     else if ( handle->nStreams[0] == 1 ) {\r
1664       if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer\r
1665         convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,\r
1666                        stream_.userBuffer[0], stream_.convertInfo[0] );\r
1667       }\r
1668       else { // copy from user buffer\r
1669         memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1670                 stream_.userBuffer[0],\r
1671                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1672       }\r
1673     }\r
1674     else { // fill multiple streams\r
1675       Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];\r
1676       if ( stream_.doConvertBuffer[0] ) {\r
1677         convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
1678         inBuffer = (Float32 *) stream_.deviceBuffer;\r
1679       }\r
1680 \r
1681       if ( stream_.deviceInterleaved[0] == false ) { // mono mode\r
1682         UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;\r
1683         for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
1684           memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1685                   (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );\r
1686         }\r
1687       }\r
1688       else { // fill multiple multi-channel streams with interleaved data\r
1689         UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;\r
1690         Float32 *out, *in;\r
1691 \r
1692         bool inInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1693         UInt32 inChannels = stream_.nUserChannels[0];\r
1694         if ( stream_.doConvertBuffer[0] ) {\r
1695           inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1696           inChannels = stream_.nDeviceChannels[0];\r
1697         }\r
1698 \r
1699         if ( inInterleaved ) inOffset = 1;\r
1700         else inOffset = stream_.bufferSize;\r
1701 \r
1702         channelsLeft = inChannels;\r
1703         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1704           in = inBuffer;\r
1705           out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;\r
1706           streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;\r
1707 \r
1708           outJump = 0;\r
1709           // Account for possible channel offset in first stream\r
1710           if ( i == 0 && stream_.channelOffset[0] > 0 ) {\r
1711             streamChannels -= stream_.channelOffset[0];\r
1712             outJump = stream_.channelOffset[0];\r
1713             out += outJump;\r
1714           }\r
1715 \r
1716           // Account for possible unfilled channels at end of the last stream\r
1717           if ( streamChannels > channelsLeft ) {\r
1718             outJump = streamChannels - channelsLeft;\r
1719             streamChannels = channelsLeft;\r
1720           }\r
1721 \r
1722           // Determine input buffer offsets and skips\r
1723           if ( inInterleaved ) {\r
1724             inJump = inChannels;\r
1725             in += inChannels - channelsLeft;\r
1726           }\r
1727           else {\r
1728             inJump = 1;\r
1729             in += (inChannels - channelsLeft) * inOffset;\r
1730           }\r
1731 \r
1732           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1733             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1734               *out++ = in[j*inOffset];\r
1735             }\r
1736             out += outJump;\r
1737             in += inJump;\r
1738           }\r
1739           channelsLeft -= streamChannels;\r
1740         }\r
1741       }\r
1742     }\r
1743   }\r
1744 \r
1745   // Don't bother draining input\r
1746   if ( handle->drainCounter ) {\r
1747     handle->drainCounter++;\r
1748     goto unlock;\r
1749   }\r
1750 \r
1751   AudioDeviceID inputDevice;\r
1752   inputDevice = handle->id[1];\r
1753   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {\r
1754 \r
1755     if ( handle->nStreams[1] == 1 ) {\r
1756       if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer\r
1757         convertBuffer( stream_.userBuffer[1],\r
1758                        (char *) inBufferList->mBuffers[handle->iStream[1]].mData,\r
1759                        stream_.convertInfo[1] );\r
1760       }\r
1761       else { // copy to user buffer\r
1762         memcpy( stream_.userBuffer[1],\r
1763                 inBufferList->mBuffers[handle->iStream[1]].mData,\r
1764                 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );\r
1765       }\r
1766     }\r
1767     else { // read from multiple streams\r
1768       Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];\r
1769       if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;\r
1770 \r
1771       if ( stream_.deviceInterleaved[1] == false ) { // mono mode\r
1772         UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;\r
1773         for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
1774           memcpy( (void *)&outBuffer[i*stream_.bufferSize],\r
1775                   inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );\r
1776         }\r
1777       }\r
1778       else { // read from multiple multi-channel streams\r
1779         UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;\r
1780         Float32 *out, *in;\r
1781 \r
1782         bool outInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1783         UInt32 outChannels = stream_.nUserChannels[1];\r
1784         if ( stream_.doConvertBuffer[1] ) {\r
1785           outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1786           outChannels = stream_.nDeviceChannels[1];\r
1787         }\r
1788 \r
1789         if ( outInterleaved ) outOffset = 1;\r
1790         else outOffset = stream_.bufferSize;\r
1791 \r
1792         channelsLeft = outChannels;\r
1793         for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {\r
1794           out = outBuffer;\r
1795           in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;\r
1796           streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;\r
1797 \r
1798           inJump = 0;\r
1799           // Account for possible channel offset in first stream\r
1800           if ( i == 0 && stream_.channelOffset[1] > 0 ) {\r
1801             streamChannels -= stream_.channelOffset[1];\r
1802             inJump = stream_.channelOffset[1];\r
1803             in += inJump;\r
1804           }\r
1805 \r
1806           // Account for possible unread channels at end of the last stream\r
1807           if ( streamChannels > channelsLeft ) {\r
1808             inJump = streamChannels - channelsLeft;\r
1809             streamChannels = channelsLeft;\r
1810           }\r
1811 \r
1812           // Determine output buffer offsets and skips\r
1813           if ( outInterleaved ) {\r
1814             outJump = outChannels;\r
1815             out += outChannels - channelsLeft;\r
1816           }\r
1817           else {\r
1818             outJump = 1;\r
1819             out += (outChannels - channelsLeft) * outOffset;\r
1820           }\r
1821 \r
1822           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1823             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1824               out[j*outOffset] = *in++;\r
1825             }\r
1826             out += outJump;\r
1827             in += inJump;\r
1828           }\r
1829           channelsLeft -= streamChannels;\r
1830         }\r
1831       }\r
1832       \r
1833       if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
1834         convertBuffer( stream_.userBuffer[1],\r
1835                        stream_.deviceBuffer,\r
1836                        stream_.convertInfo[1] );\r
1837       }\r
1838     }\r
1839   }\r
1840 \r
1841  unlock:\r
1842   //MUTEX_UNLOCK( &stream_.mutex );\r
1843 \r
1844   RtApi::tickStreamTime();\r
1845   return SUCCESS;\r
1846 }\r
1847 \r
1848 const char* RtApiCore :: getErrorCode( OSStatus code )\r
1849 {\r
1850   switch( code ) {\r
1851 \r
1852   case kAudioHardwareNotRunningError:\r
1853     return "kAudioHardwareNotRunningError";\r
1854 \r
1855   case kAudioHardwareUnspecifiedError:\r
1856     return "kAudioHardwareUnspecifiedError";\r
1857 \r
1858   case kAudioHardwareUnknownPropertyError:\r
1859     return "kAudioHardwareUnknownPropertyError";\r
1860 \r
1861   case kAudioHardwareBadPropertySizeError:\r
1862     return "kAudioHardwareBadPropertySizeError";\r
1863 \r
1864   case kAudioHardwareIllegalOperationError:\r
1865     return "kAudioHardwareIllegalOperationError";\r
1866 \r
1867   case kAudioHardwareBadObjectError:\r
1868     return "kAudioHardwareBadObjectError";\r
1869 \r
1870   case kAudioHardwareBadDeviceError:\r
1871     return "kAudioHardwareBadDeviceError";\r
1872 \r
1873   case kAudioHardwareBadStreamError:\r
1874     return "kAudioHardwareBadStreamError";\r
1875 \r
1876   case kAudioHardwareUnsupportedOperationError:\r
1877     return "kAudioHardwareUnsupportedOperationError";\r
1878 \r
1879   case kAudioDeviceUnsupportedFormatError:\r
1880     return "kAudioDeviceUnsupportedFormatError";\r
1881 \r
1882   case kAudioDevicePermissionsError:\r
1883     return "kAudioDevicePermissionsError";\r
1884 \r
1885   default:\r
1886     return "CoreAudio unknown error";\r
1887   }\r
1888 }\r
1889 \r
1890   //******************** End of __MACOSX_CORE__ *********************//\r
1891 #endif\r
1892 \r
1893 #if defined(__UNIX_JACK__)\r
1894 \r
1895 // JACK is a low-latency audio server, originally written for the\r
1896 // GNU/Linux operating system and now also ported to OS-X. It can\r
1897 // connect a number of different applications to an audio device, as\r
1898 // well as allowing them to share audio between themselves.\r
1899 //\r
1900 // When using JACK with RtAudio, "devices" refer to JACK clients that\r
1901 // have ports connected to the server.  The JACK server is typically\r
1902 // started in a terminal as follows:\r
1903 //\r
1904 // .jackd -d alsa -d hw:0\r
1905 //\r
1906 // or through an interface program such as qjackctl.  Many of the\r
1907 // parameters normally set for a stream are fixed by the JACK server\r
1908 // and can be specified when the JACK server is started.  In\r
1909 // particular,\r
1910 //\r
1911 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4\r
1912 //\r
1913 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample\r
1914 // frames, and number of buffers = 4.  Once the server is running, it\r
1915 // is not possible to override these values.  If the values are not\r
1916 // specified in the command-line, the JACK server uses default values.\r
1917 //\r
1918 // The JACK server does not have to be running when an instance of\r
1919 // RtApiJack is created, though the function getDeviceCount() will\r
1920 // report 0 devices found until JACK has been started.  When no\r
1921 // devices are available (i.e., the JACK server is not running), a\r
1922 // stream cannot be opened.\r
1923 \r
1924 #include <jack/jack.h>\r
1925 #include <unistd.h>\r
1926 #include <cstdio>\r
1927 \r
1928 // A structure to hold various information related to the Jack API\r
1929 // implementation.\r
1930 struct JackHandle {\r
1931   jack_client_t *client;\r
1932   jack_port_t **ports[2];\r
1933   std::string deviceName[2];\r
1934   bool xrun[2];\r
1935   pthread_cond_t condition;\r
1936   int drainCounter;       // Tracks callback counts when draining\r
1937   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
1938 \r
1939   JackHandle()\r
1940     :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
1941 };\r
1942 \r
1943 static void jackSilentError( const char * ) {};\r
1944 \r
1945 RtApiJack :: RtApiJack()\r
1946 {\r
1947   // Nothing to do here.\r
1948 #if !defined(__RTAUDIO_DEBUG__)\r
1949   // Turn off Jack's internal error reporting.\r
1950   jack_set_error_function( &jackSilentError );\r
1951 #endif\r
1952 }\r
1953 \r
1954 RtApiJack :: ~RtApiJack()\r
1955 {\r
1956   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
1957 }\r
1958 \r
1959 unsigned int RtApiJack :: getDeviceCount( void )\r
1960 {\r
1961   // See if we can become a jack client.\r
1962   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
1963   jack_status_t *status = NULL;\r
1964   jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );\r
1965   if ( client == 0 ) return 0;\r
1966 \r
1967   const char **ports;\r
1968   std::string port, previousPort;\r
1969   unsigned int nChannels = 0, nDevices = 0;\r
1970   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1971   if ( ports ) {\r
1972     // Parse the port names up to the first colon (:).\r
1973     size_t iColon = 0;\r
1974     do {\r
1975       port = (char *) ports[ nChannels ];\r
1976       iColon = port.find(":");\r
1977       if ( iColon != std::string::npos ) {\r
1978         port = port.substr( 0, iColon + 1 );\r
1979         if ( port != previousPort ) {\r
1980           nDevices++;\r
1981           previousPort = port;\r
1982         }\r
1983       }\r
1984     } while ( ports[++nChannels] );\r
1985     free( ports );\r
1986   }\r
1987 \r
1988   jack_client_close( client );\r
1989   return nDevices;\r
1990 }\r
1991 \r
1992 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )\r
1993 {\r
1994   RtAudio::DeviceInfo info;\r
1995   info.probed = false;\r
1996 \r
1997   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption\r
1998   jack_status_t *status = NULL;\r
1999   jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );\r
2000   if ( client == 0 ) {\r
2001     errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";\r
2002     error( RtAudioError::WARNING );\r
2003     return info;\r
2004   }\r
2005 \r
2006   const char **ports;\r
2007   std::string port, previousPort;\r
2008   unsigned int nPorts = 0, nDevices = 0;\r
2009   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2010   if ( ports ) {\r
2011     // Parse the port names up to the first colon (:).\r
2012     size_t iColon = 0;\r
2013     do {\r
2014       port = (char *) ports[ nPorts ];\r
2015       iColon = port.find(":");\r
2016       if ( iColon != std::string::npos ) {\r
2017         port = port.substr( 0, iColon );\r
2018         if ( port != previousPort ) {\r
2019           if ( nDevices == device ) info.name = port;\r
2020           nDevices++;\r
2021           previousPort = port;\r
2022         }\r
2023       }\r
2024     } while ( ports[++nPorts] );\r
2025     free( ports );\r
2026   }\r
2027 \r
2028   if ( device >= nDevices ) {\r
2029     jack_client_close( client );\r
2030     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
2031     error( RtAudioError::INVALID_USE );\r
2032     return info;\r
2033   }\r
2034 \r
2035   // Get the current jack server sample rate.\r
2036   info.sampleRates.clear();\r
2037 \r
2038   info.preferredSampleRate = jack_get_sample_rate( client );\r
2039   info.sampleRates.push_back( info.preferredSampleRate );\r
2040 \r
2041   // Count the available ports containing the client name as device\r
2042   // channels.  Jack "input ports" equal RtAudio output channels.\r
2043   unsigned int nChannels = 0;\r
2044   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );\r
2045   if ( ports ) {\r
2046     while ( ports[ nChannels ] ) nChannels++;\r
2047     free( ports );\r
2048     info.outputChannels = nChannels;\r
2049   }\r
2050 \r
2051   // Jack "output ports" equal RtAudio input channels.\r
2052   nChannels = 0;\r
2053   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );\r
2054   if ( ports ) {\r
2055     while ( ports[ nChannels ] ) nChannels++;\r
2056     free( ports );\r
2057     info.inputChannels = nChannels;\r
2058   }\r
2059 \r
2060   if ( info.outputChannels == 0 && info.inputChannels == 0 ) {\r
2061     jack_client_close(client);\r
2062     errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";\r
2063     error( RtAudioError::WARNING );\r
2064     return info;\r
2065   }\r
2066 \r
2067   // If device opens for both playback and capture, we determine the channels.\r
2068   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2069     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2070 \r
2071   // Jack always uses 32-bit floats.\r
2072   info.nativeFormats = RTAUDIO_FLOAT32;\r
2073 \r
2074   // Jack doesn't provide default devices so we'll use the first available one.\r
2075   if ( device == 0 && info.outputChannels > 0 )\r
2076     info.isDefaultOutput = true;\r
2077   if ( device == 0 && info.inputChannels > 0 )\r
2078     info.isDefaultInput = true;\r
2079 \r
2080   jack_client_close(client);\r
2081   info.probed = true;\r
2082   return info;\r
2083 }\r
2084 \r
2085 static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
2086 {\r
2087   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2088 \r
2089   RtApiJack *object = (RtApiJack *) info->object;\r
2090   if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;\r
2091 \r
2092   return 0;\r
2093 }\r
2094 \r
2095 // This function will be called by a spawned thread when the Jack\r
2096 // server signals that it is shutting down.  It is necessary to handle\r
2097 // it this way because the jackShutdown() function must return before\r
2098 // the jack_deactivate() function (in closeStream()) will return.\r
2099 static void *jackCloseStream( void *ptr )\r
2100 {\r
2101   CallbackInfo *info = (CallbackInfo *) ptr;\r
2102   RtApiJack *object = (RtApiJack *) info->object;\r
2103 \r
2104   object->closeStream();\r
2105 \r
2106   pthread_exit( NULL );\r
2107 }\r
2108 static void jackShutdown( void *infoPointer )\r
2109 {\r
2110   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2111   RtApiJack *object = (RtApiJack *) info->object;\r
2112 \r
2113   // Check current stream state.  If stopped, then we'll assume this\r
2114   // was called as a result of a call to RtApiJack::stopStream (the\r
2115   // deactivation of a client handle causes this function to be called).\r
2116   // If not, we'll assume the Jack server is shutting down or some\r
2117   // other problem occurred and we should close the stream.\r
2118   if ( object->isStreamRunning() == false ) return;\r
2119 \r
2120   ThreadHandle threadId;\r
2121   pthread_create( &threadId, NULL, jackCloseStream, info );\r
2122   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
2123 }\r
2124 \r
2125 static int jackXrun( void *infoPointer )\r
2126 {\r
2127   JackHandle *handle = (JackHandle *) infoPointer;\r
2128 \r
2129   if ( handle->ports[0] ) handle->xrun[0] = true;\r
2130   if ( handle->ports[1] ) handle->xrun[1] = true;\r
2131 \r
2132   return 0;\r
2133 }\r
2134 \r
2135 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2136                                    unsigned int firstChannel, unsigned int sampleRate,\r
2137                                    RtAudioFormat format, unsigned int *bufferSize,\r
2138                                    RtAudio::StreamOptions *options )\r
2139 {\r
2140   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2141 \r
2142   // Look for jack server and try to become a client (only do once per stream).\r
2143   jack_client_t *client = 0;\r
2144   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {\r
2145     jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
2146     jack_status_t *status = NULL;\r
2147     if ( options && !options->streamName.empty() )\r
2148       client = jack_client_open( options->streamName.c_str(), jackoptions, status );\r
2149     else\r
2150       client = jack_client_open( "RtApiJack", jackoptions, status );\r
2151     if ( client == 0 ) {\r
2152       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";\r
2153       error( RtAudioError::WARNING );\r
2154       return FAILURE;\r
2155     }\r
2156   }\r
2157   else {\r
2158     // The handle must have been created on an earlier pass.\r
2159     client = handle->client;\r
2160   }\r
2161 \r
2162   const char **ports;\r
2163   std::string port, previousPort, deviceName;\r
2164   unsigned int nPorts = 0, nDevices = 0;\r
2165   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2166   if ( ports ) {\r
2167     // Parse the port names up to the first colon (:).\r
2168     size_t iColon = 0;\r
2169     do {\r
2170       port = (char *) ports[ nPorts ];\r
2171       iColon = port.find(":");\r
2172       if ( iColon != std::string::npos ) {\r
2173         port = port.substr( 0, iColon );\r
2174         if ( port != previousPort ) {\r
2175           if ( nDevices == device ) deviceName = port;\r
2176           nDevices++;\r
2177           previousPort = port;\r
2178         }\r
2179       }\r
2180     } while ( ports[++nPorts] );\r
2181     free( ports );\r
2182   }\r
2183 \r
2184   if ( device >= nDevices ) {\r
2185     errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";\r
2186     return FAILURE;\r
2187   }\r
2188 \r
2189   // Count the available ports containing the client name as device\r
2190   // channels.  Jack "input ports" equal RtAudio output channels.\r
2191   unsigned int nChannels = 0;\r
2192   unsigned long flag = JackPortIsInput;\r
2193   if ( mode == INPUT ) flag = JackPortIsOutput;\r
2194   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2195   if ( ports ) {\r
2196     while ( ports[ nChannels ] ) nChannels++;\r
2197     free( ports );\r
2198   }\r
2199 \r
2200   // Compare the jack ports for specified client to the requested number of channels.\r
2201   if ( nChannels < (channels + firstChannel) ) {\r
2202     errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";\r
2203     errorText_ = errorStream_.str();\r
2204     return FAILURE;\r
2205   }\r
2206 \r
2207   // Check the jack server sample rate.\r
2208   unsigned int jackRate = jack_get_sample_rate( client );\r
2209   if ( sampleRate != jackRate ) {\r
2210     jack_client_close( client );\r
2211     errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";\r
2212     errorText_ = errorStream_.str();\r
2213     return FAILURE;\r
2214   }\r
2215   stream_.sampleRate = jackRate;\r
2216 \r
2217   // Get the latency of the JACK port.\r
2218   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2219   if ( ports[ firstChannel ] ) {\r
2220     // Added by Ge Wang\r
2221     jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
2222     // the range (usually the min and max are equal)\r
2223     jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
2224     // get the latency range\r
2225     jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
2226     // be optimistic, use the min!\r
2227     stream_.latency[mode] = latrange.min;\r
2228     //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
2229   }\r
2230   free( ports );\r
2231 \r
2232   // The jack server always uses 32-bit floating-point data.\r
2233   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2234   stream_.userFormat = format;\r
2235 \r
2236   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
2237   else stream_.userInterleaved = true;\r
2238 \r
2239   // Jack always uses non-interleaved buffers.\r
2240   stream_.deviceInterleaved[mode] = false;\r
2241 \r
2242   // Jack always provides host byte-ordered data.\r
2243   stream_.doByteSwap[mode] = false;\r
2244 \r
2245   // Get the buffer size.  The buffer size and number of buffers\r
2246   // (periods) is set when the jack server is started.\r
2247   stream_.bufferSize = (int) jack_get_buffer_size( client );\r
2248   *bufferSize = stream_.bufferSize;\r
2249 \r
2250   stream_.nDeviceChannels[mode] = channels;\r
2251   stream_.nUserChannels[mode] = channels;\r
2252 \r
2253   // Set flags for buffer conversion.\r
2254   stream_.doConvertBuffer[mode] = false;\r
2255   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
2256     stream_.doConvertBuffer[mode] = true;\r
2257   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
2258        stream_.nUserChannels[mode] > 1 )\r
2259     stream_.doConvertBuffer[mode] = true;\r
2260 \r
2261   // Allocate our JackHandle structure for the stream.\r
2262   if ( handle == 0 ) {\r
2263     try {\r
2264       handle = new JackHandle;\r
2265     }\r
2266     catch ( std::bad_alloc& ) {\r
2267       errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";\r
2268       goto error;\r
2269     }\r
2270 \r
2271     if ( pthread_cond_init(&handle->condition, NULL) ) {\r
2272       errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";\r
2273       goto error;\r
2274     }\r
2275     stream_.apiHandle = (void *) handle;\r
2276     handle->client = client;\r
2277   }\r
2278   handle->deviceName[mode] = deviceName;\r
2279 \r
2280   // Allocate necessary internal buffers.\r
2281   unsigned long bufferBytes;\r
2282   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
2283   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
2284   if ( stream_.userBuffer[mode] == NULL ) {\r
2285     errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";\r
2286     goto error;\r
2287   }\r
2288 \r
2289   if ( stream_.doConvertBuffer[mode] ) {\r
2290 \r
2291     bool makeBuffer = true;\r
2292     if ( mode == OUTPUT )\r
2293       bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
2294     else { // mode == INPUT\r
2295       bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );\r
2296       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
2297         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);\r
2298         if ( bufferBytes < bytesOut ) makeBuffer = false;\r
2299       }\r
2300     }\r
2301 \r
2302     if ( makeBuffer ) {\r
2303       bufferBytes *= *bufferSize;\r
2304       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
2305       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
2306       if ( stream_.deviceBuffer == NULL ) {\r
2307         errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";\r
2308         goto error;\r
2309       }\r
2310     }\r
2311   }\r
2312 \r
2313   // Allocate memory for the Jack ports (channels) identifiers.\r
2314   handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );\r
2315   if ( handle->ports[mode] == NULL )  {\r
2316     errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";\r
2317     goto error;\r
2318   }\r
2319 \r
2320   stream_.device[mode] = device;\r
2321   stream_.channelOffset[mode] = firstChannel;\r
2322   stream_.state = STREAM_STOPPED;\r
2323   stream_.callbackInfo.object = (void *) this;\r
2324 \r
2325   if ( stream_.mode == OUTPUT && mode == INPUT )\r
2326     // We had already set up the stream for output.\r
2327     stream_.mode = DUPLEX;\r
2328   else {\r
2329     stream_.mode = mode;\r
2330     jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );\r
2331     jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );\r
2332     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );\r
2333   }\r
2334 \r
2335   // Register our ports.\r
2336   char label[64];\r
2337   if ( mode == OUTPUT ) {\r
2338     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2339       snprintf( label, 64, "outport %d", i );\r
2340       handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,\r
2341                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );\r
2342     }\r
2343   }\r
2344   else {\r
2345     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2346       snprintf( label, 64, "inport %d", i );\r
2347       handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,\r
2348                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );\r
2349     }\r
2350   }\r
2351 \r
2352   // Setup the buffer conversion information structure.  We don't use\r
2353   // buffers to do channel offsets, so we override that parameter\r
2354   // here.\r
2355   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
2356 \r
2357   return SUCCESS;\r
2358 \r
2359  error:\r
2360   if ( handle ) {\r
2361     pthread_cond_destroy( &handle->condition );\r
2362     jack_client_close( handle->client );\r
2363 \r
2364     if ( handle->ports[0] ) free( handle->ports[0] );\r
2365     if ( handle->ports[1] ) free( handle->ports[1] );\r
2366 \r
2367     delete handle;\r
2368     stream_.apiHandle = 0;\r
2369   }\r
2370 \r
2371   for ( int i=0; i<2; i++ ) {\r
2372     if ( stream_.userBuffer[i] ) {\r
2373       free( stream_.userBuffer[i] );\r
2374       stream_.userBuffer[i] = 0;\r
2375     }\r
2376   }\r
2377 \r
2378   if ( stream_.deviceBuffer ) {\r
2379     free( stream_.deviceBuffer );\r
2380     stream_.deviceBuffer = 0;\r
2381   }\r
2382 \r
2383   return FAILURE;\r
2384 }\r
2385 \r
2386 void RtApiJack :: closeStream( void )\r
2387 {\r
2388   if ( stream_.state == STREAM_CLOSED ) {\r
2389     errorText_ = "RtApiJack::closeStream(): no open stream to close!";\r
2390     error( RtAudioError::WARNING );\r
2391     return;\r
2392   }\r
2393 \r
2394   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2395   if ( handle ) {\r
2396 \r
2397     if ( stream_.state == STREAM_RUNNING )\r
2398       jack_deactivate( handle->client );\r
2399 \r
2400     jack_client_close( handle->client );\r
2401   }\r
2402 \r
2403   if ( handle ) {\r
2404     if ( handle->ports[0] ) free( handle->ports[0] );\r
2405     if ( handle->ports[1] ) free( handle->ports[1] );\r
2406     pthread_cond_destroy( &handle->condition );\r
2407     delete handle;\r
2408     stream_.apiHandle = 0;\r
2409   }\r
2410 \r
2411   for ( int i=0; i<2; i++ ) {\r
2412     if ( stream_.userBuffer[i] ) {\r
2413       free( stream_.userBuffer[i] );\r
2414       stream_.userBuffer[i] = 0;\r
2415     }\r
2416   }\r
2417 \r
2418   if ( stream_.deviceBuffer ) {\r
2419     free( stream_.deviceBuffer );\r
2420     stream_.deviceBuffer = 0;\r
2421   }\r
2422 \r
2423   stream_.mode = UNINITIALIZED;\r
2424   stream_.state = STREAM_CLOSED;\r
2425 }\r
2426 \r
2427 void RtApiJack :: startStream( void )\r
2428 {\r
2429   verifyStream();\r
2430   if ( stream_.state == STREAM_RUNNING ) {\r
2431     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
2432     error( RtAudioError::WARNING );\r
2433     return;\r
2434   }\r
2435 \r
2436   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2437   int result = jack_activate( handle->client );\r
2438   if ( result ) {\r
2439     errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";\r
2440     goto unlock;\r
2441   }\r
2442 \r
2443   const char **ports;\r
2444 \r
2445   // Get the list of available ports.\r
2446   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2447     result = 1;\r
2448     ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);\r
2449     if ( ports == NULL) {\r
2450       errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";\r
2451       goto unlock;\r
2452     }\r
2453 \r
2454     // Now make the port connections.  Since RtAudio wasn't designed to\r
2455     // allow the user to select particular channels of a device, we'll\r
2456     // just open the first "nChannels" ports with offset.\r
2457     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2458       result = 1;\r
2459       if ( ports[ stream_.channelOffset[0] + i ] )\r
2460         result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );\r
2461       if ( result ) {\r
2462         free( ports );\r
2463         errorText_ = "RtApiJack::startStream(): error connecting output ports!";\r
2464         goto unlock;\r
2465       }\r
2466     }\r
2467     free(ports);\r
2468   }\r
2469 \r
2470   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2471     result = 1;\r
2472     ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );\r
2473     if ( ports == NULL) {\r
2474       errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";\r
2475       goto unlock;\r
2476     }\r
2477 \r
2478     // Now make the port connections.  See note above.\r
2479     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2480       result = 1;\r
2481       if ( ports[ stream_.channelOffset[1] + i ] )\r
2482         result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );\r
2483       if ( result ) {\r
2484         free( ports );\r
2485         errorText_ = "RtApiJack::startStream(): error connecting input ports!";\r
2486         goto unlock;\r
2487       }\r
2488     }\r
2489     free(ports);\r
2490   }\r
2491 \r
2492   handle->drainCounter = 0;\r
2493   handle->internalDrain = false;\r
2494   stream_.state = STREAM_RUNNING;\r
2495 \r
2496  unlock:\r
2497   if ( result == 0 ) return;\r
2498   error( RtAudioError::SYSTEM_ERROR );\r
2499 }\r
2500 \r
2501 void RtApiJack :: stopStream( void )\r
2502 {\r
2503   verifyStream();\r
2504   if ( stream_.state == STREAM_STOPPED ) {\r
2505     errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";\r
2506     error( RtAudioError::WARNING );\r
2507     return;\r
2508   }\r
2509 \r
2510   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2511   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2512 \r
2513     if ( handle->drainCounter == 0 ) {\r
2514       handle->drainCounter = 2;\r
2515       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
2516     }\r
2517   }\r
2518 \r
2519   jack_deactivate( handle->client );\r
2520   stream_.state = STREAM_STOPPED;\r
2521 }\r
2522 \r
2523 void RtApiJack :: abortStream( void )\r
2524 {\r
2525   verifyStream();\r
2526   if ( stream_.state == STREAM_STOPPED ) {\r
2527     errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";\r
2528     error( RtAudioError::WARNING );\r
2529     return;\r
2530   }\r
2531 \r
2532   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2533   handle->drainCounter = 2;\r
2534 \r
2535   stopStream();\r
2536 }\r
2537 \r
2538 // This function will be called by a spawned thread when the user\r
2539 // callback function signals that the stream should be stopped or\r
2540 // aborted.  It is necessary to handle it this way because the\r
2541 // callbackEvent() function must return before the jack_deactivate()\r
2542 // function will return.\r
2543 static void *jackStopStream( void *ptr )\r
2544 {\r
2545   CallbackInfo *info = (CallbackInfo *) ptr;\r
2546   RtApiJack *object = (RtApiJack *) info->object;\r
2547 \r
2548   object->stopStream();\r
2549   pthread_exit( NULL );\r
2550 }\r
2551 \r
2552 bool RtApiJack :: callbackEvent( unsigned long nframes )\r
2553 {\r
2554   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
2555   if ( stream_.state == STREAM_CLOSED ) {\r
2556     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
2557     error( RtAudioError::WARNING );\r
2558     return FAILURE;\r
2559   }\r
2560   if ( stream_.bufferSize != nframes ) {\r
2561     errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";\r
2562     error( RtAudioError::WARNING );\r
2563     return FAILURE;\r
2564   }\r
2565 \r
2566   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
2567   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2568 \r
2569   // Check if we were draining the stream and signal is finished.\r
2570   if ( handle->drainCounter > 3 ) {\r
2571     ThreadHandle threadId;\r
2572 \r
2573     stream_.state = STREAM_STOPPING;\r
2574     if ( handle->internalDrain == true )\r
2575       pthread_create( &threadId, NULL, jackStopStream, info );\r
2576     else\r
2577       pthread_cond_signal( &handle->condition );\r
2578     return SUCCESS;\r
2579   }\r
2580 \r
2581   // Invoke user callback first, to get fresh output data.\r
2582   if ( handle->drainCounter == 0 ) {\r
2583     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
2584     double streamTime = getStreamTime();\r
2585     RtAudioStreamStatus status = 0;\r
2586     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
2587       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
2588       handle->xrun[0] = false;\r
2589     }\r
2590     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
2591       status |= RTAUDIO_INPUT_OVERFLOW;\r
2592       handle->xrun[1] = false;\r
2593     }\r
2594     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
2595                                   stream_.bufferSize, streamTime, status, info->userData );\r
2596     if ( cbReturnValue == 2 ) {\r
2597       stream_.state = STREAM_STOPPING;\r
2598       handle->drainCounter = 2;\r
2599       ThreadHandle id;\r
2600       pthread_create( &id, NULL, jackStopStream, info );\r
2601       return SUCCESS;\r
2602     }\r
2603     else if ( cbReturnValue == 1 ) {\r
2604       handle->drainCounter = 1;\r
2605       handle->internalDrain = true;\r
2606     }\r
2607   }\r
2608 \r
2609   jack_default_audio_sample_t *jackbuffer;\r
2610   unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );\r
2611   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2612 \r
2613     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
2614 \r
2615       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2616         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2617         memset( jackbuffer, 0, bufferBytes );\r
2618       }\r
2619 \r
2620     }\r
2621     else if ( stream_.doConvertBuffer[0] ) {\r
2622 \r
2623       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
2624 \r
2625       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2626         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2627         memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );\r
2628       }\r
2629     }\r
2630     else { // no buffer conversion\r
2631       for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2632         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2633         memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
2634       }\r
2635     }\r
2636   }\r
2637 \r
2638   // Don't bother draining input\r
2639   if ( handle->drainCounter ) {\r
2640     handle->drainCounter++;\r
2641     goto unlock;\r
2642   }\r
2643 \r
2644   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2645 \r
2646     if ( stream_.doConvertBuffer[1] ) {\r
2647       for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {\r
2648         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2649         memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );\r
2650       }\r
2651       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
2652     }\r
2653     else { // no buffer conversion\r
2654       for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2655         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2656         memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );\r
2657       }\r
2658     }\r
2659   }\r
2660 \r
2661  unlock:\r
2662   RtApi::tickStreamTime();\r
2663   return SUCCESS;\r
2664 }\r
2665   //******************** End of __UNIX_JACK__ *********************//\r
2666 #endif\r
2667 \r
2668 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows\r
2669 \r
2670 // The ASIO API is designed around a callback scheme, so this\r
2671 // implementation is similar to that used for OS-X CoreAudio and Linux\r
2672 // Jack.  The primary constraint with ASIO is that it only allows\r
2673 // access to a single driver at a time.  Thus, it is not possible to\r
2674 // have more than one simultaneous RtAudio stream.\r
2675 //\r
2676 // This implementation also requires a number of external ASIO files\r
2677 // and a few global variables.  The ASIO callback scheme does not\r
2678 // allow for the passing of user data, so we must create a global\r
2679 // pointer to our callbackInfo structure.\r
2680 //\r
2681 // On unix systems, we make use of a pthread condition variable.\r
2682 // Since there is no equivalent in Windows, I hacked something based\r
2683 // on information found in\r
2684 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r
2685 \r
2686 #include "asiosys.h"\r
2687 #include "asio.h"\r
2688 #include "iasiothiscallresolver.h"\r
2689 #include "asiodrivers.h"\r
2690 #include <cmath>\r
2691 \r
2692 static AsioDrivers drivers;\r
2693 static ASIOCallbacks asioCallbacks;\r
2694 static ASIODriverInfo driverInfo;\r
2695 static CallbackInfo *asioCallbackInfo;\r
2696 static bool asioXRun;\r
2697 \r
2698 struct AsioHandle {\r
2699   int drainCounter;       // Tracks callback counts when draining\r
2700   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
2701   ASIOBufferInfo *bufferInfos;\r
2702   HANDLE condition;\r
2703 \r
2704   AsioHandle()\r
2705     :drainCounter(0), internalDrain(false), bufferInfos(0) {}\r
2706 };\r
2707 \r
2708 // Function declarations (definitions at end of section)\r
2709 static const char* getAsioErrorString( ASIOError result );\r
2710 static void sampleRateChanged( ASIOSampleRate sRate );\r
2711 static long asioMessages( long selector, long value, void* message, double* opt );\r
2712 \r
2713 RtApiAsio :: RtApiAsio()\r
2714 {\r
2715   // ASIO cannot run on a multi-threaded appartment. You can call\r
2716   // CoInitialize beforehand, but it must be for appartment threading\r
2717   // (in which case, CoInitilialize will return S_FALSE here).\r
2718   coInitialized_ = false;\r
2719   HRESULT hr = CoInitialize( NULL ); \r
2720   if ( FAILED(hr) ) {\r
2721     errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
2722     error( RtAudioError::WARNING );\r
2723   }\r
2724   coInitialized_ = true;\r
2725 \r
2726   drivers.removeCurrentDriver();\r
2727   driverInfo.asioVersion = 2;\r
2728 \r
2729   // See note in DirectSound implementation about GetDesktopWindow().\r
2730   driverInfo.sysRef = GetForegroundWindow();\r
2731 }\r
2732 \r
2733 RtApiAsio :: ~RtApiAsio()\r
2734 {\r
2735   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
2736   if ( coInitialized_ ) CoUninitialize();\r
2737 }\r
2738 \r
2739 unsigned int RtApiAsio :: getDeviceCount( void )\r
2740 {\r
2741   return (unsigned int) drivers.asioGetNumDev();\r
2742 }\r
2743 \r
2744 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )\r
2745 {\r
2746   RtAudio::DeviceInfo info;\r
2747   info.probed = false;\r
2748 \r
2749   // Get device ID\r
2750   unsigned int nDevices = getDeviceCount();\r
2751   if ( nDevices == 0 ) {\r
2752     errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
2753     error( RtAudioError::INVALID_USE );\r
2754     return info;\r
2755   }\r
2756 \r
2757   if ( device >= nDevices ) {\r
2758     errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
2759     error( RtAudioError::INVALID_USE );\r
2760     return info;\r
2761   }\r
2762 \r
2763   // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.\r
2764   if ( stream_.state != STREAM_CLOSED ) {\r
2765     if ( device >= devices_.size() ) {\r
2766       errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";\r
2767       error( RtAudioError::WARNING );\r
2768       return info;\r
2769     }\r
2770     return devices_[ device ];\r
2771   }\r
2772 \r
2773   char driverName[32];\r
2774   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2775   if ( result != ASE_OK ) {\r
2776     errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2777     errorText_ = errorStream_.str();\r
2778     error( RtAudioError::WARNING );\r
2779     return info;\r
2780   }\r
2781 \r
2782   info.name = driverName;\r
2783 \r
2784   if ( !drivers.loadDriver( driverName ) ) {\r
2785     errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";\r
2786     errorText_ = errorStream_.str();\r
2787     error( RtAudioError::WARNING );\r
2788     return info;\r
2789   }\r
2790 \r
2791   result = ASIOInit( &driverInfo );\r
2792   if ( result != ASE_OK ) {\r
2793     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2794     errorText_ = errorStream_.str();\r
2795     error( RtAudioError::WARNING );\r
2796     return info;\r
2797   }\r
2798 \r
2799   // Determine the device channel information.\r
2800   long inputChannels, outputChannels;\r
2801   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2802   if ( result != ASE_OK ) {\r
2803     drivers.removeCurrentDriver();\r
2804     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2805     errorText_ = errorStream_.str();\r
2806     error( RtAudioError::WARNING );\r
2807     return info;\r
2808   }\r
2809 \r
2810   info.outputChannels = outputChannels;\r
2811   info.inputChannels = inputChannels;\r
2812   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2813     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2814 \r
2815   // Determine the supported sample rates.\r
2816   info.sampleRates.clear();\r
2817   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
2818     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
2819     if ( result == ASE_OK ) {\r
2820       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
2821 \r
2822       if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
2823         info.preferredSampleRate = SAMPLE_RATES[i];\r
2824     }\r
2825   }\r
2826 \r
2827   // Determine supported data types ... just check first channel and assume rest are the same.\r
2828   ASIOChannelInfo channelInfo;\r
2829   channelInfo.channel = 0;\r
2830   channelInfo.isInput = true;\r
2831   if ( info.inputChannels <= 0 ) channelInfo.isInput = false;\r
2832   result = ASIOGetChannelInfo( &channelInfo );\r
2833   if ( result != ASE_OK ) {\r
2834     drivers.removeCurrentDriver();\r
2835     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";\r
2836     errorText_ = errorStream_.str();\r
2837     error( RtAudioError::WARNING );\r
2838     return info;\r
2839   }\r
2840 \r
2841   info.nativeFormats = 0;\r
2842   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )\r
2843     info.nativeFormats |= RTAUDIO_SINT16;\r
2844   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )\r
2845     info.nativeFormats |= RTAUDIO_SINT32;\r
2846   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )\r
2847     info.nativeFormats |= RTAUDIO_FLOAT32;\r
2848   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
2849     info.nativeFormats |= RTAUDIO_FLOAT64;\r
2850   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
2851     info.nativeFormats |= RTAUDIO_SINT24;\r
2852 \r
2853   if ( info.outputChannels > 0 )\r
2854     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
2855   if ( info.inputChannels > 0 )\r
2856     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
2857 \r
2858   info.probed = true;\r
2859   drivers.removeCurrentDriver();\r
2860   return info;\r
2861 }\r
2862 \r
2863 static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
2864 {\r
2865   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
2866   object->callbackEvent( index );\r
2867 }\r
2868 \r
2869 void RtApiAsio :: saveDeviceInfo( void )\r
2870 {\r
2871   devices_.clear();\r
2872 \r
2873   unsigned int nDevices = getDeviceCount();\r
2874   devices_.resize( nDevices );\r
2875   for ( unsigned int i=0; i<nDevices; i++ )\r
2876     devices_[i] = getDeviceInfo( i );\r
2877 }\r
2878 \r
2879 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2880                                    unsigned int firstChannel, unsigned int sampleRate,\r
2881                                    RtAudioFormat format, unsigned int *bufferSize,\r
2882                                    RtAudio::StreamOptions *options )\r
2883 {////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
2884 \r
2885   bool isDuplexInput =  mode == INPUT && stream_.mode == OUTPUT;\r
2886 \r
2887   // For ASIO, a duplex stream MUST use the same driver.\r
2888   if ( isDuplexInput && stream_.device[0] != device ) {\r
2889     errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
2890     return FAILURE;\r
2891   }\r
2892 \r
2893   char driverName[32];\r
2894   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2895   if ( result != ASE_OK ) {\r
2896     errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2897     errorText_ = errorStream_.str();\r
2898     return FAILURE;\r
2899   }\r
2900 \r
2901   // Only load the driver once for duplex stream.\r
2902   if ( !isDuplexInput ) {\r
2903     // The getDeviceInfo() function will not work when a stream is open\r
2904     // because ASIO does not allow multiple devices to run at the same\r
2905     // time.  Thus, we'll probe the system before opening a stream and\r
2906     // save the results for use by getDeviceInfo().\r
2907     this->saveDeviceInfo();\r
2908 \r
2909     if ( !drivers.loadDriver( driverName ) ) {\r
2910       errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
2911       errorText_ = errorStream_.str();\r
2912       return FAILURE;\r
2913     }\r
2914 \r
2915     result = ASIOInit( &driverInfo );\r
2916     if ( result != ASE_OK ) {\r
2917       errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2918       errorText_ = errorStream_.str();\r
2919       return FAILURE;\r
2920     }\r
2921   }\r
2922 \r
2923   // keep them before any "goto error", they are used for error cleanup + goto device boundary checks\r
2924   bool buffersAllocated = false;\r
2925   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
2926   unsigned int nChannels;\r
2927 \r
2928 \r
2929   // Check the device channel count.\r
2930   long inputChannels, outputChannels;\r
2931   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2932   if ( result != ASE_OK ) {\r
2933     errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2934     errorText_ = errorStream_.str();\r
2935     goto error;\r
2936   }\r
2937 \r
2938   if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
2939        ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
2940     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
2941     errorText_ = errorStream_.str();\r
2942     goto error;\r
2943   }\r
2944   stream_.nDeviceChannels[mode] = channels;\r
2945   stream_.nUserChannels[mode] = channels;\r
2946   stream_.channelOffset[mode] = firstChannel;\r
2947 \r
2948   // Verify the sample rate is supported.\r
2949   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
2950   if ( result != ASE_OK ) {\r
2951     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
2952     errorText_ = errorStream_.str();\r
2953     goto error;\r
2954   }\r
2955 \r
2956   // Get the current sample rate\r
2957   ASIOSampleRate currentRate;\r
2958   result = ASIOGetSampleRate( &currentRate );\r
2959   if ( result != ASE_OK ) {\r
2960     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
2961     errorText_ = errorStream_.str();\r
2962     goto error;\r
2963   }\r
2964 \r
2965   // Set the sample rate only if necessary\r
2966   if ( currentRate != sampleRate ) {\r
2967     result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
2968     if ( result != ASE_OK ) {\r
2969       errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
2970       errorText_ = errorStream_.str();\r
2971       goto error;\r
2972     }\r
2973   }\r
2974 \r
2975   // Determine the driver data type.\r
2976   ASIOChannelInfo channelInfo;\r
2977   channelInfo.channel = 0;\r
2978   if ( mode == OUTPUT ) channelInfo.isInput = false;\r
2979   else channelInfo.isInput = true;\r
2980   result = ASIOGetChannelInfo( &channelInfo );\r
2981   if ( result != ASE_OK ) {\r
2982     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
2983     errorText_ = errorStream_.str();\r
2984     goto error;\r
2985   }\r
2986 \r
2987   // Assuming WINDOWS host is always little-endian.\r
2988   stream_.doByteSwap[mode] = false;\r
2989   stream_.userFormat = format;\r
2990   stream_.deviceFormat[mode] = 0;\r
2991   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {\r
2992     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
2993     if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;\r
2994   }\r
2995   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {\r
2996     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
2997     if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;\r
2998   }\r
2999   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {\r
3000     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
3001     if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;\r
3002   }\r
3003   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {\r
3004     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
3005     if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
3006   }\r
3007   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
3008     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
3009     if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
3010   }\r
3011 \r
3012   if ( stream_.deviceFormat[mode] == 0 ) {\r
3013     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
3014     errorText_ = errorStream_.str();\r
3015     goto error;\r
3016   }\r
3017 \r
3018   // Set the buffer size.  For a duplex stream, this will end up\r
3019   // setting the buffer size based on the input constraints, which\r
3020   // should be ok.\r
3021   long minSize, maxSize, preferSize, granularity;\r
3022   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
3023   if ( result != ASE_OK ) {\r
3024     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
3025     errorText_ = errorStream_.str();\r
3026     goto error;\r
3027   }\r
3028 \r
3029   if ( isDuplexInput ) {\r
3030     // When this is the duplex input (output was opened before), then we have to use the same\r
3031     // buffersize as the output, because it might use the preferred buffer size, which most\r
3032     // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,\r
3033     // So instead of throwing an error, make them equal. The caller uses the reference\r
3034     // to the "bufferSize" param as usual to set up processing buffers.\r
3035 \r
3036     *bufferSize = stream_.bufferSize;\r
3037 \r
3038   } else {\r
3039     if ( *bufferSize == 0 ) *bufferSize = preferSize;\r
3040     else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
3041     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
3042     else if ( granularity == -1 ) {\r
3043       // Make sure bufferSize is a power of two.\r
3044       int log2_of_min_size = 0;\r
3045       int log2_of_max_size = 0;\r
3046 \r
3047       for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
3048         if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
3049         if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
3050       }\r
3051 \r
3052       long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
3053       int min_delta_num = log2_of_min_size;\r
3054 \r
3055       for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
3056         long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
3057         if (current_delta < min_delta) {\r
3058           min_delta = current_delta;\r
3059           min_delta_num = i;\r
3060         }\r
3061       }\r
3062 \r
3063       *bufferSize = ( (unsigned int)1 << min_delta_num );\r
3064       if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
3065       else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
3066     }\r
3067     else if ( granularity != 0 ) {\r
3068       // Set to an even multiple of granularity, rounding up.\r
3069       *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
3070     }\r
3071   }\r
3072 \r
3073   /*\r
3074   // we don't use it anymore, see above!\r
3075   // Just left it here for the case...\r
3076   if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {\r
3077     errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
3078     goto error;\r
3079   }\r
3080   */\r
3081 \r
3082   stream_.bufferSize = *bufferSize;\r
3083   stream_.nBuffers = 2;\r
3084 \r
3085   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
3086   else stream_.userInterleaved = true;\r
3087 \r
3088   // ASIO always uses non-interleaved buffers.\r
3089   stream_.deviceInterleaved[mode] = false;\r
3090 \r
3091   // Allocate, if necessary, our AsioHandle structure for the stream.\r
3092   if ( handle == 0 ) {\r
3093     try {\r
3094       handle = new AsioHandle;\r
3095     }\r
3096     catch ( std::bad_alloc& ) {\r
3097       errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
3098       goto error;\r
3099     }\r
3100     handle->bufferInfos = 0;\r
3101 \r
3102     // Create a manual-reset event.\r
3103     handle->condition = CreateEvent( NULL,   // no security\r
3104                                      TRUE,   // manual-reset\r
3105                                      FALSE,  // non-signaled initially\r
3106                                      NULL ); // unnamed\r
3107     stream_.apiHandle = (void *) handle;\r
3108   }\r
3109 \r
3110   // Create the ASIO internal buffers.  Since RtAudio sets up input\r
3111   // and output separately, we'll have to dispose of previously\r
3112   // created output buffers for a duplex stream.\r
3113   if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
3114     ASIODisposeBuffers();\r
3115     if ( handle->bufferInfos ) free( handle->bufferInfos );\r
3116   }\r
3117 \r
3118   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
3119   unsigned int i;\r
3120   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3121   handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
3122   if ( handle->bufferInfos == NULL ) {\r
3123     errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
3124     errorText_ = errorStream_.str();\r
3125     goto error;\r
3126   }\r
3127 \r
3128   ASIOBufferInfo *infos;\r
3129   infos = handle->bufferInfos;\r
3130   for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {\r
3131     infos->isInput = ASIOFalse;\r
3132     infos->channelNum = i + stream_.channelOffset[0];\r
3133     infos->buffers[0] = infos->buffers[1] = 0;\r
3134   }\r
3135   for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {\r
3136     infos->isInput = ASIOTrue;\r
3137     infos->channelNum = i + stream_.channelOffset[1];\r
3138     infos->buffers[0] = infos->buffers[1] = 0;\r
3139   }\r
3140 \r
3141   // prepare for callbacks\r
3142   stream_.sampleRate = sampleRate;\r
3143   stream_.device[mode] = device;\r
3144   stream_.mode = isDuplexInput ? DUPLEX : mode;\r
3145 \r
3146   // store this class instance before registering callbacks, that are going to use it\r
3147   asioCallbackInfo = &stream_.callbackInfo;\r
3148   stream_.callbackInfo.object = (void *) this;\r
3149 \r
3150   // Set up the ASIO callback structure and create the ASIO data buffers.\r
3151   asioCallbacks.bufferSwitch = &bufferSwitch;\r
3152   asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
3153   asioCallbacks.asioMessage = &asioMessages;\r
3154   asioCallbacks.bufferSwitchTimeInfo = NULL;\r
3155   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3156   if ( result != ASE_OK ) {\r
3157     // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges\r
3158     // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver\r
3159     // in that case, let's be naïve and try that instead\r
3160     *bufferSize = preferSize;\r
3161     stream_.bufferSize = *bufferSize;\r
3162     result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3163   }\r
3164 \r
3165   if ( result != ASE_OK ) {\r
3166     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
3167     errorText_ = errorStream_.str();\r
3168     goto error;\r
3169   }\r
3170   buffersAllocated = true;  \r
3171   stream_.state = STREAM_STOPPED;\r
3172 \r
3173   // Set flags for buffer conversion.\r
3174   stream_.doConvertBuffer[mode] = false;\r
3175   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
3176     stream_.doConvertBuffer[mode] = true;\r
3177   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
3178        stream_.nUserChannels[mode] > 1 )\r
3179     stream_.doConvertBuffer[mode] = true;\r
3180 \r
3181   // Allocate necessary internal buffers\r
3182   unsigned long bufferBytes;\r
3183   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
3184   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
3185   if ( stream_.userBuffer[mode] == NULL ) {\r
3186     errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";\r
3187     goto error;\r
3188   }\r
3189 \r
3190   if ( stream_.doConvertBuffer[mode] ) {\r
3191 \r
3192     bool makeBuffer = true;\r
3193     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
3194     if ( isDuplexInput && stream_.deviceBuffer ) {\r
3195       unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
3196       if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
3197     }\r
3198 \r
3199     if ( makeBuffer ) {\r
3200       bufferBytes *= *bufferSize;\r
3201       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
3202       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
3203       if ( stream_.deviceBuffer == NULL ) {\r
3204         errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";\r
3205         goto error;\r
3206       }\r
3207     }\r
3208   }\r
3209 \r
3210   // Determine device latencies\r
3211   long inputLatency, outputLatency;\r
3212   result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
3213   if ( result != ASE_OK ) {\r
3214     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
3215     errorText_ = errorStream_.str();\r
3216     error( RtAudioError::WARNING); // warn but don't fail\r
3217   }\r
3218   else {\r
3219     stream_.latency[0] = outputLatency;\r
3220     stream_.latency[1] = inputLatency;\r
3221   }\r
3222 \r
3223   // Setup the buffer conversion information structure.  We don't use\r
3224   // buffers to do channel offsets, so we override that parameter\r
3225   // here.\r
3226   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
3227 \r
3228   return SUCCESS;\r
3229 \r
3230  error:\r
3231   if ( !isDuplexInput ) {\r
3232     // the cleanup for error in the duplex input, is done by RtApi::openStream\r
3233     // So we clean up for single channel only\r
3234 \r
3235     if ( buffersAllocated )\r
3236       ASIODisposeBuffers();\r
3237 \r
3238     drivers.removeCurrentDriver();\r
3239 \r
3240     if ( handle ) {\r
3241       CloseHandle( handle->condition );\r
3242       if ( handle->bufferInfos )\r
3243         free( handle->bufferInfos );\r
3244 \r
3245       delete handle;\r
3246       stream_.apiHandle = 0;\r
3247     }\r
3248 \r
3249 \r
3250     if ( stream_.userBuffer[mode] ) {\r
3251       free( stream_.userBuffer[mode] );\r
3252       stream_.userBuffer[mode] = 0;\r
3253     }\r
3254 \r
3255     if ( stream_.deviceBuffer ) {\r
3256       free( stream_.deviceBuffer );\r
3257       stream_.deviceBuffer = 0;\r
3258     }\r
3259   }\r
3260 \r
3261   return FAILURE;\r
3262 }////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
3263 \r
3264 void RtApiAsio :: closeStream()\r
3265 {\r
3266   if ( stream_.state == STREAM_CLOSED ) {\r
3267     errorText_ = "RtApiAsio::closeStream(): no open stream to close!";\r
3268     error( RtAudioError::WARNING );\r
3269     return;\r
3270   }\r
3271 \r
3272   if ( stream_.state == STREAM_RUNNING ) {\r
3273     stream_.state = STREAM_STOPPED;\r
3274     ASIOStop();\r
3275   }\r
3276   ASIODisposeBuffers();\r
3277   drivers.removeCurrentDriver();\r
3278 \r
3279   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3280   if ( handle ) {\r
3281     CloseHandle( handle->condition );\r
3282     if ( handle->bufferInfos )\r
3283       free( handle->bufferInfos );\r
3284     delete handle;\r
3285     stream_.apiHandle = 0;\r
3286   }\r
3287 \r
3288   for ( int i=0; i<2; i++ ) {\r
3289     if ( stream_.userBuffer[i] ) {\r
3290       free( stream_.userBuffer[i] );\r
3291       stream_.userBuffer[i] = 0;\r
3292     }\r
3293   }\r
3294 \r
3295   if ( stream_.deviceBuffer ) {\r
3296     free( stream_.deviceBuffer );\r
3297     stream_.deviceBuffer = 0;\r
3298   }\r
3299 \r
3300   stream_.mode = UNINITIALIZED;\r
3301   stream_.state = STREAM_CLOSED;\r
3302 }\r
3303 \r
3304 bool stopThreadCalled = false;\r
3305 \r
3306 void RtApiAsio :: startStream()\r
3307 {\r
3308   verifyStream();\r
3309   if ( stream_.state == STREAM_RUNNING ) {\r
3310     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
3311     error( RtAudioError::WARNING );\r
3312     return;\r
3313   }\r
3314 \r
3315   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3316   ASIOError result = ASIOStart();\r
3317   if ( result != ASE_OK ) {\r
3318     errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";\r
3319     errorText_ = errorStream_.str();\r
3320     goto unlock;\r
3321   }\r
3322 \r
3323   handle->drainCounter = 0;\r
3324   handle->internalDrain = false;\r
3325   ResetEvent( handle->condition );\r
3326   stream_.state = STREAM_RUNNING;\r
3327   asioXRun = false;\r
3328 \r
3329  unlock:\r
3330   stopThreadCalled = false;\r
3331 \r
3332   if ( result == ASE_OK ) return;\r
3333   error( RtAudioError::SYSTEM_ERROR );\r
3334 }\r
3335 \r
3336 void RtApiAsio :: stopStream()\r
3337 {\r
3338   verifyStream();\r
3339   if ( stream_.state == STREAM_STOPPED ) {\r
3340     errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";\r
3341     error( RtAudioError::WARNING );\r
3342     return;\r
3343   }\r
3344 \r
3345   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3346   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3347     if ( handle->drainCounter == 0 ) {\r
3348       handle->drainCounter = 2;\r
3349       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
3350     }\r
3351   }\r
3352 \r
3353   stream_.state = STREAM_STOPPED;\r
3354 \r
3355   ASIOError result = ASIOStop();\r
3356   if ( result != ASE_OK ) {\r
3357     errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";\r
3358     errorText_ = errorStream_.str();\r
3359   }\r
3360 \r
3361   if ( result == ASE_OK ) return;\r
3362   error( RtAudioError::SYSTEM_ERROR );\r
3363 }\r
3364 \r
3365 void RtApiAsio :: abortStream()\r
3366 {\r
3367   verifyStream();\r
3368   if ( stream_.state == STREAM_STOPPED ) {\r
3369     errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";\r
3370     error( RtAudioError::WARNING );\r
3371     return;\r
3372   }\r
3373 \r
3374   // The following lines were commented-out because some behavior was\r
3375   // noted where the device buffers need to be zeroed to avoid\r
3376   // continuing sound, even when the device buffers are completely\r
3377   // disposed.  So now, calling abort is the same as calling stop.\r
3378   // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3379   // handle->drainCounter = 2;\r
3380   stopStream();\r
3381 }\r
3382 \r
3383 // This function will be called by a spawned thread when the user\r
3384 // callback function signals that the stream should be stopped or\r
3385 // aborted.  It is necessary to handle it this way because the\r
3386 // callbackEvent() function must return before the ASIOStop()\r
3387 // function will return.\r
3388 static unsigned __stdcall asioStopStream( void *ptr )\r
3389 {\r
3390   CallbackInfo *info = (CallbackInfo *) ptr;\r
3391   RtApiAsio *object = (RtApiAsio *) info->object;\r
3392 \r
3393   object->stopStream();\r
3394   _endthreadex( 0 );\r
3395   return 0;\r
3396 }\r
3397 \r
3398 bool RtApiAsio :: callbackEvent( long bufferIndex )\r
3399 {\r
3400   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
3401   if ( stream_.state == STREAM_CLOSED ) {\r
3402     errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
3403     error( RtAudioError::WARNING );\r
3404     return FAILURE;\r
3405   }\r
3406 \r
3407   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
3408   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3409 \r
3410   // Check if we were draining the stream and signal if finished.\r
3411   if ( handle->drainCounter > 3 ) {\r
3412 \r
3413     stream_.state = STREAM_STOPPING;\r
3414     if ( handle->internalDrain == false )\r
3415       SetEvent( handle->condition );\r
3416     else { // spawn a thread to stop the stream\r
3417       unsigned threadId;\r
3418       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3419                                                     &stream_.callbackInfo, 0, &threadId );\r
3420     }\r
3421     return SUCCESS;\r
3422   }\r
3423 \r
3424   // Invoke user callback to get fresh output data UNLESS we are\r
3425   // draining stream.\r
3426   if ( handle->drainCounter == 0 ) {\r
3427     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
3428     double streamTime = getStreamTime();\r
3429     RtAudioStreamStatus status = 0;\r
3430     if ( stream_.mode != INPUT && asioXRun == true ) {\r
3431       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
3432       asioXRun = false;\r
3433     }\r
3434     if ( stream_.mode != OUTPUT && asioXRun == true ) {\r
3435       status |= RTAUDIO_INPUT_OVERFLOW;\r
3436       asioXRun = false;\r
3437     }\r
3438     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
3439                                      stream_.bufferSize, streamTime, status, info->userData );\r
3440     if ( cbReturnValue == 2 ) {\r
3441       stream_.state = STREAM_STOPPING;\r
3442       handle->drainCounter = 2;\r
3443       unsigned threadId;\r
3444       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3445                                                     &stream_.callbackInfo, 0, &threadId );\r
3446       return SUCCESS;\r
3447     }\r
3448     else if ( cbReturnValue == 1 ) {\r
3449       handle->drainCounter = 1;\r
3450       handle->internalDrain = true;\r
3451     }\r
3452   }\r
3453 \r
3454   unsigned int nChannels, bufferBytes, i, j;\r
3455   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3456   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3457 \r
3458     bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );\r
3459 \r
3460     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
3461 \r
3462       for ( i=0, j=0; i<nChannels; i++ ) {\r
3463         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3464           memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );\r
3465       }\r
3466 \r
3467     }\r
3468     else if ( stream_.doConvertBuffer[0] ) {\r
3469 \r
3470       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
3471       if ( stream_.doByteSwap[0] )\r
3472         byteSwapBuffer( stream_.deviceBuffer,\r
3473                         stream_.bufferSize * stream_.nDeviceChannels[0],\r
3474                         stream_.deviceFormat[0] );\r
3475 \r
3476       for ( i=0, j=0; i<nChannels; i++ ) {\r
3477         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3478           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3479                   &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );\r
3480       }\r
3481 \r
3482     }\r
3483     else {\r
3484 \r
3485       if ( stream_.doByteSwap[0] )\r
3486         byteSwapBuffer( stream_.userBuffer[0],\r
3487                         stream_.bufferSize * stream_.nUserChannels[0],\r
3488                         stream_.userFormat );\r
3489 \r
3490       for ( i=0, j=0; i<nChannels; i++ ) {\r
3491         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3492           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3493                   &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );\r
3494       }\r
3495 \r
3496     }\r
3497   }\r
3498 \r
3499   // Don't bother draining input\r
3500   if ( handle->drainCounter ) {\r
3501     handle->drainCounter++;\r
3502     goto unlock;\r
3503   }\r
3504 \r
3505   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
3506 \r
3507     bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);\r
3508 \r
3509     if (stream_.doConvertBuffer[1]) {\r
3510 \r
3511       // Always interleave ASIO input data.\r
3512       for ( i=0, j=0; i<nChannels; i++ ) {\r
3513         if ( handle->bufferInfos[i].isInput == ASIOTrue )\r
3514           memcpy( &stream_.deviceBuffer[j++*bufferBytes],\r
3515                   handle->bufferInfos[i].buffers[bufferIndex],\r
3516                   bufferBytes );\r
3517       }\r
3518 \r
3519       if ( stream_.doByteSwap[1] )\r
3520         byteSwapBuffer( stream_.deviceBuffer,\r
3521                         stream_.bufferSize * stream_.nDeviceChannels[1],\r
3522                         stream_.deviceFormat[1] );\r
3523       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
3524 \r
3525     }\r
3526     else {\r
3527       for ( i=0, j=0; i<nChannels; i++ ) {\r
3528         if ( handle->bufferInfos[i].isInput == ASIOTrue ) {\r
3529           memcpy( &stream_.userBuffer[1][bufferBytes*j++],\r
3530                   handle->bufferInfos[i].buffers[bufferIndex],\r
3531                   bufferBytes );\r
3532         }\r
3533       }\r
3534 \r
3535       if ( stream_.doByteSwap[1] )\r
3536         byteSwapBuffer( stream_.userBuffer[1],\r
3537                         stream_.bufferSize * stream_.nUserChannels[1],\r
3538                         stream_.userFormat );\r
3539     }\r
3540   }\r
3541 \r
3542  unlock:\r
3543   // The following call was suggested by Malte Clasen.  While the API\r
3544   // documentation indicates it should not be required, some device\r
3545   // drivers apparently do not function correctly without it.\r
3546   ASIOOutputReady();\r
3547 \r
3548   RtApi::tickStreamTime();\r
3549   return SUCCESS;\r
3550 }\r
3551 \r
3552 static void sampleRateChanged( ASIOSampleRate sRate )\r
3553 {\r
3554   // The ASIO documentation says that this usually only happens during\r
3555   // external sync.  Audio processing is not stopped by the driver,\r
3556   // actual sample rate might not have even changed, maybe only the\r
3557   // sample rate status of an AES/EBU or S/PDIF digital input at the\r
3558   // audio device.\r
3559 \r
3560   RtApi *object = (RtApi *) asioCallbackInfo->object;\r
3561   try {\r
3562     object->stopStream();\r
3563   }\r
3564   catch ( RtAudioError &exception ) {\r
3565     std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;\r
3566     return;\r
3567   }\r
3568 \r
3569   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
3570 }\r
3571 \r
3572 static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
3573 {\r
3574   long ret = 0;\r
3575 \r
3576   switch( selector ) {\r
3577   case kAsioSelectorSupported:\r
3578     if ( value == kAsioResetRequest\r
3579          || value == kAsioEngineVersion\r
3580          || value == kAsioResyncRequest\r
3581          || value == kAsioLatenciesChanged\r
3582          // The following three were added for ASIO 2.0, you don't\r
3583          // necessarily have to support them.\r
3584          || value == kAsioSupportsTimeInfo\r
3585          || value == kAsioSupportsTimeCode\r
3586          || value == kAsioSupportsInputMonitor)\r
3587       ret = 1L;\r
3588     break;\r
3589   case kAsioResetRequest:\r
3590     // Defer the task and perform the reset of the driver during the\r
3591     // next "safe" situation.  You cannot reset the driver right now,\r
3592     // as this code is called from the driver.  Reset the driver is\r
3593     // done by completely destruct is. I.e. ASIOStop(),\r
3594     // ASIODisposeBuffers(), Destruction Afterwards you initialize the\r
3595     // driver again.\r
3596     std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;\r
3597     ret = 1L;\r
3598     break;\r
3599   case kAsioResyncRequest:\r
3600     // This informs the application that the driver encountered some\r
3601     // non-fatal data loss.  It is used for synchronization purposes\r
3602     // of different media.  Added mainly to work around the Win16Mutex\r
3603     // problems in Windows 95/98 with the Windows Multimedia system,\r
3604     // which could lose data because the Mutex was held too long by\r
3605     // another thread.  However a driver can issue it in other\r
3606     // situations, too.\r
3607     // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;\r
3608     asioXRun = true;\r
3609     ret = 1L;\r
3610     break;\r
3611   case kAsioLatenciesChanged:\r
3612     // This will inform the host application that the drivers were\r
3613     // latencies changed.  Beware, it this does not mean that the\r
3614     // buffer sizes have changed!  You might need to update internal\r
3615     // delay data.\r
3616     std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;\r
3617     ret = 1L;\r
3618     break;\r
3619   case kAsioEngineVersion:\r
3620     // Return the supported ASIO version of the host application.  If\r
3621     // a host application does not implement this selector, ASIO 1.0\r
3622     // is assumed by the driver.\r
3623     ret = 2L;\r
3624     break;\r
3625   case kAsioSupportsTimeInfo:\r
3626     // Informs the driver whether the\r
3627     // asioCallbacks.bufferSwitchTimeInfo() callback is supported.\r
3628     // For compatibility with ASIO 1.0 drivers the host application\r
3629     // should always support the "old" bufferSwitch method, too.\r
3630     ret = 0;\r
3631     break;\r
3632   case kAsioSupportsTimeCode:\r
3633     // Informs the driver whether application is interested in time\r
3634     // code info.  If an application does not need to know about time\r
3635     // code, the driver has less work to do.\r
3636     ret = 0;\r
3637     break;\r
3638   }\r
3639   return ret;\r
3640 }\r
3641 \r
3642 static const char* getAsioErrorString( ASIOError result )\r
3643 {\r
3644   struct Messages \r
3645   {\r
3646     ASIOError value;\r
3647     const char*message;\r
3648   };\r
3649 \r
3650   static const Messages m[] = \r
3651     {\r
3652       {   ASE_NotPresent,    "Hardware input or output is not present or available." },\r
3653       {   ASE_HWMalfunction,  "Hardware is malfunctioning." },\r
3654       {   ASE_InvalidParameter, "Invalid input parameter." },\r
3655       {   ASE_InvalidMode,      "Invalid mode." },\r
3656       {   ASE_SPNotAdvancing,     "Sample position not advancing." },\r
3657       {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },\r
3658       {   ASE_NoMemory,           "Not enough memory to complete the request." }\r
3659     };\r
3660 \r
3661   for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )\r
3662     if ( m[i].value == result ) return m[i].message;\r
3663 \r
3664   return "Unknown error.";\r
3665 }\r
3666 \r
3667 //******************** End of __WINDOWS_ASIO__ *********************//\r
3668 #endif\r
3669 \r
3670 \r
3671 #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
3672 \r
3673 // Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
3674 // - Introduces support for the Windows WASAPI API\r
3675 // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
3676 // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
3677 // - Includes automatic internal conversion of sample rate and buffer size between hardware and the user\r
3678 \r
3679 #ifndef INITGUID\r
3680   #define INITGUID\r
3681 #endif\r
3682 #include <audioclient.h>\r
3683 #include <avrt.h>\r
3684 #include <mmdeviceapi.h>\r
3685 #include <functiondiscoverykeys_devpkey.h>\r
3686 \r
3687 //=============================================================================\r
3688 \r
3689 #define SAFE_RELEASE( objectPtr )\\r
3690 if ( objectPtr )\\r
3691 {\\r
3692   objectPtr->Release();\\r
3693   objectPtr = NULL;\\r
3694 }\r
3695 \r
3696 typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
3697 \r
3698 //-----------------------------------------------------------------------------\r
3699 \r
3700 // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
3701 // Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
3702 // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
3703 // provide intermediate storage for read / write synchronization.\r
3704 class WasapiBuffer\r
3705 {\r
3706 public:\r
3707   WasapiBuffer()\r
3708     : buffer_( NULL ),\r
3709       bufferSize_( 0 ),\r
3710       inIndex_( 0 ),\r
3711       outIndex_( 0 ) {}\r
3712 \r
3713   ~WasapiBuffer() {\r
3714     free( buffer_ );\r
3715   }\r
3716 \r
3717   // sets the length of the internal ring buffer\r
3718   void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
3719     free( buffer_ );\r
3720 \r
3721     buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
3722 \r
3723     bufferSize_ = bufferSize;\r
3724     inIndex_ = 0;\r
3725     outIndex_ = 0;\r
3726   }\r
3727 \r
3728   // attempt to push a buffer into the ring buffer at the current "in" index\r
3729   bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3730   {\r
3731     if ( !buffer ||                 // incoming buffer is NULL\r
3732          bufferSize == 0 ||         // incoming buffer has no data\r
3733          bufferSize > bufferSize_ ) // incoming buffer too large\r
3734     {\r
3735       return false;\r
3736     }\r
3737 \r
3738     unsigned int relOutIndex = outIndex_;\r
3739     unsigned int inIndexEnd = inIndex_ + bufferSize;\r
3740     if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
3741       relOutIndex += bufferSize_;\r
3742     }\r
3743 \r
3744     // "in" index can end on the "out" index but cannot begin at it\r
3745     if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
3746       return false; // not enough space between "in" index and "out" index\r
3747     }\r
3748 \r
3749     // copy buffer from external to internal\r
3750     int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
3751     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3752     int fromInSize = bufferSize - fromZeroSize;\r
3753 \r
3754     switch( format )\r
3755       {\r
3756       case RTAUDIO_SINT8:\r
3757         memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
3758         memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
3759         break;\r
3760       case RTAUDIO_SINT16:\r
3761         memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
3762         memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
3763         break;\r
3764       case RTAUDIO_SINT24:\r
3765         memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
3766         memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
3767         break;\r
3768       case RTAUDIO_SINT32:\r
3769         memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
3770         memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
3771         break;\r
3772       case RTAUDIO_FLOAT32:\r
3773         memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
3774         memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
3775         break;\r
3776       case RTAUDIO_FLOAT64:\r
3777         memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
3778         memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
3779         break;\r
3780     }\r
3781 \r
3782     // update "in" index\r
3783     inIndex_ += bufferSize;\r
3784     inIndex_ %= bufferSize_;\r
3785 \r
3786     return true;\r
3787   }\r
3788 \r
3789   // attempt to pull a buffer from the ring buffer from the current "out" index\r
3790   bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3791   {\r
3792     if ( !buffer ||                 // incoming buffer is NULL\r
3793          bufferSize == 0 ||         // incoming buffer has no data\r
3794          bufferSize > bufferSize_ ) // incoming buffer too large\r
3795     {\r
3796       return false;\r
3797     }\r
3798 \r
3799     unsigned int relInIndex = inIndex_;\r
3800     unsigned int outIndexEnd = outIndex_ + bufferSize;\r
3801     if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
3802       relInIndex += bufferSize_;\r
3803     }\r
3804 \r
3805     // "out" index can begin at and end on the "in" index\r
3806     if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
3807       return false; // not enough space between "out" index and "in" index\r
3808     }\r
3809 \r
3810     // copy buffer from internal to external\r
3811     int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
3812     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3813     int fromOutSize = bufferSize - fromZeroSize;\r
3814 \r
3815     switch( format )\r
3816     {\r
3817       case RTAUDIO_SINT8:\r
3818         memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
3819         memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
3820         break;\r
3821       case RTAUDIO_SINT16:\r
3822         memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
3823         memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
3824         break;\r
3825       case RTAUDIO_SINT24:\r
3826         memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
3827         memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
3828         break;\r
3829       case RTAUDIO_SINT32:\r
3830         memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
3831         memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
3832         break;\r
3833       case RTAUDIO_FLOAT32:\r
3834         memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
3835         memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
3836         break;\r
3837       case RTAUDIO_FLOAT64:\r
3838         memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
3839         memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
3840         break;\r
3841     }\r
3842 \r
3843     // update "out" index\r
3844     outIndex_ += bufferSize;\r
3845     outIndex_ %= bufferSize_;\r
3846 \r
3847     return true;\r
3848   }\r
3849 \r
3850 private:\r
3851   char* buffer_;\r
3852   unsigned int bufferSize_;\r
3853   unsigned int inIndex_;\r
3854   unsigned int outIndex_;\r
3855 };\r
3856 \r
3857 //-----------------------------------------------------------------------------\r
3858 \r
3859 // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate\r
3860 // between HW and the user. The convertBufferWasapi function is used to perform this conversion\r
3861 // between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
3862 // This sample rate converter works best with conversions between one rate and its multiple.\r
3863 void convertBufferWasapi( char* outBuffer,\r
3864                           const char* inBuffer,\r
3865                           const unsigned int& channelCount,\r
3866                           const unsigned int& inSampleRate,\r
3867                           const unsigned int& outSampleRate,\r
3868                           const unsigned int& inSampleCount,\r
3869                           unsigned int& outSampleCount,\r
3870                           const RtAudioFormat& format )\r
3871 {\r
3872   // calculate the new outSampleCount and relative sampleStep\r
3873   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
3874   float sampleRatioInv = ( float ) 1 / sampleRatio;\r
3875   float sampleStep = 1.0f / sampleRatio;\r
3876   float inSampleFraction = 0.0f;\r
3877 \r
3878   outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio );\r
3879 \r
3880   // if inSampleRate is a multiple of outSampleRate (or vice versa) there's no need to interpolate\r
3881   if ( floor( sampleRatio ) == sampleRatio || floor( sampleRatioInv ) == sampleRatioInv )\r
3882   {\r
3883     // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3884     for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3885     {\r
3886       unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3887 \r
3888       switch ( format )\r
3889       {\r
3890         case RTAUDIO_SINT8:\r
3891           memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
3892           break;\r
3893         case RTAUDIO_SINT16:\r
3894           memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
3895           break;\r
3896         case RTAUDIO_SINT24:\r
3897           memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
3898           break;\r
3899         case RTAUDIO_SINT32:\r
3900           memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
3901           break;\r
3902         case RTAUDIO_FLOAT32:\r
3903           memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
3904           break;\r
3905         case RTAUDIO_FLOAT64:\r
3906           memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
3907           break;\r
3908       }\r
3909 \r
3910       // jump to next in sample\r
3911       inSampleFraction += sampleStep;\r
3912     }\r
3913   }\r
3914   else // else interpolate\r
3915   {\r
3916     // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3917     for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3918     {\r
3919       unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3920       float inSampleDec = inSampleFraction - inSample;\r
3921       unsigned int frameInSample = inSample * channelCount;\r
3922       unsigned int frameOutSample = outSample * channelCount;\r
3923 \r
3924       switch ( format )\r
3925       {\r
3926         case RTAUDIO_SINT8:\r
3927         {\r
3928           char* convInBuffer = ( char* ) inBuffer;\r
3929           for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
3930           {\r
3931             char fromSample = convInBuffer[ frameInSample + channel ];\r
3932             char toSample = convInBuffer[ frameInSample + channelCount + channel ];\r
3933             char sampleDiff = ( char ) ( ( toSample - fromSample ) * inSampleDec );\r
3934             ( ( char* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
3935           }\r
3936           break;\r
3937         }\r
3938         case RTAUDIO_SINT16:\r
3939         {\r
3940           short* convInBuffer = ( short* ) inBuffer;\r
3941           for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
3942           {\r
3943             short fromSample = convInBuffer[ frameInSample + channel ];\r
3944             short toSample = convInBuffer[ frameInSample + channelCount + channel ];\r
3945             short sampleDiff = ( short ) ( ( toSample - fromSample ) * inSampleDec );\r
3946             ( ( short* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
3947           }\r
3948           break;\r
3949         }\r
3950         case RTAUDIO_SINT24:\r
3951         {\r
3952           S24* convInBuffer = ( S24* ) inBuffer;\r
3953           for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
3954           {\r
3955             int fromSample = convInBuffer[ frameInSample + channel ].asInt();\r
3956             int toSample = convInBuffer[ frameInSample + channelCount + channel ].asInt();\r
3957             int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec );\r
3958             ( ( S24* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
3959           }\r
3960           break;\r
3961         }\r
3962         case RTAUDIO_SINT32:\r
3963         {\r
3964           int* convInBuffer = ( int* ) inBuffer;\r
3965           for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
3966           {\r
3967             int fromSample = convInBuffer[ frameInSample + channel ];\r
3968             int toSample = convInBuffer[ frameInSample + channelCount + channel ];\r
3969             int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec );\r
3970             ( ( int* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
3971           }\r
3972           break;\r
3973         }\r
3974         case RTAUDIO_FLOAT32:\r
3975         {\r
3976           float* convInBuffer = ( float* ) inBuffer;\r
3977           for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
3978           {\r
3979             float fromSample = convInBuffer[ frameInSample + channel ];\r
3980             float toSample = convInBuffer[ frameInSample + channelCount + channel ];\r
3981             float sampleDiff = ( toSample - fromSample ) * inSampleDec;\r
3982             ( ( float* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
3983           }\r
3984           break;\r
3985         }\r
3986         case RTAUDIO_FLOAT64:\r
3987         {\r
3988           double* convInBuffer = ( double* ) inBuffer;\r
3989           for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
3990           {\r
3991             double fromSample = convInBuffer[ frameInSample + channel ];\r
3992             double toSample = convInBuffer[ frameInSample + channelCount + channel ];\r
3993             double sampleDiff = ( toSample - fromSample ) * inSampleDec;\r
3994             ( ( double* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
3995           }\r
3996           break;\r
3997         }\r
3998       }\r
3999 \r
4000       // jump to next in sample\r
4001       inSampleFraction += sampleStep;\r
4002     }\r
4003   }\r
4004 }\r
4005 \r
4006 //-----------------------------------------------------------------------------\r
4007 \r
4008 // A structure to hold various information related to the WASAPI implementation.\r
4009 struct WasapiHandle\r
4010 {\r
4011   IAudioClient* captureAudioClient;\r
4012   IAudioClient* renderAudioClient;\r
4013   IAudioCaptureClient* captureClient;\r
4014   IAudioRenderClient* renderClient;\r
4015   HANDLE captureEvent;\r
4016   HANDLE renderEvent;\r
4017 \r
4018   WasapiHandle()\r
4019   : captureAudioClient( NULL ),\r
4020     renderAudioClient( NULL ),\r
4021     captureClient( NULL ),\r
4022     renderClient( NULL ),\r
4023     captureEvent( NULL ),\r
4024     renderEvent( NULL ) {}\r
4025 };\r
4026 \r
4027 //=============================================================================\r
4028 \r
4029 RtApiWasapi::RtApiWasapi()\r
4030   : coInitialized_( false ), deviceEnumerator_( NULL )\r
4031 {\r
4032   // WASAPI can run either apartment or multi-threaded\r
4033   HRESULT hr = CoInitialize( NULL );\r
4034   if ( !FAILED( hr ) )\r
4035     coInitialized_ = true;\r
4036 \r
4037   // Instantiate device enumerator\r
4038   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
4039                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
4040                          ( void** ) &deviceEnumerator_ );\r
4041 \r
4042   if ( FAILED( hr ) ) {\r
4043     errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
4044     error( RtAudioError::DRIVER_ERROR );\r
4045   }\r
4046 }\r
4047 \r
4048 //-----------------------------------------------------------------------------\r
4049 \r
4050 RtApiWasapi::~RtApiWasapi()\r
4051 {\r
4052   if ( stream_.state != STREAM_CLOSED )\r
4053     closeStream();\r
4054 \r
4055   SAFE_RELEASE( deviceEnumerator_ );\r
4056 \r
4057   // If this object previously called CoInitialize()\r
4058   if ( coInitialized_ )\r
4059     CoUninitialize();\r
4060 }\r
4061 \r
4062 //=============================================================================\r
4063 \r
4064 unsigned int RtApiWasapi::getDeviceCount( void )\r
4065 {\r
4066   unsigned int captureDeviceCount = 0;\r
4067   unsigned int renderDeviceCount = 0;\r
4068 \r
4069   IMMDeviceCollection* captureDevices = NULL;\r
4070   IMMDeviceCollection* renderDevices = NULL;\r
4071 \r
4072   // Count capture devices\r
4073   errorText_.clear();\r
4074   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4075   if ( FAILED( hr ) ) {\r
4076     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
4077     goto Exit;\r
4078   }\r
4079 \r
4080   hr = captureDevices->GetCount( &captureDeviceCount );\r
4081   if ( FAILED( hr ) ) {\r
4082     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
4083     goto Exit;\r
4084   }\r
4085 \r
4086   // Count render devices\r
4087   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4088   if ( FAILED( hr ) ) {\r
4089     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
4090     goto Exit;\r
4091   }\r
4092 \r
4093   hr = renderDevices->GetCount( &renderDeviceCount );\r
4094   if ( FAILED( hr ) ) {\r
4095     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
4096     goto Exit;\r
4097   }\r
4098 \r
4099 Exit:\r
4100   // release all references\r
4101   SAFE_RELEASE( captureDevices );\r
4102   SAFE_RELEASE( renderDevices );\r
4103 \r
4104   if ( errorText_.empty() )\r
4105     return captureDeviceCount + renderDeviceCount;\r
4106 \r
4107   error( RtAudioError::DRIVER_ERROR );\r
4108   return 0;\r
4109 }\r
4110 \r
4111 //-----------------------------------------------------------------------------\r
4112 \r
4113 RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
4114 {\r
4115   RtAudio::DeviceInfo info;\r
4116   unsigned int captureDeviceCount = 0;\r
4117   unsigned int renderDeviceCount = 0;\r
4118   std::string defaultDeviceName;\r
4119   bool isCaptureDevice = false;\r
4120 \r
4121   PROPVARIANT deviceNameProp;\r
4122   PROPVARIANT defaultDeviceNameProp;\r
4123 \r
4124   IMMDeviceCollection* captureDevices = NULL;\r
4125   IMMDeviceCollection* renderDevices = NULL;\r
4126   IMMDevice* devicePtr = NULL;\r
4127   IMMDevice* defaultDevicePtr = NULL;\r
4128   IAudioClient* audioClient = NULL;\r
4129   IPropertyStore* devicePropStore = NULL;\r
4130   IPropertyStore* defaultDevicePropStore = NULL;\r
4131 \r
4132   WAVEFORMATEX* deviceFormat = NULL;\r
4133   WAVEFORMATEX* closestMatchFormat = NULL;\r
4134 \r
4135   // probed\r
4136   info.probed = false;\r
4137 \r
4138   // Count capture devices\r
4139   errorText_.clear();\r
4140   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4141   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4142   if ( FAILED( hr ) ) {\r
4143     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
4144     goto Exit;\r
4145   }\r
4146 \r
4147   hr = captureDevices->GetCount( &captureDeviceCount );\r
4148   if ( FAILED( hr ) ) {\r
4149     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
4150     goto Exit;\r
4151   }\r
4152 \r
4153   // Count render devices\r
4154   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4155   if ( FAILED( hr ) ) {\r
4156     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
4157     goto Exit;\r
4158   }\r
4159 \r
4160   hr = renderDevices->GetCount( &renderDeviceCount );\r
4161   if ( FAILED( hr ) ) {\r
4162     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
4163     goto Exit;\r
4164   }\r
4165 \r
4166   // validate device index\r
4167   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4168     errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
4169     errorType = RtAudioError::INVALID_USE;\r
4170     goto Exit;\r
4171   }\r
4172 \r
4173   // determine whether index falls within capture or render devices\r
4174   if ( device >= renderDeviceCount ) {\r
4175     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4176     if ( FAILED( hr ) ) {\r
4177       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
4178       goto Exit;\r
4179     }\r
4180     isCaptureDevice = true;\r
4181   }\r
4182   else {\r
4183     hr = renderDevices->Item( device, &devicePtr );\r
4184     if ( FAILED( hr ) ) {\r
4185       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
4186       goto Exit;\r
4187     }\r
4188     isCaptureDevice = false;\r
4189   }\r
4190 \r
4191   // get default device name\r
4192   if ( isCaptureDevice ) {\r
4193     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
4194     if ( FAILED( hr ) ) {\r
4195       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
4196       goto Exit;\r
4197     }\r
4198   }\r
4199   else {\r
4200     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
4201     if ( FAILED( hr ) ) {\r
4202       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
4203       goto Exit;\r
4204     }\r
4205   }\r
4206 \r
4207   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
4208   if ( FAILED( hr ) ) {\r
4209     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
4210     goto Exit;\r
4211   }\r
4212   PropVariantInit( &defaultDeviceNameProp );\r
4213 \r
4214   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
4215   if ( FAILED( hr ) ) {\r
4216     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
4217     goto Exit;\r
4218   }\r
4219 \r
4220   defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);\r
4221 \r
4222   // name\r
4223   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
4224   if ( FAILED( hr ) ) {\r
4225     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
4226     goto Exit;\r
4227   }\r
4228 \r
4229   PropVariantInit( &deviceNameProp );\r
4230 \r
4231   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
4232   if ( FAILED( hr ) ) {\r
4233     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
4234     goto Exit;\r
4235   }\r
4236 \r
4237   info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);\r
4238 \r
4239   // is default\r
4240   if ( isCaptureDevice ) {\r
4241     info.isDefaultInput = info.name == defaultDeviceName;\r
4242     info.isDefaultOutput = false;\r
4243   }\r
4244   else {\r
4245     info.isDefaultInput = false;\r
4246     info.isDefaultOutput = info.name == defaultDeviceName;\r
4247   }\r
4248 \r
4249   // channel count\r
4250   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
4251   if ( FAILED( hr ) ) {\r
4252     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
4253     goto Exit;\r
4254   }\r
4255 \r
4256   hr = audioClient->GetMixFormat( &deviceFormat );\r
4257   if ( FAILED( hr ) ) {\r
4258     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
4259     goto Exit;\r
4260   }\r
4261 \r
4262   if ( isCaptureDevice ) {\r
4263     info.inputChannels = deviceFormat->nChannels;\r
4264     info.outputChannels = 0;\r
4265     info.duplexChannels = 0;\r
4266   }\r
4267   else {\r
4268     info.inputChannels = 0;\r
4269     info.outputChannels = deviceFormat->nChannels;\r
4270     info.duplexChannels = 0;\r
4271   }\r
4272 \r
4273   // sample rates\r
4274   info.sampleRates.clear();\r
4275 \r
4276   // allow support for all sample rates as we have a built-in sample rate converter\r
4277   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
4278     info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4279   }\r
4280   info.preferredSampleRate = deviceFormat->nSamplesPerSec;\r
4281 \r
4282   // native format\r
4283   info.nativeFormats = 0;\r
4284 \r
4285   if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
4286        ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4287          ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
4288   {\r
4289     if ( deviceFormat->wBitsPerSample == 32 ) {\r
4290       info.nativeFormats |= RTAUDIO_FLOAT32;\r
4291     }\r
4292     else if ( deviceFormat->wBitsPerSample == 64 ) {\r
4293       info.nativeFormats |= RTAUDIO_FLOAT64;\r
4294     }\r
4295   }\r
4296   else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
4297            ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4298              ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
4299   {\r
4300     if ( deviceFormat->wBitsPerSample == 8 ) {\r
4301       info.nativeFormats |= RTAUDIO_SINT8;\r
4302     }\r
4303     else if ( deviceFormat->wBitsPerSample == 16 ) {\r
4304       info.nativeFormats |= RTAUDIO_SINT16;\r
4305     }\r
4306     else if ( deviceFormat->wBitsPerSample == 24 ) {\r
4307       info.nativeFormats |= RTAUDIO_SINT24;\r
4308     }\r
4309     else if ( deviceFormat->wBitsPerSample == 32 ) {\r
4310       info.nativeFormats |= RTAUDIO_SINT32;\r
4311     }\r
4312   }\r
4313 \r
4314   // probed\r
4315   info.probed = true;\r
4316 \r
4317 Exit:\r
4318   // release all references\r
4319   PropVariantClear( &deviceNameProp );\r
4320   PropVariantClear( &defaultDeviceNameProp );\r
4321 \r
4322   SAFE_RELEASE( captureDevices );\r
4323   SAFE_RELEASE( renderDevices );\r
4324   SAFE_RELEASE( devicePtr );\r
4325   SAFE_RELEASE( defaultDevicePtr );\r
4326   SAFE_RELEASE( audioClient );\r
4327   SAFE_RELEASE( devicePropStore );\r
4328   SAFE_RELEASE( defaultDevicePropStore );\r
4329 \r
4330   CoTaskMemFree( deviceFormat );\r
4331   CoTaskMemFree( closestMatchFormat );\r
4332 \r
4333   if ( !errorText_.empty() )\r
4334     error( errorType );\r
4335   return info;\r
4336 }\r
4337 \r
4338 //-----------------------------------------------------------------------------\r
4339 \r
4340 unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
4341 {\r
4342   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4343     if ( getDeviceInfo( i ).isDefaultOutput ) {\r
4344       return i;\r
4345     }\r
4346   }\r
4347 \r
4348   return 0;\r
4349 }\r
4350 \r
4351 //-----------------------------------------------------------------------------\r
4352 \r
4353 unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
4354 {\r
4355   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4356     if ( getDeviceInfo( i ).isDefaultInput ) {\r
4357       return i;\r
4358     }\r
4359   }\r
4360 \r
4361   return 0;\r
4362 }\r
4363 \r
4364 //-----------------------------------------------------------------------------\r
4365 \r
4366 void RtApiWasapi::closeStream( void )\r
4367 {\r
4368   if ( stream_.state == STREAM_CLOSED ) {\r
4369     errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
4370     error( RtAudioError::WARNING );\r
4371     return;\r
4372   }\r
4373 \r
4374   if ( stream_.state != STREAM_STOPPED )\r
4375     stopStream();\r
4376 \r
4377   // clean up stream memory\r
4378   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
4379   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
4380 \r
4381   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
4382   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
4383 \r
4384   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
4385     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
4386 \r
4387   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
4388     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
4389 \r
4390   delete ( WasapiHandle* ) stream_.apiHandle;\r
4391   stream_.apiHandle = NULL;\r
4392 \r
4393   for ( int i = 0; i < 2; i++ ) {\r
4394     if ( stream_.userBuffer[i] ) {\r
4395       free( stream_.userBuffer[i] );\r
4396       stream_.userBuffer[i] = 0;\r
4397     }\r
4398   }\r
4399 \r
4400   if ( stream_.deviceBuffer ) {\r
4401     free( stream_.deviceBuffer );\r
4402     stream_.deviceBuffer = 0;\r
4403   }\r
4404 \r
4405   // update stream state\r
4406   stream_.state = STREAM_CLOSED;\r
4407 }\r
4408 \r
4409 //-----------------------------------------------------------------------------\r
4410 \r
4411 void RtApiWasapi::startStream( void )\r
4412 {\r
4413   verifyStream();\r
4414 \r
4415   if ( stream_.state == STREAM_RUNNING ) {\r
4416     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
4417     error( RtAudioError::WARNING );\r
4418     return;\r
4419   }\r
4420 \r
4421   // update stream state\r
4422   stream_.state = STREAM_RUNNING;\r
4423 \r
4424   // create WASAPI stream thread\r
4425   stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
4426 \r
4427   if ( !stream_.callbackInfo.thread ) {\r
4428     errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
4429     error( RtAudioError::THREAD_ERROR );\r
4430   }\r
4431   else {\r
4432     SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
4433     ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
4434   }\r
4435 }\r
4436 \r
4437 //-----------------------------------------------------------------------------\r
4438 \r
4439 void RtApiWasapi::stopStream( void )\r
4440 {\r
4441   verifyStream();\r
4442 \r
4443   if ( stream_.state == STREAM_STOPPED ) {\r
4444     errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
4445     error( RtAudioError::WARNING );\r
4446     return;\r
4447   }\r
4448 \r
4449   // inform stream thread by setting stream state to STREAM_STOPPING\r
4450   stream_.state = STREAM_STOPPING;\r
4451 \r
4452   // wait until stream thread is stopped\r
4453   while( stream_.state != STREAM_STOPPED ) {\r
4454     Sleep( 1 );\r
4455   }\r
4456 \r
4457   // Wait for the last buffer to play before stopping.\r
4458   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
4459 \r
4460   // stop capture client if applicable\r
4461   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4462     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4463     if ( FAILED( hr ) ) {\r
4464       errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
4465       error( RtAudioError::DRIVER_ERROR );\r
4466       return;\r
4467     }\r
4468   }\r
4469 \r
4470   // stop render client if applicable\r
4471   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4472     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4473     if ( FAILED( hr ) ) {\r
4474       errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
4475       error( RtAudioError::DRIVER_ERROR );\r
4476       return;\r
4477     }\r
4478   }\r
4479 \r
4480   // close thread handle\r
4481   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4482     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
4483     error( RtAudioError::THREAD_ERROR );\r
4484     return;\r
4485   }\r
4486 \r
4487   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4488 }\r
4489 \r
4490 //-----------------------------------------------------------------------------\r
4491 \r
4492 void RtApiWasapi::abortStream( void )\r
4493 {\r
4494   verifyStream();\r
4495 \r
4496   if ( stream_.state == STREAM_STOPPED ) {\r
4497     errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
4498     error( RtAudioError::WARNING );\r
4499     return;\r
4500   }\r
4501 \r
4502   // inform stream thread by setting stream state to STREAM_STOPPING\r
4503   stream_.state = STREAM_STOPPING;\r
4504 \r
4505   // wait until stream thread is stopped\r
4506   while ( stream_.state != STREAM_STOPPED ) {\r
4507     Sleep( 1 );\r
4508   }\r
4509 \r
4510   // stop capture client if applicable\r
4511   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4512     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4513     if ( FAILED( hr ) ) {\r
4514       errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
4515       error( RtAudioError::DRIVER_ERROR );\r
4516       return;\r
4517     }\r
4518   }\r
4519 \r
4520   // stop render client if applicable\r
4521   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4522     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4523     if ( FAILED( hr ) ) {\r
4524       errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
4525       error( RtAudioError::DRIVER_ERROR );\r
4526       return;\r
4527     }\r
4528   }\r
4529 \r
4530   // close thread handle\r
4531   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4532     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
4533     error( RtAudioError::THREAD_ERROR );\r
4534     return;\r
4535   }\r
4536 \r
4537   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4538 }\r
4539 \r
4540 //-----------------------------------------------------------------------------\r
4541 \r
4542 bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
4543                                    unsigned int firstChannel, unsigned int sampleRate,\r
4544                                    RtAudioFormat format, unsigned int* bufferSize,\r
4545                                    RtAudio::StreamOptions* options )\r
4546 {\r
4547   bool methodResult = FAILURE;\r
4548   unsigned int captureDeviceCount = 0;\r
4549   unsigned int renderDeviceCount = 0;\r
4550 \r
4551   IMMDeviceCollection* captureDevices = NULL;\r
4552   IMMDeviceCollection* renderDevices = NULL;\r
4553   IMMDevice* devicePtr = NULL;\r
4554   WAVEFORMATEX* deviceFormat = NULL;\r
4555   unsigned int bufferBytes;\r
4556   stream_.state = STREAM_STOPPED;\r
4557 \r
4558   // create API Handle if not already created\r
4559   if ( !stream_.apiHandle )\r
4560     stream_.apiHandle = ( void* ) new WasapiHandle();\r
4561 \r
4562   // Count capture devices\r
4563   errorText_.clear();\r
4564   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4565   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4566   if ( FAILED( hr ) ) {\r
4567     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
4568     goto Exit;\r
4569   }\r
4570 \r
4571   hr = captureDevices->GetCount( &captureDeviceCount );\r
4572   if ( FAILED( hr ) ) {\r
4573     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
4574     goto Exit;\r
4575   }\r
4576 \r
4577   // Count render devices\r
4578   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4579   if ( FAILED( hr ) ) {\r
4580     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
4581     goto Exit;\r
4582   }\r
4583 \r
4584   hr = renderDevices->GetCount( &renderDeviceCount );\r
4585   if ( FAILED( hr ) ) {\r
4586     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
4587     goto Exit;\r
4588   }\r
4589 \r
4590   // validate device index\r
4591   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4592     errorType = RtAudioError::INVALID_USE;\r
4593     errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
4594     goto Exit;\r
4595   }\r
4596 \r
4597   // determine whether index falls within capture or render devices\r
4598   if ( device >= renderDeviceCount ) {\r
4599     if ( mode != INPUT ) {\r
4600       errorType = RtAudioError::INVALID_USE;\r
4601       errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
4602       goto Exit;\r
4603     }\r
4604 \r
4605     // retrieve captureAudioClient from devicePtr\r
4606     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4607 \r
4608     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4609     if ( FAILED( hr ) ) {\r
4610       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
4611       goto Exit;\r
4612     }\r
4613 \r
4614     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4615                               NULL, ( void** ) &captureAudioClient );\r
4616     if ( FAILED( hr ) ) {\r
4617       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4618       goto Exit;\r
4619     }\r
4620 \r
4621     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
4622     if ( FAILED( hr ) ) {\r
4623       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4624       goto Exit;\r
4625     }\r
4626 \r
4627     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4628     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4629   }\r
4630   else {\r
4631     if ( mode != OUTPUT ) {\r
4632       errorType = RtAudioError::INVALID_USE;\r
4633       errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
4634       goto Exit;\r
4635     }\r
4636 \r
4637     // retrieve renderAudioClient from devicePtr\r
4638     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4639 \r
4640     hr = renderDevices->Item( device, &devicePtr );\r
4641     if ( FAILED( hr ) ) {\r
4642       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
4643       goto Exit;\r
4644     }\r
4645 \r
4646     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4647                               NULL, ( void** ) &renderAudioClient );\r
4648     if ( FAILED( hr ) ) {\r
4649       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4650       goto Exit;\r
4651     }\r
4652 \r
4653     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
4654     if ( FAILED( hr ) ) {\r
4655       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4656       goto Exit;\r
4657     }\r
4658 \r
4659     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4660     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4661   }\r
4662 \r
4663   // fill stream data\r
4664   if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
4665        ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
4666     stream_.mode = DUPLEX;\r
4667   }\r
4668   else {\r
4669     stream_.mode = mode;\r
4670   }\r
4671 \r
4672   stream_.device[mode] = device;\r
4673   stream_.doByteSwap[mode] = false;\r
4674   stream_.sampleRate = sampleRate;\r
4675   stream_.bufferSize = *bufferSize;\r
4676   stream_.nBuffers = 1;\r
4677   stream_.nUserChannels[mode] = channels;\r
4678   stream_.channelOffset[mode] = firstChannel;\r
4679   stream_.userFormat = format;\r
4680   stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
4681 \r
4682   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
4683     stream_.userInterleaved = false;\r
4684   else\r
4685     stream_.userInterleaved = true;\r
4686   stream_.deviceInterleaved[mode] = true;\r
4687 \r
4688   // Set flags for buffer conversion.\r
4689   stream_.doConvertBuffer[mode] = false;\r
4690   if ( stream_.userFormat != stream_.deviceFormat[mode] ||\r
4691        stream_.nUserChannels != stream_.nDeviceChannels )\r
4692     stream_.doConvertBuffer[mode] = true;\r
4693   else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
4694             stream_.nUserChannels[mode] > 1 )\r
4695     stream_.doConvertBuffer[mode] = true;\r
4696 \r
4697   if ( stream_.doConvertBuffer[mode] )\r
4698     setConvertInfo( mode, 0 );\r
4699 \r
4700   // Allocate necessary internal buffers\r
4701   bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
4702 \r
4703   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
4704   if ( !stream_.userBuffer[mode] ) {\r
4705     errorType = RtAudioError::MEMORY_ERROR;\r
4706     errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
4707     goto Exit;\r
4708   }\r
4709 \r
4710   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
4711     stream_.callbackInfo.priority = 15;\r
4712   else\r
4713     stream_.callbackInfo.priority = 0;\r
4714 \r
4715   ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
4716   ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
4717 \r
4718   methodResult = SUCCESS;\r
4719 \r
4720 Exit:\r
4721   //clean up\r
4722   SAFE_RELEASE( captureDevices );\r
4723   SAFE_RELEASE( renderDevices );\r
4724   SAFE_RELEASE( devicePtr );\r
4725   CoTaskMemFree( deviceFormat );\r
4726 \r
4727   // if method failed, close the stream\r
4728   if ( methodResult == FAILURE )\r
4729     closeStream();\r
4730 \r
4731   if ( !errorText_.empty() )\r
4732     error( errorType );\r
4733   return methodResult;\r
4734 }\r
4735 \r
4736 //=============================================================================\r
4737 \r
4738 DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
4739 {\r
4740   if ( wasapiPtr )\r
4741     ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
4742 \r
4743   return 0;\r
4744 }\r
4745 \r
4746 DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
4747 {\r
4748   if ( wasapiPtr )\r
4749     ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
4750 \r
4751   return 0;\r
4752 }\r
4753 \r
4754 DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
4755 {\r
4756   if ( wasapiPtr )\r
4757     ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
4758 \r
4759   return 0;\r
4760 }\r
4761 \r
4762 //-----------------------------------------------------------------------------\r
4763 \r
4764 void RtApiWasapi::wasapiThread()\r
4765 {\r
4766   // as this is a new thread, we must CoInitialize it\r
4767   CoInitialize( NULL );\r
4768 \r
4769   HRESULT hr;\r
4770 \r
4771   IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4772   IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4773   IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
4774   IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
4775   HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
4776   HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
4777 \r
4778   WAVEFORMATEX* captureFormat = NULL;\r
4779   WAVEFORMATEX* renderFormat = NULL;\r
4780   float captureSrRatio = 0.0f;\r
4781   float renderSrRatio = 0.0f;\r
4782   WasapiBuffer captureBuffer;\r
4783   WasapiBuffer renderBuffer;\r
4784 \r
4785   // declare local stream variables\r
4786   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
4787   BYTE* streamBuffer = NULL;\r
4788   unsigned long captureFlags = 0;\r
4789   unsigned int bufferFrameCount = 0;\r
4790   unsigned int numFramesPadding = 0;\r
4791   unsigned int convBufferSize = 0;\r
4792   bool callbackPushed = false;\r
4793   bool callbackPulled = false;\r
4794   bool callbackStopped = false;\r
4795   int callbackResult = 0;\r
4796 \r
4797   // convBuffer is used to store converted buffers between WASAPI and the user\r
4798   char* convBuffer = NULL;\r
4799   unsigned int convBuffSize = 0;\r
4800   unsigned int deviceBuffSize = 0;\r
4801 \r
4802   errorText_.clear();\r
4803   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4804 \r
4805   // Attempt to assign "Pro Audio" characteristic to thread\r
4806   HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );\r
4807   if ( AvrtDll ) {\r
4808     DWORD taskIndex = 0;\r
4809     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
4810     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
4811     FreeLibrary( AvrtDll );\r
4812   }\r
4813 \r
4814   // start capture stream if applicable\r
4815   if ( captureAudioClient ) {\r
4816     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
4817     if ( FAILED( hr ) ) {\r
4818       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4819       goto Exit;\r
4820     }\r
4821 \r
4822     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
4823 \r
4824     // initialize capture stream according to desire buffer size\r
4825     float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
4826     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
4827 \r
4828     if ( !captureClient ) {\r
4829       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4830                                            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4831                                            desiredBufferPeriod,\r
4832                                            desiredBufferPeriod,\r
4833                                            captureFormat,\r
4834                                            NULL );\r
4835       if ( FAILED( hr ) ) {\r
4836         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
4837         goto Exit;\r
4838       }\r
4839 \r
4840       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
4841                                            ( void** ) &captureClient );\r
4842       if ( FAILED( hr ) ) {\r
4843         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
4844         goto Exit;\r
4845       }\r
4846 \r
4847       // configure captureEvent to trigger on every available capture buffer\r
4848       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4849       if ( !captureEvent ) {\r
4850         errorType = RtAudioError::SYSTEM_ERROR;\r
4851         errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
4852         goto Exit;\r
4853       }\r
4854 \r
4855       hr = captureAudioClient->SetEventHandle( captureEvent );\r
4856       if ( FAILED( hr ) ) {\r
4857         errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
4858         goto Exit;\r
4859       }\r
4860 \r
4861       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
4862       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
4863     }\r
4864 \r
4865     unsigned int inBufferSize = 0;\r
4866     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
4867     if ( FAILED( hr ) ) {\r
4868       errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
4869       goto Exit;\r
4870     }\r
4871 \r
4872     // scale outBufferSize according to stream->user sample rate ratio\r
4873     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
4874     inBufferSize *= stream_.nDeviceChannels[INPUT];\r
4875 \r
4876     // set captureBuffer size\r
4877     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
4878 \r
4879     // reset the capture stream\r
4880     hr = captureAudioClient->Reset();\r
4881     if ( FAILED( hr ) ) {\r
4882       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
4883       goto Exit;\r
4884     }\r
4885 \r
4886     // start the capture stream\r
4887     hr = captureAudioClient->Start();\r
4888     if ( FAILED( hr ) ) {\r
4889       errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
4890       goto Exit;\r
4891     }\r
4892   }\r
4893 \r
4894   // start render stream if applicable\r
4895   if ( renderAudioClient ) {\r
4896     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
4897     if ( FAILED( hr ) ) {\r
4898       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4899       goto Exit;\r
4900     }\r
4901 \r
4902     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
4903 \r
4904     // initialize render stream according to desire buffer size\r
4905     float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
4906     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
4907 \r
4908     if ( !renderClient ) {\r
4909       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4910                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4911                                           desiredBufferPeriod,\r
4912                                           desiredBufferPeriod,\r
4913                                           renderFormat,\r
4914                                           NULL );\r
4915       if ( FAILED( hr ) ) {\r
4916         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
4917         goto Exit;\r
4918       }\r
4919 \r
4920       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
4921                                           ( void** ) &renderClient );\r
4922       if ( FAILED( hr ) ) {\r
4923         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
4924         goto Exit;\r
4925       }\r
4926 \r
4927       // configure renderEvent to trigger on every available render buffer\r
4928       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4929       if ( !renderEvent ) {\r
4930         errorType = RtAudioError::SYSTEM_ERROR;\r
4931         errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
4932         goto Exit;\r
4933       }\r
4934 \r
4935       hr = renderAudioClient->SetEventHandle( renderEvent );\r
4936       if ( FAILED( hr ) ) {\r
4937         errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
4938         goto Exit;\r
4939       }\r
4940 \r
4941       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
4942       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
4943     }\r
4944 \r
4945     unsigned int outBufferSize = 0;\r
4946     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
4947     if ( FAILED( hr ) ) {\r
4948       errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
4949       goto Exit;\r
4950     }\r
4951 \r
4952     // scale inBufferSize according to user->stream sample rate ratio\r
4953     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
4954     outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
4955 \r
4956     // set renderBuffer size\r
4957     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4958 \r
4959     // reset the render stream\r
4960     hr = renderAudioClient->Reset();\r
4961     if ( FAILED( hr ) ) {\r
4962       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
4963       goto Exit;\r
4964     }\r
4965 \r
4966     // start the render stream\r
4967     hr = renderAudioClient->Start();\r
4968     if ( FAILED( hr ) ) {\r
4969       errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
4970       goto Exit;\r
4971     }\r
4972   }\r
4973 \r
4974   if ( stream_.mode == INPUT ) {\r
4975     convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4976     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4977   }\r
4978   else if ( stream_.mode == OUTPUT ) {\r
4979     convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4980     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4981   }\r
4982   else if ( stream_.mode == DUPLEX ) {\r
4983     convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4984                              ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4985     deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4986                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4987   }\r
4988 \r
4989   convBuffer = ( char* ) malloc( convBuffSize );\r
4990   stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
4991   if ( !convBuffer || !stream_.deviceBuffer ) {\r
4992     errorType = RtAudioError::MEMORY_ERROR;\r
4993     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
4994     goto Exit;\r
4995   }\r
4996 \r
4997   // stream process loop\r
4998   while ( stream_.state != STREAM_STOPPING ) {\r
4999     if ( !callbackPulled ) {\r
5000       // Callback Input\r
5001       // ==============\r
5002       // 1. Pull callback buffer from inputBuffer\r
5003       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
5004       //                          Convert callback buffer to user format\r
5005 \r
5006       if ( captureAudioClient ) {\r
5007         // Pull callback buffer from inputBuffer\r
5008         callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
5009                                                    ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
5010                                                    stream_.deviceFormat[INPUT] );\r
5011 \r
5012         if ( callbackPulled ) {\r
5013           // Convert callback buffer to user sample rate\r
5014           convertBufferWasapi( stream_.deviceBuffer,\r
5015                                convBuffer,\r
5016                                stream_.nDeviceChannels[INPUT],\r
5017                                captureFormat->nSamplesPerSec,\r
5018                                stream_.sampleRate,\r
5019                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
5020                                convBufferSize,\r
5021                                stream_.deviceFormat[INPUT] );\r
5022 \r
5023           if ( stream_.doConvertBuffer[INPUT] ) {\r
5024             // Convert callback buffer to user format\r
5025             convertBuffer( stream_.userBuffer[INPUT],\r
5026                            stream_.deviceBuffer,\r
5027                            stream_.convertInfo[INPUT] );\r
5028           }\r
5029           else {\r
5030             // no further conversion, simple copy deviceBuffer to userBuffer\r
5031             memcpy( stream_.userBuffer[INPUT],\r
5032                     stream_.deviceBuffer,\r
5033                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
5034           }\r
5035         }\r
5036       }\r
5037       else {\r
5038         // if there is no capture stream, set callbackPulled flag\r
5039         callbackPulled = true;\r
5040       }\r
5041 \r
5042       // Execute Callback\r
5043       // ================\r
5044       // 1. Execute user callback method\r
5045       // 2. Handle return value from callback\r
5046 \r
5047       // if callback has not requested the stream to stop\r
5048       if ( callbackPulled && !callbackStopped ) {\r
5049         // Execute user callback method\r
5050         callbackResult = callback( stream_.userBuffer[OUTPUT],\r
5051                                    stream_.userBuffer[INPUT],\r
5052                                    stream_.bufferSize,\r
5053                                    getStreamTime(),\r
5054                                    captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
5055                                    stream_.callbackInfo.userData );\r
5056 \r
5057         // Handle return value from callback\r
5058         if ( callbackResult == 1 ) {\r
5059           // instantiate a thread to stop this thread\r
5060           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
5061           if ( !threadHandle ) {\r
5062             errorType = RtAudioError::THREAD_ERROR;\r
5063             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
5064             goto Exit;\r
5065           }\r
5066           else if ( !CloseHandle( threadHandle ) ) {\r
5067             errorType = RtAudioError::THREAD_ERROR;\r
5068             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
5069             goto Exit;\r
5070           }\r
5071 \r
5072           callbackStopped = true;\r
5073         }\r
5074         else if ( callbackResult == 2 ) {\r
5075           // instantiate a thread to stop this thread\r
5076           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
5077           if ( !threadHandle ) {\r
5078             errorType = RtAudioError::THREAD_ERROR;\r
5079             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
5080             goto Exit;\r
5081           }\r
5082           else if ( !CloseHandle( threadHandle ) ) {\r
5083             errorType = RtAudioError::THREAD_ERROR;\r
5084             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
5085             goto Exit;\r
5086           }\r
5087 \r
5088           callbackStopped = true;\r
5089         }\r
5090       }\r
5091     }\r
5092 \r
5093     // Callback Output\r
5094     // ===============\r
5095     // 1. Convert callback buffer to stream format\r
5096     // 2. Convert callback buffer to stream sample rate and channel count\r
5097     // 3. Push callback buffer into outputBuffer\r
5098 \r
5099     if ( renderAudioClient && callbackPulled ) {\r
5100       if ( stream_.doConvertBuffer[OUTPUT] ) {\r
5101         // Convert callback buffer to stream format\r
5102         convertBuffer( stream_.deviceBuffer,\r
5103                        stream_.userBuffer[OUTPUT],\r
5104                        stream_.convertInfo[OUTPUT] );\r
5105 \r
5106       }\r
5107 \r
5108       // Convert callback buffer to stream sample rate\r
5109       convertBufferWasapi( convBuffer,\r
5110                            stream_.deviceBuffer,\r
5111                            stream_.nDeviceChannels[OUTPUT],\r
5112                            stream_.sampleRate,\r
5113                            renderFormat->nSamplesPerSec,\r
5114                            stream_.bufferSize,\r
5115                            convBufferSize,\r
5116                            stream_.deviceFormat[OUTPUT] );\r
5117 \r
5118       // Push callback buffer into outputBuffer\r
5119       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
5120                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
5121                                                 stream_.deviceFormat[OUTPUT] );\r
5122     }\r
5123     else {\r
5124       // if there is no render stream, set callbackPushed flag\r
5125       callbackPushed = true;\r
5126     }\r
5127 \r
5128     // Stream Capture\r
5129     // ==============\r
5130     // 1. Get capture buffer from stream\r
5131     // 2. Push capture buffer into inputBuffer\r
5132     // 3. If 2. was successful: Release capture buffer\r
5133 \r
5134     if ( captureAudioClient ) {\r
5135       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
5136       if ( !callbackPulled ) {\r
5137         WaitForSingleObject( captureEvent, INFINITE );\r
5138       }\r
5139 \r
5140       // Get capture buffer from stream\r
5141       hr = captureClient->GetBuffer( &streamBuffer,\r
5142                                      &bufferFrameCount,\r
5143                                      &captureFlags, NULL, NULL );\r
5144       if ( FAILED( hr ) ) {\r
5145         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
5146         goto Exit;\r
5147       }\r
5148 \r
5149       if ( bufferFrameCount != 0 ) {\r
5150         // Push capture buffer into inputBuffer\r
5151         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
5152                                        bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
5153                                        stream_.deviceFormat[INPUT] ) )\r
5154         {\r
5155           // Release capture buffer\r
5156           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
5157           if ( FAILED( hr ) ) {\r
5158             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5159             goto Exit;\r
5160           }\r
5161         }\r
5162         else\r
5163         {\r
5164           // Inform WASAPI that capture was unsuccessful\r
5165           hr = captureClient->ReleaseBuffer( 0 );\r
5166           if ( FAILED( hr ) ) {\r
5167             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5168             goto Exit;\r
5169           }\r
5170         }\r
5171       }\r
5172       else\r
5173       {\r
5174         // Inform WASAPI that capture was unsuccessful\r
5175         hr = captureClient->ReleaseBuffer( 0 );\r
5176         if ( FAILED( hr ) ) {\r
5177           errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5178           goto Exit;\r
5179         }\r
5180       }\r
5181     }\r
5182 \r
5183     // Stream Render\r
5184     // =============\r
5185     // 1. Get render buffer from stream\r
5186     // 2. Pull next buffer from outputBuffer\r
5187     // 3. If 2. was successful: Fill render buffer with next buffer\r
5188     //                          Release render buffer\r
5189 \r
5190     if ( renderAudioClient ) {\r
5191       // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
5192       if ( callbackPulled && !callbackPushed ) {\r
5193         WaitForSingleObject( renderEvent, INFINITE );\r
5194       }\r
5195 \r
5196       // Get render buffer from stream\r
5197       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
5198       if ( FAILED( hr ) ) {\r
5199         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
5200         goto Exit;\r
5201       }\r
5202 \r
5203       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
5204       if ( FAILED( hr ) ) {\r
5205         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
5206         goto Exit;\r
5207       }\r
5208 \r
5209       bufferFrameCount -= numFramesPadding;\r
5210 \r
5211       if ( bufferFrameCount != 0 ) {\r
5212         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
5213         if ( FAILED( hr ) ) {\r
5214           errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
5215           goto Exit;\r
5216         }\r
5217 \r
5218         // Pull next buffer from outputBuffer\r
5219         // Fill render buffer with next buffer\r
5220         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
5221                                       bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
5222                                       stream_.deviceFormat[OUTPUT] ) )\r
5223         {\r
5224           // Release render buffer\r
5225           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
5226           if ( FAILED( hr ) ) {\r
5227             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5228             goto Exit;\r
5229           }\r
5230         }\r
5231         else\r
5232         {\r
5233           // Inform WASAPI that render was unsuccessful\r
5234           hr = renderClient->ReleaseBuffer( 0, 0 );\r
5235           if ( FAILED( hr ) ) {\r
5236             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5237             goto Exit;\r
5238           }\r
5239         }\r
5240       }\r
5241       else\r
5242       {\r
5243         // Inform WASAPI that render was unsuccessful\r
5244         hr = renderClient->ReleaseBuffer( 0, 0 );\r
5245         if ( FAILED( hr ) ) {\r
5246           errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5247           goto Exit;\r
5248         }\r
5249       }\r
5250     }\r
5251 \r
5252     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
5253     if ( callbackPushed ) {\r
5254       callbackPulled = false;\r
5255       // tick stream time\r
5256       RtApi::tickStreamTime();\r
5257     }\r
5258 \r
5259   }\r
5260 \r
5261 Exit:\r
5262   // clean up\r
5263   CoTaskMemFree( captureFormat );\r
5264   CoTaskMemFree( renderFormat );\r
5265 \r
5266   free ( convBuffer );\r
5267 \r
5268   CoUninitialize();\r
5269 \r
5270   // update stream state\r
5271   stream_.state = STREAM_STOPPED;\r
5272 \r
5273   if ( errorText_.empty() )\r
5274     return;\r
5275   else\r
5276     error( errorType );\r
5277 }\r
5278 \r
5279 //******************** End of __WINDOWS_WASAPI__ *********************//\r
5280 #endif\r
5281 \r
5282 \r
5283 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
5284 \r
5285 // Modified by Robin Davies, October 2005\r
5286 // - Improvements to DirectX pointer chasing. \r
5287 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
5288 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
5289 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
5290 // Changed device query structure for RtAudio 4.0.7, January 2010\r
5291 \r
5292 #include <dsound.h>\r
5293 #include <assert.h>\r
5294 #include <algorithm>\r
5295 \r
5296 #if defined(__MINGW32__)\r
5297   // missing from latest mingw winapi\r
5298 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
5299 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
5300 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
5301 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
5302 #endif\r
5303 \r
5304 #define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
5305 \r
5306 #ifdef _MSC_VER // if Microsoft Visual C++\r
5307 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
5308 #endif\r
5309 \r
5310 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
5311 {\r
5312   if ( pointer > bufferSize ) pointer -= bufferSize;\r
5313   if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
5314   if ( pointer < earlierPointer ) pointer += bufferSize;\r
5315   return pointer >= earlierPointer && pointer < laterPointer;\r
5316 }\r
5317 \r
5318 // A structure to hold various information related to the DirectSound\r
5319 // API implementation.\r
5320 struct DsHandle {\r
5321   unsigned int drainCounter; // Tracks callback counts when draining\r
5322   bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
5323   void *id[2];\r
5324   void *buffer[2];\r
5325   bool xrun[2];\r
5326   UINT bufferPointer[2];  \r
5327   DWORD dsBufferSize[2];\r
5328   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
5329   HANDLE condition;\r
5330 \r
5331   DsHandle()\r
5332     :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
5333 };\r
5334 \r
5335 // Declarations for utility functions, callbacks, and structures\r
5336 // specific to the DirectSound implementation.\r
5337 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
5338                                           LPCTSTR description,\r
5339                                           LPCTSTR module,\r
5340                                           LPVOID lpContext );\r
5341 \r
5342 static const char* getErrorString( int code );\r
5343 \r
5344 static unsigned __stdcall callbackHandler( void *ptr );\r
5345 \r
5346 struct DsDevice {\r
5347   LPGUID id[2];\r
5348   bool validId[2];\r
5349   bool found;\r
5350   std::string name;\r
5351 \r
5352   DsDevice()\r
5353   : found(false) { validId[0] = false; validId[1] = false; }\r
5354 };\r
5355 \r
5356 struct DsProbeData {\r
5357   bool isInput;\r
5358   std::vector<struct DsDevice>* dsDevices;\r
5359 };\r
5360 \r
5361 RtApiDs :: RtApiDs()\r
5362 {\r
5363   // Dsound will run both-threaded. If CoInitialize fails, then just\r
5364   // accept whatever the mainline chose for a threading model.\r
5365   coInitialized_ = false;\r
5366   HRESULT hr = CoInitialize( NULL );\r
5367   if ( !FAILED( hr ) ) coInitialized_ = true;\r
5368 }\r
5369 \r
5370 RtApiDs :: ~RtApiDs()\r
5371 {\r
5372   if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
5373   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
5374 }\r
5375 \r
5376 // The DirectSound default output is always the first device.\r
5377 unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
5378 {\r
5379   return 0;\r
5380 }\r
5381 \r
5382 // The DirectSound default input is always the first input device,\r
5383 // which is the first capture device enumerated.\r
5384 unsigned int RtApiDs :: getDefaultInputDevice( void )\r
5385 {\r
5386   return 0;\r
5387 }\r
5388 \r
5389 unsigned int RtApiDs :: getDeviceCount( void )\r
5390 {\r
5391   // Set query flag for previously found devices to false, so that we\r
5392   // can check for any devices that have disappeared.\r
5393   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5394     dsDevices[i].found = false;\r
5395 \r
5396   // Query DirectSound devices.\r
5397   struct DsProbeData probeInfo;\r
5398   probeInfo.isInput = false;\r
5399   probeInfo.dsDevices = &dsDevices;\r
5400   HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5401   if ( FAILED( result ) ) {\r
5402     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
5403     errorText_ = errorStream_.str();\r
5404     error( RtAudioError::WARNING );\r
5405   }\r
5406 \r
5407   // Query DirectSoundCapture devices.\r
5408   probeInfo.isInput = true;\r
5409   result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5410   if ( FAILED( result ) ) {\r
5411     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
5412     errorText_ = errorStream_.str();\r
5413     error( RtAudioError::WARNING );\r
5414   }\r
5415 \r
5416   // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).\r
5417   for ( unsigned int i=0; i<dsDevices.size(); ) {\r
5418     if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );\r
5419     else i++;\r
5420   }\r
5421 \r
5422   return static_cast<unsigned int>(dsDevices.size());\r
5423 }\r
5424 \r
5425 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
5426 {\r
5427   RtAudio::DeviceInfo info;\r
5428   info.probed = false;\r
5429 \r
5430   if ( dsDevices.size() == 0 ) {\r
5431     // Force a query of all devices\r
5432     getDeviceCount();\r
5433     if ( dsDevices.size() == 0 ) {\r
5434       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
5435       error( RtAudioError::INVALID_USE );\r
5436       return info;\r
5437     }\r
5438   }\r
5439 \r
5440   if ( device >= dsDevices.size() ) {\r
5441     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
5442     error( RtAudioError::INVALID_USE );\r
5443     return info;\r
5444   }\r
5445 \r
5446   HRESULT result;\r
5447   if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
5448 \r
5449   LPDIRECTSOUND output;\r
5450   DSCAPS outCaps;\r
5451   result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5452   if ( FAILED( result ) ) {\r
5453     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5454     errorText_ = errorStream_.str();\r
5455     error( RtAudioError::WARNING );\r
5456     goto probeInput;\r
5457   }\r
5458 \r
5459   outCaps.dwSize = sizeof( outCaps );\r
5460   result = output->GetCaps( &outCaps );\r
5461   if ( FAILED( result ) ) {\r
5462     output->Release();\r
5463     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
5464     errorText_ = errorStream_.str();\r
5465     error( RtAudioError::WARNING );\r
5466     goto probeInput;\r
5467   }\r
5468 \r
5469   // Get output channel information.\r
5470   info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
5471 \r
5472   // Get sample rate information.\r
5473   info.sampleRates.clear();\r
5474   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
5475     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
5476          SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {\r
5477       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
5478 \r
5479       if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
5480         info.preferredSampleRate = SAMPLE_RATES[k];\r
5481     }\r
5482   }\r
5483 \r
5484   // Get format information.\r
5485   if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
5486   if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
5487 \r
5488   output->Release();\r
5489 \r
5490   if ( getDefaultOutputDevice() == device )\r
5491     info.isDefaultOutput = true;\r
5492 \r
5493   if ( dsDevices[ device ].validId[1] == false ) {\r
5494     info.name = dsDevices[ device ].name;\r
5495     info.probed = true;\r
5496     return info;\r
5497   }\r
5498 \r
5499  probeInput:\r
5500 \r
5501   LPDIRECTSOUNDCAPTURE input;\r
5502   result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5503   if ( FAILED( result ) ) {\r
5504     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5505     errorText_ = errorStream_.str();\r
5506     error( RtAudioError::WARNING );\r
5507     return info;\r
5508   }\r
5509 \r
5510   DSCCAPS inCaps;\r
5511   inCaps.dwSize = sizeof( inCaps );\r
5512   result = input->GetCaps( &inCaps );\r
5513   if ( FAILED( result ) ) {\r
5514     input->Release();\r
5515     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
5516     errorText_ = errorStream_.str();\r
5517     error( RtAudioError::WARNING );\r
5518     return info;\r
5519   }\r
5520 \r
5521   // Get input channel information.\r
5522   info.inputChannels = inCaps.dwChannels;\r
5523 \r
5524   // Get sample rate and format information.\r
5525   std::vector<unsigned int> rates;\r
5526   if ( inCaps.dwChannels >= 2 ) {\r
5527     if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5528     if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5529     if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5530     if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5531     if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5532     if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5533     if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5534     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5535 \r
5536     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5537       if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
5538       if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
5539       if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
5540       if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
5541     }\r
5542     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5543       if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
5544       if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
5545       if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
5546       if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
5547     }\r
5548   }\r
5549   else if ( inCaps.dwChannels == 1 ) {\r
5550     if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5551     if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5552     if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5553     if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5554     if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5555     if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5556     if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5557     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5558 \r
5559     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5560       if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
5561       if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
5562       if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
5563       if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
5564     }\r
5565     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5566       if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
5567       if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
5568       if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
5569       if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
5570     }\r
5571   }\r
5572   else info.inputChannels = 0; // technically, this would be an error\r
5573 \r
5574   input->Release();\r
5575 \r
5576   if ( info.inputChannels == 0 ) return info;\r
5577 \r
5578   // Copy the supported rates to the info structure but avoid duplication.\r
5579   bool found;\r
5580   for ( unsigned int i=0; i<rates.size(); i++ ) {\r
5581     found = false;\r
5582     for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
5583       if ( rates[i] == info.sampleRates[j] ) {\r
5584         found = true;\r
5585         break;\r
5586       }\r
5587     }\r
5588     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
5589   }\r
5590   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
5591 \r
5592   // If device opens for both playback and capture, we determine the channels.\r
5593   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
5594     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
5595 \r
5596   if ( device == 0 ) info.isDefaultInput = true;\r
5597 \r
5598   // Copy name and return.\r
5599   info.name = dsDevices[ device ].name;\r
5600   info.probed = true;\r
5601   return info;\r
5602 }\r
5603 \r
5604 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
5605                                  unsigned int firstChannel, unsigned int sampleRate,\r
5606                                  RtAudioFormat format, unsigned int *bufferSize,\r
5607                                  RtAudio::StreamOptions *options )\r
5608 {\r
5609   if ( channels + firstChannel > 2 ) {\r
5610     errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
5611     return FAILURE;\r
5612   }\r
5613 \r
5614   size_t nDevices = dsDevices.size();\r
5615   if ( nDevices == 0 ) {\r
5616     // This should not happen because a check is made before this function is called.\r
5617     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
5618     return FAILURE;\r
5619   }\r
5620 \r
5621   if ( device >= nDevices ) {\r
5622     // This should not happen because a check is made before this function is called.\r
5623     errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
5624     return FAILURE;\r
5625   }\r
5626 \r
5627   if ( mode == OUTPUT ) {\r
5628     if ( dsDevices[ device ].validId[0] == false ) {\r
5629       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
5630       errorText_ = errorStream_.str();\r
5631       return FAILURE;\r
5632     }\r
5633   }\r
5634   else { // mode == INPUT\r
5635     if ( dsDevices[ device ].validId[1] == false ) {\r
5636       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
5637       errorText_ = errorStream_.str();\r
5638       return FAILURE;\r
5639     }\r
5640   }\r
5641 \r
5642   // According to a note in PortAudio, using GetDesktopWindow()\r
5643   // instead of GetForegroundWindow() is supposed to avoid problems\r
5644   // that occur when the application's window is not the foreground\r
5645   // window.  Also, if the application window closes before the\r
5646   // DirectSound buffer, DirectSound can crash.  In the past, I had\r
5647   // problems when using GetDesktopWindow() but it seems fine now\r
5648   // (January 2010).  I'll leave it commented here.\r
5649   // HWND hWnd = GetForegroundWindow();\r
5650   HWND hWnd = GetDesktopWindow();\r
5651 \r
5652   // Check the numberOfBuffers parameter and limit the lowest value to\r
5653   // two.  This is a judgement call and a value of two is probably too\r
5654   // low for capture, but it should work for playback.\r
5655   int nBuffers = 0;\r
5656   if ( options ) nBuffers = options->numberOfBuffers;\r
5657   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
5658   if ( nBuffers < 2 ) nBuffers = 3;\r
5659 \r
5660   // Check the lower range of the user-specified buffer size and set\r
5661   // (arbitrarily) to a lower bound of 32.\r
5662   if ( *bufferSize < 32 ) *bufferSize = 32;\r
5663 \r
5664   // Create the wave format structure.  The data format setting will\r
5665   // be determined later.\r
5666   WAVEFORMATEX waveFormat;\r
5667   ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
5668   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
5669   waveFormat.nChannels = channels + firstChannel;\r
5670   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
5671 \r
5672   // Determine the device buffer size. By default, we'll use the value\r
5673   // defined above (32K), but we will grow it to make allowances for\r
5674   // very large software buffer sizes.\r
5675   DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
5676   DWORD dsPointerLeadTime = 0;\r
5677 \r
5678   void *ohandle = 0, *bhandle = 0;\r
5679   HRESULT result;\r
5680   if ( mode == OUTPUT ) {\r
5681 \r
5682     LPDIRECTSOUND output;\r
5683     result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5684     if ( FAILED( result ) ) {\r
5685       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5686       errorText_ = errorStream_.str();\r
5687       return FAILURE;\r
5688     }\r
5689 \r
5690     DSCAPS outCaps;\r
5691     outCaps.dwSize = sizeof( outCaps );\r
5692     result = output->GetCaps( &outCaps );\r
5693     if ( FAILED( result ) ) {\r
5694       output->Release();\r
5695       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
5696       errorText_ = errorStream_.str();\r
5697       return FAILURE;\r
5698     }\r
5699 \r
5700     // Check channel information.\r
5701     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
5702       errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
5703       errorText_ = errorStream_.str();\r
5704       return FAILURE;\r
5705     }\r
5706 \r
5707     // Check format information.  Use 16-bit format unless not\r
5708     // supported or user requests 8-bit.\r
5709     if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
5710          !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
5711       waveFormat.wBitsPerSample = 16;\r
5712       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5713     }\r
5714     else {\r
5715       waveFormat.wBitsPerSample = 8;\r
5716       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5717     }\r
5718     stream_.userFormat = format;\r
5719 \r
5720     // Update wave format structure and buffer information.\r
5721     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5722     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5723     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5724 \r
5725     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5726     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5727       dsBufferSize *= 2;\r
5728 \r
5729     // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
5730     // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
5731     // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
5732     result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
5733     if ( FAILED( result ) ) {\r
5734       output->Release();\r
5735       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
5736       errorText_ = errorStream_.str();\r
5737       return FAILURE;\r
5738     }\r
5739 \r
5740     // Even though we will write to the secondary buffer, we need to\r
5741     // access the primary buffer to set the correct output format\r
5742     // (since the default is 8-bit, 22 kHz!).  Setup the DS primary\r
5743     // buffer description.\r
5744     DSBUFFERDESC bufferDescription;\r
5745     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5746     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5747     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
5748 \r
5749     // Obtain the primary buffer\r
5750     LPDIRECTSOUNDBUFFER buffer;\r
5751     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5752     if ( FAILED( result ) ) {\r
5753       output->Release();\r
5754       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
5755       errorText_ = errorStream_.str();\r
5756       return FAILURE;\r
5757     }\r
5758 \r
5759     // Set the primary DS buffer sound format.\r
5760     result = buffer->SetFormat( &waveFormat );\r
5761     if ( FAILED( result ) ) {\r
5762       output->Release();\r
5763       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
5764       errorText_ = errorStream_.str();\r
5765       return FAILURE;\r
5766     }\r
5767 \r
5768     // Setup the secondary DS buffer description.\r
5769     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5770     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5771     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5772                                   DSBCAPS_GLOBALFOCUS |\r
5773                                   DSBCAPS_GETCURRENTPOSITION2 |\r
5774                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
5775     bufferDescription.dwBufferBytes = dsBufferSize;\r
5776     bufferDescription.lpwfxFormat = &waveFormat;\r
5777 \r
5778     // Try to create the secondary DS buffer.  If that doesn't work,\r
5779     // try to use software mixing.  Otherwise, there's a problem.\r
5780     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5781     if ( FAILED( result ) ) {\r
5782       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5783                                     DSBCAPS_GLOBALFOCUS |\r
5784                                     DSBCAPS_GETCURRENTPOSITION2 |\r
5785                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
5786       result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5787       if ( FAILED( result ) ) {\r
5788         output->Release();\r
5789         errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
5790         errorText_ = errorStream_.str();\r
5791         return FAILURE;\r
5792       }\r
5793     }\r
5794 \r
5795     // Get the buffer size ... might be different from what we specified.\r
5796     DSBCAPS dsbcaps;\r
5797     dsbcaps.dwSize = sizeof( DSBCAPS );\r
5798     result = buffer->GetCaps( &dsbcaps );\r
5799     if ( FAILED( result ) ) {\r
5800       output->Release();\r
5801       buffer->Release();\r
5802       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5803       errorText_ = errorStream_.str();\r
5804       return FAILURE;\r
5805     }\r
5806 \r
5807     dsBufferSize = dsbcaps.dwBufferBytes;\r
5808 \r
5809     // Lock the DS buffer\r
5810     LPVOID audioPtr;\r
5811     DWORD dataLen;\r
5812     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5813     if ( FAILED( result ) ) {\r
5814       output->Release();\r
5815       buffer->Release();\r
5816       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
5817       errorText_ = errorStream_.str();\r
5818       return FAILURE;\r
5819     }\r
5820 \r
5821     // Zero the DS buffer\r
5822     ZeroMemory( audioPtr, dataLen );\r
5823 \r
5824     // Unlock the DS buffer\r
5825     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5826     if ( FAILED( result ) ) {\r
5827       output->Release();\r
5828       buffer->Release();\r
5829       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
5830       errorText_ = errorStream_.str();\r
5831       return FAILURE;\r
5832     }\r
5833 \r
5834     ohandle = (void *) output;\r
5835     bhandle = (void *) buffer;\r
5836   }\r
5837 \r
5838   if ( mode == INPUT ) {\r
5839 \r
5840     LPDIRECTSOUNDCAPTURE input;\r
5841     result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5842     if ( FAILED( result ) ) {\r
5843       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5844       errorText_ = errorStream_.str();\r
5845       return FAILURE;\r
5846     }\r
5847 \r
5848     DSCCAPS inCaps;\r
5849     inCaps.dwSize = sizeof( inCaps );\r
5850     result = input->GetCaps( &inCaps );\r
5851     if ( FAILED( result ) ) {\r
5852       input->Release();\r
5853       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
5854       errorText_ = errorStream_.str();\r
5855       return FAILURE;\r
5856     }\r
5857 \r
5858     // Check channel information.\r
5859     if ( inCaps.dwChannels < channels + firstChannel ) {\r
5860       errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
5861       return FAILURE;\r
5862     }\r
5863 \r
5864     // Check format information.  Use 16-bit format unless user\r
5865     // requests 8-bit.\r
5866     DWORD deviceFormats;\r
5867     if ( channels + firstChannel == 2 ) {\r
5868       deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
5869       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5870         waveFormat.wBitsPerSample = 8;\r
5871         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5872       }\r
5873       else { // assume 16-bit is supported\r
5874         waveFormat.wBitsPerSample = 16;\r
5875         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5876       }\r
5877     }\r
5878     else { // channel == 1\r
5879       deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
5880       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5881         waveFormat.wBitsPerSample = 8;\r
5882         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5883       }\r
5884       else { // assume 16-bit is supported\r
5885         waveFormat.wBitsPerSample = 16;\r
5886         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5887       }\r
5888     }\r
5889     stream_.userFormat = format;\r
5890 \r
5891     // Update wave format structure and buffer information.\r
5892     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5893     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5894     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5895 \r
5896     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5897     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5898       dsBufferSize *= 2;\r
5899 \r
5900     // Setup the secondary DS buffer description.\r
5901     DSCBUFFERDESC bufferDescription;\r
5902     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
5903     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
5904     bufferDescription.dwFlags = 0;\r
5905     bufferDescription.dwReserved = 0;\r
5906     bufferDescription.dwBufferBytes = dsBufferSize;\r
5907     bufferDescription.lpwfxFormat = &waveFormat;\r
5908 \r
5909     // Create the capture buffer.\r
5910     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
5911     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
5912     if ( FAILED( result ) ) {\r
5913       input->Release();\r
5914       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
5915       errorText_ = errorStream_.str();\r
5916       return FAILURE;\r
5917     }\r
5918 \r
5919     // Get the buffer size ... might be different from what we specified.\r
5920     DSCBCAPS dscbcaps;\r
5921     dscbcaps.dwSize = sizeof( DSCBCAPS );\r
5922     result = buffer->GetCaps( &dscbcaps );\r
5923     if ( FAILED( result ) ) {\r
5924       input->Release();\r
5925       buffer->Release();\r
5926       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5927       errorText_ = errorStream_.str();\r
5928       return FAILURE;\r
5929     }\r
5930 \r
5931     dsBufferSize = dscbcaps.dwBufferBytes;\r
5932 \r
5933     // NOTE: We could have a problem here if this is a duplex stream\r
5934     // and the play and capture hardware buffer sizes are different\r
5935     // (I'm actually not sure if that is a problem or not).\r
5936     // Currently, we are not verifying that.\r
5937 \r
5938     // Lock the capture buffer\r
5939     LPVOID audioPtr;\r
5940     DWORD dataLen;\r
5941     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5942     if ( FAILED( result ) ) {\r
5943       input->Release();\r
5944       buffer->Release();\r
5945       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
5946       errorText_ = errorStream_.str();\r
5947       return FAILURE;\r
5948     }\r
5949 \r
5950     // Zero the buffer\r
5951     ZeroMemory( audioPtr, dataLen );\r
5952 \r
5953     // Unlock the buffer\r
5954     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5955     if ( FAILED( result ) ) {\r
5956       input->Release();\r
5957       buffer->Release();\r
5958       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
5959       errorText_ = errorStream_.str();\r
5960       return FAILURE;\r
5961     }\r
5962 \r
5963     ohandle = (void *) input;\r
5964     bhandle = (void *) buffer;\r
5965   }\r
5966 \r
5967   // Set various stream parameters\r
5968   DsHandle *handle = 0;\r
5969   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
5970   stream_.nUserChannels[mode] = channels;\r
5971   stream_.bufferSize = *bufferSize;\r
5972   stream_.channelOffset[mode] = firstChannel;\r
5973   stream_.deviceInterleaved[mode] = true;\r
5974   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
5975   else stream_.userInterleaved = true;\r
5976 \r
5977   // Set flag for buffer conversion\r
5978   stream_.doConvertBuffer[mode] = false;\r
5979   if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
5980     stream_.doConvertBuffer[mode] = true;\r
5981   if (stream_.userFormat != stream_.deviceFormat[mode])\r
5982     stream_.doConvertBuffer[mode] = true;\r
5983   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
5984        stream_.nUserChannels[mode] > 1 )\r
5985     stream_.doConvertBuffer[mode] = true;\r
5986 \r
5987   // Allocate necessary internal buffers\r
5988   long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
5989   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
5990   if ( stream_.userBuffer[mode] == NULL ) {\r
5991     errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
5992     goto error;\r
5993   }\r
5994 \r
5995   if ( stream_.doConvertBuffer[mode] ) {\r
5996 \r
5997     bool makeBuffer = true;\r
5998     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
5999     if ( mode == INPUT ) {\r
6000       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
6001         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
6002         if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
6003       }\r
6004     }\r
6005 \r
6006     if ( makeBuffer ) {\r
6007       bufferBytes *= *bufferSize;\r
6008       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
6009       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
6010       if ( stream_.deviceBuffer == NULL ) {\r
6011         errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
6012         goto error;\r
6013       }\r
6014     }\r
6015   }\r
6016 \r
6017   // Allocate our DsHandle structures for the stream.\r
6018   if ( stream_.apiHandle == 0 ) {\r
6019     try {\r
6020       handle = new DsHandle;\r
6021     }\r
6022     catch ( std::bad_alloc& ) {\r
6023       errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
6024       goto error;\r
6025     }\r
6026 \r
6027     // Create a manual-reset event.\r
6028     handle->condition = CreateEvent( NULL,   // no security\r
6029                                      TRUE,   // manual-reset\r
6030                                      FALSE,  // non-signaled initially\r
6031                                      NULL ); // unnamed\r
6032     stream_.apiHandle = (void *) handle;\r
6033   }\r
6034   else\r
6035     handle = (DsHandle *) stream_.apiHandle;\r
6036   handle->id[mode] = ohandle;\r
6037   handle->buffer[mode] = bhandle;\r
6038   handle->dsBufferSize[mode] = dsBufferSize;\r
6039   handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
6040 \r
6041   stream_.device[mode] = device;\r
6042   stream_.state = STREAM_STOPPED;\r
6043   if ( stream_.mode == OUTPUT && mode == INPUT )\r
6044     // We had already set up an output stream.\r
6045     stream_.mode = DUPLEX;\r
6046   else\r
6047     stream_.mode = mode;\r
6048   stream_.nBuffers = nBuffers;\r
6049   stream_.sampleRate = sampleRate;\r
6050 \r
6051   // Setup the buffer conversion information structure.\r
6052   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
6053 \r
6054   // Setup the callback thread.\r
6055   if ( stream_.callbackInfo.isRunning == false ) {\r
6056     unsigned threadId;\r
6057     stream_.callbackInfo.isRunning = true;\r
6058     stream_.callbackInfo.object = (void *) this;\r
6059     stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
6060                                                   &stream_.callbackInfo, 0, &threadId );\r
6061     if ( stream_.callbackInfo.thread == 0 ) {\r
6062       errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
6063       goto error;\r
6064     }\r
6065 \r
6066     // Boost DS thread priority\r
6067     SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
6068   }\r
6069   return SUCCESS;\r
6070 \r
6071  error:\r
6072   if ( handle ) {\r
6073     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
6074       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
6075       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6076       if ( buffer ) buffer->Release();\r
6077       object->Release();\r
6078     }\r
6079     if ( handle->buffer[1] ) {\r
6080       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
6081       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6082       if ( buffer ) buffer->Release();\r
6083       object->Release();\r
6084     }\r
6085     CloseHandle( handle->condition );\r
6086     delete handle;\r
6087     stream_.apiHandle = 0;\r
6088   }\r
6089 \r
6090   for ( int i=0; i<2; i++ ) {\r
6091     if ( stream_.userBuffer[i] ) {\r
6092       free( stream_.userBuffer[i] );\r
6093       stream_.userBuffer[i] = 0;\r
6094     }\r
6095   }\r
6096 \r
6097   if ( stream_.deviceBuffer ) {\r
6098     free( stream_.deviceBuffer );\r
6099     stream_.deviceBuffer = 0;\r
6100   }\r
6101 \r
6102   stream_.state = STREAM_CLOSED;\r
6103   return FAILURE;\r
6104 }\r
6105 \r
6106 void RtApiDs :: closeStream()\r
6107 {\r
6108   if ( stream_.state == STREAM_CLOSED ) {\r
6109     errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
6110     error( RtAudioError::WARNING );\r
6111     return;\r
6112   }\r
6113 \r
6114   // Stop the callback thread.\r
6115   stream_.callbackInfo.isRunning = false;\r
6116   WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
6117   CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
6118 \r
6119   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6120   if ( handle ) {\r
6121     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
6122       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
6123       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6124       if ( buffer ) {\r
6125         buffer->Stop();\r
6126         buffer->Release();\r
6127       }\r
6128       object->Release();\r
6129     }\r
6130     if ( handle->buffer[1] ) {\r
6131       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
6132       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6133       if ( buffer ) {\r
6134         buffer->Stop();\r
6135         buffer->Release();\r
6136       }\r
6137       object->Release();\r
6138     }\r
6139     CloseHandle( handle->condition );\r
6140     delete handle;\r
6141     stream_.apiHandle = 0;\r
6142   }\r
6143 \r
6144   for ( int i=0; i<2; i++ ) {\r
6145     if ( stream_.userBuffer[i] ) {\r
6146       free( stream_.userBuffer[i] );\r
6147       stream_.userBuffer[i] = 0;\r
6148     }\r
6149   }\r
6150 \r
6151   if ( stream_.deviceBuffer ) {\r
6152     free( stream_.deviceBuffer );\r
6153     stream_.deviceBuffer = 0;\r
6154   }\r
6155 \r
6156   stream_.mode = UNINITIALIZED;\r
6157   stream_.state = STREAM_CLOSED;\r
6158 }\r
6159 \r
6160 void RtApiDs :: startStream()\r
6161 {\r
6162   verifyStream();\r
6163   if ( stream_.state == STREAM_RUNNING ) {\r
6164     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
6165     error( RtAudioError::WARNING );\r
6166     return;\r
6167   }\r
6168 \r
6169   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6170 \r
6171   // Increase scheduler frequency on lesser windows (a side-effect of\r
6172   // increasing timer accuracy).  On greater windows (Win2K or later),\r
6173   // this is already in effect.\r
6174   timeBeginPeriod( 1 ); \r
6175 \r
6176   buffersRolling = false;\r
6177   duplexPrerollBytes = 0;\r
6178 \r
6179   if ( stream_.mode == DUPLEX ) {\r
6180     // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
6181     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
6182   }\r
6183 \r
6184   HRESULT result = 0;\r
6185   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6186 \r
6187     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6188     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
6189     if ( FAILED( result ) ) {\r
6190       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
6191       errorText_ = errorStream_.str();\r
6192       goto unlock;\r
6193     }\r
6194   }\r
6195 \r
6196   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6197 \r
6198     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6199     result = buffer->Start( DSCBSTART_LOOPING );\r
6200     if ( FAILED( result ) ) {\r
6201       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
6202       errorText_ = errorStream_.str();\r
6203       goto unlock;\r
6204     }\r
6205   }\r
6206 \r
6207   handle->drainCounter = 0;\r
6208   handle->internalDrain = false;\r
6209   ResetEvent( handle->condition );\r
6210   stream_.state = STREAM_RUNNING;\r
6211 \r
6212  unlock:\r
6213   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6214 }\r
6215 \r
6216 void RtApiDs :: stopStream()\r
6217 {\r
6218   verifyStream();\r
6219   if ( stream_.state == STREAM_STOPPED ) {\r
6220     errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
6221     error( RtAudioError::WARNING );\r
6222     return;\r
6223   }\r
6224 \r
6225   HRESULT result = 0;\r
6226   LPVOID audioPtr;\r
6227   DWORD dataLen;\r
6228   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6229   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6230     if ( handle->drainCounter == 0 ) {\r
6231       handle->drainCounter = 2;\r
6232       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
6233     }\r
6234 \r
6235     stream_.state = STREAM_STOPPED;\r
6236 \r
6237     MUTEX_LOCK( &stream_.mutex );\r
6238 \r
6239     // Stop the buffer and clear memory\r
6240     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6241     result = buffer->Stop();\r
6242     if ( FAILED( result ) ) {\r
6243       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
6244       errorText_ = errorStream_.str();\r
6245       goto unlock;\r
6246     }\r
6247 \r
6248     // Lock the buffer and clear it so that if we start to play again,\r
6249     // we won't have old data playing.\r
6250     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6251     if ( FAILED( result ) ) {\r
6252       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
6253       errorText_ = errorStream_.str();\r
6254       goto unlock;\r
6255     }\r
6256 \r
6257     // Zero the DS buffer\r
6258     ZeroMemory( audioPtr, dataLen );\r
6259 \r
6260     // Unlock the DS buffer\r
6261     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6262     if ( FAILED( result ) ) {\r
6263       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
6264       errorText_ = errorStream_.str();\r
6265       goto unlock;\r
6266     }\r
6267 \r
6268     // If we start playing again, we must begin at beginning of buffer.\r
6269     handle->bufferPointer[0] = 0;\r
6270   }\r
6271 \r
6272   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6273     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6274     audioPtr = NULL;\r
6275     dataLen = 0;\r
6276 \r
6277     stream_.state = STREAM_STOPPED;\r
6278 \r
6279     if ( stream_.mode != DUPLEX )\r
6280       MUTEX_LOCK( &stream_.mutex );\r
6281 \r
6282     result = buffer->Stop();\r
6283     if ( FAILED( result ) ) {\r
6284       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
6285       errorText_ = errorStream_.str();\r
6286       goto unlock;\r
6287     }\r
6288 \r
6289     // Lock the buffer and clear it so that if we start to play again,\r
6290     // we won't have old data playing.\r
6291     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6292     if ( FAILED( result ) ) {\r
6293       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
6294       errorText_ = errorStream_.str();\r
6295       goto unlock;\r
6296     }\r
6297 \r
6298     // Zero the DS buffer\r
6299     ZeroMemory( audioPtr, dataLen );\r
6300 \r
6301     // Unlock the DS buffer\r
6302     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6303     if ( FAILED( result ) ) {\r
6304       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
6305       errorText_ = errorStream_.str();\r
6306       goto unlock;\r
6307     }\r
6308 \r
6309     // If we start recording again, we must begin at beginning of buffer.\r
6310     handle->bufferPointer[1] = 0;\r
6311   }\r
6312 \r
6313  unlock:\r
6314   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
6315   MUTEX_UNLOCK( &stream_.mutex );\r
6316 \r
6317   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6318 }\r
6319 \r
6320 void RtApiDs :: abortStream()\r
6321 {\r
6322   verifyStream();\r
6323   if ( stream_.state == STREAM_STOPPED ) {\r
6324     errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
6325     error( RtAudioError::WARNING );\r
6326     return;\r
6327   }\r
6328 \r
6329   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6330   handle->drainCounter = 2;\r
6331 \r
6332   stopStream();\r
6333 }\r
6334 \r
6335 void RtApiDs :: callbackEvent()\r
6336 {\r
6337   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
6338     Sleep( 50 ); // sleep 50 milliseconds\r
6339     return;\r
6340   }\r
6341 \r
6342   if ( stream_.state == STREAM_CLOSED ) {\r
6343     errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
6344     error( RtAudioError::WARNING );\r
6345     return;\r
6346   }\r
6347 \r
6348   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
6349   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6350 \r
6351   // Check if we were draining the stream and signal is finished.\r
6352   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
6353 \r
6354     stream_.state = STREAM_STOPPING;\r
6355     if ( handle->internalDrain == false )\r
6356       SetEvent( handle->condition );\r
6357     else\r
6358       stopStream();\r
6359     return;\r
6360   }\r
6361 \r
6362   // Invoke user callback to get fresh output data UNLESS we are\r
6363   // draining stream.\r
6364   if ( handle->drainCounter == 0 ) {\r
6365     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
6366     double streamTime = getStreamTime();\r
6367     RtAudioStreamStatus status = 0;\r
6368     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
6369       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
6370       handle->xrun[0] = false;\r
6371     }\r
6372     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
6373       status |= RTAUDIO_INPUT_OVERFLOW;\r
6374       handle->xrun[1] = false;\r
6375     }\r
6376     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
6377                                   stream_.bufferSize, streamTime, status, info->userData );\r
6378     if ( cbReturnValue == 2 ) {\r
6379       stream_.state = STREAM_STOPPING;\r
6380       handle->drainCounter = 2;\r
6381       abortStream();\r
6382       return;\r
6383     }\r
6384     else if ( cbReturnValue == 1 ) {\r
6385       handle->drainCounter = 1;\r
6386       handle->internalDrain = true;\r
6387     }\r
6388   }\r
6389 \r
6390   HRESULT result;\r
6391   DWORD currentWritePointer, safeWritePointer;\r
6392   DWORD currentReadPointer, safeReadPointer;\r
6393   UINT nextWritePointer;\r
6394 \r
6395   LPVOID buffer1 = NULL;\r
6396   LPVOID buffer2 = NULL;\r
6397   DWORD bufferSize1 = 0;\r
6398   DWORD bufferSize2 = 0;\r
6399 \r
6400   char *buffer;\r
6401   long bufferBytes;\r
6402 \r
6403   MUTEX_LOCK( &stream_.mutex );\r
6404   if ( stream_.state == STREAM_STOPPED ) {\r
6405     MUTEX_UNLOCK( &stream_.mutex );\r
6406     return;\r
6407   }\r
6408 \r
6409   if ( buffersRolling == false ) {\r
6410     if ( stream_.mode == DUPLEX ) {\r
6411       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6412 \r
6413       // It takes a while for the devices to get rolling. As a result,\r
6414       // there's no guarantee that the capture and write device pointers\r
6415       // will move in lockstep.  Wait here for both devices to start\r
6416       // rolling, and then set our buffer pointers accordingly.\r
6417       // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
6418       // bytes later than the write buffer.\r
6419 \r
6420       // Stub: a serious risk of having a pre-emptive scheduling round\r
6421       // take place between the two GetCurrentPosition calls... but I'm\r
6422       // really not sure how to solve the problem.  Temporarily boost to\r
6423       // Realtime priority, maybe; but I'm not sure what priority the\r
6424       // DirectSound service threads run at. We *should* be roughly\r
6425       // within a ms or so of correct.\r
6426 \r
6427       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6428       LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6429 \r
6430       DWORD startSafeWritePointer, startSafeReadPointer;\r
6431 \r
6432       result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
6433       if ( FAILED( result ) ) {\r
6434         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6435         errorText_ = errorStream_.str();\r
6436         MUTEX_UNLOCK( &stream_.mutex );\r
6437         error( RtAudioError::SYSTEM_ERROR );\r
6438         return;\r
6439       }\r
6440       result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
6441       if ( FAILED( result ) ) {\r
6442         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6443         errorText_ = errorStream_.str();\r
6444         MUTEX_UNLOCK( &stream_.mutex );\r
6445         error( RtAudioError::SYSTEM_ERROR );\r
6446         return;\r
6447       }\r
6448       while ( true ) {\r
6449         result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
6450         if ( FAILED( result ) ) {\r
6451           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6452           errorText_ = errorStream_.str();\r
6453           MUTEX_UNLOCK( &stream_.mutex );\r
6454           error( RtAudioError::SYSTEM_ERROR );\r
6455           return;\r
6456         }\r
6457         result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
6458         if ( FAILED( result ) ) {\r
6459           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6460           errorText_ = errorStream_.str();\r
6461           MUTEX_UNLOCK( &stream_.mutex );\r
6462           error( RtAudioError::SYSTEM_ERROR );\r
6463           return;\r
6464         }\r
6465         if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
6466         Sleep( 1 );\r
6467       }\r
6468 \r
6469       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6470 \r
6471       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6472       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6473       handle->bufferPointer[1] = safeReadPointer;\r
6474     }\r
6475     else if ( stream_.mode == OUTPUT ) {\r
6476 \r
6477       // Set the proper nextWritePosition after initial startup.\r
6478       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6479       result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6480       if ( FAILED( result ) ) {\r
6481         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6482         errorText_ = errorStream_.str();\r
6483         MUTEX_UNLOCK( &stream_.mutex );\r
6484         error( RtAudioError::SYSTEM_ERROR );\r
6485         return;\r
6486       }\r
6487       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6488       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6489     }\r
6490 \r
6491     buffersRolling = true;\r
6492   }\r
6493 \r
6494   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6495     \r
6496     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6497 \r
6498     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
6499       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6500       bufferBytes *= formatBytes( stream_.userFormat );\r
6501       memset( stream_.userBuffer[0], 0, bufferBytes );\r
6502     }\r
6503 \r
6504     // Setup parameters and do buffer conversion if necessary.\r
6505     if ( stream_.doConvertBuffer[0] ) {\r
6506       buffer = stream_.deviceBuffer;\r
6507       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
6508       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
6509       bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
6510     }\r
6511     else {\r
6512       buffer = stream_.userBuffer[0];\r
6513       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6514       bufferBytes *= formatBytes( stream_.userFormat );\r
6515     }\r
6516 \r
6517     // No byte swapping necessary in DirectSound implementation.\r
6518 \r
6519     // Ahhh ... windoze.  16-bit data is signed but 8-bit data is\r
6520     // unsigned.  So, we need to convert our signed 8-bit data here to\r
6521     // unsigned.\r
6522     if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
6523       for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
6524 \r
6525     DWORD dsBufferSize = handle->dsBufferSize[0];\r
6526     nextWritePointer = handle->bufferPointer[0];\r
6527 \r
6528     DWORD endWrite, leadPointer;\r
6529     while ( true ) {\r
6530       // Find out where the read and "safe write" pointers are.\r
6531       result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6532       if ( FAILED( result ) ) {\r
6533         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6534         errorText_ = errorStream_.str();\r
6535         MUTEX_UNLOCK( &stream_.mutex );\r
6536         error( RtAudioError::SYSTEM_ERROR );\r
6537         return;\r
6538       }\r
6539 \r
6540       // We will copy our output buffer into the region between\r
6541       // safeWritePointer and leadPointer.  If leadPointer is not\r
6542       // beyond the next endWrite position, wait until it is.\r
6543       leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
6544       //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
6545       if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
6546       if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
6547       endWrite = nextWritePointer + bufferBytes;\r
6548 \r
6549       // Check whether the entire write region is behind the play pointer.\r
6550       if ( leadPointer >= endWrite ) break;\r
6551 \r
6552       // If we are here, then we must wait until the leadPointer advances\r
6553       // beyond the end of our next write region. We use the\r
6554       // Sleep() function to suspend operation until that happens.\r
6555       double millis = ( endWrite - leadPointer ) * 1000.0;\r
6556       millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
6557       if ( millis < 1.0 ) millis = 1.0;\r
6558       Sleep( (DWORD) millis );\r
6559     }\r
6560 \r
6561     if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
6562          || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
6563       // We've strayed into the forbidden zone ... resync the read pointer.\r
6564       handle->xrun[0] = true;\r
6565       nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
6566       if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
6567       handle->bufferPointer[0] = nextWritePointer;\r
6568       endWrite = nextWritePointer + bufferBytes;\r
6569     }\r
6570 \r
6571     // Lock free space in the buffer\r
6572     result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
6573                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6574     if ( FAILED( result ) ) {\r
6575       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
6576       errorText_ = errorStream_.str();\r
6577       MUTEX_UNLOCK( &stream_.mutex );\r
6578       error( RtAudioError::SYSTEM_ERROR );\r
6579       return;\r
6580     }\r
6581 \r
6582     // Copy our buffer into the DS buffer\r
6583     CopyMemory( buffer1, buffer, bufferSize1 );\r
6584     if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
6585 \r
6586     // Update our buffer offset and unlock sound buffer\r
6587     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6588     if ( FAILED( result ) ) {\r
6589       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
6590       errorText_ = errorStream_.str();\r
6591       MUTEX_UNLOCK( &stream_.mutex );\r
6592       error( RtAudioError::SYSTEM_ERROR );\r
6593       return;\r
6594     }\r
6595     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6596     handle->bufferPointer[0] = nextWritePointer;\r
6597   }\r
6598 \r
6599   // Don't bother draining input\r
6600   if ( handle->drainCounter ) {\r
6601     handle->drainCounter++;\r
6602     goto unlock;\r
6603   }\r
6604 \r
6605   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6606 \r
6607     // Setup parameters.\r
6608     if ( stream_.doConvertBuffer[1] ) {\r
6609       buffer = stream_.deviceBuffer;\r
6610       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
6611       bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
6612     }\r
6613     else {\r
6614       buffer = stream_.userBuffer[1];\r
6615       bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
6616       bufferBytes *= formatBytes( stream_.userFormat );\r
6617     }\r
6618 \r
6619     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6620     long nextReadPointer = handle->bufferPointer[1];\r
6621     DWORD dsBufferSize = handle->dsBufferSize[1];\r
6622 \r
6623     // Find out where the write and "safe read" pointers are.\r
6624     result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6625     if ( FAILED( result ) ) {\r
6626       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6627       errorText_ = errorStream_.str();\r
6628       MUTEX_UNLOCK( &stream_.mutex );\r
6629       error( RtAudioError::SYSTEM_ERROR );\r
6630       return;\r
6631     }\r
6632 \r
6633     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6634     DWORD endRead = nextReadPointer + bufferBytes;\r
6635 \r
6636     // Handling depends on whether we are INPUT or DUPLEX. \r
6637     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
6638     // then a wait here will drag the write pointers into the forbidden zone.\r
6639     // \r
6640     // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
6641     // it's in a safe position. This causes dropouts, but it seems to be the only \r
6642     // practical way to sync up the read and write pointers reliably, given the \r
6643     // the very complex relationship between phase and increment of the read and write \r
6644     // pointers.\r
6645     //\r
6646     // In order to minimize audible dropouts in DUPLEX mode, we will\r
6647     // provide a pre-roll period of 0.5 seconds in which we return\r
6648     // zeros from the read buffer while the pointers sync up.\r
6649 \r
6650     if ( stream_.mode == DUPLEX ) {\r
6651       if ( safeReadPointer < endRead ) {\r
6652         if ( duplexPrerollBytes <= 0 ) {\r
6653           // Pre-roll time over. Be more agressive.\r
6654           int adjustment = endRead-safeReadPointer;\r
6655 \r
6656           handle->xrun[1] = true;\r
6657           // Two cases:\r
6658           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
6659           //     and perform fine adjustments later.\r
6660           //   - small adjustments: back off by twice as much.\r
6661           if ( adjustment >= 2*bufferBytes )\r
6662             nextReadPointer = safeReadPointer-2*bufferBytes;\r
6663           else\r
6664             nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
6665 \r
6666           if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6667 \r
6668         }\r
6669         else {\r
6670           // In pre=roll time. Just do it.\r
6671           nextReadPointer = safeReadPointer - bufferBytes;\r
6672           while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6673         }\r
6674         endRead = nextReadPointer + bufferBytes;\r
6675       }\r
6676     }\r
6677     else { // mode == INPUT\r
6678       while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
6679         // See comments for playback.\r
6680         double millis = (endRead - safeReadPointer) * 1000.0;\r
6681         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
6682         if ( millis < 1.0 ) millis = 1.0;\r
6683         Sleep( (DWORD) millis );\r
6684 \r
6685         // Wake up and find out where we are now.\r
6686         result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6687         if ( FAILED( result ) ) {\r
6688           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6689           errorText_ = errorStream_.str();\r
6690           MUTEX_UNLOCK( &stream_.mutex );\r
6691           error( RtAudioError::SYSTEM_ERROR );\r
6692           return;\r
6693         }\r
6694       \r
6695         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6696       }\r
6697     }\r
6698 \r
6699     // Lock free space in the buffer\r
6700     result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
6701                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6702     if ( FAILED( result ) ) {\r
6703       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
6704       errorText_ = errorStream_.str();\r
6705       MUTEX_UNLOCK( &stream_.mutex );\r
6706       error( RtAudioError::SYSTEM_ERROR );\r
6707       return;\r
6708     }\r
6709 \r
6710     if ( duplexPrerollBytes <= 0 ) {\r
6711       // Copy our buffer into the DS buffer\r
6712       CopyMemory( buffer, buffer1, bufferSize1 );\r
6713       if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
6714     }\r
6715     else {\r
6716       memset( buffer, 0, bufferSize1 );\r
6717       if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
6718       duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
6719     }\r
6720 \r
6721     // Update our buffer offset and unlock sound buffer\r
6722     nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6723     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6724     if ( FAILED( result ) ) {\r
6725       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
6726       errorText_ = errorStream_.str();\r
6727       MUTEX_UNLOCK( &stream_.mutex );\r
6728       error( RtAudioError::SYSTEM_ERROR );\r
6729       return;\r
6730     }\r
6731     handle->bufferPointer[1] = nextReadPointer;\r
6732 \r
6733     // No byte swapping necessary in DirectSound implementation.\r
6734 \r
6735     // If necessary, convert 8-bit data from unsigned to signed.\r
6736     if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
6737       for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
6738 \r
6739     // Do buffer conversion if necessary.\r
6740     if ( stream_.doConvertBuffer[1] )\r
6741       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
6742   }\r
6743 \r
6744  unlock:\r
6745   MUTEX_UNLOCK( &stream_.mutex );\r
6746   RtApi::tickStreamTime();\r
6747 }\r
6748 \r
6749 // Definitions for utility functions and callbacks\r
6750 // specific to the DirectSound implementation.\r
6751 \r
6752 static unsigned __stdcall callbackHandler( void *ptr )\r
6753 {\r
6754   CallbackInfo *info = (CallbackInfo *) ptr;\r
6755   RtApiDs *object = (RtApiDs *) info->object;\r
6756   bool* isRunning = &info->isRunning;\r
6757 \r
6758   while ( *isRunning == true ) {\r
6759     object->callbackEvent();\r
6760   }\r
6761 \r
6762   _endthreadex( 0 );\r
6763   return 0;\r
6764 }\r
6765 \r
6766 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
6767                                           LPCTSTR description,\r
6768                                           LPCTSTR /*module*/,\r
6769                                           LPVOID lpContext )\r
6770 {\r
6771   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
6772   std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
6773 \r
6774   HRESULT hr;\r
6775   bool validDevice = false;\r
6776   if ( probeInfo.isInput == true ) {\r
6777     DSCCAPS caps;\r
6778     LPDIRECTSOUNDCAPTURE object;\r
6779 \r
6780     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
6781     if ( hr != DS_OK ) return TRUE;\r
6782 \r
6783     caps.dwSize = sizeof(caps);\r
6784     hr = object->GetCaps( &caps );\r
6785     if ( hr == DS_OK ) {\r
6786       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
6787         validDevice = true;\r
6788     }\r
6789     object->Release();\r
6790   }\r
6791   else {\r
6792     DSCAPS caps;\r
6793     LPDIRECTSOUND object;\r
6794     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
6795     if ( hr != DS_OK ) return TRUE;\r
6796 \r
6797     caps.dwSize = sizeof(caps);\r
6798     hr = object->GetCaps( &caps );\r
6799     if ( hr == DS_OK ) {\r
6800       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
6801         validDevice = true;\r
6802     }\r
6803     object->Release();\r
6804   }\r
6805 \r
6806   // If good device, then save its name and guid.\r
6807   std::string name = convertCharPointerToStdString( description );\r
6808   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
6809   if ( lpguid == NULL )\r
6810     name = "Default Device";\r
6811   if ( validDevice ) {\r
6812     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
6813       if ( dsDevices[i].name == name ) {\r
6814         dsDevices[i].found = true;\r
6815         if ( probeInfo.isInput ) {\r
6816           dsDevices[i].id[1] = lpguid;\r
6817           dsDevices[i].validId[1] = true;\r
6818         }\r
6819         else {\r
6820           dsDevices[i].id[0] = lpguid;\r
6821           dsDevices[i].validId[0] = true;\r
6822         }\r
6823         return TRUE;\r
6824       }\r
6825     }\r
6826 \r
6827     DsDevice device;\r
6828     device.name = name;\r
6829     device.found = true;\r
6830     if ( probeInfo.isInput ) {\r
6831       device.id[1] = lpguid;\r
6832       device.validId[1] = true;\r
6833     }\r
6834     else {\r
6835       device.id[0] = lpguid;\r
6836       device.validId[0] = true;\r
6837     }\r
6838     dsDevices.push_back( device );\r
6839   }\r
6840 \r
6841   return TRUE;\r
6842 }\r
6843 \r
6844 static const char* getErrorString( int code )\r
6845 {\r
6846   switch ( code ) {\r
6847 \r
6848   case DSERR_ALLOCATED:\r
6849     return "Already allocated";\r
6850 \r
6851   case DSERR_CONTROLUNAVAIL:\r
6852     return "Control unavailable";\r
6853 \r
6854   case DSERR_INVALIDPARAM:\r
6855     return "Invalid parameter";\r
6856 \r
6857   case DSERR_INVALIDCALL:\r
6858     return "Invalid call";\r
6859 \r
6860   case DSERR_GENERIC:\r
6861     return "Generic error";\r
6862 \r
6863   case DSERR_PRIOLEVELNEEDED:\r
6864     return "Priority level needed";\r
6865 \r
6866   case DSERR_OUTOFMEMORY:\r
6867     return "Out of memory";\r
6868 \r
6869   case DSERR_BADFORMAT:\r
6870     return "The sample rate or the channel format is not supported";\r
6871 \r
6872   case DSERR_UNSUPPORTED:\r
6873     return "Not supported";\r
6874 \r
6875   case DSERR_NODRIVER:\r
6876     return "No driver";\r
6877 \r
6878   case DSERR_ALREADYINITIALIZED:\r
6879     return "Already initialized";\r
6880 \r
6881   case DSERR_NOAGGREGATION:\r
6882     return "No aggregation";\r
6883 \r
6884   case DSERR_BUFFERLOST:\r
6885     return "Buffer lost";\r
6886 \r
6887   case DSERR_OTHERAPPHASPRIO:\r
6888     return "Another application already has priority";\r
6889 \r
6890   case DSERR_UNINITIALIZED:\r
6891     return "Uninitialized";\r
6892 \r
6893   default:\r
6894     return "DirectSound unknown error";\r
6895   }\r
6896 }\r
6897 //******************** End of __WINDOWS_DS__ *********************//\r
6898 #endif\r
6899 \r
6900 \r
6901 #if defined(__LINUX_ALSA__)\r
6902 \r
6903 #include <alsa/asoundlib.h>\r
6904 #include <unistd.h>\r
6905 \r
6906   // A structure to hold various information related to the ALSA API\r
6907   // implementation.\r
6908 struct AlsaHandle {\r
6909   snd_pcm_t *handles[2];\r
6910   bool synchronized;\r
6911   bool xrun[2];\r
6912   pthread_cond_t runnable_cv;\r
6913   bool runnable;\r
6914 \r
6915   AlsaHandle()\r
6916     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
6917 };\r
6918 \r
6919 static void *alsaCallbackHandler( void * ptr );\r
6920 \r
6921 RtApiAlsa :: RtApiAlsa()\r
6922 {\r
6923   // Nothing to do here.\r
6924 }\r
6925 \r
6926 RtApiAlsa :: ~RtApiAlsa()\r
6927 {\r
6928   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
6929 }\r
6930 \r
6931 unsigned int RtApiAlsa :: getDeviceCount( void )\r
6932 {\r
6933   unsigned nDevices = 0;\r
6934   int result, subdevice, card;\r
6935   char name[64];\r
6936   snd_ctl_t *handle;\r
6937 \r
6938   // Count cards and devices\r
6939   card = -1;\r
6940   snd_card_next( &card );\r
6941   while ( card >= 0 ) {\r
6942     sprintf( name, "hw:%d", card );\r
6943     result = snd_ctl_open( &handle, name, 0 );\r
6944     if ( result < 0 ) {\r
6945       errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6946       errorText_ = errorStream_.str();\r
6947       error( RtAudioError::WARNING );\r
6948       goto nextcard;\r
6949     }\r
6950     subdevice = -1;\r
6951     while( 1 ) {\r
6952       result = snd_ctl_pcm_next_device( handle, &subdevice );\r
6953       if ( result < 0 ) {\r
6954         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6955         errorText_ = errorStream_.str();\r
6956         error( RtAudioError::WARNING );\r
6957         break;\r
6958       }\r
6959       if ( subdevice < 0 )\r
6960         break;\r
6961       nDevices++;\r
6962     }\r
6963   nextcard:\r
6964     snd_ctl_close( handle );\r
6965     snd_card_next( &card );\r
6966   }\r
6967 \r
6968   result = snd_ctl_open( &handle, "default", 0 );\r
6969   if (result == 0) {\r
6970     nDevices++;\r
6971     snd_ctl_close( handle );\r
6972   }\r
6973 \r
6974   return nDevices;\r
6975 }\r
6976 \r
6977 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
6978 {\r
6979   RtAudio::DeviceInfo info;\r
6980   info.probed = false;\r
6981 \r
6982   unsigned nDevices = 0;\r
6983   int result, subdevice, card;\r
6984   char name[64];\r
6985   snd_ctl_t *chandle;\r
6986 \r
6987   // Count cards and devices\r
6988   card = -1;\r
6989   subdevice = -1;\r
6990   snd_card_next( &card );\r
6991   while ( card >= 0 ) {\r
6992     sprintf( name, "hw:%d", card );\r
6993     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6994     if ( result < 0 ) {\r
6995       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6996       errorText_ = errorStream_.str();\r
6997       error( RtAudioError::WARNING );\r
6998       goto nextcard;\r
6999     }\r
7000     subdevice = -1;\r
7001     while( 1 ) {\r
7002       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7003       if ( result < 0 ) {\r
7004         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
7005         errorText_ = errorStream_.str();\r
7006         error( RtAudioError::WARNING );\r
7007         break;\r
7008       }\r
7009       if ( subdevice < 0 ) break;\r
7010       if ( nDevices == device ) {\r
7011         sprintf( name, "hw:%d,%d", card, subdevice );\r
7012         goto foundDevice;\r
7013       }\r
7014       nDevices++;\r
7015     }\r
7016   nextcard:\r
7017     snd_ctl_close( chandle );\r
7018     snd_card_next( &card );\r
7019   }\r
7020 \r
7021   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7022   if ( result == 0 ) {\r
7023     if ( nDevices == device ) {\r
7024       strcpy( name, "default" );\r
7025       goto foundDevice;\r
7026     }\r
7027     nDevices++;\r
7028   }\r
7029 \r
7030   if ( nDevices == 0 ) {\r
7031     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
7032     error( RtAudioError::INVALID_USE );\r
7033     return info;\r
7034   }\r
7035 \r
7036   if ( device >= nDevices ) {\r
7037     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
7038     error( RtAudioError::INVALID_USE );\r
7039     return info;\r
7040   }\r
7041 \r
7042  foundDevice:\r
7043 \r
7044   // If a stream is already open, we cannot probe the stream devices.\r
7045   // Thus, use the saved results.\r
7046   if ( stream_.state != STREAM_CLOSED &&\r
7047        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
7048     snd_ctl_close( chandle );\r
7049     if ( device >= devices_.size() ) {\r
7050       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
7051       error( RtAudioError::WARNING );\r
7052       return info;\r
7053     }\r
7054     return devices_[ device ];\r
7055   }\r
7056 \r
7057   int openMode = SND_PCM_ASYNC;\r
7058   snd_pcm_stream_t stream;\r
7059   snd_pcm_info_t *pcminfo;\r
7060   snd_pcm_info_alloca( &pcminfo );\r
7061   snd_pcm_t *phandle;\r
7062   snd_pcm_hw_params_t *params;\r
7063   snd_pcm_hw_params_alloca( &params );\r
7064 \r
7065   // First try for playback unless default device (which has subdev -1)\r
7066   stream = SND_PCM_STREAM_PLAYBACK;\r
7067   snd_pcm_info_set_stream( pcminfo, stream );\r
7068   if ( subdevice != -1 ) {\r
7069     snd_pcm_info_set_device( pcminfo, subdevice );\r
7070     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
7071 \r
7072     result = snd_ctl_pcm_info( chandle, pcminfo );\r
7073     if ( result < 0 ) {\r
7074       // Device probably doesn't support playback.\r
7075       goto captureProbe;\r
7076     }\r
7077   }\r
7078 \r
7079   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
7080   if ( result < 0 ) {\r
7081     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7082     errorText_ = errorStream_.str();\r
7083     error( RtAudioError::WARNING );\r
7084     goto captureProbe;\r
7085   }\r
7086 \r
7087   // The device is open ... fill the parameter structure.\r
7088   result = snd_pcm_hw_params_any( phandle, params );\r
7089   if ( result < 0 ) {\r
7090     snd_pcm_close( phandle );\r
7091     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7092     errorText_ = errorStream_.str();\r
7093     error( RtAudioError::WARNING );\r
7094     goto captureProbe;\r
7095   }\r
7096 \r
7097   // Get output channel information.\r
7098   unsigned int value;\r
7099   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
7100   if ( result < 0 ) {\r
7101     snd_pcm_close( phandle );\r
7102     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
7103     errorText_ = errorStream_.str();\r
7104     error( RtAudioError::WARNING );\r
7105     goto captureProbe;\r
7106   }\r
7107   info.outputChannels = value;\r
7108   snd_pcm_close( phandle );\r
7109 \r
7110  captureProbe:\r
7111   stream = SND_PCM_STREAM_CAPTURE;\r
7112   snd_pcm_info_set_stream( pcminfo, stream );\r
7113 \r
7114   // Now try for capture unless default device (with subdev = -1)\r
7115   if ( subdevice != -1 ) {\r
7116     result = snd_ctl_pcm_info( chandle, pcminfo );\r
7117     snd_ctl_close( chandle );\r
7118     if ( result < 0 ) {\r
7119       // Device probably doesn't support capture.\r
7120       if ( info.outputChannels == 0 ) return info;\r
7121       goto probeParameters;\r
7122     }\r
7123   }\r
7124   else\r
7125     snd_ctl_close( chandle );\r
7126 \r
7127   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7128   if ( result < 0 ) {\r
7129     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7130     errorText_ = errorStream_.str();\r
7131     error( RtAudioError::WARNING );\r
7132     if ( info.outputChannels == 0 ) return info;\r
7133     goto probeParameters;\r
7134   }\r
7135 \r
7136   // The device is open ... fill the parameter structure.\r
7137   result = snd_pcm_hw_params_any( phandle, params );\r
7138   if ( result < 0 ) {\r
7139     snd_pcm_close( phandle );\r
7140     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7141     errorText_ = errorStream_.str();\r
7142     error( RtAudioError::WARNING );\r
7143     if ( info.outputChannels == 0 ) return info;\r
7144     goto probeParameters;\r
7145   }\r
7146 \r
7147   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
7148   if ( result < 0 ) {\r
7149     snd_pcm_close( phandle );\r
7150     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
7151     errorText_ = errorStream_.str();\r
7152     error( RtAudioError::WARNING );\r
7153     if ( info.outputChannels == 0 ) return info;\r
7154     goto probeParameters;\r
7155   }\r
7156   info.inputChannels = value;\r
7157   snd_pcm_close( phandle );\r
7158 \r
7159   // If device opens for both playback and capture, we determine the channels.\r
7160   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
7161     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
7162 \r
7163   // ALSA doesn't provide default devices so we'll use the first available one.\r
7164   if ( device == 0 && info.outputChannels > 0 )\r
7165     info.isDefaultOutput = true;\r
7166   if ( device == 0 && info.inputChannels > 0 )\r
7167     info.isDefaultInput = true;\r
7168 \r
7169  probeParameters:\r
7170   // At this point, we just need to figure out the supported data\r
7171   // formats and sample rates.  We'll proceed by opening the device in\r
7172   // the direction with the maximum number of channels, or playback if\r
7173   // they are equal.  This might limit our sample rate options, but so\r
7174   // be it.\r
7175 \r
7176   if ( info.outputChannels >= info.inputChannels )\r
7177     stream = SND_PCM_STREAM_PLAYBACK;\r
7178   else\r
7179     stream = SND_PCM_STREAM_CAPTURE;\r
7180   snd_pcm_info_set_stream( pcminfo, stream );\r
7181 \r
7182   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7183   if ( result < 0 ) {\r
7184     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7185     errorText_ = errorStream_.str();\r
7186     error( RtAudioError::WARNING );\r
7187     return info;\r
7188   }\r
7189 \r
7190   // The device is open ... fill the parameter structure.\r
7191   result = snd_pcm_hw_params_any( phandle, params );\r
7192   if ( result < 0 ) {\r
7193     snd_pcm_close( phandle );\r
7194     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7195     errorText_ = errorStream_.str();\r
7196     error( RtAudioError::WARNING );\r
7197     return info;\r
7198   }\r
7199 \r
7200   // Test our discrete set of sample rate values.\r
7201   info.sampleRates.clear();\r
7202   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7203     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {\r
7204       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7205 \r
7206       if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
7207         info.preferredSampleRate = SAMPLE_RATES[i];\r
7208     }\r
7209   }\r
7210   if ( info.sampleRates.size() == 0 ) {\r
7211     snd_pcm_close( phandle );\r
7212     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7213     errorText_ = errorStream_.str();\r
7214     error( RtAudioError::WARNING );\r
7215     return info;\r
7216   }\r
7217 \r
7218   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7219   snd_pcm_format_t format;\r
7220   info.nativeFormats = 0;\r
7221   format = SND_PCM_FORMAT_S8;\r
7222   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7223     info.nativeFormats |= RTAUDIO_SINT8;\r
7224   format = SND_PCM_FORMAT_S16;\r
7225   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7226     info.nativeFormats |= RTAUDIO_SINT16;\r
7227   format = SND_PCM_FORMAT_S24;\r
7228   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7229     info.nativeFormats |= RTAUDIO_SINT24;\r
7230   format = SND_PCM_FORMAT_S32;\r
7231   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7232     info.nativeFormats |= RTAUDIO_SINT32;\r
7233   format = SND_PCM_FORMAT_FLOAT;\r
7234   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7235     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7236   format = SND_PCM_FORMAT_FLOAT64;\r
7237   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7238     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7239 \r
7240   // Check that we have at least one supported format\r
7241   if ( info.nativeFormats == 0 ) {\r
7242     snd_pcm_close( phandle );\r
7243     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7244     errorText_ = errorStream_.str();\r
7245     error( RtAudioError::WARNING );\r
7246     return info;\r
7247   }\r
7248 \r
7249   // Get the device name\r
7250   char *cardname;\r
7251   result = snd_card_get_name( card, &cardname );\r
7252   if ( result >= 0 ) {\r
7253     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7254     free( cardname );\r
7255   }\r
7256   info.name = name;\r
7257 \r
7258   // That's all ... close the device and return\r
7259   snd_pcm_close( phandle );\r
7260   info.probed = true;\r
7261   return info;\r
7262 }\r
7263 \r
7264 void RtApiAlsa :: saveDeviceInfo( void )\r
7265 {\r
7266   devices_.clear();\r
7267 \r
7268   unsigned int nDevices = getDeviceCount();\r
7269   devices_.resize( nDevices );\r
7270   for ( unsigned int i=0; i<nDevices; i++ )\r
7271     devices_[i] = getDeviceInfo( i );\r
7272 }\r
7273 \r
7274 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7275                                    unsigned int firstChannel, unsigned int sampleRate,\r
7276                                    RtAudioFormat format, unsigned int *bufferSize,\r
7277                                    RtAudio::StreamOptions *options )\r
7278 \r
7279 {\r
7280 #if defined(__RTAUDIO_DEBUG__)\r
7281   snd_output_t *out;\r
7282   snd_output_stdio_attach(&out, stderr, 0);\r
7283 #endif\r
7284 \r
7285   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7286 \r
7287   unsigned nDevices = 0;\r
7288   int result, subdevice, card;\r
7289   char name[64];\r
7290   snd_ctl_t *chandle;\r
7291 \r
7292   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7293     snprintf(name, sizeof(name), "%s", "default");\r
7294   else {\r
7295     // Count cards and devices\r
7296     card = -1;\r
7297     snd_card_next( &card );\r
7298     while ( card >= 0 ) {\r
7299       sprintf( name, "hw:%d", card );\r
7300       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7301       if ( result < 0 ) {\r
7302         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7303         errorText_ = errorStream_.str();\r
7304         return FAILURE;\r
7305       }\r
7306       subdevice = -1;\r
7307       while( 1 ) {\r
7308         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7309         if ( result < 0 ) break;\r
7310         if ( subdevice < 0 ) break;\r
7311         if ( nDevices == device ) {\r
7312           sprintf( name, "hw:%d,%d", card, subdevice );\r
7313           snd_ctl_close( chandle );\r
7314           goto foundDevice;\r
7315         }\r
7316         nDevices++;\r
7317       }\r
7318       snd_ctl_close( chandle );\r
7319       snd_card_next( &card );\r
7320     }\r
7321 \r
7322     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7323     if ( result == 0 ) {\r
7324       if ( nDevices == device ) {\r
7325         strcpy( name, "default" );\r
7326         goto foundDevice;\r
7327       }\r
7328       nDevices++;\r
7329     }\r
7330 \r
7331     if ( nDevices == 0 ) {\r
7332       // This should not happen because a check is made before this function is called.\r
7333       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7334       return FAILURE;\r
7335     }\r
7336 \r
7337     if ( device >= nDevices ) {\r
7338       // This should not happen because a check is made before this function is called.\r
7339       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7340       return FAILURE;\r
7341     }\r
7342   }\r
7343 \r
7344  foundDevice:\r
7345 \r
7346   // The getDeviceInfo() function will not work for a device that is\r
7347   // already open.  Thus, we'll probe the system before opening a\r
7348   // stream and save the results for use by getDeviceInfo().\r
7349   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7350     this->saveDeviceInfo();\r
7351 \r
7352   snd_pcm_stream_t stream;\r
7353   if ( mode == OUTPUT )\r
7354     stream = SND_PCM_STREAM_PLAYBACK;\r
7355   else\r
7356     stream = SND_PCM_STREAM_CAPTURE;\r
7357 \r
7358   snd_pcm_t *phandle;\r
7359   int openMode = SND_PCM_ASYNC;\r
7360   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7361   if ( result < 0 ) {\r
7362     if ( mode == OUTPUT )\r
7363       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7364     else\r
7365       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7366     errorText_ = errorStream_.str();\r
7367     return FAILURE;\r
7368   }\r
7369 \r
7370   // Fill the parameter structure.\r
7371   snd_pcm_hw_params_t *hw_params;\r
7372   snd_pcm_hw_params_alloca( &hw_params );\r
7373   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7374   if ( result < 0 ) {\r
7375     snd_pcm_close( phandle );\r
7376     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7377     errorText_ = errorStream_.str();\r
7378     return FAILURE;\r
7379   }\r
7380 \r
7381 #if defined(__RTAUDIO_DEBUG__)\r
7382   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7383   snd_pcm_hw_params_dump( hw_params, out );\r
7384 #endif\r
7385 \r
7386   // Set access ... check user preference.\r
7387   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7388     stream_.userInterleaved = false;\r
7389     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7390     if ( result < 0 ) {\r
7391       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7392       stream_.deviceInterleaved[mode] =  true;\r
7393     }\r
7394     else\r
7395       stream_.deviceInterleaved[mode] = false;\r
7396   }\r
7397   else {\r
7398     stream_.userInterleaved = true;\r
7399     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7400     if ( result < 0 ) {\r
7401       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7402       stream_.deviceInterleaved[mode] =  false;\r
7403     }\r
7404     else\r
7405       stream_.deviceInterleaved[mode] =  true;\r
7406   }\r
7407 \r
7408   if ( result < 0 ) {\r
7409     snd_pcm_close( phandle );\r
7410     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7411     errorText_ = errorStream_.str();\r
7412     return FAILURE;\r
7413   }\r
7414 \r
7415   // Determine how to set the device format.\r
7416   stream_.userFormat = format;\r
7417   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7418 \r
7419   if ( format == RTAUDIO_SINT8 )\r
7420     deviceFormat = SND_PCM_FORMAT_S8;\r
7421   else if ( format == RTAUDIO_SINT16 )\r
7422     deviceFormat = SND_PCM_FORMAT_S16;\r
7423   else if ( format == RTAUDIO_SINT24 )\r
7424     deviceFormat = SND_PCM_FORMAT_S24;\r
7425   else if ( format == RTAUDIO_SINT32 )\r
7426     deviceFormat = SND_PCM_FORMAT_S32;\r
7427   else if ( format == RTAUDIO_FLOAT32 )\r
7428     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7429   else if ( format == RTAUDIO_FLOAT64 )\r
7430     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7431 \r
7432   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7433     stream_.deviceFormat[mode] = format;\r
7434     goto setFormat;\r
7435   }\r
7436 \r
7437   // The user requested format is not natively supported by the device.\r
7438   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7439   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7440     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7441     goto setFormat;\r
7442   }\r
7443 \r
7444   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7445   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7446     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7447     goto setFormat;\r
7448   }\r
7449 \r
7450   deviceFormat = SND_PCM_FORMAT_S32;\r
7451   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7452     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7453     goto setFormat;\r
7454   }\r
7455 \r
7456   deviceFormat = SND_PCM_FORMAT_S24;\r
7457   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7458     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7459     goto setFormat;\r
7460   }\r
7461 \r
7462   deviceFormat = SND_PCM_FORMAT_S16;\r
7463   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7464     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7465     goto setFormat;\r
7466   }\r
7467 \r
7468   deviceFormat = SND_PCM_FORMAT_S8;\r
7469   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7470     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7471     goto setFormat;\r
7472   }\r
7473 \r
7474   // If we get here, no supported format was found.\r
7475   snd_pcm_close( phandle );\r
7476   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7477   errorText_ = errorStream_.str();\r
7478   return FAILURE;\r
7479 \r
7480  setFormat:\r
7481   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7482   if ( result < 0 ) {\r
7483     snd_pcm_close( phandle );\r
7484     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7485     errorText_ = errorStream_.str();\r
7486     return FAILURE;\r
7487   }\r
7488 \r
7489   // Determine whether byte-swaping is necessary.\r
7490   stream_.doByteSwap[mode] = false;\r
7491   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7492     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7493     if ( result == 0 )\r
7494       stream_.doByteSwap[mode] = true;\r
7495     else if (result < 0) {\r
7496       snd_pcm_close( phandle );\r
7497       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7498       errorText_ = errorStream_.str();\r
7499       return FAILURE;\r
7500     }\r
7501   }\r
7502 \r
7503   // Set the sample rate.\r
7504   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7505   if ( result < 0 ) {\r
7506     snd_pcm_close( phandle );\r
7507     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7508     errorText_ = errorStream_.str();\r
7509     return FAILURE;\r
7510   }\r
7511 \r
7512   // Determine the number of channels for this device.  We support a possible\r
7513   // minimum device channel number > than the value requested by the user.\r
7514   stream_.nUserChannels[mode] = channels;\r
7515   unsigned int value;\r
7516   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7517   unsigned int deviceChannels = value;\r
7518   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7519     snd_pcm_close( phandle );\r
7520     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7521     errorText_ = errorStream_.str();\r
7522     return FAILURE;\r
7523   }\r
7524 \r
7525   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7526   if ( result < 0 ) {\r
7527     snd_pcm_close( phandle );\r
7528     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7529     errorText_ = errorStream_.str();\r
7530     return FAILURE;\r
7531   }\r
7532   deviceChannels = value;\r
7533   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7534   stream_.nDeviceChannels[mode] = deviceChannels;\r
7535 \r
7536   // Set the device channels.\r
7537   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7538   if ( result < 0 ) {\r
7539     snd_pcm_close( phandle );\r
7540     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7541     errorText_ = errorStream_.str();\r
7542     return FAILURE;\r
7543   }\r
7544 \r
7545   // Set the buffer (or period) size.\r
7546   int dir = 0;\r
7547   snd_pcm_uframes_t periodSize = *bufferSize;\r
7548   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7549   if ( result < 0 ) {\r
7550     snd_pcm_close( phandle );\r
7551     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7552     errorText_ = errorStream_.str();\r
7553     return FAILURE;\r
7554   }\r
7555   *bufferSize = periodSize;\r
7556 \r
7557   // Set the buffer number, which in ALSA is referred to as the "period".\r
7558   unsigned int periods = 0;\r
7559   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7560   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7561   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7562   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7563   if ( result < 0 ) {\r
7564     snd_pcm_close( phandle );\r
7565     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7566     errorText_ = errorStream_.str();\r
7567     return FAILURE;\r
7568   }\r
7569 \r
7570   // If attempting to setup a duplex stream, the bufferSize parameter\r
7571   // MUST be the same in both directions!\r
7572   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7573     snd_pcm_close( phandle );\r
7574     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7575     errorText_ = errorStream_.str();\r
7576     return FAILURE;\r
7577   }\r
7578 \r
7579   stream_.bufferSize = *bufferSize;\r
7580 \r
7581   // Install the hardware configuration\r
7582   result = snd_pcm_hw_params( phandle, hw_params );\r
7583   if ( result < 0 ) {\r
7584     snd_pcm_close( phandle );\r
7585     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7586     errorText_ = errorStream_.str();\r
7587     return FAILURE;\r
7588   }\r
7589 \r
7590 #if defined(__RTAUDIO_DEBUG__)\r
7591   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7592   snd_pcm_hw_params_dump( hw_params, out );\r
7593 #endif\r
7594 \r
7595   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7596   snd_pcm_sw_params_t *sw_params = NULL;\r
7597   snd_pcm_sw_params_alloca( &sw_params );\r
7598   snd_pcm_sw_params_current( phandle, sw_params );\r
7599   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7600   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7601   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7602 \r
7603   // The following two settings were suggested by Theo Veenker\r
7604   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7605   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7606 \r
7607   // here are two options for a fix\r
7608   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7609   snd_pcm_uframes_t val;\r
7610   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7611   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7612 \r
7613   result = snd_pcm_sw_params( phandle, sw_params );\r
7614   if ( result < 0 ) {\r
7615     snd_pcm_close( phandle );\r
7616     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7617     errorText_ = errorStream_.str();\r
7618     return FAILURE;\r
7619   }\r
7620 \r
7621 #if defined(__RTAUDIO_DEBUG__)\r
7622   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7623   snd_pcm_sw_params_dump( sw_params, out );\r
7624 #endif\r
7625 \r
7626   // Set flags for buffer conversion\r
7627   stream_.doConvertBuffer[mode] = false;\r
7628   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7629     stream_.doConvertBuffer[mode] = true;\r
7630   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7631     stream_.doConvertBuffer[mode] = true;\r
7632   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7633        stream_.nUserChannels[mode] > 1 )\r
7634     stream_.doConvertBuffer[mode] = true;\r
7635 \r
7636   // Allocate the ApiHandle if necessary and then save.\r
7637   AlsaHandle *apiInfo = 0;\r
7638   if ( stream_.apiHandle == 0 ) {\r
7639     try {\r
7640       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7641     }\r
7642     catch ( std::bad_alloc& ) {\r
7643       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7644       goto error;\r
7645     }\r
7646 \r
7647     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7648       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7649       goto error;\r
7650     }\r
7651 \r
7652     stream_.apiHandle = (void *) apiInfo;\r
7653     apiInfo->handles[0] = 0;\r
7654     apiInfo->handles[1] = 0;\r
7655   }\r
7656   else {\r
7657     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7658   }\r
7659   apiInfo->handles[mode] = phandle;\r
7660   phandle = 0;\r
7661 \r
7662   // Allocate necessary internal buffers.\r
7663   unsigned long bufferBytes;\r
7664   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7665   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7666   if ( stream_.userBuffer[mode] == NULL ) {\r
7667     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7668     goto error;\r
7669   }\r
7670 \r
7671   if ( stream_.doConvertBuffer[mode] ) {\r
7672 \r
7673     bool makeBuffer = true;\r
7674     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7675     if ( mode == INPUT ) {\r
7676       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7677         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7678         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7679       }\r
7680     }\r
7681 \r
7682     if ( makeBuffer ) {\r
7683       bufferBytes *= *bufferSize;\r
7684       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7685       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7686       if ( stream_.deviceBuffer == NULL ) {\r
7687         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7688         goto error;\r
7689       }\r
7690     }\r
7691   }\r
7692 \r
7693   stream_.sampleRate = sampleRate;\r
7694   stream_.nBuffers = periods;\r
7695   stream_.device[mode] = device;\r
7696   stream_.state = STREAM_STOPPED;\r
7697 \r
7698   // Setup the buffer conversion information structure.\r
7699   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7700 \r
7701   // Setup thread if necessary.\r
7702   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7703     // We had already set up an output stream.\r
7704     stream_.mode = DUPLEX;\r
7705     // Link the streams if possible.\r
7706     apiInfo->synchronized = false;\r
7707     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7708       apiInfo->synchronized = true;\r
7709     else {\r
7710       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7711       error( RtAudioError::WARNING );\r
7712     }\r
7713   }\r
7714   else {\r
7715     stream_.mode = mode;\r
7716 \r
7717     // Setup callback thread.\r
7718     stream_.callbackInfo.object = (void *) this;\r
7719 \r
7720     // Set the thread attributes for joinable and realtime scheduling\r
7721     // priority (optional).  The higher priority will only take affect\r
7722     // if the program is run as root or suid. Note, under Linux\r
7723     // processes with CAP_SYS_NICE privilege, a user can change\r
7724     // scheduling policy and priority (thus need not be root). See\r
7725     // POSIX "capabilities".\r
7726     pthread_attr_t attr;\r
7727     pthread_attr_init( &attr );\r
7728     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7729 \r
7730 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7731     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7732       // We previously attempted to increase the audio callback priority\r
7733       // to SCHED_RR here via the attributes.  However, while no errors\r
7734       // were reported in doing so, it did not work.  So, now this is\r
7735       // done in the alsaCallbackHandler function.\r
7736       stream_.callbackInfo.doRealtime = true;\r
7737       int priority = options->priority;\r
7738       int min = sched_get_priority_min( SCHED_RR );\r
7739       int max = sched_get_priority_max( SCHED_RR );\r
7740       if ( priority < min ) priority = min;\r
7741       else if ( priority > max ) priority = max;\r
7742       stream_.callbackInfo.priority = priority;\r
7743     }\r
7744 #endif\r
7745 \r
7746     stream_.callbackInfo.isRunning = true;\r
7747     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7748     pthread_attr_destroy( &attr );\r
7749     if ( result ) {\r
7750       stream_.callbackInfo.isRunning = false;\r
7751       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7752       goto error;\r
7753     }\r
7754   }\r
7755 \r
7756   return SUCCESS;\r
7757 \r
7758  error:\r
7759   if ( apiInfo ) {\r
7760     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7761     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7762     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7763     delete apiInfo;\r
7764     stream_.apiHandle = 0;\r
7765   }\r
7766 \r
7767   if ( phandle) snd_pcm_close( phandle );\r
7768 \r
7769   for ( int i=0; i<2; i++ ) {\r
7770     if ( stream_.userBuffer[i] ) {\r
7771       free( stream_.userBuffer[i] );\r
7772       stream_.userBuffer[i] = 0;\r
7773     }\r
7774   }\r
7775 \r
7776   if ( stream_.deviceBuffer ) {\r
7777     free( stream_.deviceBuffer );\r
7778     stream_.deviceBuffer = 0;\r
7779   }\r
7780 \r
7781   stream_.state = STREAM_CLOSED;\r
7782   return FAILURE;\r
7783 }\r
7784 \r
7785 void RtApiAlsa :: closeStream()\r
7786 {\r
7787   if ( stream_.state == STREAM_CLOSED ) {\r
7788     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7789     error( RtAudioError::WARNING );\r
7790     return;\r
7791   }\r
7792 \r
7793   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7794   stream_.callbackInfo.isRunning = false;\r
7795   MUTEX_LOCK( &stream_.mutex );\r
7796   if ( stream_.state == STREAM_STOPPED ) {\r
7797     apiInfo->runnable = true;\r
7798     pthread_cond_signal( &apiInfo->runnable_cv );\r
7799   }\r
7800   MUTEX_UNLOCK( &stream_.mutex );\r
7801   pthread_join( stream_.callbackInfo.thread, NULL );\r
7802 \r
7803   if ( stream_.state == STREAM_RUNNING ) {\r
7804     stream_.state = STREAM_STOPPED;\r
7805     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7806       snd_pcm_drop( apiInfo->handles[0] );\r
7807     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7808       snd_pcm_drop( apiInfo->handles[1] );\r
7809   }\r
7810 \r
7811   if ( apiInfo ) {\r
7812     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7813     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7814     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7815     delete apiInfo;\r
7816     stream_.apiHandle = 0;\r
7817   }\r
7818 \r
7819   for ( int i=0; i<2; i++ ) {\r
7820     if ( stream_.userBuffer[i] ) {\r
7821       free( stream_.userBuffer[i] );\r
7822       stream_.userBuffer[i] = 0;\r
7823     }\r
7824   }\r
7825 \r
7826   if ( stream_.deviceBuffer ) {\r
7827     free( stream_.deviceBuffer );\r
7828     stream_.deviceBuffer = 0;\r
7829   }\r
7830 \r
7831   stream_.mode = UNINITIALIZED;\r
7832   stream_.state = STREAM_CLOSED;\r
7833 }\r
7834 \r
7835 void RtApiAlsa :: startStream()\r
7836 {\r
7837   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7838 \r
7839   verifyStream();\r
7840   if ( stream_.state == STREAM_RUNNING ) {\r
7841     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7842     error( RtAudioError::WARNING );\r
7843     return;\r
7844   }\r
7845 \r
7846   MUTEX_LOCK( &stream_.mutex );\r
7847 \r
7848   int result = 0;\r
7849   snd_pcm_state_t state;\r
7850   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7851   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7852   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7853     state = snd_pcm_state( handle[0] );\r
7854     if ( state != SND_PCM_STATE_PREPARED ) {\r
7855       result = snd_pcm_prepare( handle[0] );\r
7856       if ( result < 0 ) {\r
7857         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7858         errorText_ = errorStream_.str();\r
7859         goto unlock;\r
7860       }\r
7861     }\r
7862   }\r
7863 \r
7864   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7865     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7866     state = snd_pcm_state( handle[1] );\r
7867     if ( state != SND_PCM_STATE_PREPARED ) {\r
7868       result = snd_pcm_prepare( handle[1] );\r
7869       if ( result < 0 ) {\r
7870         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7871         errorText_ = errorStream_.str();\r
7872         goto unlock;\r
7873       }\r
7874     }\r
7875   }\r
7876 \r
7877   stream_.state = STREAM_RUNNING;\r
7878 \r
7879  unlock:\r
7880   apiInfo->runnable = true;\r
7881   pthread_cond_signal( &apiInfo->runnable_cv );\r
7882   MUTEX_UNLOCK( &stream_.mutex );\r
7883 \r
7884   if ( result >= 0 ) return;\r
7885   error( RtAudioError::SYSTEM_ERROR );\r
7886 }\r
7887 \r
7888 void RtApiAlsa :: stopStream()\r
7889 {\r
7890   verifyStream();\r
7891   if ( stream_.state == STREAM_STOPPED ) {\r
7892     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7893     error( RtAudioError::WARNING );\r
7894     return;\r
7895   }\r
7896 \r
7897   stream_.state = STREAM_STOPPED;\r
7898   MUTEX_LOCK( &stream_.mutex );\r
7899 \r
7900   int result = 0;\r
7901   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7902   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7903   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7904     if ( apiInfo->synchronized ) \r
7905       result = snd_pcm_drop( handle[0] );\r
7906     else\r
7907       result = snd_pcm_drain( handle[0] );\r
7908     if ( result < 0 ) {\r
7909       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7910       errorText_ = errorStream_.str();\r
7911       goto unlock;\r
7912     }\r
7913   }\r
7914 \r
7915   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7916     result = snd_pcm_drop( handle[1] );\r
7917     if ( result < 0 ) {\r
7918       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7919       errorText_ = errorStream_.str();\r
7920       goto unlock;\r
7921     }\r
7922   }\r
7923 \r
7924  unlock:\r
7925   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7926   MUTEX_UNLOCK( &stream_.mutex );\r
7927 \r
7928   if ( result >= 0 ) return;\r
7929   error( RtAudioError::SYSTEM_ERROR );\r
7930 }\r
7931 \r
7932 void RtApiAlsa :: abortStream()\r
7933 {\r
7934   verifyStream();\r
7935   if ( stream_.state == STREAM_STOPPED ) {\r
7936     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7937     error( RtAudioError::WARNING );\r
7938     return;\r
7939   }\r
7940 \r
7941   stream_.state = STREAM_STOPPED;\r
7942   MUTEX_LOCK( &stream_.mutex );\r
7943 \r
7944   int result = 0;\r
7945   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7946   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7947   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7948     result = snd_pcm_drop( handle[0] );\r
7949     if ( result < 0 ) {\r
7950       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7951       errorText_ = errorStream_.str();\r
7952       goto unlock;\r
7953     }\r
7954   }\r
7955 \r
7956   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7957     result = snd_pcm_drop( handle[1] );\r
7958     if ( result < 0 ) {\r
7959       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7960       errorText_ = errorStream_.str();\r
7961       goto unlock;\r
7962     }\r
7963   }\r
7964 \r
7965  unlock:\r
7966   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7967   MUTEX_UNLOCK( &stream_.mutex );\r
7968 \r
7969   if ( result >= 0 ) return;\r
7970   error( RtAudioError::SYSTEM_ERROR );\r
7971 }\r
7972 \r
7973 void RtApiAlsa :: callbackEvent()\r
7974 {\r
7975   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7976   if ( stream_.state == STREAM_STOPPED ) {\r
7977     MUTEX_LOCK( &stream_.mutex );\r
7978     while ( !apiInfo->runnable )\r
7979       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7980 \r
7981     if ( stream_.state != STREAM_RUNNING ) {\r
7982       MUTEX_UNLOCK( &stream_.mutex );\r
7983       return;\r
7984     }\r
7985     MUTEX_UNLOCK( &stream_.mutex );\r
7986   }\r
7987 \r
7988   if ( stream_.state == STREAM_CLOSED ) {\r
7989     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7990     error( RtAudioError::WARNING );\r
7991     return;\r
7992   }\r
7993 \r
7994   int doStopStream = 0;\r
7995   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7996   double streamTime = getStreamTime();\r
7997   RtAudioStreamStatus status = 0;\r
7998   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7999     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
8000     apiInfo->xrun[0] = false;\r
8001   }\r
8002   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
8003     status |= RTAUDIO_INPUT_OVERFLOW;\r
8004     apiInfo->xrun[1] = false;\r
8005   }\r
8006   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
8007                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
8008 \r
8009   if ( doStopStream == 2 ) {\r
8010     abortStream();\r
8011     return;\r
8012   }\r
8013 \r
8014   MUTEX_LOCK( &stream_.mutex );\r
8015 \r
8016   // The state might change while waiting on a mutex.\r
8017   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
8018 \r
8019   int result;\r
8020   char *buffer;\r
8021   int channels;\r
8022   snd_pcm_t **handle;\r
8023   snd_pcm_sframes_t frames;\r
8024   RtAudioFormat format;\r
8025   handle = (snd_pcm_t **) apiInfo->handles;\r
8026 \r
8027   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
8028 \r
8029     // Setup parameters.\r
8030     if ( stream_.doConvertBuffer[1] ) {\r
8031       buffer = stream_.deviceBuffer;\r
8032       channels = stream_.nDeviceChannels[1];\r
8033       format = stream_.deviceFormat[1];\r
8034     }\r
8035     else {\r
8036       buffer = stream_.userBuffer[1];\r
8037       channels = stream_.nUserChannels[1];\r
8038       format = stream_.userFormat;\r
8039     }\r
8040 \r
8041     // Read samples from device in interleaved/non-interleaved format.\r
8042     if ( stream_.deviceInterleaved[1] )\r
8043       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
8044     else {\r
8045       void *bufs[channels];\r
8046       size_t offset = stream_.bufferSize * formatBytes( format );\r
8047       for ( int i=0; i<channels; i++ )\r
8048         bufs[i] = (void *) (buffer + (i * offset));\r
8049       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
8050     }\r
8051 \r
8052     if ( result < (int) stream_.bufferSize ) {\r
8053       // Either an error or overrun occured.\r
8054       if ( result == -EPIPE ) {\r
8055         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
8056         if ( state == SND_PCM_STATE_XRUN ) {\r
8057           apiInfo->xrun[1] = true;\r
8058           result = snd_pcm_prepare( handle[1] );\r
8059           if ( result < 0 ) {\r
8060             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
8061             errorText_ = errorStream_.str();\r
8062           }\r
8063         }\r
8064         else {\r
8065           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
8066           errorText_ = errorStream_.str();\r
8067         }\r
8068       }\r
8069       else {\r
8070         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
8071         errorText_ = errorStream_.str();\r
8072       }\r
8073       error( RtAudioError::WARNING );\r
8074       goto tryOutput;\r
8075     }\r
8076 \r
8077     // Do byte swapping if necessary.\r
8078     if ( stream_.doByteSwap[1] )\r
8079       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
8080 \r
8081     // Do buffer conversion if necessary.\r
8082     if ( stream_.doConvertBuffer[1] )\r
8083       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
8084 \r
8085     // Check stream latency\r
8086     result = snd_pcm_delay( handle[1], &frames );\r
8087     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
8088   }\r
8089 \r
8090  tryOutput:\r
8091 \r
8092   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8093 \r
8094     // Setup parameters and do buffer conversion if necessary.\r
8095     if ( stream_.doConvertBuffer[0] ) {\r
8096       buffer = stream_.deviceBuffer;\r
8097       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
8098       channels = stream_.nDeviceChannels[0];\r
8099       format = stream_.deviceFormat[0];\r
8100     }\r
8101     else {\r
8102       buffer = stream_.userBuffer[0];\r
8103       channels = stream_.nUserChannels[0];\r
8104       format = stream_.userFormat;\r
8105     }\r
8106 \r
8107     // Do byte swapping if necessary.\r
8108     if ( stream_.doByteSwap[0] )\r
8109       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
8110 \r
8111     // Write samples to device in interleaved/non-interleaved format.\r
8112     if ( stream_.deviceInterleaved[0] )\r
8113       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
8114     else {\r
8115       void *bufs[channels];\r
8116       size_t offset = stream_.bufferSize * formatBytes( format );\r
8117       for ( int i=0; i<channels; i++ )\r
8118         bufs[i] = (void *) (buffer + (i * offset));\r
8119       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
8120     }\r
8121 \r
8122     if ( result < (int) stream_.bufferSize ) {\r
8123       // Either an error or underrun occured.\r
8124       if ( result == -EPIPE ) {\r
8125         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
8126         if ( state == SND_PCM_STATE_XRUN ) {\r
8127           apiInfo->xrun[0] = true;\r
8128           result = snd_pcm_prepare( handle[0] );\r
8129           if ( result < 0 ) {\r
8130             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
8131             errorText_ = errorStream_.str();\r
8132           }\r
8133           else\r
8134             errorText_ =  "RtApiAlsa::callbackEvent: audio write error, underrun.";\r
8135         }\r
8136         else {\r
8137           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
8138           errorText_ = errorStream_.str();\r
8139         }\r
8140       }\r
8141       else {\r
8142         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
8143         errorText_ = errorStream_.str();\r
8144       }\r
8145       error( RtAudioError::WARNING );\r
8146       goto unlock;\r
8147     }\r
8148 \r
8149     // Check stream latency\r
8150     result = snd_pcm_delay( handle[0], &frames );\r
8151     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
8152   }\r
8153 \r
8154  unlock:\r
8155   MUTEX_UNLOCK( &stream_.mutex );\r
8156 \r
8157   RtApi::tickStreamTime();\r
8158   if ( doStopStream == 1 ) this->stopStream();\r
8159 }\r
8160 \r
8161 static void *alsaCallbackHandler( void *ptr )\r
8162 {\r
8163   CallbackInfo *info = (CallbackInfo *) ptr;\r
8164   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
8165   bool *isRunning = &info->isRunning;\r
8166 \r
8167 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
8168   if ( info->doRealtime ) {\r
8169     pthread_t tID = pthread_self();      // ID of this thread\r
8170     sched_param prio = { info->priority }; // scheduling priority of thread\r
8171     pthread_setschedparam( tID, SCHED_RR, &prio );\r
8172   }\r
8173 #endif\r
8174 \r
8175   while ( *isRunning == true ) {\r
8176     pthread_testcancel();\r
8177     object->callbackEvent();\r
8178   }\r
8179 \r
8180   pthread_exit( NULL );\r
8181 }\r
8182 \r
8183 //******************** End of __LINUX_ALSA__ *********************//\r
8184 #endif\r
8185 \r
8186 #if defined(__LINUX_PULSE__)\r
8187 \r
8188 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8189 // and Tristan Matthews.\r
8190 \r
8191 #include <pulse/error.h>\r
8192 #include <pulse/simple.h>\r
8193 #include <cstdio>\r
8194 \r
8195 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8196                                                       44100, 48000, 96000, 0};\r
8197 \r
8198 struct rtaudio_pa_format_mapping_t {\r
8199   RtAudioFormat rtaudio_format;\r
8200   pa_sample_format_t pa_format;\r
8201 };\r
8202 \r
8203 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8204   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8205   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8206   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8207   {0, PA_SAMPLE_INVALID}};\r
8208 \r
8209 struct PulseAudioHandle {\r
8210   pa_simple *s_play;\r
8211   pa_simple *s_rec;\r
8212   pthread_t thread;\r
8213   pthread_cond_t runnable_cv;\r
8214   bool runnable;\r
8215   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8216 };\r
8217 \r
8218 RtApiPulse::~RtApiPulse()\r
8219 {\r
8220   if ( stream_.state != STREAM_CLOSED )\r
8221     closeStream();\r
8222 }\r
8223 \r
8224 unsigned int RtApiPulse::getDeviceCount( void )\r
8225 {\r
8226   return 1;\r
8227 }\r
8228 \r
8229 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8230 {\r
8231   RtAudio::DeviceInfo info;\r
8232   info.probed = true;\r
8233   info.name = "PulseAudio";\r
8234   info.outputChannels = 2;\r
8235   info.inputChannels = 2;\r
8236   info.duplexChannels = 2;\r
8237   info.isDefaultOutput = true;\r
8238   info.isDefaultInput = true;\r
8239 \r
8240   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8241     info.sampleRates.push_back( *sr );\r
8242 \r
8243   info.preferredSampleRate = 48000;\r
8244   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8245 \r
8246   return info;\r
8247 }\r
8248 \r
8249 static void *pulseaudio_callback( void * user )\r
8250 {\r
8251   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8252   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8253   volatile bool *isRunning = &cbi->isRunning;\r
8254 \r
8255   while ( *isRunning ) {\r
8256     pthread_testcancel();\r
8257     context->callbackEvent();\r
8258   }\r
8259 \r
8260   pthread_exit( NULL );\r
8261 }\r
8262 \r
8263 void RtApiPulse::closeStream( void )\r
8264 {\r
8265   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8266 \r
8267   stream_.callbackInfo.isRunning = false;\r
8268   if ( pah ) {\r
8269     MUTEX_LOCK( &stream_.mutex );\r
8270     if ( stream_.state == STREAM_STOPPED ) {\r
8271       pah->runnable = true;\r
8272       pthread_cond_signal( &pah->runnable_cv );\r
8273     }\r
8274     MUTEX_UNLOCK( &stream_.mutex );\r
8275 \r
8276     pthread_join( pah->thread, 0 );\r
8277     if ( pah->s_play ) {\r
8278       pa_simple_flush( pah->s_play, NULL );\r
8279       pa_simple_free( pah->s_play );\r
8280     }\r
8281     if ( pah->s_rec )\r
8282       pa_simple_free( pah->s_rec );\r
8283 \r
8284     pthread_cond_destroy( &pah->runnable_cv );\r
8285     delete pah;\r
8286     stream_.apiHandle = 0;\r
8287   }\r
8288 \r
8289   if ( stream_.userBuffer[0] ) {\r
8290     free( stream_.userBuffer[0] );\r
8291     stream_.userBuffer[0] = 0;\r
8292   }\r
8293   if ( stream_.userBuffer[1] ) {\r
8294     free( stream_.userBuffer[1] );\r
8295     stream_.userBuffer[1] = 0;\r
8296   }\r
8297 \r
8298   stream_.state = STREAM_CLOSED;\r
8299   stream_.mode = UNINITIALIZED;\r
8300 }\r
8301 \r
8302 void RtApiPulse::callbackEvent( void )\r
8303 {\r
8304   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8305 \r
8306   if ( stream_.state == STREAM_STOPPED ) {\r
8307     MUTEX_LOCK( &stream_.mutex );\r
8308     while ( !pah->runnable )\r
8309       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8310 \r
8311     if ( stream_.state != STREAM_RUNNING ) {\r
8312       MUTEX_UNLOCK( &stream_.mutex );\r
8313       return;\r
8314     }\r
8315     MUTEX_UNLOCK( &stream_.mutex );\r
8316   }\r
8317 \r
8318   if ( stream_.state == STREAM_CLOSED ) {\r
8319     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8320       "this shouldn't happen!";\r
8321     error( RtAudioError::WARNING );\r
8322     return;\r
8323   }\r
8324 \r
8325   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8326   double streamTime = getStreamTime();\r
8327   RtAudioStreamStatus status = 0;\r
8328   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8329                                stream_.bufferSize, streamTime, status,\r
8330                                stream_.callbackInfo.userData );\r
8331 \r
8332   if ( doStopStream == 2 ) {\r
8333     abortStream();\r
8334     return;\r
8335   }\r
8336 \r
8337   MUTEX_LOCK( &stream_.mutex );\r
8338   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8339   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8340 \r
8341   if ( stream_.state != STREAM_RUNNING )\r
8342     goto unlock;\r
8343 \r
8344   int pa_error;\r
8345   size_t bytes;\r
8346   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8347     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8348         convertBuffer( stream_.deviceBuffer,\r
8349                        stream_.userBuffer[OUTPUT],\r
8350                        stream_.convertInfo[OUTPUT] );\r
8351         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8352                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8353     } else\r
8354         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8355                 formatBytes( stream_.userFormat );\r
8356 \r
8357     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8358       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8359         pa_strerror( pa_error ) << ".";\r
8360       errorText_ = errorStream_.str();\r
8361       error( RtAudioError::WARNING );\r
8362     }\r
8363   }\r
8364 \r
8365   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8366     if ( stream_.doConvertBuffer[INPUT] )\r
8367       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8368         formatBytes( stream_.deviceFormat[INPUT] );\r
8369     else\r
8370       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8371         formatBytes( stream_.userFormat );\r
8372             \r
8373     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8374       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8375         pa_strerror( pa_error ) << ".";\r
8376       errorText_ = errorStream_.str();\r
8377       error( RtAudioError::WARNING );\r
8378     }\r
8379     if ( stream_.doConvertBuffer[INPUT] ) {\r
8380       convertBuffer( stream_.userBuffer[INPUT],\r
8381                      stream_.deviceBuffer,\r
8382                      stream_.convertInfo[INPUT] );\r
8383     }\r
8384   }\r
8385 \r
8386  unlock:\r
8387   MUTEX_UNLOCK( &stream_.mutex );\r
8388   RtApi::tickStreamTime();\r
8389 \r
8390   if ( doStopStream == 1 )\r
8391     stopStream();\r
8392 }\r
8393 \r
8394 void RtApiPulse::startStream( void )\r
8395 {\r
8396   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8397 \r
8398   if ( stream_.state == STREAM_CLOSED ) {\r
8399     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8400     error( RtAudioError::INVALID_USE );\r
8401     return;\r
8402   }\r
8403   if ( stream_.state == STREAM_RUNNING ) {\r
8404     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8405     error( RtAudioError::WARNING );\r
8406     return;\r
8407   }\r
8408 \r
8409   MUTEX_LOCK( &stream_.mutex );\r
8410 \r
8411   stream_.state = STREAM_RUNNING;\r
8412 \r
8413   pah->runnable = true;\r
8414   pthread_cond_signal( &pah->runnable_cv );\r
8415   MUTEX_UNLOCK( &stream_.mutex );\r
8416 }\r
8417 \r
8418 void RtApiPulse::stopStream( void )\r
8419 {\r
8420   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8421 \r
8422   if ( stream_.state == STREAM_CLOSED ) {\r
8423     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8424     error( RtAudioError::INVALID_USE );\r
8425     return;\r
8426   }\r
8427   if ( stream_.state == STREAM_STOPPED ) {\r
8428     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8429     error( RtAudioError::WARNING );\r
8430     return;\r
8431   }\r
8432 \r
8433   stream_.state = STREAM_STOPPED;\r
8434   MUTEX_LOCK( &stream_.mutex );\r
8435 \r
8436   if ( pah && pah->s_play ) {\r
8437     int pa_error;\r
8438     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8439       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8440         pa_strerror( pa_error ) << ".";\r
8441       errorText_ = errorStream_.str();\r
8442       MUTEX_UNLOCK( &stream_.mutex );\r
8443       error( RtAudioError::SYSTEM_ERROR );\r
8444       return;\r
8445     }\r
8446   }\r
8447 \r
8448   stream_.state = STREAM_STOPPED;\r
8449   MUTEX_UNLOCK( &stream_.mutex );\r
8450 }\r
8451 \r
8452 void RtApiPulse::abortStream( void )\r
8453 {\r
8454   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8455 \r
8456   if ( stream_.state == STREAM_CLOSED ) {\r
8457     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8458     error( RtAudioError::INVALID_USE );\r
8459     return;\r
8460   }\r
8461   if ( stream_.state == STREAM_STOPPED ) {\r
8462     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8463     error( RtAudioError::WARNING );\r
8464     return;\r
8465   }\r
8466 \r
8467   stream_.state = STREAM_STOPPED;\r
8468   MUTEX_LOCK( &stream_.mutex );\r
8469 \r
8470   if ( pah && pah->s_play ) {\r
8471     int pa_error;\r
8472     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8473       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8474         pa_strerror( pa_error ) << ".";\r
8475       errorText_ = errorStream_.str();\r
8476       MUTEX_UNLOCK( &stream_.mutex );\r
8477       error( RtAudioError::SYSTEM_ERROR );\r
8478       return;\r
8479     }\r
8480   }\r
8481 \r
8482   stream_.state = STREAM_STOPPED;\r
8483   MUTEX_UNLOCK( &stream_.mutex );\r
8484 }\r
8485 \r
8486 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8487                                   unsigned int channels, unsigned int firstChannel,\r
8488                                   unsigned int sampleRate, RtAudioFormat format,\r
8489                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8490 {\r
8491   PulseAudioHandle *pah = 0;\r
8492   unsigned long bufferBytes = 0;\r
8493   pa_sample_spec ss;\r
8494 \r
8495   if ( device != 0 ) return false;\r
8496   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8497   if ( channels != 1 && channels != 2 ) {\r
8498     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
8499     return false;\r
8500   }\r
8501   ss.channels = channels;\r
8502 \r
8503   if ( firstChannel != 0 ) return false;\r
8504 \r
8505   bool sr_found = false;\r
8506   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8507     if ( sampleRate == *sr ) {\r
8508       sr_found = true;\r
8509       stream_.sampleRate = sampleRate;\r
8510       ss.rate = sampleRate;\r
8511       break;\r
8512     }\r
8513   }\r
8514   if ( !sr_found ) {\r
8515     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8516     return false;\r
8517   }\r
8518 \r
8519   bool sf_found = 0;\r
8520   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8521         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8522     if ( format == sf->rtaudio_format ) {\r
8523       sf_found = true;\r
8524       stream_.userFormat = sf->rtaudio_format;\r
8525       stream_.deviceFormat[mode] = stream_.userFormat;\r
8526       ss.format = sf->pa_format;\r
8527       break;\r
8528     }\r
8529   }\r
8530   if ( !sf_found ) { // Use internal data format conversion.\r
8531     stream_.userFormat = format;\r
8532     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8533     ss.format = PA_SAMPLE_FLOAT32LE;\r
8534   }\r
8535 \r
8536   // Set other stream parameters.\r
8537   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8538   else stream_.userInterleaved = true;\r
8539   stream_.deviceInterleaved[mode] = true;\r
8540   stream_.nBuffers = 1;\r
8541   stream_.doByteSwap[mode] = false;\r
8542   stream_.nUserChannels[mode] = channels;\r
8543   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8544   stream_.channelOffset[mode] = 0;\r
8545   std::string streamName = "RtAudio";\r
8546 \r
8547   // Set flags for buffer conversion.\r
8548   stream_.doConvertBuffer[mode] = false;\r
8549   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8550     stream_.doConvertBuffer[mode] = true;\r
8551   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8552     stream_.doConvertBuffer[mode] = true;\r
8553 \r
8554   // Allocate necessary internal buffers.\r
8555   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8556   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8557   if ( stream_.userBuffer[mode] == NULL ) {\r
8558     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8559     goto error;\r
8560   }\r
8561   stream_.bufferSize = *bufferSize;\r
8562 \r
8563   if ( stream_.doConvertBuffer[mode] ) {\r
8564 \r
8565     bool makeBuffer = true;\r
8566     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8567     if ( mode == INPUT ) {\r
8568       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8569         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8570         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8571       }\r
8572     }\r
8573 \r
8574     if ( makeBuffer ) {\r
8575       bufferBytes *= *bufferSize;\r
8576       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8577       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8578       if ( stream_.deviceBuffer == NULL ) {\r
8579         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8580         goto error;\r
8581       }\r
8582     }\r
8583   }\r
8584 \r
8585   stream_.device[mode] = device;\r
8586 \r
8587   // Setup the buffer conversion information structure.\r
8588   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8589 \r
8590   if ( !stream_.apiHandle ) {\r
8591     PulseAudioHandle *pah = new PulseAudioHandle;\r
8592     if ( !pah ) {\r
8593       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8594       goto error;\r
8595     }\r
8596 \r
8597     stream_.apiHandle = pah;\r
8598     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8599       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8600       goto error;\r
8601     }\r
8602   }\r
8603   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8604 \r
8605   int error;\r
8606   if ( options && !options->streamName.empty() ) streamName = options->streamName;\r
8607   switch ( mode ) {\r
8608   case INPUT:\r
8609     pa_buffer_attr buffer_attr;\r
8610     buffer_attr.fragsize = bufferBytes;\r
8611     buffer_attr.maxlength = -1;\r
8612 \r
8613     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8614     if ( !pah->s_rec ) {\r
8615       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8616       goto error;\r
8617     }\r
8618     break;\r
8619   case OUTPUT:\r
8620     pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
8621     if ( !pah->s_play ) {\r
8622       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8623       goto error;\r
8624     }\r
8625     break;\r
8626   default:\r
8627     goto error;\r
8628   }\r
8629 \r
8630   if ( stream_.mode == UNINITIALIZED )\r
8631     stream_.mode = mode;\r
8632   else if ( stream_.mode == mode )\r
8633     goto error;\r
8634   else\r
8635     stream_.mode = DUPLEX;\r
8636 \r
8637   if ( !stream_.callbackInfo.isRunning ) {\r
8638     stream_.callbackInfo.object = this;\r
8639     stream_.callbackInfo.isRunning = true;\r
8640     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8641       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8642       goto error;\r
8643     }\r
8644   }\r
8645 \r
8646   stream_.state = STREAM_STOPPED;\r
8647   return true;\r
8648  \r
8649  error:\r
8650   if ( pah && stream_.callbackInfo.isRunning ) {\r
8651     pthread_cond_destroy( &pah->runnable_cv );\r
8652     delete pah;\r
8653     stream_.apiHandle = 0;\r
8654   }\r
8655 \r
8656   for ( int i=0; i<2; i++ ) {\r
8657     if ( stream_.userBuffer[i] ) {\r
8658       free( stream_.userBuffer[i] );\r
8659       stream_.userBuffer[i] = 0;\r
8660     }\r
8661   }\r
8662 \r
8663   if ( stream_.deviceBuffer ) {\r
8664     free( stream_.deviceBuffer );\r
8665     stream_.deviceBuffer = 0;\r
8666   }\r
8667 \r
8668   return FAILURE;\r
8669 }\r
8670 \r
8671 //******************** End of __LINUX_PULSE__ *********************//\r
8672 #endif\r
8673 \r
8674 #if defined(__LINUX_OSS__)\r
8675 \r
8676 #include <unistd.h>\r
8677 #include <sys/ioctl.h>\r
8678 #include <unistd.h>\r
8679 #include <fcntl.h>\r
8680 #include <sys/soundcard.h>\r
8681 #include <errno.h>\r
8682 #include <math.h>\r
8683 \r
8684 static void *ossCallbackHandler(void * ptr);\r
8685 \r
8686 // A structure to hold various information related to the OSS API\r
8687 // implementation.\r
8688 struct OssHandle {\r
8689   int id[2];    // device ids\r
8690   bool xrun[2];\r
8691   bool triggered;\r
8692   pthread_cond_t runnable;\r
8693 \r
8694   OssHandle()\r
8695     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8696 };\r
8697 \r
8698 RtApiOss :: RtApiOss()\r
8699 {\r
8700   // Nothing to do here.\r
8701 }\r
8702 \r
8703 RtApiOss :: ~RtApiOss()\r
8704 {\r
8705   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8706 }\r
8707 \r
8708 unsigned int RtApiOss :: getDeviceCount( void )\r
8709 {\r
8710   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8711   if ( mixerfd == -1 ) {\r
8712     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8713     error( RtAudioError::WARNING );\r
8714     return 0;\r
8715   }\r
8716 \r
8717   oss_sysinfo sysinfo;\r
8718   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8719     close( mixerfd );\r
8720     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8721     error( RtAudioError::WARNING );\r
8722     return 0;\r
8723   }\r
8724 \r
8725   close( mixerfd );\r
8726   return sysinfo.numaudios;\r
8727 }\r
8728 \r
8729 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8730 {\r
8731   RtAudio::DeviceInfo info;\r
8732   info.probed = false;\r
8733 \r
8734   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8735   if ( mixerfd == -1 ) {\r
8736     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8737     error( RtAudioError::WARNING );\r
8738     return info;\r
8739   }\r
8740 \r
8741   oss_sysinfo sysinfo;\r
8742   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8743   if ( result == -1 ) {\r
8744     close( mixerfd );\r
8745     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8746     error( RtAudioError::WARNING );\r
8747     return info;\r
8748   }\r
8749 \r
8750   unsigned nDevices = sysinfo.numaudios;\r
8751   if ( nDevices == 0 ) {\r
8752     close( mixerfd );\r
8753     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8754     error( RtAudioError::INVALID_USE );\r
8755     return info;\r
8756   }\r
8757 \r
8758   if ( device >= nDevices ) {\r
8759     close( mixerfd );\r
8760     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8761     error( RtAudioError::INVALID_USE );\r
8762     return info;\r
8763   }\r
8764 \r
8765   oss_audioinfo ainfo;\r
8766   ainfo.dev = device;\r
8767   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8768   close( mixerfd );\r
8769   if ( result == -1 ) {\r
8770     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8771     errorText_ = errorStream_.str();\r
8772     error( RtAudioError::WARNING );\r
8773     return info;\r
8774   }\r
8775 \r
8776   // Probe channels\r
8777   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8778   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8779   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8780     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8781       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8782   }\r
8783 \r
8784   // Probe data formats ... do for input\r
8785   unsigned long mask = ainfo.iformats;\r
8786   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8787     info.nativeFormats |= RTAUDIO_SINT16;\r
8788   if ( mask & AFMT_S8 )\r
8789     info.nativeFormats |= RTAUDIO_SINT8;\r
8790   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8791     info.nativeFormats |= RTAUDIO_SINT32;\r
8792   if ( mask & AFMT_FLOAT )\r
8793     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8794   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8795     info.nativeFormats |= RTAUDIO_SINT24;\r
8796 \r
8797   // Check that we have at least one supported format\r
8798   if ( info.nativeFormats == 0 ) {\r
8799     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8800     errorText_ = errorStream_.str();\r
8801     error( RtAudioError::WARNING );\r
8802     return info;\r
8803   }\r
8804 \r
8805   // Probe the supported sample rates.\r
8806   info.sampleRates.clear();\r
8807   if ( ainfo.nrates ) {\r
8808     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8809       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8810         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8811           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8812 \r
8813           if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
8814             info.preferredSampleRate = SAMPLE_RATES[k];\r
8815 \r
8816           break;\r
8817         }\r
8818       }\r
8819     }\r
8820   }\r
8821   else {\r
8822     // Check min and max rate values;\r
8823     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8824       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {\r
8825         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8826 \r
8827         if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
8828           info.preferredSampleRate = SAMPLE_RATES[k];\r
8829       }\r
8830     }\r
8831   }\r
8832 \r
8833   if ( info.sampleRates.size() == 0 ) {\r
8834     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8835     errorText_ = errorStream_.str();\r
8836     error( RtAudioError::WARNING );\r
8837   }\r
8838   else {\r
8839     info.probed = true;\r
8840     info.name = ainfo.name;\r
8841   }\r
8842 \r
8843   return info;\r
8844 }\r
8845 \r
8846 \r
8847 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8848                                   unsigned int firstChannel, unsigned int sampleRate,\r
8849                                   RtAudioFormat format, unsigned int *bufferSize,\r
8850                                   RtAudio::StreamOptions *options )\r
8851 {\r
8852   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8853   if ( mixerfd == -1 ) {\r
8854     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8855     return FAILURE;\r
8856   }\r
8857 \r
8858   oss_sysinfo sysinfo;\r
8859   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8860   if ( result == -1 ) {\r
8861     close( mixerfd );\r
8862     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8863     return FAILURE;\r
8864   }\r
8865 \r
8866   unsigned nDevices = sysinfo.numaudios;\r
8867   if ( nDevices == 0 ) {\r
8868     // This should not happen because a check is made before this function is called.\r
8869     close( mixerfd );\r
8870     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8871     return FAILURE;\r
8872   }\r
8873 \r
8874   if ( device >= nDevices ) {\r
8875     // This should not happen because a check is made before this function is called.\r
8876     close( mixerfd );\r
8877     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8878     return FAILURE;\r
8879   }\r
8880 \r
8881   oss_audioinfo ainfo;\r
8882   ainfo.dev = device;\r
8883   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8884   close( mixerfd );\r
8885   if ( result == -1 ) {\r
8886     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8887     errorText_ = errorStream_.str();\r
8888     return FAILURE;\r
8889   }\r
8890 \r
8891   // Check if device supports input or output\r
8892   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8893        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8894     if ( mode == OUTPUT )\r
8895       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8896     else\r
8897       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8898     errorText_ = errorStream_.str();\r
8899     return FAILURE;\r
8900   }\r
8901 \r
8902   int flags = 0;\r
8903   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8904   if ( mode == OUTPUT )\r
8905     flags |= O_WRONLY;\r
8906   else { // mode == INPUT\r
8907     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8908       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8909       close( handle->id[0] );\r
8910       handle->id[0] = 0;\r
8911       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8912         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8913         errorText_ = errorStream_.str();\r
8914         return FAILURE;\r
8915       }\r
8916       // Check that the number previously set channels is the same.\r
8917       if ( stream_.nUserChannels[0] != channels ) {\r
8918         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8919         errorText_ = errorStream_.str();\r
8920         return FAILURE;\r
8921       }\r
8922       flags |= O_RDWR;\r
8923     }\r
8924     else\r
8925       flags |= O_RDONLY;\r
8926   }\r
8927 \r
8928   // Set exclusive access if specified.\r
8929   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8930 \r
8931   // Try to open the device.\r
8932   int fd;\r
8933   fd = open( ainfo.devnode, flags, 0 );\r
8934   if ( fd == -1 ) {\r
8935     if ( errno == EBUSY )\r
8936       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8937     else\r
8938       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8939     errorText_ = errorStream_.str();\r
8940     return FAILURE;\r
8941   }\r
8942 \r
8943   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8944   /*\r
8945     if ( flags | O_RDWR ) {\r
8946     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
8947     if ( result == -1) {\r
8948     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
8949     errorText_ = errorStream_.str();\r
8950     return FAILURE;\r
8951     }\r
8952     }\r
8953   */\r
8954 \r
8955   // Check the device channel support.\r
8956   stream_.nUserChannels[mode] = channels;\r
8957   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
8958     close( fd );\r
8959     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
8960     errorText_ = errorStream_.str();\r
8961     return FAILURE;\r
8962   }\r
8963 \r
8964   // Set the number of channels.\r
8965   int deviceChannels = channels + firstChannel;\r
8966   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
8967   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
8968     close( fd );\r
8969     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
8970     errorText_ = errorStream_.str();\r
8971     return FAILURE;\r
8972   }\r
8973   stream_.nDeviceChannels[mode] = deviceChannels;\r
8974 \r
8975   // Get the data format mask\r
8976   int mask;\r
8977   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
8978   if ( result == -1 ) {\r
8979     close( fd );\r
8980     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
8981     errorText_ = errorStream_.str();\r
8982     return FAILURE;\r
8983   }\r
8984 \r
8985   // Determine how to set the device format.\r
8986   stream_.userFormat = format;\r
8987   int deviceFormat = -1;\r
8988   stream_.doByteSwap[mode] = false;\r
8989   if ( format == RTAUDIO_SINT8 ) {\r
8990     if ( mask & AFMT_S8 ) {\r
8991       deviceFormat = AFMT_S8;\r
8992       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8993     }\r
8994   }\r
8995   else if ( format == RTAUDIO_SINT16 ) {\r
8996     if ( mask & AFMT_S16_NE ) {\r
8997       deviceFormat = AFMT_S16_NE;\r
8998       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8999     }\r
9000     else if ( mask & AFMT_S16_OE ) {\r
9001       deviceFormat = AFMT_S16_OE;\r
9002       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9003       stream_.doByteSwap[mode] = true;\r
9004     }\r
9005   }\r
9006   else if ( format == RTAUDIO_SINT24 ) {\r
9007     if ( mask & AFMT_S24_NE ) {\r
9008       deviceFormat = AFMT_S24_NE;\r
9009       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9010     }\r
9011     else if ( mask & AFMT_S24_OE ) {\r
9012       deviceFormat = AFMT_S24_OE;\r
9013       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9014       stream_.doByteSwap[mode] = true;\r
9015     }\r
9016   }\r
9017   else if ( format == RTAUDIO_SINT32 ) {\r
9018     if ( mask & AFMT_S32_NE ) {\r
9019       deviceFormat = AFMT_S32_NE;\r
9020       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9021     }\r
9022     else if ( mask & AFMT_S32_OE ) {\r
9023       deviceFormat = AFMT_S32_OE;\r
9024       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9025       stream_.doByteSwap[mode] = true;\r
9026     }\r
9027   }\r
9028 \r
9029   if ( deviceFormat == -1 ) {\r
9030     // The user requested format is not natively supported by the device.\r
9031     if ( mask & AFMT_S16_NE ) {\r
9032       deviceFormat = AFMT_S16_NE;\r
9033       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9034     }\r
9035     else if ( mask & AFMT_S32_NE ) {\r
9036       deviceFormat = AFMT_S32_NE;\r
9037       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9038     }\r
9039     else if ( mask & AFMT_S24_NE ) {\r
9040       deviceFormat = AFMT_S24_NE;\r
9041       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9042     }\r
9043     else if ( mask & AFMT_S16_OE ) {\r
9044       deviceFormat = AFMT_S16_OE;\r
9045       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9046       stream_.doByteSwap[mode] = true;\r
9047     }\r
9048     else if ( mask & AFMT_S32_OE ) {\r
9049       deviceFormat = AFMT_S32_OE;\r
9050       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9051       stream_.doByteSwap[mode] = true;\r
9052     }\r
9053     else if ( mask & AFMT_S24_OE ) {\r
9054       deviceFormat = AFMT_S24_OE;\r
9055       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9056       stream_.doByteSwap[mode] = true;\r
9057     }\r
9058     else if ( mask & AFMT_S8) {\r
9059       deviceFormat = AFMT_S8;\r
9060       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
9061     }\r
9062   }\r
9063 \r
9064   if ( stream_.deviceFormat[mode] == 0 ) {\r
9065     // This really shouldn't happen ...\r
9066     close( fd );\r
9067     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
9068     errorText_ = errorStream_.str();\r
9069     return FAILURE;\r
9070   }\r
9071 \r
9072   // Set the data format.\r
9073   int temp = deviceFormat;\r
9074   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
9075   if ( result == -1 || deviceFormat != temp ) {\r
9076     close( fd );\r
9077     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
9078     errorText_ = errorStream_.str();\r
9079     return FAILURE;\r
9080   }\r
9081 \r
9082   // Attempt to set the buffer size.  According to OSS, the minimum\r
9083   // number of buffers is two.  The supposed minimum buffer size is 16\r
9084   // bytes, so that will be our lower bound.  The argument to this\r
9085   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
9086   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
9087   // We'll check the actual value used near the end of the setup\r
9088   // procedure.\r
9089   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
9090   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
9091   int buffers = 0;\r
9092   if ( options ) buffers = options->numberOfBuffers;\r
9093   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
9094   if ( buffers < 2 ) buffers = 3;\r
9095   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
9096   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
9097   if ( result == -1 ) {\r
9098     close( fd );\r
9099     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
9100     errorText_ = errorStream_.str();\r
9101     return FAILURE;\r
9102   }\r
9103   stream_.nBuffers = buffers;\r
9104 \r
9105   // Save buffer size (in sample frames).\r
9106   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
9107   stream_.bufferSize = *bufferSize;\r
9108 \r
9109   // Set the sample rate.\r
9110   int srate = sampleRate;\r
9111   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
9112   if ( result == -1 ) {\r
9113     close( fd );\r
9114     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
9115     errorText_ = errorStream_.str();\r
9116     return FAILURE;\r
9117   }\r
9118 \r
9119   // Verify the sample rate setup worked.\r
9120   if ( abs( srate - sampleRate ) > 100 ) {\r
9121     close( fd );\r
9122     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
9123     errorText_ = errorStream_.str();\r
9124     return FAILURE;\r
9125   }\r
9126   stream_.sampleRate = sampleRate;\r
9127 \r
9128   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
9129     // We're doing duplex setup here.\r
9130     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
9131     stream_.nDeviceChannels[0] = deviceChannels;\r
9132   }\r
9133 \r
9134   // Set interleaving parameters.\r
9135   stream_.userInterleaved = true;\r
9136   stream_.deviceInterleaved[mode] =  true;\r
9137   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
9138     stream_.userInterleaved = false;\r
9139 \r
9140   // Set flags for buffer conversion\r
9141   stream_.doConvertBuffer[mode] = false;\r
9142   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
9143     stream_.doConvertBuffer[mode] = true;\r
9144   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
9145     stream_.doConvertBuffer[mode] = true;\r
9146   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
9147        stream_.nUserChannels[mode] > 1 )\r
9148     stream_.doConvertBuffer[mode] = true;\r
9149 \r
9150   // Allocate the stream handles if necessary and then save.\r
9151   if ( stream_.apiHandle == 0 ) {\r
9152     try {\r
9153       handle = new OssHandle;\r
9154     }\r
9155     catch ( std::bad_alloc& ) {\r
9156       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
9157       goto error;\r
9158     }\r
9159 \r
9160     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
9161       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
9162       goto error;\r
9163     }\r
9164 \r
9165     stream_.apiHandle = (void *) handle;\r
9166   }\r
9167   else {\r
9168     handle = (OssHandle *) stream_.apiHandle;\r
9169   }\r
9170   handle->id[mode] = fd;\r
9171 \r
9172   // Allocate necessary internal buffers.\r
9173   unsigned long bufferBytes;\r
9174   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
9175   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
9176   if ( stream_.userBuffer[mode] == NULL ) {\r
9177     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
9178     goto error;\r
9179   }\r
9180 \r
9181   if ( stream_.doConvertBuffer[mode] ) {\r
9182 \r
9183     bool makeBuffer = true;\r
9184     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
9185     if ( mode == INPUT ) {\r
9186       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
9187         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
9188         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
9189       }\r
9190     }\r
9191 \r
9192     if ( makeBuffer ) {\r
9193       bufferBytes *= *bufferSize;\r
9194       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9195       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9196       if ( stream_.deviceBuffer == NULL ) {\r
9197         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9198         goto error;\r
9199       }\r
9200     }\r
9201   }\r
9202 \r
9203   stream_.device[mode] = device;\r
9204   stream_.state = STREAM_STOPPED;\r
9205 \r
9206   // Setup the buffer conversion information structure.\r
9207   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9208 \r
9209   // Setup thread if necessary.\r
9210   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9211     // We had already set up an output stream.\r
9212     stream_.mode = DUPLEX;\r
9213     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9214   }\r
9215   else {\r
9216     stream_.mode = mode;\r
9217 \r
9218     // Setup callback thread.\r
9219     stream_.callbackInfo.object = (void *) this;\r
9220 \r
9221     // Set the thread attributes for joinable and realtime scheduling\r
9222     // priority.  The higher priority will only take affect if the\r
9223     // program is run as root or suid.\r
9224     pthread_attr_t attr;\r
9225     pthread_attr_init( &attr );\r
9226     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9227 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9228     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9229       struct sched_param param;\r
9230       int priority = options->priority;\r
9231       int min = sched_get_priority_min( SCHED_RR );\r
9232       int max = sched_get_priority_max( SCHED_RR );\r
9233       if ( priority < min ) priority = min;\r
9234       else if ( priority > max ) priority = max;\r
9235       param.sched_priority = priority;\r
9236       pthread_attr_setschedparam( &attr, &param );\r
9237       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9238     }\r
9239     else\r
9240       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9241 #else\r
9242     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9243 #endif\r
9244 \r
9245     stream_.callbackInfo.isRunning = true;\r
9246     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9247     pthread_attr_destroy( &attr );\r
9248     if ( result ) {\r
9249       stream_.callbackInfo.isRunning = false;\r
9250       errorText_ = "RtApiOss::error creating callback thread!";\r
9251       goto error;\r
9252     }\r
9253   }\r
9254 \r
9255   return SUCCESS;\r
9256 \r
9257  error:\r
9258   if ( handle ) {\r
9259     pthread_cond_destroy( &handle->runnable );\r
9260     if ( handle->id[0] ) close( handle->id[0] );\r
9261     if ( handle->id[1] ) close( handle->id[1] );\r
9262     delete handle;\r
9263     stream_.apiHandle = 0;\r
9264   }\r
9265 \r
9266   for ( int i=0; i<2; i++ ) {\r
9267     if ( stream_.userBuffer[i] ) {\r
9268       free( stream_.userBuffer[i] );\r
9269       stream_.userBuffer[i] = 0;\r
9270     }\r
9271   }\r
9272 \r
9273   if ( stream_.deviceBuffer ) {\r
9274     free( stream_.deviceBuffer );\r
9275     stream_.deviceBuffer = 0;\r
9276   }\r
9277 \r
9278   return FAILURE;\r
9279 }\r
9280 \r
9281 void RtApiOss :: closeStream()\r
9282 {\r
9283   if ( stream_.state == STREAM_CLOSED ) {\r
9284     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9285     error( RtAudioError::WARNING );\r
9286     return;\r
9287   }\r
9288 \r
9289   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9290   stream_.callbackInfo.isRunning = false;\r
9291   MUTEX_LOCK( &stream_.mutex );\r
9292   if ( stream_.state == STREAM_STOPPED )\r
9293     pthread_cond_signal( &handle->runnable );\r
9294   MUTEX_UNLOCK( &stream_.mutex );\r
9295   pthread_join( stream_.callbackInfo.thread, NULL );\r
9296 \r
9297   if ( stream_.state == STREAM_RUNNING ) {\r
9298     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9299       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9300     else\r
9301       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9302     stream_.state = STREAM_STOPPED;\r
9303   }\r
9304 \r
9305   if ( handle ) {\r
9306     pthread_cond_destroy( &handle->runnable );\r
9307     if ( handle->id[0] ) close( handle->id[0] );\r
9308     if ( handle->id[1] ) close( handle->id[1] );\r
9309     delete handle;\r
9310     stream_.apiHandle = 0;\r
9311   }\r
9312 \r
9313   for ( int i=0; i<2; i++ ) {\r
9314     if ( stream_.userBuffer[i] ) {\r
9315       free( stream_.userBuffer[i] );\r
9316       stream_.userBuffer[i] = 0;\r
9317     }\r
9318   }\r
9319 \r
9320   if ( stream_.deviceBuffer ) {\r
9321     free( stream_.deviceBuffer );\r
9322     stream_.deviceBuffer = 0;\r
9323   }\r
9324 \r
9325   stream_.mode = UNINITIALIZED;\r
9326   stream_.state = STREAM_CLOSED;\r
9327 }\r
9328 \r
9329 void RtApiOss :: startStream()\r
9330 {\r
9331   verifyStream();\r
9332   if ( stream_.state == STREAM_RUNNING ) {\r
9333     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9334     error( RtAudioError::WARNING );\r
9335     return;\r
9336   }\r
9337 \r
9338   MUTEX_LOCK( &stream_.mutex );\r
9339 \r
9340   stream_.state = STREAM_RUNNING;\r
9341 \r
9342   // No need to do anything else here ... OSS automatically starts\r
9343   // when fed samples.\r
9344 \r
9345   MUTEX_UNLOCK( &stream_.mutex );\r
9346 \r
9347   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9348   pthread_cond_signal( &handle->runnable );\r
9349 }\r
9350 \r
9351 void RtApiOss :: stopStream()\r
9352 {\r
9353   verifyStream();\r
9354   if ( stream_.state == STREAM_STOPPED ) {\r
9355     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9356     error( RtAudioError::WARNING );\r
9357     return;\r
9358   }\r
9359 \r
9360   MUTEX_LOCK( &stream_.mutex );\r
9361 \r
9362   // The state might change while waiting on a mutex.\r
9363   if ( stream_.state == STREAM_STOPPED ) {\r
9364     MUTEX_UNLOCK( &stream_.mutex );\r
9365     return;\r
9366   }\r
9367 \r
9368   int result = 0;\r
9369   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9370   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9371 \r
9372     // Flush the output with zeros a few times.\r
9373     char *buffer;\r
9374     int samples;\r
9375     RtAudioFormat format;\r
9376 \r
9377     if ( stream_.doConvertBuffer[0] ) {\r
9378       buffer = stream_.deviceBuffer;\r
9379       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9380       format = stream_.deviceFormat[0];\r
9381     }\r
9382     else {\r
9383       buffer = stream_.userBuffer[0];\r
9384       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9385       format = stream_.userFormat;\r
9386     }\r
9387 \r
9388     memset( buffer, 0, samples * formatBytes(format) );\r
9389     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9390       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9391       if ( result == -1 ) {\r
9392         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9393         error( RtAudioError::WARNING );\r
9394       }\r
9395     }\r
9396 \r
9397     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9398     if ( result == -1 ) {\r
9399       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9400       errorText_ = errorStream_.str();\r
9401       goto unlock;\r
9402     }\r
9403     handle->triggered = false;\r
9404   }\r
9405 \r
9406   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9407     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9408     if ( result == -1 ) {\r
9409       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9410       errorText_ = errorStream_.str();\r
9411       goto unlock;\r
9412     }\r
9413   }\r
9414 \r
9415  unlock:\r
9416   stream_.state = STREAM_STOPPED;\r
9417   MUTEX_UNLOCK( &stream_.mutex );\r
9418 \r
9419   if ( result != -1 ) return;\r
9420   error( RtAudioError::SYSTEM_ERROR );\r
9421 }\r
9422 \r
9423 void RtApiOss :: abortStream()\r
9424 {\r
9425   verifyStream();\r
9426   if ( stream_.state == STREAM_STOPPED ) {\r
9427     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9428     error( RtAudioError::WARNING );\r
9429     return;\r
9430   }\r
9431 \r
9432   MUTEX_LOCK( &stream_.mutex );\r
9433 \r
9434   // The state might change while waiting on a mutex.\r
9435   if ( stream_.state == STREAM_STOPPED ) {\r
9436     MUTEX_UNLOCK( &stream_.mutex );\r
9437     return;\r
9438   }\r
9439 \r
9440   int result = 0;\r
9441   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9442   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9443     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9444     if ( result == -1 ) {\r
9445       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9446       errorText_ = errorStream_.str();\r
9447       goto unlock;\r
9448     }\r
9449     handle->triggered = false;\r
9450   }\r
9451 \r
9452   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9453     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9454     if ( result == -1 ) {\r
9455       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9456       errorText_ = errorStream_.str();\r
9457       goto unlock;\r
9458     }\r
9459   }\r
9460 \r
9461  unlock:\r
9462   stream_.state = STREAM_STOPPED;\r
9463   MUTEX_UNLOCK( &stream_.mutex );\r
9464 \r
9465   if ( result != -1 ) return;\r
9466   error( RtAudioError::SYSTEM_ERROR );\r
9467 }\r
9468 \r
9469 void RtApiOss :: callbackEvent()\r
9470 {\r
9471   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9472   if ( stream_.state == STREAM_STOPPED ) {\r
9473     MUTEX_LOCK( &stream_.mutex );\r
9474     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9475     if ( stream_.state != STREAM_RUNNING ) {\r
9476       MUTEX_UNLOCK( &stream_.mutex );\r
9477       return;\r
9478     }\r
9479     MUTEX_UNLOCK( &stream_.mutex );\r
9480   }\r
9481 \r
9482   if ( stream_.state == STREAM_CLOSED ) {\r
9483     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9484     error( RtAudioError::WARNING );\r
9485     return;\r
9486   }\r
9487 \r
9488   // Invoke user callback to get fresh output data.\r
9489   int doStopStream = 0;\r
9490   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9491   double streamTime = getStreamTime();\r
9492   RtAudioStreamStatus status = 0;\r
9493   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9494     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9495     handle->xrun[0] = false;\r
9496   }\r
9497   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9498     status |= RTAUDIO_INPUT_OVERFLOW;\r
9499     handle->xrun[1] = false;\r
9500   }\r
9501   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9502                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9503   if ( doStopStream == 2 ) {\r
9504     this->abortStream();\r
9505     return;\r
9506   }\r
9507 \r
9508   MUTEX_LOCK( &stream_.mutex );\r
9509 \r
9510   // The state might change while waiting on a mutex.\r
9511   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9512 \r
9513   int result;\r
9514   char *buffer;\r
9515   int samples;\r
9516   RtAudioFormat format;\r
9517 \r
9518   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9519 \r
9520     // Setup parameters and do buffer conversion if necessary.\r
9521     if ( stream_.doConvertBuffer[0] ) {\r
9522       buffer = stream_.deviceBuffer;\r
9523       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9524       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9525       format = stream_.deviceFormat[0];\r
9526     }\r
9527     else {\r
9528       buffer = stream_.userBuffer[0];\r
9529       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9530       format = stream_.userFormat;\r
9531     }\r
9532 \r
9533     // Do byte swapping if necessary.\r
9534     if ( stream_.doByteSwap[0] )\r
9535       byteSwapBuffer( buffer, samples, format );\r
9536 \r
9537     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9538       int trig = 0;\r
9539       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9540       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9541       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9542       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9543       handle->triggered = true;\r
9544     }\r
9545     else\r
9546       // Write samples to device.\r
9547       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9548 \r
9549     if ( result == -1 ) {\r
9550       // We'll assume this is an underrun, though there isn't a\r
9551       // specific means for determining that.\r
9552       handle->xrun[0] = true;\r
9553       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9554       error( RtAudioError::WARNING );\r
9555       // Continue on to input section.\r
9556     }\r
9557   }\r
9558 \r
9559   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9560 \r
9561     // Setup parameters.\r
9562     if ( stream_.doConvertBuffer[1] ) {\r
9563       buffer = stream_.deviceBuffer;\r
9564       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9565       format = stream_.deviceFormat[1];\r
9566     }\r
9567     else {\r
9568       buffer = stream_.userBuffer[1];\r
9569       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9570       format = stream_.userFormat;\r
9571     }\r
9572 \r
9573     // Read samples from device.\r
9574     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9575 \r
9576     if ( result == -1 ) {\r
9577       // We'll assume this is an overrun, though there isn't a\r
9578       // specific means for determining that.\r
9579       handle->xrun[1] = true;\r
9580       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9581       error( RtAudioError::WARNING );\r
9582       goto unlock;\r
9583     }\r
9584 \r
9585     // Do byte swapping if necessary.\r
9586     if ( stream_.doByteSwap[1] )\r
9587       byteSwapBuffer( buffer, samples, format );\r
9588 \r
9589     // Do buffer conversion if necessary.\r
9590     if ( stream_.doConvertBuffer[1] )\r
9591       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9592   }\r
9593 \r
9594  unlock:\r
9595   MUTEX_UNLOCK( &stream_.mutex );\r
9596 \r
9597   RtApi::tickStreamTime();\r
9598   if ( doStopStream == 1 ) this->stopStream();\r
9599 }\r
9600 \r
9601 static void *ossCallbackHandler( void *ptr )\r
9602 {\r
9603   CallbackInfo *info = (CallbackInfo *) ptr;\r
9604   RtApiOss *object = (RtApiOss *) info->object;\r
9605   bool *isRunning = &info->isRunning;\r
9606 \r
9607   while ( *isRunning == true ) {\r
9608     pthread_testcancel();\r
9609     object->callbackEvent();\r
9610   }\r
9611 \r
9612   pthread_exit( NULL );\r
9613 }\r
9614 \r
9615 //******************** End of __LINUX_OSS__ *********************//\r
9616 #endif\r
9617 \r
9618 \r
9619 // *************************************************** //\r
9620 //\r
9621 // Protected common (OS-independent) RtAudio methods.\r
9622 //\r
9623 // *************************************************** //\r
9624 \r
9625 // This method can be modified to control the behavior of error\r
9626 // message printing.\r
9627 void RtApi :: error( RtAudioError::Type type )\r
9628 {\r
9629   errorStream_.str(""); // clear the ostringstream\r
9630 \r
9631   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9632   if ( errorCallback ) {\r
9633     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9634 \r
9635     if ( firstErrorOccurred_ )\r
9636       return;\r
9637 \r
9638     firstErrorOccurred_ = true;\r
9639     const std::string errorMessage = errorText_;\r
9640 \r
9641     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9642       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9643       abortStream();\r
9644     }\r
9645 \r
9646     errorCallback( type, errorMessage );\r
9647     firstErrorOccurred_ = false;\r
9648     return;\r
9649   }\r
9650 \r
9651   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9652     std::cerr << '\n' << errorText_ << "\n\n";\r
9653   else if ( type != RtAudioError::WARNING )\r
9654     throw( RtAudioError( errorText_, type ) );\r
9655 }\r
9656 \r
9657 void RtApi :: verifyStream()\r
9658 {\r
9659   if ( stream_.state == STREAM_CLOSED ) {\r
9660     errorText_ = "RtApi:: a stream is not open!";\r
9661     error( RtAudioError::INVALID_USE );\r
9662   }\r
9663 }\r
9664 \r
9665 void RtApi :: clearStreamInfo()\r
9666 {\r
9667   stream_.mode = UNINITIALIZED;\r
9668   stream_.state = STREAM_CLOSED;\r
9669   stream_.sampleRate = 0;\r
9670   stream_.bufferSize = 0;\r
9671   stream_.nBuffers = 0;\r
9672   stream_.userFormat = 0;\r
9673   stream_.userInterleaved = true;\r
9674   stream_.streamTime = 0.0;\r
9675   stream_.apiHandle = 0;\r
9676   stream_.deviceBuffer = 0;\r
9677   stream_.callbackInfo.callback = 0;\r
9678   stream_.callbackInfo.userData = 0;\r
9679   stream_.callbackInfo.isRunning = false;\r
9680   stream_.callbackInfo.errorCallback = 0;\r
9681   for ( int i=0; i<2; i++ ) {\r
9682     stream_.device[i] = 11111;\r
9683     stream_.doConvertBuffer[i] = false;\r
9684     stream_.deviceInterleaved[i] = true;\r
9685     stream_.doByteSwap[i] = false;\r
9686     stream_.nUserChannels[i] = 0;\r
9687     stream_.nDeviceChannels[i] = 0;\r
9688     stream_.channelOffset[i] = 0;\r
9689     stream_.deviceFormat[i] = 0;\r
9690     stream_.latency[i] = 0;\r
9691     stream_.userBuffer[i] = 0;\r
9692     stream_.convertInfo[i].channels = 0;\r
9693     stream_.convertInfo[i].inJump = 0;\r
9694     stream_.convertInfo[i].outJump = 0;\r
9695     stream_.convertInfo[i].inFormat = 0;\r
9696     stream_.convertInfo[i].outFormat = 0;\r
9697     stream_.convertInfo[i].inOffset.clear();\r
9698     stream_.convertInfo[i].outOffset.clear();\r
9699   }\r
9700 }\r
9701 \r
9702 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9703 {\r
9704   if ( format == RTAUDIO_SINT16 )\r
9705     return 2;\r
9706   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9707     return 4;\r
9708   else if ( format == RTAUDIO_FLOAT64 )\r
9709     return 8;\r
9710   else if ( format == RTAUDIO_SINT24 )\r
9711     return 3;\r
9712   else if ( format == RTAUDIO_SINT8 )\r
9713     return 1;\r
9714 \r
9715   errorText_ = "RtApi::formatBytes: undefined format.";\r
9716   error( RtAudioError::WARNING );\r
9717 \r
9718   return 0;\r
9719 }\r
9720 \r
9721 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9722 {\r
9723   if ( mode == INPUT ) { // convert device to user buffer\r
9724     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9725     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9726     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9727     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9728   }\r
9729   else { // convert user to device buffer\r
9730     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9731     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9732     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9733     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9734   }\r
9735 \r
9736   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9737     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9738   else\r
9739     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9740 \r
9741   // Set up the interleave/deinterleave offsets.\r
9742   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9743     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9744          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9745       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9746         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9747         stream_.convertInfo[mode].outOffset.push_back( k );\r
9748         stream_.convertInfo[mode].inJump = 1;\r
9749       }\r
9750     }\r
9751     else {\r
9752       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9753         stream_.convertInfo[mode].inOffset.push_back( k );\r
9754         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9755         stream_.convertInfo[mode].outJump = 1;\r
9756       }\r
9757     }\r
9758   }\r
9759   else { // no (de)interleaving\r
9760     if ( stream_.userInterleaved ) {\r
9761       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9762         stream_.convertInfo[mode].inOffset.push_back( k );\r
9763         stream_.convertInfo[mode].outOffset.push_back( k );\r
9764       }\r
9765     }\r
9766     else {\r
9767       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9768         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9769         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9770         stream_.convertInfo[mode].inJump = 1;\r
9771         stream_.convertInfo[mode].outJump = 1;\r
9772       }\r
9773     }\r
9774   }\r
9775 \r
9776   // Add channel offset.\r
9777   if ( firstChannel > 0 ) {\r
9778     if ( stream_.deviceInterleaved[mode] ) {\r
9779       if ( mode == OUTPUT ) {\r
9780         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9781           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9782       }\r
9783       else {\r
9784         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9785           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9786       }\r
9787     }\r
9788     else {\r
9789       if ( mode == OUTPUT ) {\r
9790         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9791           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9792       }\r
9793       else {\r
9794         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9795           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9796       }\r
9797     }\r
9798   }\r
9799 }\r
9800 \r
9801 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9802 {\r
9803   // This function does format conversion, input/output channel compensation, and\r
9804   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9805   // the lower three bytes of a 32-bit integer.\r
9806 \r
9807   // Clear our device buffer when in/out duplex device channels are different\r
9808   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9809        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9810     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9811 \r
9812   int j;\r
9813   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9814     Float64 scale;\r
9815     Float64 *out = (Float64 *)outBuffer;\r
9816 \r
9817     if (info.inFormat == RTAUDIO_SINT8) {\r
9818       signed char *in = (signed char *)inBuffer;\r
9819       scale = 1.0 / 127.5;\r
9820       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9821         for (j=0; j<info.channels; j++) {\r
9822           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9823           out[info.outOffset[j]] += 0.5;\r
9824           out[info.outOffset[j]] *= scale;\r
9825         }\r
9826         in += info.inJump;\r
9827         out += info.outJump;\r
9828       }\r
9829     }\r
9830     else if (info.inFormat == RTAUDIO_SINT16) {\r
9831       Int16 *in = (Int16 *)inBuffer;\r
9832       scale = 1.0 / 32767.5;\r
9833       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9834         for (j=0; j<info.channels; j++) {\r
9835           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9836           out[info.outOffset[j]] += 0.5;\r
9837           out[info.outOffset[j]] *= scale;\r
9838         }\r
9839         in += info.inJump;\r
9840         out += info.outJump;\r
9841       }\r
9842     }\r
9843     else if (info.inFormat == RTAUDIO_SINT24) {\r
9844       Int24 *in = (Int24 *)inBuffer;\r
9845       scale = 1.0 / 8388607.5;\r
9846       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9847         for (j=0; j<info.channels; j++) {\r
9848           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9849           out[info.outOffset[j]] += 0.5;\r
9850           out[info.outOffset[j]] *= scale;\r
9851         }\r
9852         in += info.inJump;\r
9853         out += info.outJump;\r
9854       }\r
9855     }\r
9856     else if (info.inFormat == RTAUDIO_SINT32) {\r
9857       Int32 *in = (Int32 *)inBuffer;\r
9858       scale = 1.0 / 2147483647.5;\r
9859       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9860         for (j=0; j<info.channels; j++) {\r
9861           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9862           out[info.outOffset[j]] += 0.5;\r
9863           out[info.outOffset[j]] *= scale;\r
9864         }\r
9865         in += info.inJump;\r
9866         out += info.outJump;\r
9867       }\r
9868     }\r
9869     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9870       Float32 *in = (Float32 *)inBuffer;\r
9871       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9872         for (j=0; j<info.channels; j++) {\r
9873           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9874         }\r
9875         in += info.inJump;\r
9876         out += info.outJump;\r
9877       }\r
9878     }\r
9879     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9880       // Channel compensation and/or (de)interleaving only.\r
9881       Float64 *in = (Float64 *)inBuffer;\r
9882       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9883         for (j=0; j<info.channels; j++) {\r
9884           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9885         }\r
9886         in += info.inJump;\r
9887         out += info.outJump;\r
9888       }\r
9889     }\r
9890   }\r
9891   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9892     Float32 scale;\r
9893     Float32 *out = (Float32 *)outBuffer;\r
9894 \r
9895     if (info.inFormat == RTAUDIO_SINT8) {\r
9896       signed char *in = (signed char *)inBuffer;\r
9897       scale = (Float32) ( 1.0 / 127.5 );\r
9898       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9899         for (j=0; j<info.channels; j++) {\r
9900           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9901           out[info.outOffset[j]] += 0.5;\r
9902           out[info.outOffset[j]] *= scale;\r
9903         }\r
9904         in += info.inJump;\r
9905         out += info.outJump;\r
9906       }\r
9907     }\r
9908     else if (info.inFormat == RTAUDIO_SINT16) {\r
9909       Int16 *in = (Int16 *)inBuffer;\r
9910       scale = (Float32) ( 1.0 / 32767.5 );\r
9911       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9912         for (j=0; j<info.channels; j++) {\r
9913           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9914           out[info.outOffset[j]] += 0.5;\r
9915           out[info.outOffset[j]] *= scale;\r
9916         }\r
9917         in += info.inJump;\r
9918         out += info.outJump;\r
9919       }\r
9920     }\r
9921     else if (info.inFormat == RTAUDIO_SINT24) {\r
9922       Int24 *in = (Int24 *)inBuffer;\r
9923       scale = (Float32) ( 1.0 / 8388607.5 );\r
9924       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9925         for (j=0; j<info.channels; j++) {\r
9926           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9927           out[info.outOffset[j]] += 0.5;\r
9928           out[info.outOffset[j]] *= scale;\r
9929         }\r
9930         in += info.inJump;\r
9931         out += info.outJump;\r
9932       }\r
9933     }\r
9934     else if (info.inFormat == RTAUDIO_SINT32) {\r
9935       Int32 *in = (Int32 *)inBuffer;\r
9936       scale = (Float32) ( 1.0 / 2147483647.5 );\r
9937       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9938         for (j=0; j<info.channels; j++) {\r
9939           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9940           out[info.outOffset[j]] += 0.5;\r
9941           out[info.outOffset[j]] *= scale;\r
9942         }\r
9943         in += info.inJump;\r
9944         out += info.outJump;\r
9945       }\r
9946     }\r
9947     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9948       // Channel compensation and/or (de)interleaving only.\r
9949       Float32 *in = (Float32 *)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]] = in[info.inOffset[j]];\r
9953         }\r
9954         in += info.inJump;\r
9955         out += info.outJump;\r
9956       }\r
9957     }\r
9958     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9959       Float64 *in = (Float64 *)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]] = (Float32) in[info.inOffset[j]];\r
9963         }\r
9964         in += info.inJump;\r
9965         out += info.outJump;\r
9966       }\r
9967     }\r
9968   }\r
9969   else if (info.outFormat == RTAUDIO_SINT32) {\r
9970     Int32 *out = (Int32 *)outBuffer;\r
9971     if (info.inFormat == RTAUDIO_SINT8) {\r
9972       signed char *in = (signed char *)inBuffer;\r
9973       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9974         for (j=0; j<info.channels; j++) {\r
9975           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9976           out[info.outOffset[j]] <<= 24;\r
9977         }\r
9978         in += info.inJump;\r
9979         out += info.outJump;\r
9980       }\r
9981     }\r
9982     else if (info.inFormat == RTAUDIO_SINT16) {\r
9983       Int16 *in = (Int16 *)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]] = (Int32) in[info.inOffset[j]];\r
9987           out[info.outOffset[j]] <<= 16;\r
9988         }\r
9989         in += info.inJump;\r
9990         out += info.outJump;\r
9991       }\r
9992     }\r
9993     else if (info.inFormat == RTAUDIO_SINT24) {\r
9994       Int24 *in = (Int24 *)inBuffer;\r
9995       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9996         for (j=0; j<info.channels; j++) {\r
9997           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
9998           out[info.outOffset[j]] <<= 8;\r
9999         }\r
10000         in += info.inJump;\r
10001         out += info.outJump;\r
10002       }\r
10003     }\r
10004     else if (info.inFormat == RTAUDIO_SINT32) {\r
10005       // Channel compensation and/or (de)interleaving only.\r
10006       Int32 *in = (Int32 *)inBuffer;\r
10007       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10008         for (j=0; j<info.channels; j++) {\r
10009           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10010         }\r
10011         in += info.inJump;\r
10012         out += info.outJump;\r
10013       }\r
10014     }\r
10015     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10016       Float32 *in = (Float32 *)inBuffer;\r
10017       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10018         for (j=0; j<info.channels; j++) {\r
10019           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
10020         }\r
10021         in += info.inJump;\r
10022         out += info.outJump;\r
10023       }\r
10024     }\r
10025     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10026       Float64 *in = (Float64 *)inBuffer;\r
10027       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10028         for (j=0; j<info.channels; j++) {\r
10029           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
10030         }\r
10031         in += info.inJump;\r
10032         out += info.outJump;\r
10033       }\r
10034     }\r
10035   }\r
10036   else if (info.outFormat == RTAUDIO_SINT24) {\r
10037     Int24 *out = (Int24 *)outBuffer;\r
10038     if (info.inFormat == RTAUDIO_SINT8) {\r
10039       signed char *in = (signed char *)inBuffer;\r
10040       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10041         for (j=0; j<info.channels; j++) {\r
10042           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
10043           //out[info.outOffset[j]] <<= 16;\r
10044         }\r
10045         in += info.inJump;\r
10046         out += info.outJump;\r
10047       }\r
10048     }\r
10049     else if (info.inFormat == RTAUDIO_SINT16) {\r
10050       Int16 *in = (Int16 *)inBuffer;\r
10051       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10052         for (j=0; j<info.channels; j++) {\r
10053           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
10054           //out[info.outOffset[j]] <<= 8;\r
10055         }\r
10056         in += info.inJump;\r
10057         out += info.outJump;\r
10058       }\r
10059     }\r
10060     else if (info.inFormat == RTAUDIO_SINT24) {\r
10061       // Channel compensation and/or (de)interleaving only.\r
10062       Int24 *in = (Int24 *)inBuffer;\r
10063       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10064         for (j=0; j<info.channels; j++) {\r
10065           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10066         }\r
10067         in += info.inJump;\r
10068         out += info.outJump;\r
10069       }\r
10070     }\r
10071     else if (info.inFormat == RTAUDIO_SINT32) {\r
10072       Int32 *in = (Int32 *)inBuffer;\r
10073       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10074         for (j=0; j<info.channels; j++) {\r
10075           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
10076           //out[info.outOffset[j]] >>= 8;\r
10077         }\r
10078         in += info.inJump;\r
10079         out += info.outJump;\r
10080       }\r
10081     }\r
10082     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10083       Float32 *in = (Float32 *)inBuffer;\r
10084       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10085         for (j=0; j<info.channels; j++) {\r
10086           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
10087         }\r
10088         in += info.inJump;\r
10089         out += info.outJump;\r
10090       }\r
10091     }\r
10092     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10093       Float64 *in = (Float64 *)inBuffer;\r
10094       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10095         for (j=0; j<info.channels; j++) {\r
10096           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
10097         }\r
10098         in += info.inJump;\r
10099         out += info.outJump;\r
10100       }\r
10101     }\r
10102   }\r
10103   else if (info.outFormat == RTAUDIO_SINT16) {\r
10104     Int16 *out = (Int16 *)outBuffer;\r
10105     if (info.inFormat == RTAUDIO_SINT8) {\r
10106       signed char *in = (signed char *)inBuffer;\r
10107       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10108         for (j=0; j<info.channels; j++) {\r
10109           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
10110           out[info.outOffset[j]] <<= 8;\r
10111         }\r
10112         in += info.inJump;\r
10113         out += info.outJump;\r
10114       }\r
10115     }\r
10116     else if (info.inFormat == RTAUDIO_SINT16) {\r
10117       // Channel compensation and/or (de)interleaving only.\r
10118       Int16 *in = (Int16 *)inBuffer;\r
10119       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10120         for (j=0; j<info.channels; j++) {\r
10121           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10122         }\r
10123         in += info.inJump;\r
10124         out += info.outJump;\r
10125       }\r
10126     }\r
10127     else if (info.inFormat == RTAUDIO_SINT24) {\r
10128       Int24 *in = (Int24 *)inBuffer;\r
10129       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10130         for (j=0; j<info.channels; j++) {\r
10131           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
10132         }\r
10133         in += info.inJump;\r
10134         out += info.outJump;\r
10135       }\r
10136     }\r
10137     else if (info.inFormat == RTAUDIO_SINT32) {\r
10138       Int32 *in = (Int32 *)inBuffer;\r
10139       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10140         for (j=0; j<info.channels; j++) {\r
10141           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
10142         }\r
10143         in += info.inJump;\r
10144         out += info.outJump;\r
10145       }\r
10146     }\r
10147     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10148       Float32 *in = (Float32 *)inBuffer;\r
10149       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10150         for (j=0; j<info.channels; j++) {\r
10151           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
10152         }\r
10153         in += info.inJump;\r
10154         out += info.outJump;\r
10155       }\r
10156     }\r
10157     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10158       Float64 *in = (Float64 *)inBuffer;\r
10159       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10160         for (j=0; j<info.channels; j++) {\r
10161           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
10162         }\r
10163         in += info.inJump;\r
10164         out += info.outJump;\r
10165       }\r
10166     }\r
10167   }\r
10168   else if (info.outFormat == RTAUDIO_SINT8) {\r
10169     signed char *out = (signed char *)outBuffer;\r
10170     if (info.inFormat == RTAUDIO_SINT8) {\r
10171       // Channel compensation and/or (de)interleaving only.\r
10172       signed char *in = (signed char *)inBuffer;\r
10173       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10174         for (j=0; j<info.channels; j++) {\r
10175           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10176         }\r
10177         in += info.inJump;\r
10178         out += info.outJump;\r
10179       }\r
10180     }\r
10181     if (info.inFormat == RTAUDIO_SINT16) {\r
10182       Int16 *in = (Int16 *)inBuffer;\r
10183       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10184         for (j=0; j<info.channels; j++) {\r
10185           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
10186         }\r
10187         in += info.inJump;\r
10188         out += info.outJump;\r
10189       }\r
10190     }\r
10191     else if (info.inFormat == RTAUDIO_SINT24) {\r
10192       Int24 *in = (Int24 *)inBuffer;\r
10193       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10194         for (j=0; j<info.channels; j++) {\r
10195           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10196         }\r
10197         in += info.inJump;\r
10198         out += info.outJump;\r
10199       }\r
10200     }\r
10201     else if (info.inFormat == RTAUDIO_SINT32) {\r
10202       Int32 *in = (Int32 *)inBuffer;\r
10203       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10204         for (j=0; j<info.channels; j++) {\r
10205           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10206         }\r
10207         in += info.inJump;\r
10208         out += info.outJump;\r
10209       }\r
10210     }\r
10211     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10212       Float32 *in = (Float32 *)inBuffer;\r
10213       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10214         for (j=0; j<info.channels; j++) {\r
10215           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10216         }\r
10217         in += info.inJump;\r
10218         out += info.outJump;\r
10219       }\r
10220     }\r
10221     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10222       Float64 *in = (Float64 *)inBuffer;\r
10223       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10224         for (j=0; j<info.channels; j++) {\r
10225           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10226         }\r
10227         in += info.inJump;\r
10228         out += info.outJump;\r
10229       }\r
10230     }\r
10231   }\r
10232 }\r
10233 \r
10234 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10235 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10236 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10237 \r
10238 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10239 {\r
10240   char val;\r
10241   char *ptr;\r
10242 \r
10243   ptr = buffer;\r
10244   if ( format == RTAUDIO_SINT16 ) {\r
10245     for ( unsigned int i=0; i<samples; i++ ) {\r
10246       // Swap 1st and 2nd bytes.\r
10247       val = *(ptr);\r
10248       *(ptr) = *(ptr+1);\r
10249       *(ptr+1) = val;\r
10250 \r
10251       // Increment 2 bytes.\r
10252       ptr += 2;\r
10253     }\r
10254   }\r
10255   else if ( format == RTAUDIO_SINT32 ||\r
10256             format == RTAUDIO_FLOAT32 ) {\r
10257     for ( unsigned int i=0; i<samples; i++ ) {\r
10258       // Swap 1st and 4th bytes.\r
10259       val = *(ptr);\r
10260       *(ptr) = *(ptr+3);\r
10261       *(ptr+3) = val;\r
10262 \r
10263       // Swap 2nd and 3rd bytes.\r
10264       ptr += 1;\r
10265       val = *(ptr);\r
10266       *(ptr) = *(ptr+1);\r
10267       *(ptr+1) = val;\r
10268 \r
10269       // Increment 3 more bytes.\r
10270       ptr += 3;\r
10271     }\r
10272   }\r
10273   else if ( format == RTAUDIO_SINT24 ) {\r
10274     for ( unsigned int i=0; i<samples; i++ ) {\r
10275       // Swap 1st and 3rd bytes.\r
10276       val = *(ptr);\r
10277       *(ptr) = *(ptr+2);\r
10278       *(ptr+2) = val;\r
10279 \r
10280       // Increment 2 more bytes.\r
10281       ptr += 2;\r
10282     }\r
10283   }\r
10284   else if ( format == RTAUDIO_FLOAT64 ) {\r
10285     for ( unsigned int i=0; i<samples; i++ ) {\r
10286       // Swap 1st and 8th bytes\r
10287       val = *(ptr);\r
10288       *(ptr) = *(ptr+7);\r
10289       *(ptr+7) = val;\r
10290 \r
10291       // Swap 2nd and 7th bytes\r
10292       ptr += 1;\r
10293       val = *(ptr);\r
10294       *(ptr) = *(ptr+5);\r
10295       *(ptr+5) = val;\r
10296 \r
10297       // Swap 3rd and 6th bytes\r
10298       ptr += 1;\r
10299       val = *(ptr);\r
10300       *(ptr) = *(ptr+3);\r
10301       *(ptr+3) = val;\r
10302 \r
10303       // Swap 4th and 5th bytes\r
10304       ptr += 1;\r
10305       val = *(ptr);\r
10306       *(ptr) = *(ptr+1);\r
10307       *(ptr+1) = val;\r
10308 \r
10309       // Increment 5 more bytes.\r
10310       ptr += 5;\r
10311     }\r
10312   }\r
10313 }\r
10314 \r
10315   // Indentation settings for Vim and Emacs\r
10316   //\r
10317   // Local Variables:\r
10318   // c-basic-offset: 2\r
10319   // indent-tabs-mode: nil\r
10320   // End:\r
10321   //\r
10322   // vim: et sts=2 sw=2\r
10323 \r