Add mmsystem.h and mmreg.h for dsound.
[rtaudio.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 favors speed over quality, and works best with conversions between\r
3863 // one rate and its multiple.\r
3864 void convertBufferWasapi( char* outBuffer,\r
3865                           const char* inBuffer,\r
3866                           const unsigned int& channelCount,\r
3867                           const unsigned int& inSampleRate,\r
3868                           const unsigned int& outSampleRate,\r
3869                           const unsigned int& inSampleCount,\r
3870                           unsigned int& outSampleCount,\r
3871                           const RtAudioFormat& format )\r
3872 {\r
3873   // calculate the new outSampleCount and relative sampleStep\r
3874   float sampleRatio = ( float ) outSampleRate / inSampleRate;\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   // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3881   for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3882   {\r
3883     unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3884 \r
3885     switch ( format )\r
3886     {\r
3887       case RTAUDIO_SINT8:\r
3888         memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
3889         break;\r
3890       case RTAUDIO_SINT16:\r
3891         memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
3892         break;\r
3893       case RTAUDIO_SINT24:\r
3894         memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
3895         break;\r
3896       case RTAUDIO_SINT32:\r
3897         memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
3898         break;\r
3899       case RTAUDIO_FLOAT32:\r
3900         memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
3901         break;\r
3902       case RTAUDIO_FLOAT64:\r
3903         memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
3904         break;\r
3905     }\r
3906 \r
3907     // jump to next in sample\r
3908     inSampleFraction += sampleStep;\r
3909   }\r
3910 }\r
3911 \r
3912 //-----------------------------------------------------------------------------\r
3913 \r
3914 // A structure to hold various information related to the WASAPI implementation.\r
3915 struct WasapiHandle\r
3916 {\r
3917   IAudioClient* captureAudioClient;\r
3918   IAudioClient* renderAudioClient;\r
3919   IAudioCaptureClient* captureClient;\r
3920   IAudioRenderClient* renderClient;\r
3921   HANDLE captureEvent;\r
3922   HANDLE renderEvent;\r
3923 \r
3924   WasapiHandle()\r
3925   : captureAudioClient( NULL ),\r
3926     renderAudioClient( NULL ),\r
3927     captureClient( NULL ),\r
3928     renderClient( NULL ),\r
3929     captureEvent( NULL ),\r
3930     renderEvent( NULL ) {}\r
3931 };\r
3932 \r
3933 //=============================================================================\r
3934 \r
3935 RtApiWasapi::RtApiWasapi()\r
3936   : coInitialized_( false ), deviceEnumerator_( NULL )\r
3937 {\r
3938   // WASAPI can run either apartment or multi-threaded\r
3939   HRESULT hr = CoInitialize( NULL );\r
3940   if ( !FAILED( hr ) )\r
3941     coInitialized_ = true;\r
3942 \r
3943   // Instantiate device enumerator\r
3944   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
3945                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
3946                          ( void** ) &deviceEnumerator_ );\r
3947 \r
3948   if ( FAILED( hr ) ) {\r
3949     errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
3950     error( RtAudioError::DRIVER_ERROR );\r
3951   }\r
3952 }\r
3953 \r
3954 //-----------------------------------------------------------------------------\r
3955 \r
3956 RtApiWasapi::~RtApiWasapi()\r
3957 {\r
3958   if ( stream_.state != STREAM_CLOSED )\r
3959     closeStream();\r
3960 \r
3961   SAFE_RELEASE( deviceEnumerator_ );\r
3962 \r
3963   // If this object previously called CoInitialize()\r
3964   if ( coInitialized_ )\r
3965     CoUninitialize();\r
3966 }\r
3967 \r
3968 //=============================================================================\r
3969 \r
3970 unsigned int RtApiWasapi::getDeviceCount( void )\r
3971 {\r
3972   unsigned int captureDeviceCount = 0;\r
3973   unsigned int renderDeviceCount = 0;\r
3974 \r
3975   IMMDeviceCollection* captureDevices = NULL;\r
3976   IMMDeviceCollection* renderDevices = NULL;\r
3977 \r
3978   // Count capture devices\r
3979   errorText_.clear();\r
3980   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3981   if ( FAILED( hr ) ) {\r
3982     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
3983     goto Exit;\r
3984   }\r
3985 \r
3986   hr = captureDevices->GetCount( &captureDeviceCount );\r
3987   if ( FAILED( hr ) ) {\r
3988     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
3989     goto Exit;\r
3990   }\r
3991 \r
3992   // Count render devices\r
3993   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
3994   if ( FAILED( hr ) ) {\r
3995     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
3996     goto Exit;\r
3997   }\r
3998 \r
3999   hr = renderDevices->GetCount( &renderDeviceCount );\r
4000   if ( FAILED( hr ) ) {\r
4001     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
4002     goto Exit;\r
4003   }\r
4004 \r
4005 Exit:\r
4006   // release all references\r
4007   SAFE_RELEASE( captureDevices );\r
4008   SAFE_RELEASE( renderDevices );\r
4009 \r
4010   if ( errorText_.empty() )\r
4011     return captureDeviceCount + renderDeviceCount;\r
4012 \r
4013   error( RtAudioError::DRIVER_ERROR );\r
4014   return 0;\r
4015 }\r
4016 \r
4017 //-----------------------------------------------------------------------------\r
4018 \r
4019 RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
4020 {\r
4021   RtAudio::DeviceInfo info;\r
4022   unsigned int captureDeviceCount = 0;\r
4023   unsigned int renderDeviceCount = 0;\r
4024   std::string defaultDeviceName;\r
4025   bool isCaptureDevice = false;\r
4026 \r
4027   PROPVARIANT deviceNameProp;\r
4028   PROPVARIANT defaultDeviceNameProp;\r
4029 \r
4030   IMMDeviceCollection* captureDevices = NULL;\r
4031   IMMDeviceCollection* renderDevices = NULL;\r
4032   IMMDevice* devicePtr = NULL;\r
4033   IMMDevice* defaultDevicePtr = NULL;\r
4034   IAudioClient* audioClient = NULL;\r
4035   IPropertyStore* devicePropStore = NULL;\r
4036   IPropertyStore* defaultDevicePropStore = NULL;\r
4037 \r
4038   WAVEFORMATEX* deviceFormat = NULL;\r
4039   WAVEFORMATEX* closestMatchFormat = NULL;\r
4040 \r
4041   // probed\r
4042   info.probed = false;\r
4043 \r
4044   // Count capture devices\r
4045   errorText_.clear();\r
4046   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4047   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4048   if ( FAILED( hr ) ) {\r
4049     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
4050     goto Exit;\r
4051   }\r
4052 \r
4053   hr = captureDevices->GetCount( &captureDeviceCount );\r
4054   if ( FAILED( hr ) ) {\r
4055     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
4056     goto Exit;\r
4057   }\r
4058 \r
4059   // Count render devices\r
4060   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4061   if ( FAILED( hr ) ) {\r
4062     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
4063     goto Exit;\r
4064   }\r
4065 \r
4066   hr = renderDevices->GetCount( &renderDeviceCount );\r
4067   if ( FAILED( hr ) ) {\r
4068     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
4069     goto Exit;\r
4070   }\r
4071 \r
4072   // validate device index\r
4073   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4074     errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
4075     errorType = RtAudioError::INVALID_USE;\r
4076     goto Exit;\r
4077   }\r
4078 \r
4079   // determine whether index falls within capture or render devices\r
4080   if ( device >= renderDeviceCount ) {\r
4081     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4082     if ( FAILED( hr ) ) {\r
4083       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
4084       goto Exit;\r
4085     }\r
4086     isCaptureDevice = true;\r
4087   }\r
4088   else {\r
4089     hr = renderDevices->Item( device, &devicePtr );\r
4090     if ( FAILED( hr ) ) {\r
4091       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
4092       goto Exit;\r
4093     }\r
4094     isCaptureDevice = false;\r
4095   }\r
4096 \r
4097   // get default device name\r
4098   if ( isCaptureDevice ) {\r
4099     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
4100     if ( FAILED( hr ) ) {\r
4101       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
4102       goto Exit;\r
4103     }\r
4104   }\r
4105   else {\r
4106     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
4107     if ( FAILED( hr ) ) {\r
4108       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
4109       goto Exit;\r
4110     }\r
4111   }\r
4112 \r
4113   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
4114   if ( FAILED( hr ) ) {\r
4115     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
4116     goto Exit;\r
4117   }\r
4118   PropVariantInit( &defaultDeviceNameProp );\r
4119 \r
4120   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
4121   if ( FAILED( hr ) ) {\r
4122     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
4123     goto Exit;\r
4124   }\r
4125 \r
4126   defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);\r
4127 \r
4128   // name\r
4129   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
4130   if ( FAILED( hr ) ) {\r
4131     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
4132     goto Exit;\r
4133   }\r
4134 \r
4135   PropVariantInit( &deviceNameProp );\r
4136 \r
4137   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
4138   if ( FAILED( hr ) ) {\r
4139     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
4140     goto Exit;\r
4141   }\r
4142 \r
4143   info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);\r
4144 \r
4145   // is default\r
4146   if ( isCaptureDevice ) {\r
4147     info.isDefaultInput = info.name == defaultDeviceName;\r
4148     info.isDefaultOutput = false;\r
4149   }\r
4150   else {\r
4151     info.isDefaultInput = false;\r
4152     info.isDefaultOutput = info.name == defaultDeviceName;\r
4153   }\r
4154 \r
4155   // channel count\r
4156   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
4157   if ( FAILED( hr ) ) {\r
4158     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
4159     goto Exit;\r
4160   }\r
4161 \r
4162   hr = audioClient->GetMixFormat( &deviceFormat );\r
4163   if ( FAILED( hr ) ) {\r
4164     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
4165     goto Exit;\r
4166   }\r
4167 \r
4168   if ( isCaptureDevice ) {\r
4169     info.inputChannels = deviceFormat->nChannels;\r
4170     info.outputChannels = 0;\r
4171     info.duplexChannels = 0;\r
4172   }\r
4173   else {\r
4174     info.inputChannels = 0;\r
4175     info.outputChannels = deviceFormat->nChannels;\r
4176     info.duplexChannels = 0;\r
4177   }\r
4178 \r
4179   // sample rates\r
4180   info.sampleRates.clear();\r
4181 \r
4182   // allow support for all sample rates as we have a built-in sample rate converter\r
4183   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
4184     info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4185   }\r
4186   info.preferredSampleRate = deviceFormat->nSamplesPerSec;\r
4187 \r
4188   // native format\r
4189   info.nativeFormats = 0;\r
4190 \r
4191   if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
4192        ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4193          ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
4194   {\r
4195     if ( deviceFormat->wBitsPerSample == 32 ) {\r
4196       info.nativeFormats |= RTAUDIO_FLOAT32;\r
4197     }\r
4198     else if ( deviceFormat->wBitsPerSample == 64 ) {\r
4199       info.nativeFormats |= RTAUDIO_FLOAT64;\r
4200     }\r
4201   }\r
4202   else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
4203            ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4204              ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
4205   {\r
4206     if ( deviceFormat->wBitsPerSample == 8 ) {\r
4207       info.nativeFormats |= RTAUDIO_SINT8;\r
4208     }\r
4209     else if ( deviceFormat->wBitsPerSample == 16 ) {\r
4210       info.nativeFormats |= RTAUDIO_SINT16;\r
4211     }\r
4212     else if ( deviceFormat->wBitsPerSample == 24 ) {\r
4213       info.nativeFormats |= RTAUDIO_SINT24;\r
4214     }\r
4215     else if ( deviceFormat->wBitsPerSample == 32 ) {\r
4216       info.nativeFormats |= RTAUDIO_SINT32;\r
4217     }\r
4218   }\r
4219 \r
4220   // probed\r
4221   info.probed = true;\r
4222 \r
4223 Exit:\r
4224   // release all references\r
4225   PropVariantClear( &deviceNameProp );\r
4226   PropVariantClear( &defaultDeviceNameProp );\r
4227 \r
4228   SAFE_RELEASE( captureDevices );\r
4229   SAFE_RELEASE( renderDevices );\r
4230   SAFE_RELEASE( devicePtr );\r
4231   SAFE_RELEASE( defaultDevicePtr );\r
4232   SAFE_RELEASE( audioClient );\r
4233   SAFE_RELEASE( devicePropStore );\r
4234   SAFE_RELEASE( defaultDevicePropStore );\r
4235 \r
4236   CoTaskMemFree( deviceFormat );\r
4237   CoTaskMemFree( closestMatchFormat );\r
4238 \r
4239   if ( !errorText_.empty() )\r
4240     error( errorType );\r
4241   return info;\r
4242 }\r
4243 \r
4244 //-----------------------------------------------------------------------------\r
4245 \r
4246 unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
4247 {\r
4248   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4249     if ( getDeviceInfo( i ).isDefaultOutput ) {\r
4250       return i;\r
4251     }\r
4252   }\r
4253 \r
4254   return 0;\r
4255 }\r
4256 \r
4257 //-----------------------------------------------------------------------------\r
4258 \r
4259 unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
4260 {\r
4261   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4262     if ( getDeviceInfo( i ).isDefaultInput ) {\r
4263       return i;\r
4264     }\r
4265   }\r
4266 \r
4267   return 0;\r
4268 }\r
4269 \r
4270 //-----------------------------------------------------------------------------\r
4271 \r
4272 void RtApiWasapi::closeStream( void )\r
4273 {\r
4274   if ( stream_.state == STREAM_CLOSED ) {\r
4275     errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
4276     error( RtAudioError::WARNING );\r
4277     return;\r
4278   }\r
4279 \r
4280   if ( stream_.state != STREAM_STOPPED )\r
4281     stopStream();\r
4282 \r
4283   // clean up stream memory\r
4284   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
4285   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
4286 \r
4287   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
4288   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
4289 \r
4290   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
4291     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
4292 \r
4293   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
4294     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
4295 \r
4296   delete ( WasapiHandle* ) stream_.apiHandle;\r
4297   stream_.apiHandle = NULL;\r
4298 \r
4299   for ( int i = 0; i < 2; i++ ) {\r
4300     if ( stream_.userBuffer[i] ) {\r
4301       free( stream_.userBuffer[i] );\r
4302       stream_.userBuffer[i] = 0;\r
4303     }\r
4304   }\r
4305 \r
4306   if ( stream_.deviceBuffer ) {\r
4307     free( stream_.deviceBuffer );\r
4308     stream_.deviceBuffer = 0;\r
4309   }\r
4310 \r
4311   // update stream state\r
4312   stream_.state = STREAM_CLOSED;\r
4313 }\r
4314 \r
4315 //-----------------------------------------------------------------------------\r
4316 \r
4317 void RtApiWasapi::startStream( void )\r
4318 {\r
4319   verifyStream();\r
4320 \r
4321   if ( stream_.state == STREAM_RUNNING ) {\r
4322     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
4323     error( RtAudioError::WARNING );\r
4324     return;\r
4325   }\r
4326 \r
4327   // update stream state\r
4328   stream_.state = STREAM_RUNNING;\r
4329 \r
4330   // create WASAPI stream thread\r
4331   stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
4332 \r
4333   if ( !stream_.callbackInfo.thread ) {\r
4334     errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
4335     error( RtAudioError::THREAD_ERROR );\r
4336   }\r
4337   else {\r
4338     SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
4339     ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
4340   }\r
4341 }\r
4342 \r
4343 //-----------------------------------------------------------------------------\r
4344 \r
4345 void RtApiWasapi::stopStream( void )\r
4346 {\r
4347   verifyStream();\r
4348 \r
4349   if ( stream_.state == STREAM_STOPPED ) {\r
4350     errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
4351     error( RtAudioError::WARNING );\r
4352     return;\r
4353   }\r
4354 \r
4355   // inform stream thread by setting stream state to STREAM_STOPPING\r
4356   stream_.state = STREAM_STOPPING;\r
4357 \r
4358   // wait until stream thread is stopped\r
4359   while( stream_.state != STREAM_STOPPED ) {\r
4360     Sleep( 1 );\r
4361   }\r
4362 \r
4363   // Wait for the last buffer to play before stopping.\r
4364   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
4365 \r
4366   // stop capture client if applicable\r
4367   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4368     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4369     if ( FAILED( hr ) ) {\r
4370       errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
4371       error( RtAudioError::DRIVER_ERROR );\r
4372       return;\r
4373     }\r
4374   }\r
4375 \r
4376   // stop render client if applicable\r
4377   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4378     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4379     if ( FAILED( hr ) ) {\r
4380       errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
4381       error( RtAudioError::DRIVER_ERROR );\r
4382       return;\r
4383     }\r
4384   }\r
4385 \r
4386   // close thread handle\r
4387   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4388     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
4389     error( RtAudioError::THREAD_ERROR );\r
4390     return;\r
4391   }\r
4392 \r
4393   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4394 }\r
4395 \r
4396 //-----------------------------------------------------------------------------\r
4397 \r
4398 void RtApiWasapi::abortStream( void )\r
4399 {\r
4400   verifyStream();\r
4401 \r
4402   if ( stream_.state == STREAM_STOPPED ) {\r
4403     errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
4404     error( RtAudioError::WARNING );\r
4405     return;\r
4406   }\r
4407 \r
4408   // inform stream thread by setting stream state to STREAM_STOPPING\r
4409   stream_.state = STREAM_STOPPING;\r
4410 \r
4411   // wait until stream thread is stopped\r
4412   while ( stream_.state != STREAM_STOPPED ) {\r
4413     Sleep( 1 );\r
4414   }\r
4415 \r
4416   // stop capture client if applicable\r
4417   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4418     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4419     if ( FAILED( hr ) ) {\r
4420       errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
4421       error( RtAudioError::DRIVER_ERROR );\r
4422       return;\r
4423     }\r
4424   }\r
4425 \r
4426   // stop render client if applicable\r
4427   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4428     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4429     if ( FAILED( hr ) ) {\r
4430       errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
4431       error( RtAudioError::DRIVER_ERROR );\r
4432       return;\r
4433     }\r
4434   }\r
4435 \r
4436   // close thread handle\r
4437   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4438     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
4439     error( RtAudioError::THREAD_ERROR );\r
4440     return;\r
4441   }\r
4442 \r
4443   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4444 }\r
4445 \r
4446 //-----------------------------------------------------------------------------\r
4447 \r
4448 bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
4449                                    unsigned int firstChannel, unsigned int sampleRate,\r
4450                                    RtAudioFormat format, unsigned int* bufferSize,\r
4451                                    RtAudio::StreamOptions* options )\r
4452 {\r
4453   bool methodResult = FAILURE;\r
4454   unsigned int captureDeviceCount = 0;\r
4455   unsigned int renderDeviceCount = 0;\r
4456 \r
4457   IMMDeviceCollection* captureDevices = NULL;\r
4458   IMMDeviceCollection* renderDevices = NULL;\r
4459   IMMDevice* devicePtr = NULL;\r
4460   WAVEFORMATEX* deviceFormat = NULL;\r
4461   unsigned int bufferBytes;\r
4462   stream_.state = STREAM_STOPPED;\r
4463 \r
4464   // create API Handle if not already created\r
4465   if ( !stream_.apiHandle )\r
4466     stream_.apiHandle = ( void* ) new WasapiHandle();\r
4467 \r
4468   // Count capture devices\r
4469   errorText_.clear();\r
4470   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4471   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4472   if ( FAILED( hr ) ) {\r
4473     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
4474     goto Exit;\r
4475   }\r
4476 \r
4477   hr = captureDevices->GetCount( &captureDeviceCount );\r
4478   if ( FAILED( hr ) ) {\r
4479     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
4480     goto Exit;\r
4481   }\r
4482 \r
4483   // Count render devices\r
4484   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4485   if ( FAILED( hr ) ) {\r
4486     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
4487     goto Exit;\r
4488   }\r
4489 \r
4490   hr = renderDevices->GetCount( &renderDeviceCount );\r
4491   if ( FAILED( hr ) ) {\r
4492     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
4493     goto Exit;\r
4494   }\r
4495 \r
4496   // validate device index\r
4497   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4498     errorType = RtAudioError::INVALID_USE;\r
4499     errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
4500     goto Exit;\r
4501   }\r
4502 \r
4503   // determine whether index falls within capture or render devices\r
4504   if ( device >= renderDeviceCount ) {\r
4505     if ( mode != INPUT ) {\r
4506       errorType = RtAudioError::INVALID_USE;\r
4507       errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
4508       goto Exit;\r
4509     }\r
4510 \r
4511     // retrieve captureAudioClient from devicePtr\r
4512     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4513 \r
4514     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4515     if ( FAILED( hr ) ) {\r
4516       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
4517       goto Exit;\r
4518     }\r
4519 \r
4520     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4521                               NULL, ( void** ) &captureAudioClient );\r
4522     if ( FAILED( hr ) ) {\r
4523       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4524       goto Exit;\r
4525     }\r
4526 \r
4527     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
4528     if ( FAILED( hr ) ) {\r
4529       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4530       goto Exit;\r
4531     }\r
4532 \r
4533     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4534     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4535   }\r
4536   else {\r
4537     if ( mode != OUTPUT ) {\r
4538       errorType = RtAudioError::INVALID_USE;\r
4539       errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
4540       goto Exit;\r
4541     }\r
4542 \r
4543     // retrieve renderAudioClient from devicePtr\r
4544     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4545 \r
4546     hr = renderDevices->Item( device, &devicePtr );\r
4547     if ( FAILED( hr ) ) {\r
4548       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
4549       goto Exit;\r
4550     }\r
4551 \r
4552     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4553                               NULL, ( void** ) &renderAudioClient );\r
4554     if ( FAILED( hr ) ) {\r
4555       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4556       goto Exit;\r
4557     }\r
4558 \r
4559     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
4560     if ( FAILED( hr ) ) {\r
4561       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4562       goto Exit;\r
4563     }\r
4564 \r
4565     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4566     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4567   }\r
4568 \r
4569   // fill stream data\r
4570   if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
4571        ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
4572     stream_.mode = DUPLEX;\r
4573   }\r
4574   else {\r
4575     stream_.mode = mode;\r
4576   }\r
4577 \r
4578   stream_.device[mode] = device;\r
4579   stream_.doByteSwap[mode] = false;\r
4580   stream_.sampleRate = sampleRate;\r
4581   stream_.bufferSize = *bufferSize;\r
4582   stream_.nBuffers = 1;\r
4583   stream_.nUserChannels[mode] = channels;\r
4584   stream_.channelOffset[mode] = firstChannel;\r
4585   stream_.userFormat = format;\r
4586   stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
4587 \r
4588   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
4589     stream_.userInterleaved = false;\r
4590   else\r
4591     stream_.userInterleaved = true;\r
4592   stream_.deviceInterleaved[mode] = true;\r
4593 \r
4594   // Set flags for buffer conversion.\r
4595   stream_.doConvertBuffer[mode] = false;\r
4596   if ( stream_.userFormat != stream_.deviceFormat[mode] ||\r
4597        stream_.nUserChannels != stream_.nDeviceChannels )\r
4598     stream_.doConvertBuffer[mode] = true;\r
4599   else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
4600             stream_.nUserChannels[mode] > 1 )\r
4601     stream_.doConvertBuffer[mode] = true;\r
4602 \r
4603   if ( stream_.doConvertBuffer[mode] )\r
4604     setConvertInfo( mode, 0 );\r
4605 \r
4606   // Allocate necessary internal buffers\r
4607   bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
4608 \r
4609   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
4610   if ( !stream_.userBuffer[mode] ) {\r
4611     errorType = RtAudioError::MEMORY_ERROR;\r
4612     errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
4613     goto Exit;\r
4614   }\r
4615 \r
4616   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
4617     stream_.callbackInfo.priority = 15;\r
4618   else\r
4619     stream_.callbackInfo.priority = 0;\r
4620 \r
4621   ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
4622   ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
4623 \r
4624   methodResult = SUCCESS;\r
4625 \r
4626 Exit:\r
4627   //clean up\r
4628   SAFE_RELEASE( captureDevices );\r
4629   SAFE_RELEASE( renderDevices );\r
4630   SAFE_RELEASE( devicePtr );\r
4631   CoTaskMemFree( deviceFormat );\r
4632 \r
4633   // if method failed, close the stream\r
4634   if ( methodResult == FAILURE )\r
4635     closeStream();\r
4636 \r
4637   if ( !errorText_.empty() )\r
4638     error( errorType );\r
4639   return methodResult;\r
4640 }\r
4641 \r
4642 //=============================================================================\r
4643 \r
4644 DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
4645 {\r
4646   if ( wasapiPtr )\r
4647     ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
4648 \r
4649   return 0;\r
4650 }\r
4651 \r
4652 DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
4653 {\r
4654   if ( wasapiPtr )\r
4655     ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
4656 \r
4657   return 0;\r
4658 }\r
4659 \r
4660 DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
4661 {\r
4662   if ( wasapiPtr )\r
4663     ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
4664 \r
4665   return 0;\r
4666 }\r
4667 \r
4668 //-----------------------------------------------------------------------------\r
4669 \r
4670 void RtApiWasapi::wasapiThread()\r
4671 {\r
4672   // as this is a new thread, we must CoInitialize it\r
4673   CoInitialize( NULL );\r
4674 \r
4675   HRESULT hr;\r
4676 \r
4677   IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4678   IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4679   IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
4680   IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
4681   HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
4682   HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
4683 \r
4684   WAVEFORMATEX* captureFormat = NULL;\r
4685   WAVEFORMATEX* renderFormat = NULL;\r
4686   float captureSrRatio = 0.0f;\r
4687   float renderSrRatio = 0.0f;\r
4688   WasapiBuffer captureBuffer;\r
4689   WasapiBuffer renderBuffer;\r
4690 \r
4691   // declare local stream variables\r
4692   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
4693   BYTE* streamBuffer = NULL;\r
4694   unsigned long captureFlags = 0;\r
4695   unsigned int bufferFrameCount = 0;\r
4696   unsigned int numFramesPadding = 0;\r
4697   unsigned int convBufferSize = 0;\r
4698   bool callbackPushed = false;\r
4699   bool callbackPulled = false;\r
4700   bool callbackStopped = false;\r
4701   int callbackResult = 0;\r
4702 \r
4703   // convBuffer is used to store converted buffers between WASAPI and the user\r
4704   char* convBuffer = NULL;\r
4705   unsigned int convBuffSize = 0;\r
4706   unsigned int deviceBuffSize = 0;\r
4707 \r
4708   errorText_.clear();\r
4709   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4710 \r
4711   // Attempt to assign "Pro Audio" characteristic to thread\r
4712   HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );\r
4713   if ( AvrtDll ) {\r
4714     DWORD taskIndex = 0;\r
4715     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
4716     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
4717     FreeLibrary( AvrtDll );\r
4718   }\r
4719 \r
4720   // start capture stream if applicable\r
4721   if ( captureAudioClient ) {\r
4722     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
4723     if ( FAILED( hr ) ) {\r
4724       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4725       goto Exit;\r
4726     }\r
4727 \r
4728     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
4729 \r
4730     // initialize capture stream according to desire buffer size\r
4731     float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
4732     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
4733 \r
4734     if ( !captureClient ) {\r
4735       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4736                                            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4737                                            desiredBufferPeriod,\r
4738                                            desiredBufferPeriod,\r
4739                                            captureFormat,\r
4740                                            NULL );\r
4741       if ( FAILED( hr ) ) {\r
4742         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
4743         goto Exit;\r
4744       }\r
4745 \r
4746       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
4747                                            ( void** ) &captureClient );\r
4748       if ( FAILED( hr ) ) {\r
4749         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
4750         goto Exit;\r
4751       }\r
4752 \r
4753       // configure captureEvent to trigger on every available capture buffer\r
4754       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4755       if ( !captureEvent ) {\r
4756         errorType = RtAudioError::SYSTEM_ERROR;\r
4757         errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
4758         goto Exit;\r
4759       }\r
4760 \r
4761       hr = captureAudioClient->SetEventHandle( captureEvent );\r
4762       if ( FAILED( hr ) ) {\r
4763         errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
4764         goto Exit;\r
4765       }\r
4766 \r
4767       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
4768       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
4769     }\r
4770 \r
4771     unsigned int inBufferSize = 0;\r
4772     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
4773     if ( FAILED( hr ) ) {\r
4774       errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
4775       goto Exit;\r
4776     }\r
4777 \r
4778     // scale outBufferSize according to stream->user sample rate ratio\r
4779     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
4780     inBufferSize *= stream_.nDeviceChannels[INPUT];\r
4781 \r
4782     // set captureBuffer size\r
4783     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
4784 \r
4785     // reset the capture stream\r
4786     hr = captureAudioClient->Reset();\r
4787     if ( FAILED( hr ) ) {\r
4788       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
4789       goto Exit;\r
4790     }\r
4791 \r
4792     // start the capture stream\r
4793     hr = captureAudioClient->Start();\r
4794     if ( FAILED( hr ) ) {\r
4795       errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
4796       goto Exit;\r
4797     }\r
4798   }\r
4799 \r
4800   // start render stream if applicable\r
4801   if ( renderAudioClient ) {\r
4802     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
4803     if ( FAILED( hr ) ) {\r
4804       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4805       goto Exit;\r
4806     }\r
4807 \r
4808     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
4809 \r
4810     // initialize render stream according to desire buffer size\r
4811     float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
4812     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
4813 \r
4814     if ( !renderClient ) {\r
4815       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4816                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4817                                           desiredBufferPeriod,\r
4818                                           desiredBufferPeriod,\r
4819                                           renderFormat,\r
4820                                           NULL );\r
4821       if ( FAILED( hr ) ) {\r
4822         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
4823         goto Exit;\r
4824       }\r
4825 \r
4826       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
4827                                           ( void** ) &renderClient );\r
4828       if ( FAILED( hr ) ) {\r
4829         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
4830         goto Exit;\r
4831       }\r
4832 \r
4833       // configure renderEvent to trigger on every available render buffer\r
4834       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4835       if ( !renderEvent ) {\r
4836         errorType = RtAudioError::SYSTEM_ERROR;\r
4837         errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
4838         goto Exit;\r
4839       }\r
4840 \r
4841       hr = renderAudioClient->SetEventHandle( renderEvent );\r
4842       if ( FAILED( hr ) ) {\r
4843         errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
4844         goto Exit;\r
4845       }\r
4846 \r
4847       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
4848       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
4849     }\r
4850 \r
4851     unsigned int outBufferSize = 0;\r
4852     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
4853     if ( FAILED( hr ) ) {\r
4854       errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
4855       goto Exit;\r
4856     }\r
4857 \r
4858     // scale inBufferSize according to user->stream sample rate ratio\r
4859     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
4860     outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
4861 \r
4862     // set renderBuffer size\r
4863     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4864 \r
4865     // reset the render stream\r
4866     hr = renderAudioClient->Reset();\r
4867     if ( FAILED( hr ) ) {\r
4868       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
4869       goto Exit;\r
4870     }\r
4871 \r
4872     // start the render stream\r
4873     hr = renderAudioClient->Start();\r
4874     if ( FAILED( hr ) ) {\r
4875       errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
4876       goto Exit;\r
4877     }\r
4878   }\r
4879 \r
4880   if ( stream_.mode == INPUT ) {\r
4881     convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4882     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4883   }\r
4884   else if ( stream_.mode == OUTPUT ) {\r
4885     convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4886     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4887   }\r
4888   else if ( stream_.mode == DUPLEX ) {\r
4889     convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4890                              ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4891     deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4892                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4893   }\r
4894 \r
4895   convBuffer = ( char* ) malloc( convBuffSize );\r
4896   stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
4897   if ( !convBuffer || !stream_.deviceBuffer ) {\r
4898     errorType = RtAudioError::MEMORY_ERROR;\r
4899     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
4900     goto Exit;\r
4901   }\r
4902 \r
4903   // stream process loop\r
4904   while ( stream_.state != STREAM_STOPPING ) {\r
4905     if ( !callbackPulled ) {\r
4906       // Callback Input\r
4907       // ==============\r
4908       // 1. Pull callback buffer from inputBuffer\r
4909       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
4910       //                          Convert callback buffer to user format\r
4911 \r
4912       if ( captureAudioClient ) {\r
4913         // Pull callback buffer from inputBuffer\r
4914         callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
4915                                                    ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
4916                                                    stream_.deviceFormat[INPUT] );\r
4917 \r
4918         if ( callbackPulled ) {\r
4919           // Convert callback buffer to user sample rate\r
4920           convertBufferWasapi( stream_.deviceBuffer,\r
4921                                convBuffer,\r
4922                                stream_.nDeviceChannels[INPUT],\r
4923                                captureFormat->nSamplesPerSec,\r
4924                                stream_.sampleRate,\r
4925                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
4926                                convBufferSize,\r
4927                                stream_.deviceFormat[INPUT] );\r
4928 \r
4929           if ( stream_.doConvertBuffer[INPUT] ) {\r
4930             // Convert callback buffer to user format\r
4931             convertBuffer( stream_.userBuffer[INPUT],\r
4932                            stream_.deviceBuffer,\r
4933                            stream_.convertInfo[INPUT] );\r
4934           }\r
4935           else {\r
4936             // no further conversion, simple copy deviceBuffer to userBuffer\r
4937             memcpy( stream_.userBuffer[INPUT],\r
4938                     stream_.deviceBuffer,\r
4939                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
4940           }\r
4941         }\r
4942       }\r
4943       else {\r
4944         // if there is no capture stream, set callbackPulled flag\r
4945         callbackPulled = true;\r
4946       }\r
4947 \r
4948       // Execute Callback\r
4949       // ================\r
4950       // 1. Execute user callback method\r
4951       // 2. Handle return value from callback\r
4952 \r
4953       // if callback has not requested the stream to stop\r
4954       if ( callbackPulled && !callbackStopped ) {\r
4955         // Execute user callback method\r
4956         callbackResult = callback( stream_.userBuffer[OUTPUT],\r
4957                                    stream_.userBuffer[INPUT],\r
4958                                    stream_.bufferSize,\r
4959                                    getStreamTime(),\r
4960                                    captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
4961                                    stream_.callbackInfo.userData );\r
4962 \r
4963         // Handle return value from callback\r
4964         if ( callbackResult == 1 ) {\r
4965           // instantiate a thread to stop this thread\r
4966           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
4967           if ( !threadHandle ) {\r
4968             errorType = RtAudioError::THREAD_ERROR;\r
4969             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
4970             goto Exit;\r
4971           }\r
4972           else if ( !CloseHandle( threadHandle ) ) {\r
4973             errorType = RtAudioError::THREAD_ERROR;\r
4974             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
4975             goto Exit;\r
4976           }\r
4977 \r
4978           callbackStopped = true;\r
4979         }\r
4980         else if ( callbackResult == 2 ) {\r
4981           // instantiate a thread to stop this thread\r
4982           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
4983           if ( !threadHandle ) {\r
4984             errorType = RtAudioError::THREAD_ERROR;\r
4985             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
4986             goto Exit;\r
4987           }\r
4988           else if ( !CloseHandle( threadHandle ) ) {\r
4989             errorType = RtAudioError::THREAD_ERROR;\r
4990             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
4991             goto Exit;\r
4992           }\r
4993 \r
4994           callbackStopped = true;\r
4995         }\r
4996       }\r
4997     }\r
4998 \r
4999     // Callback Output\r
5000     // ===============\r
5001     // 1. Convert callback buffer to stream format\r
5002     // 2. Convert callback buffer to stream sample rate and channel count\r
5003     // 3. Push callback buffer into outputBuffer\r
5004 \r
5005     if ( renderAudioClient && callbackPulled ) {\r
5006       if ( stream_.doConvertBuffer[OUTPUT] ) {\r
5007         // Convert callback buffer to stream format\r
5008         convertBuffer( stream_.deviceBuffer,\r
5009                        stream_.userBuffer[OUTPUT],\r
5010                        stream_.convertInfo[OUTPUT] );\r
5011 \r
5012       }\r
5013 \r
5014       // Convert callback buffer to stream sample rate\r
5015       convertBufferWasapi( convBuffer,\r
5016                            stream_.deviceBuffer,\r
5017                            stream_.nDeviceChannels[OUTPUT],\r
5018                            stream_.sampleRate,\r
5019                            renderFormat->nSamplesPerSec,\r
5020                            stream_.bufferSize,\r
5021                            convBufferSize,\r
5022                            stream_.deviceFormat[OUTPUT] );\r
5023 \r
5024       // Push callback buffer into outputBuffer\r
5025       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
5026                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
5027                                                 stream_.deviceFormat[OUTPUT] );\r
5028     }\r
5029     else {\r
5030       // if there is no render stream, set callbackPushed flag\r
5031       callbackPushed = true;\r
5032     }\r
5033 \r
5034     // Stream Capture\r
5035     // ==============\r
5036     // 1. Get capture buffer from stream\r
5037     // 2. Push capture buffer into inputBuffer\r
5038     // 3. If 2. was successful: Release capture buffer\r
5039 \r
5040     if ( captureAudioClient ) {\r
5041       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
5042       if ( !callbackPulled ) {\r
5043         WaitForSingleObject( captureEvent, INFINITE );\r
5044       }\r
5045 \r
5046       // Get capture buffer from stream\r
5047       hr = captureClient->GetBuffer( &streamBuffer,\r
5048                                      &bufferFrameCount,\r
5049                                      &captureFlags, NULL, NULL );\r
5050       if ( FAILED( hr ) ) {\r
5051         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
5052         goto Exit;\r
5053       }\r
5054 \r
5055       if ( bufferFrameCount != 0 ) {\r
5056         // Push capture buffer into inputBuffer\r
5057         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
5058                                        bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
5059                                        stream_.deviceFormat[INPUT] ) )\r
5060         {\r
5061           // Release capture buffer\r
5062           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
5063           if ( FAILED( hr ) ) {\r
5064             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5065             goto Exit;\r
5066           }\r
5067         }\r
5068         else\r
5069         {\r
5070           // Inform WASAPI that capture was unsuccessful\r
5071           hr = captureClient->ReleaseBuffer( 0 );\r
5072           if ( FAILED( hr ) ) {\r
5073             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5074             goto Exit;\r
5075           }\r
5076         }\r
5077       }\r
5078       else\r
5079       {\r
5080         // Inform WASAPI that capture was unsuccessful\r
5081         hr = captureClient->ReleaseBuffer( 0 );\r
5082         if ( FAILED( hr ) ) {\r
5083           errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5084           goto Exit;\r
5085         }\r
5086       }\r
5087     }\r
5088 \r
5089     // Stream Render\r
5090     // =============\r
5091     // 1. Get render buffer from stream\r
5092     // 2. Pull next buffer from outputBuffer\r
5093     // 3. If 2. was successful: Fill render buffer with next buffer\r
5094     //                          Release render buffer\r
5095 \r
5096     if ( renderAudioClient ) {\r
5097       // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
5098       if ( callbackPulled && !callbackPushed ) {\r
5099         WaitForSingleObject( renderEvent, INFINITE );\r
5100       }\r
5101 \r
5102       // Get render buffer from stream\r
5103       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
5104       if ( FAILED( hr ) ) {\r
5105         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
5106         goto Exit;\r
5107       }\r
5108 \r
5109       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
5110       if ( FAILED( hr ) ) {\r
5111         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
5112         goto Exit;\r
5113       }\r
5114 \r
5115       bufferFrameCount -= numFramesPadding;\r
5116 \r
5117       if ( bufferFrameCount != 0 ) {\r
5118         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
5119         if ( FAILED( hr ) ) {\r
5120           errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
5121           goto Exit;\r
5122         }\r
5123 \r
5124         // Pull next buffer from outputBuffer\r
5125         // Fill render buffer with next buffer\r
5126         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
5127                                       bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
5128                                       stream_.deviceFormat[OUTPUT] ) )\r
5129         {\r
5130           // Release render buffer\r
5131           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
5132           if ( FAILED( hr ) ) {\r
5133             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5134             goto Exit;\r
5135           }\r
5136         }\r
5137         else\r
5138         {\r
5139           // Inform WASAPI that render was unsuccessful\r
5140           hr = renderClient->ReleaseBuffer( 0, 0 );\r
5141           if ( FAILED( hr ) ) {\r
5142             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5143             goto Exit;\r
5144           }\r
5145         }\r
5146       }\r
5147       else\r
5148       {\r
5149         // Inform WASAPI that render was unsuccessful\r
5150         hr = renderClient->ReleaseBuffer( 0, 0 );\r
5151         if ( FAILED( hr ) ) {\r
5152           errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5153           goto Exit;\r
5154         }\r
5155       }\r
5156     }\r
5157 \r
5158     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
5159     if ( callbackPushed ) {\r
5160       callbackPulled = false;\r
5161       // tick stream time\r
5162       RtApi::tickStreamTime();\r
5163     }\r
5164 \r
5165   }\r
5166 \r
5167 Exit:\r
5168   // clean up\r
5169   CoTaskMemFree( captureFormat );\r
5170   CoTaskMemFree( renderFormat );\r
5171 \r
5172   free ( convBuffer );\r
5173 \r
5174   CoUninitialize();\r
5175 \r
5176   // update stream state\r
5177   stream_.state = STREAM_STOPPED;\r
5178 \r
5179   if ( errorText_.empty() )\r
5180     return;\r
5181   else\r
5182     error( errorType );\r
5183 }\r
5184 \r
5185 //******************** End of __WINDOWS_WASAPI__ *********************//\r
5186 #endif\r
5187 \r
5188 \r
5189 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
5190 \r
5191 // Modified by Robin Davies, October 2005\r
5192 // - Improvements to DirectX pointer chasing. \r
5193 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
5194 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
5195 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
5196 // Changed device query structure for RtAudio 4.0.7, January 2010\r
5197 \r
5198 #include <mmsystem.h>\r
5199 #include <mmreg.h>\r
5200 #include <dsound.h>\r
5201 #include <assert.h>\r
5202 #include <algorithm>\r
5203 \r
5204 #if defined(__MINGW32__)\r
5205   // missing from latest mingw winapi\r
5206 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
5207 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
5208 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
5209 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
5210 #endif\r
5211 \r
5212 #define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
5213 \r
5214 #ifdef _MSC_VER // if Microsoft Visual C++\r
5215 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
5216 #endif\r
5217 \r
5218 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
5219 {\r
5220   if ( pointer > bufferSize ) pointer -= bufferSize;\r
5221   if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
5222   if ( pointer < earlierPointer ) pointer += bufferSize;\r
5223   return pointer >= earlierPointer && pointer < laterPointer;\r
5224 }\r
5225 \r
5226 // A structure to hold various information related to the DirectSound\r
5227 // API implementation.\r
5228 struct DsHandle {\r
5229   unsigned int drainCounter; // Tracks callback counts when draining\r
5230   bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
5231   void *id[2];\r
5232   void *buffer[2];\r
5233   bool xrun[2];\r
5234   UINT bufferPointer[2];  \r
5235   DWORD dsBufferSize[2];\r
5236   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
5237   HANDLE condition;\r
5238 \r
5239   DsHandle()\r
5240     :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
5241 };\r
5242 \r
5243 // Declarations for utility functions, callbacks, and structures\r
5244 // specific to the DirectSound implementation.\r
5245 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
5246                                           LPCTSTR description,\r
5247                                           LPCTSTR module,\r
5248                                           LPVOID lpContext );\r
5249 \r
5250 static const char* getErrorString( int code );\r
5251 \r
5252 static unsigned __stdcall callbackHandler( void *ptr );\r
5253 \r
5254 struct DsDevice {\r
5255   LPGUID id[2];\r
5256   bool validId[2];\r
5257   bool found;\r
5258   std::string name;\r
5259 \r
5260   DsDevice()\r
5261   : found(false) { validId[0] = false; validId[1] = false; }\r
5262 };\r
5263 \r
5264 struct DsProbeData {\r
5265   bool isInput;\r
5266   std::vector<struct DsDevice>* dsDevices;\r
5267 };\r
5268 \r
5269 RtApiDs :: RtApiDs()\r
5270 {\r
5271   // Dsound will run both-threaded. If CoInitialize fails, then just\r
5272   // accept whatever the mainline chose for a threading model.\r
5273   coInitialized_ = false;\r
5274   HRESULT hr = CoInitialize( NULL );\r
5275   if ( !FAILED( hr ) ) coInitialized_ = true;\r
5276 }\r
5277 \r
5278 RtApiDs :: ~RtApiDs()\r
5279 {\r
5280   if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
5281   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
5282 }\r
5283 \r
5284 // The DirectSound default output is always the first device.\r
5285 unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
5286 {\r
5287   return 0;\r
5288 }\r
5289 \r
5290 // The DirectSound default input is always the first input device,\r
5291 // which is the first capture device enumerated.\r
5292 unsigned int RtApiDs :: getDefaultInputDevice( void )\r
5293 {\r
5294   return 0;\r
5295 }\r
5296 \r
5297 unsigned int RtApiDs :: getDeviceCount( void )\r
5298 {\r
5299   // Set query flag for previously found devices to false, so that we\r
5300   // can check for any devices that have disappeared.\r
5301   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5302     dsDevices[i].found = false;\r
5303 \r
5304   // Query DirectSound devices.\r
5305   struct DsProbeData probeInfo;\r
5306   probeInfo.isInput = false;\r
5307   probeInfo.dsDevices = &dsDevices;\r
5308   HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5309   if ( FAILED( result ) ) {\r
5310     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
5311     errorText_ = errorStream_.str();\r
5312     error( RtAudioError::WARNING );\r
5313   }\r
5314 \r
5315   // Query DirectSoundCapture devices.\r
5316   probeInfo.isInput = true;\r
5317   result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5318   if ( FAILED( result ) ) {\r
5319     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
5320     errorText_ = errorStream_.str();\r
5321     error( RtAudioError::WARNING );\r
5322   }\r
5323 \r
5324   // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).\r
5325   for ( unsigned int i=0; i<dsDevices.size(); ) {\r
5326     if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );\r
5327     else i++;\r
5328   }\r
5329 \r
5330   return static_cast<unsigned int>(dsDevices.size());\r
5331 }\r
5332 \r
5333 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
5334 {\r
5335   RtAudio::DeviceInfo info;\r
5336   info.probed = false;\r
5337 \r
5338   if ( dsDevices.size() == 0 ) {\r
5339     // Force a query of all devices\r
5340     getDeviceCount();\r
5341     if ( dsDevices.size() == 0 ) {\r
5342       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
5343       error( RtAudioError::INVALID_USE );\r
5344       return info;\r
5345     }\r
5346   }\r
5347 \r
5348   if ( device >= dsDevices.size() ) {\r
5349     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
5350     error( RtAudioError::INVALID_USE );\r
5351     return info;\r
5352   }\r
5353 \r
5354   HRESULT result;\r
5355   if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
5356 \r
5357   LPDIRECTSOUND output;\r
5358   DSCAPS outCaps;\r
5359   result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5360   if ( FAILED( result ) ) {\r
5361     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5362     errorText_ = errorStream_.str();\r
5363     error( RtAudioError::WARNING );\r
5364     goto probeInput;\r
5365   }\r
5366 \r
5367   outCaps.dwSize = sizeof( outCaps );\r
5368   result = output->GetCaps( &outCaps );\r
5369   if ( FAILED( result ) ) {\r
5370     output->Release();\r
5371     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
5372     errorText_ = errorStream_.str();\r
5373     error( RtAudioError::WARNING );\r
5374     goto probeInput;\r
5375   }\r
5376 \r
5377   // Get output channel information.\r
5378   info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
5379 \r
5380   // Get sample rate information.\r
5381   info.sampleRates.clear();\r
5382   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
5383     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
5384          SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {\r
5385       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
5386 \r
5387       if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
5388         info.preferredSampleRate = SAMPLE_RATES[k];\r
5389     }\r
5390   }\r
5391 \r
5392   // Get format information.\r
5393   if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
5394   if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
5395 \r
5396   output->Release();\r
5397 \r
5398   if ( getDefaultOutputDevice() == device )\r
5399     info.isDefaultOutput = true;\r
5400 \r
5401   if ( dsDevices[ device ].validId[1] == false ) {\r
5402     info.name = dsDevices[ device ].name;\r
5403     info.probed = true;\r
5404     return info;\r
5405   }\r
5406 \r
5407  probeInput:\r
5408 \r
5409   LPDIRECTSOUNDCAPTURE input;\r
5410   result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5411   if ( FAILED( result ) ) {\r
5412     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5413     errorText_ = errorStream_.str();\r
5414     error( RtAudioError::WARNING );\r
5415     return info;\r
5416   }\r
5417 \r
5418   DSCCAPS inCaps;\r
5419   inCaps.dwSize = sizeof( inCaps );\r
5420   result = input->GetCaps( &inCaps );\r
5421   if ( FAILED( result ) ) {\r
5422     input->Release();\r
5423     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
5424     errorText_ = errorStream_.str();\r
5425     error( RtAudioError::WARNING );\r
5426     return info;\r
5427   }\r
5428 \r
5429   // Get input channel information.\r
5430   info.inputChannels = inCaps.dwChannels;\r
5431 \r
5432   // Get sample rate and format information.\r
5433   std::vector<unsigned int> rates;\r
5434   if ( inCaps.dwChannels >= 2 ) {\r
5435     if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5436     if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5437     if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5438     if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5439     if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5440     if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5441     if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5442     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5443 \r
5444     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5445       if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
5446       if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
5447       if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
5448       if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
5449     }\r
5450     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5451       if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
5452       if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
5453       if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
5454       if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
5455     }\r
5456   }\r
5457   else if ( inCaps.dwChannels == 1 ) {\r
5458     if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5459     if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5460     if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5461     if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5462     if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5463     if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5464     if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5465     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5466 \r
5467     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5468       if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
5469       if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
5470       if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
5471       if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
5472     }\r
5473     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5474       if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
5475       if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
5476       if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
5477       if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
5478     }\r
5479   }\r
5480   else info.inputChannels = 0; // technically, this would be an error\r
5481 \r
5482   input->Release();\r
5483 \r
5484   if ( info.inputChannels == 0 ) return info;\r
5485 \r
5486   // Copy the supported rates to the info structure but avoid duplication.\r
5487   bool found;\r
5488   for ( unsigned int i=0; i<rates.size(); i++ ) {\r
5489     found = false;\r
5490     for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
5491       if ( rates[i] == info.sampleRates[j] ) {\r
5492         found = true;\r
5493         break;\r
5494       }\r
5495     }\r
5496     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
5497   }\r
5498   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
5499 \r
5500   // If device opens for both playback and capture, we determine the channels.\r
5501   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
5502     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
5503 \r
5504   if ( device == 0 ) info.isDefaultInput = true;\r
5505 \r
5506   // Copy name and return.\r
5507   info.name = dsDevices[ device ].name;\r
5508   info.probed = true;\r
5509   return info;\r
5510 }\r
5511 \r
5512 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
5513                                  unsigned int firstChannel, unsigned int sampleRate,\r
5514                                  RtAudioFormat format, unsigned int *bufferSize,\r
5515                                  RtAudio::StreamOptions *options )\r
5516 {\r
5517   if ( channels + firstChannel > 2 ) {\r
5518     errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
5519     return FAILURE;\r
5520   }\r
5521 \r
5522   size_t nDevices = dsDevices.size();\r
5523   if ( nDevices == 0 ) {\r
5524     // This should not happen because a check is made before this function is called.\r
5525     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
5526     return FAILURE;\r
5527   }\r
5528 \r
5529   if ( device >= nDevices ) {\r
5530     // This should not happen because a check is made before this function is called.\r
5531     errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
5532     return FAILURE;\r
5533   }\r
5534 \r
5535   if ( mode == OUTPUT ) {\r
5536     if ( dsDevices[ device ].validId[0] == false ) {\r
5537       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
5538       errorText_ = errorStream_.str();\r
5539       return FAILURE;\r
5540     }\r
5541   }\r
5542   else { // mode == INPUT\r
5543     if ( dsDevices[ device ].validId[1] == false ) {\r
5544       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
5545       errorText_ = errorStream_.str();\r
5546       return FAILURE;\r
5547     }\r
5548   }\r
5549 \r
5550   // According to a note in PortAudio, using GetDesktopWindow()\r
5551   // instead of GetForegroundWindow() is supposed to avoid problems\r
5552   // that occur when the application's window is not the foreground\r
5553   // window.  Also, if the application window closes before the\r
5554   // DirectSound buffer, DirectSound can crash.  In the past, I had\r
5555   // problems when using GetDesktopWindow() but it seems fine now\r
5556   // (January 2010).  I'll leave it commented here.\r
5557   // HWND hWnd = GetForegroundWindow();\r
5558   HWND hWnd = GetDesktopWindow();\r
5559 \r
5560   // Check the numberOfBuffers parameter and limit the lowest value to\r
5561   // two.  This is a judgement call and a value of two is probably too\r
5562   // low for capture, but it should work for playback.\r
5563   int nBuffers = 0;\r
5564   if ( options ) nBuffers = options->numberOfBuffers;\r
5565   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
5566   if ( nBuffers < 2 ) nBuffers = 3;\r
5567 \r
5568   // Check the lower range of the user-specified buffer size and set\r
5569   // (arbitrarily) to a lower bound of 32.\r
5570   if ( *bufferSize < 32 ) *bufferSize = 32;\r
5571 \r
5572   // Create the wave format structure.  The data format setting will\r
5573   // be determined later.\r
5574   WAVEFORMATEX waveFormat;\r
5575   ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
5576   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
5577   waveFormat.nChannels = channels + firstChannel;\r
5578   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
5579 \r
5580   // Determine the device buffer size. By default, we'll use the value\r
5581   // defined above (32K), but we will grow it to make allowances for\r
5582   // very large software buffer sizes.\r
5583   DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
5584   DWORD dsPointerLeadTime = 0;\r
5585 \r
5586   void *ohandle = 0, *bhandle = 0;\r
5587   HRESULT result;\r
5588   if ( mode == OUTPUT ) {\r
5589 \r
5590     LPDIRECTSOUND output;\r
5591     result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5592     if ( FAILED( result ) ) {\r
5593       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5594       errorText_ = errorStream_.str();\r
5595       return FAILURE;\r
5596     }\r
5597 \r
5598     DSCAPS outCaps;\r
5599     outCaps.dwSize = sizeof( outCaps );\r
5600     result = output->GetCaps( &outCaps );\r
5601     if ( FAILED( result ) ) {\r
5602       output->Release();\r
5603       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
5604       errorText_ = errorStream_.str();\r
5605       return FAILURE;\r
5606     }\r
5607 \r
5608     // Check channel information.\r
5609     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
5610       errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
5611       errorText_ = errorStream_.str();\r
5612       return FAILURE;\r
5613     }\r
5614 \r
5615     // Check format information.  Use 16-bit format unless not\r
5616     // supported or user requests 8-bit.\r
5617     if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
5618          !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
5619       waveFormat.wBitsPerSample = 16;\r
5620       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5621     }\r
5622     else {\r
5623       waveFormat.wBitsPerSample = 8;\r
5624       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5625     }\r
5626     stream_.userFormat = format;\r
5627 \r
5628     // Update wave format structure and buffer information.\r
5629     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5630     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5631     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5632 \r
5633     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5634     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5635       dsBufferSize *= 2;\r
5636 \r
5637     // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
5638     // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
5639     // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
5640     result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
5641     if ( FAILED( result ) ) {\r
5642       output->Release();\r
5643       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
5644       errorText_ = errorStream_.str();\r
5645       return FAILURE;\r
5646     }\r
5647 \r
5648     // Even though we will write to the secondary buffer, we need to\r
5649     // access the primary buffer to set the correct output format\r
5650     // (since the default is 8-bit, 22 kHz!).  Setup the DS primary\r
5651     // buffer description.\r
5652     DSBUFFERDESC bufferDescription;\r
5653     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5654     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5655     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
5656 \r
5657     // Obtain the primary buffer\r
5658     LPDIRECTSOUNDBUFFER buffer;\r
5659     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5660     if ( FAILED( result ) ) {\r
5661       output->Release();\r
5662       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
5663       errorText_ = errorStream_.str();\r
5664       return FAILURE;\r
5665     }\r
5666 \r
5667     // Set the primary DS buffer sound format.\r
5668     result = buffer->SetFormat( &waveFormat );\r
5669     if ( FAILED( result ) ) {\r
5670       output->Release();\r
5671       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
5672       errorText_ = errorStream_.str();\r
5673       return FAILURE;\r
5674     }\r
5675 \r
5676     // Setup the secondary DS buffer description.\r
5677     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5678     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5679     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5680                                   DSBCAPS_GLOBALFOCUS |\r
5681                                   DSBCAPS_GETCURRENTPOSITION2 |\r
5682                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
5683     bufferDescription.dwBufferBytes = dsBufferSize;\r
5684     bufferDescription.lpwfxFormat = &waveFormat;\r
5685 \r
5686     // Try to create the secondary DS buffer.  If that doesn't work,\r
5687     // try to use software mixing.  Otherwise, there's a problem.\r
5688     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5689     if ( FAILED( result ) ) {\r
5690       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5691                                     DSBCAPS_GLOBALFOCUS |\r
5692                                     DSBCAPS_GETCURRENTPOSITION2 |\r
5693                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
5694       result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5695       if ( FAILED( result ) ) {\r
5696         output->Release();\r
5697         errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
5698         errorText_ = errorStream_.str();\r
5699         return FAILURE;\r
5700       }\r
5701     }\r
5702 \r
5703     // Get the buffer size ... might be different from what we specified.\r
5704     DSBCAPS dsbcaps;\r
5705     dsbcaps.dwSize = sizeof( DSBCAPS );\r
5706     result = buffer->GetCaps( &dsbcaps );\r
5707     if ( FAILED( result ) ) {\r
5708       output->Release();\r
5709       buffer->Release();\r
5710       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5711       errorText_ = errorStream_.str();\r
5712       return FAILURE;\r
5713     }\r
5714 \r
5715     dsBufferSize = dsbcaps.dwBufferBytes;\r
5716 \r
5717     // Lock the DS buffer\r
5718     LPVOID audioPtr;\r
5719     DWORD dataLen;\r
5720     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5721     if ( FAILED( result ) ) {\r
5722       output->Release();\r
5723       buffer->Release();\r
5724       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
5725       errorText_ = errorStream_.str();\r
5726       return FAILURE;\r
5727     }\r
5728 \r
5729     // Zero the DS buffer\r
5730     ZeroMemory( audioPtr, dataLen );\r
5731 \r
5732     // Unlock the DS buffer\r
5733     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5734     if ( FAILED( result ) ) {\r
5735       output->Release();\r
5736       buffer->Release();\r
5737       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
5738       errorText_ = errorStream_.str();\r
5739       return FAILURE;\r
5740     }\r
5741 \r
5742     ohandle = (void *) output;\r
5743     bhandle = (void *) buffer;\r
5744   }\r
5745 \r
5746   if ( mode == INPUT ) {\r
5747 \r
5748     LPDIRECTSOUNDCAPTURE input;\r
5749     result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5750     if ( FAILED( result ) ) {\r
5751       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5752       errorText_ = errorStream_.str();\r
5753       return FAILURE;\r
5754     }\r
5755 \r
5756     DSCCAPS inCaps;\r
5757     inCaps.dwSize = sizeof( inCaps );\r
5758     result = input->GetCaps( &inCaps );\r
5759     if ( FAILED( result ) ) {\r
5760       input->Release();\r
5761       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
5762       errorText_ = errorStream_.str();\r
5763       return FAILURE;\r
5764     }\r
5765 \r
5766     // Check channel information.\r
5767     if ( inCaps.dwChannels < channels + firstChannel ) {\r
5768       errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
5769       return FAILURE;\r
5770     }\r
5771 \r
5772     // Check format information.  Use 16-bit format unless user\r
5773     // requests 8-bit.\r
5774     DWORD deviceFormats;\r
5775     if ( channels + firstChannel == 2 ) {\r
5776       deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
5777       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5778         waveFormat.wBitsPerSample = 8;\r
5779         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5780       }\r
5781       else { // assume 16-bit is supported\r
5782         waveFormat.wBitsPerSample = 16;\r
5783         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5784       }\r
5785     }\r
5786     else { // channel == 1\r
5787       deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
5788       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5789         waveFormat.wBitsPerSample = 8;\r
5790         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5791       }\r
5792       else { // assume 16-bit is supported\r
5793         waveFormat.wBitsPerSample = 16;\r
5794         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5795       }\r
5796     }\r
5797     stream_.userFormat = format;\r
5798 \r
5799     // Update wave format structure and buffer information.\r
5800     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5801     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5802     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5803 \r
5804     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5805     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5806       dsBufferSize *= 2;\r
5807 \r
5808     // Setup the secondary DS buffer description.\r
5809     DSCBUFFERDESC bufferDescription;\r
5810     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
5811     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
5812     bufferDescription.dwFlags = 0;\r
5813     bufferDescription.dwReserved = 0;\r
5814     bufferDescription.dwBufferBytes = dsBufferSize;\r
5815     bufferDescription.lpwfxFormat = &waveFormat;\r
5816 \r
5817     // Create the capture buffer.\r
5818     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
5819     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
5820     if ( FAILED( result ) ) {\r
5821       input->Release();\r
5822       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
5823       errorText_ = errorStream_.str();\r
5824       return FAILURE;\r
5825     }\r
5826 \r
5827     // Get the buffer size ... might be different from what we specified.\r
5828     DSCBCAPS dscbcaps;\r
5829     dscbcaps.dwSize = sizeof( DSCBCAPS );\r
5830     result = buffer->GetCaps( &dscbcaps );\r
5831     if ( FAILED( result ) ) {\r
5832       input->Release();\r
5833       buffer->Release();\r
5834       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5835       errorText_ = errorStream_.str();\r
5836       return FAILURE;\r
5837     }\r
5838 \r
5839     dsBufferSize = dscbcaps.dwBufferBytes;\r
5840 \r
5841     // NOTE: We could have a problem here if this is a duplex stream\r
5842     // and the play and capture hardware buffer sizes are different\r
5843     // (I'm actually not sure if that is a problem or not).\r
5844     // Currently, we are not verifying that.\r
5845 \r
5846     // Lock the capture buffer\r
5847     LPVOID audioPtr;\r
5848     DWORD dataLen;\r
5849     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5850     if ( FAILED( result ) ) {\r
5851       input->Release();\r
5852       buffer->Release();\r
5853       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
5854       errorText_ = errorStream_.str();\r
5855       return FAILURE;\r
5856     }\r
5857 \r
5858     // Zero the buffer\r
5859     ZeroMemory( audioPtr, dataLen );\r
5860 \r
5861     // Unlock the buffer\r
5862     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5863     if ( FAILED( result ) ) {\r
5864       input->Release();\r
5865       buffer->Release();\r
5866       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
5867       errorText_ = errorStream_.str();\r
5868       return FAILURE;\r
5869     }\r
5870 \r
5871     ohandle = (void *) input;\r
5872     bhandle = (void *) buffer;\r
5873   }\r
5874 \r
5875   // Set various stream parameters\r
5876   DsHandle *handle = 0;\r
5877   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
5878   stream_.nUserChannels[mode] = channels;\r
5879   stream_.bufferSize = *bufferSize;\r
5880   stream_.channelOffset[mode] = firstChannel;\r
5881   stream_.deviceInterleaved[mode] = true;\r
5882   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
5883   else stream_.userInterleaved = true;\r
5884 \r
5885   // Set flag for buffer conversion\r
5886   stream_.doConvertBuffer[mode] = false;\r
5887   if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
5888     stream_.doConvertBuffer[mode] = true;\r
5889   if (stream_.userFormat != stream_.deviceFormat[mode])\r
5890     stream_.doConvertBuffer[mode] = true;\r
5891   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
5892        stream_.nUserChannels[mode] > 1 )\r
5893     stream_.doConvertBuffer[mode] = true;\r
5894 \r
5895   // Allocate necessary internal buffers\r
5896   long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
5897   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
5898   if ( stream_.userBuffer[mode] == NULL ) {\r
5899     errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
5900     goto error;\r
5901   }\r
5902 \r
5903   if ( stream_.doConvertBuffer[mode] ) {\r
5904 \r
5905     bool makeBuffer = true;\r
5906     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
5907     if ( mode == INPUT ) {\r
5908       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
5909         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
5910         if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
5911       }\r
5912     }\r
5913 \r
5914     if ( makeBuffer ) {\r
5915       bufferBytes *= *bufferSize;\r
5916       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
5917       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
5918       if ( stream_.deviceBuffer == NULL ) {\r
5919         errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
5920         goto error;\r
5921       }\r
5922     }\r
5923   }\r
5924 \r
5925   // Allocate our DsHandle structures for the stream.\r
5926   if ( stream_.apiHandle == 0 ) {\r
5927     try {\r
5928       handle = new DsHandle;\r
5929     }\r
5930     catch ( std::bad_alloc& ) {\r
5931       errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
5932       goto error;\r
5933     }\r
5934 \r
5935     // Create a manual-reset event.\r
5936     handle->condition = CreateEvent( NULL,   // no security\r
5937                                      TRUE,   // manual-reset\r
5938                                      FALSE,  // non-signaled initially\r
5939                                      NULL ); // unnamed\r
5940     stream_.apiHandle = (void *) handle;\r
5941   }\r
5942   else\r
5943     handle = (DsHandle *) stream_.apiHandle;\r
5944   handle->id[mode] = ohandle;\r
5945   handle->buffer[mode] = bhandle;\r
5946   handle->dsBufferSize[mode] = dsBufferSize;\r
5947   handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
5948 \r
5949   stream_.device[mode] = device;\r
5950   stream_.state = STREAM_STOPPED;\r
5951   if ( stream_.mode == OUTPUT && mode == INPUT )\r
5952     // We had already set up an output stream.\r
5953     stream_.mode = DUPLEX;\r
5954   else\r
5955     stream_.mode = mode;\r
5956   stream_.nBuffers = nBuffers;\r
5957   stream_.sampleRate = sampleRate;\r
5958 \r
5959   // Setup the buffer conversion information structure.\r
5960   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
5961 \r
5962   // Setup the callback thread.\r
5963   if ( stream_.callbackInfo.isRunning == false ) {\r
5964     unsigned threadId;\r
5965     stream_.callbackInfo.isRunning = true;\r
5966     stream_.callbackInfo.object = (void *) this;\r
5967     stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
5968                                                   &stream_.callbackInfo, 0, &threadId );\r
5969     if ( stream_.callbackInfo.thread == 0 ) {\r
5970       errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
5971       goto error;\r
5972     }\r
5973 \r
5974     // Boost DS thread priority\r
5975     SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
5976   }\r
5977   return SUCCESS;\r
5978 \r
5979  error:\r
5980   if ( handle ) {\r
5981     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5982       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5983       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5984       if ( buffer ) buffer->Release();\r
5985       object->Release();\r
5986     }\r
5987     if ( handle->buffer[1] ) {\r
5988       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
5989       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
5990       if ( buffer ) buffer->Release();\r
5991       object->Release();\r
5992     }\r
5993     CloseHandle( handle->condition );\r
5994     delete handle;\r
5995     stream_.apiHandle = 0;\r
5996   }\r
5997 \r
5998   for ( int i=0; i<2; i++ ) {\r
5999     if ( stream_.userBuffer[i] ) {\r
6000       free( stream_.userBuffer[i] );\r
6001       stream_.userBuffer[i] = 0;\r
6002     }\r
6003   }\r
6004 \r
6005   if ( stream_.deviceBuffer ) {\r
6006     free( stream_.deviceBuffer );\r
6007     stream_.deviceBuffer = 0;\r
6008   }\r
6009 \r
6010   stream_.state = STREAM_CLOSED;\r
6011   return FAILURE;\r
6012 }\r
6013 \r
6014 void RtApiDs :: closeStream()\r
6015 {\r
6016   if ( stream_.state == STREAM_CLOSED ) {\r
6017     errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
6018     error( RtAudioError::WARNING );\r
6019     return;\r
6020   }\r
6021 \r
6022   // Stop the callback thread.\r
6023   stream_.callbackInfo.isRunning = false;\r
6024   WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
6025   CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
6026 \r
6027   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6028   if ( handle ) {\r
6029     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
6030       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
6031       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6032       if ( buffer ) {\r
6033         buffer->Stop();\r
6034         buffer->Release();\r
6035       }\r
6036       object->Release();\r
6037     }\r
6038     if ( handle->buffer[1] ) {\r
6039       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
6040       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6041       if ( buffer ) {\r
6042         buffer->Stop();\r
6043         buffer->Release();\r
6044       }\r
6045       object->Release();\r
6046     }\r
6047     CloseHandle( handle->condition );\r
6048     delete handle;\r
6049     stream_.apiHandle = 0;\r
6050   }\r
6051 \r
6052   for ( int i=0; i<2; i++ ) {\r
6053     if ( stream_.userBuffer[i] ) {\r
6054       free( stream_.userBuffer[i] );\r
6055       stream_.userBuffer[i] = 0;\r
6056     }\r
6057   }\r
6058 \r
6059   if ( stream_.deviceBuffer ) {\r
6060     free( stream_.deviceBuffer );\r
6061     stream_.deviceBuffer = 0;\r
6062   }\r
6063 \r
6064   stream_.mode = UNINITIALIZED;\r
6065   stream_.state = STREAM_CLOSED;\r
6066 }\r
6067 \r
6068 void RtApiDs :: startStream()\r
6069 {\r
6070   verifyStream();\r
6071   if ( stream_.state == STREAM_RUNNING ) {\r
6072     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
6073     error( RtAudioError::WARNING );\r
6074     return;\r
6075   }\r
6076 \r
6077   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6078 \r
6079   // Increase scheduler frequency on lesser windows (a side-effect of\r
6080   // increasing timer accuracy).  On greater windows (Win2K or later),\r
6081   // this is already in effect.\r
6082   timeBeginPeriod( 1 ); \r
6083 \r
6084   buffersRolling = false;\r
6085   duplexPrerollBytes = 0;\r
6086 \r
6087   if ( stream_.mode == DUPLEX ) {\r
6088     // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
6089     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
6090   }\r
6091 \r
6092   HRESULT result = 0;\r
6093   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6094 \r
6095     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6096     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
6097     if ( FAILED( result ) ) {\r
6098       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
6099       errorText_ = errorStream_.str();\r
6100       goto unlock;\r
6101     }\r
6102   }\r
6103 \r
6104   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6105 \r
6106     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6107     result = buffer->Start( DSCBSTART_LOOPING );\r
6108     if ( FAILED( result ) ) {\r
6109       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
6110       errorText_ = errorStream_.str();\r
6111       goto unlock;\r
6112     }\r
6113   }\r
6114 \r
6115   handle->drainCounter = 0;\r
6116   handle->internalDrain = false;\r
6117   ResetEvent( handle->condition );\r
6118   stream_.state = STREAM_RUNNING;\r
6119 \r
6120  unlock:\r
6121   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6122 }\r
6123 \r
6124 void RtApiDs :: stopStream()\r
6125 {\r
6126   verifyStream();\r
6127   if ( stream_.state == STREAM_STOPPED ) {\r
6128     errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
6129     error( RtAudioError::WARNING );\r
6130     return;\r
6131   }\r
6132 \r
6133   HRESULT result = 0;\r
6134   LPVOID audioPtr;\r
6135   DWORD dataLen;\r
6136   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6137   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6138     if ( handle->drainCounter == 0 ) {\r
6139       handle->drainCounter = 2;\r
6140       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
6141     }\r
6142 \r
6143     stream_.state = STREAM_STOPPED;\r
6144 \r
6145     MUTEX_LOCK( &stream_.mutex );\r
6146 \r
6147     // Stop the buffer and clear memory\r
6148     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6149     result = buffer->Stop();\r
6150     if ( FAILED( result ) ) {\r
6151       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
6152       errorText_ = errorStream_.str();\r
6153       goto unlock;\r
6154     }\r
6155 \r
6156     // Lock the buffer and clear it so that if we start to play again,\r
6157     // we won't have old data playing.\r
6158     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6159     if ( FAILED( result ) ) {\r
6160       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
6161       errorText_ = errorStream_.str();\r
6162       goto unlock;\r
6163     }\r
6164 \r
6165     // Zero the DS buffer\r
6166     ZeroMemory( audioPtr, dataLen );\r
6167 \r
6168     // Unlock the DS buffer\r
6169     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6170     if ( FAILED( result ) ) {\r
6171       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
6172       errorText_ = errorStream_.str();\r
6173       goto unlock;\r
6174     }\r
6175 \r
6176     // If we start playing again, we must begin at beginning of buffer.\r
6177     handle->bufferPointer[0] = 0;\r
6178   }\r
6179 \r
6180   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6181     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6182     audioPtr = NULL;\r
6183     dataLen = 0;\r
6184 \r
6185     stream_.state = STREAM_STOPPED;\r
6186 \r
6187     if ( stream_.mode != DUPLEX )\r
6188       MUTEX_LOCK( &stream_.mutex );\r
6189 \r
6190     result = buffer->Stop();\r
6191     if ( FAILED( result ) ) {\r
6192       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
6193       errorText_ = errorStream_.str();\r
6194       goto unlock;\r
6195     }\r
6196 \r
6197     // Lock the buffer and clear it so that if we start to play again,\r
6198     // we won't have old data playing.\r
6199     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6200     if ( FAILED( result ) ) {\r
6201       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
6202       errorText_ = errorStream_.str();\r
6203       goto unlock;\r
6204     }\r
6205 \r
6206     // Zero the DS buffer\r
6207     ZeroMemory( audioPtr, dataLen );\r
6208 \r
6209     // Unlock the DS buffer\r
6210     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6211     if ( FAILED( result ) ) {\r
6212       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
6213       errorText_ = errorStream_.str();\r
6214       goto unlock;\r
6215     }\r
6216 \r
6217     // If we start recording again, we must begin at beginning of buffer.\r
6218     handle->bufferPointer[1] = 0;\r
6219   }\r
6220 \r
6221  unlock:\r
6222   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
6223   MUTEX_UNLOCK( &stream_.mutex );\r
6224 \r
6225   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6226 }\r
6227 \r
6228 void RtApiDs :: abortStream()\r
6229 {\r
6230   verifyStream();\r
6231   if ( stream_.state == STREAM_STOPPED ) {\r
6232     errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
6233     error( RtAudioError::WARNING );\r
6234     return;\r
6235   }\r
6236 \r
6237   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6238   handle->drainCounter = 2;\r
6239 \r
6240   stopStream();\r
6241 }\r
6242 \r
6243 void RtApiDs :: callbackEvent()\r
6244 {\r
6245   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
6246     Sleep( 50 ); // sleep 50 milliseconds\r
6247     return;\r
6248   }\r
6249 \r
6250   if ( stream_.state == STREAM_CLOSED ) {\r
6251     errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
6252     error( RtAudioError::WARNING );\r
6253     return;\r
6254   }\r
6255 \r
6256   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
6257   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6258 \r
6259   // Check if we were draining the stream and signal is finished.\r
6260   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
6261 \r
6262     stream_.state = STREAM_STOPPING;\r
6263     if ( handle->internalDrain == false )\r
6264       SetEvent( handle->condition );\r
6265     else\r
6266       stopStream();\r
6267     return;\r
6268   }\r
6269 \r
6270   // Invoke user callback to get fresh output data UNLESS we are\r
6271   // draining stream.\r
6272   if ( handle->drainCounter == 0 ) {\r
6273     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
6274     double streamTime = getStreamTime();\r
6275     RtAudioStreamStatus status = 0;\r
6276     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
6277       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
6278       handle->xrun[0] = false;\r
6279     }\r
6280     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
6281       status |= RTAUDIO_INPUT_OVERFLOW;\r
6282       handle->xrun[1] = false;\r
6283     }\r
6284     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
6285                                   stream_.bufferSize, streamTime, status, info->userData );\r
6286     if ( cbReturnValue == 2 ) {\r
6287       stream_.state = STREAM_STOPPING;\r
6288       handle->drainCounter = 2;\r
6289       abortStream();\r
6290       return;\r
6291     }\r
6292     else if ( cbReturnValue == 1 ) {\r
6293       handle->drainCounter = 1;\r
6294       handle->internalDrain = true;\r
6295     }\r
6296   }\r
6297 \r
6298   HRESULT result;\r
6299   DWORD currentWritePointer, safeWritePointer;\r
6300   DWORD currentReadPointer, safeReadPointer;\r
6301   UINT nextWritePointer;\r
6302 \r
6303   LPVOID buffer1 = NULL;\r
6304   LPVOID buffer2 = NULL;\r
6305   DWORD bufferSize1 = 0;\r
6306   DWORD bufferSize2 = 0;\r
6307 \r
6308   char *buffer;\r
6309   long bufferBytes;\r
6310 \r
6311   MUTEX_LOCK( &stream_.mutex );\r
6312   if ( stream_.state == STREAM_STOPPED ) {\r
6313     MUTEX_UNLOCK( &stream_.mutex );\r
6314     return;\r
6315   }\r
6316 \r
6317   if ( buffersRolling == false ) {\r
6318     if ( stream_.mode == DUPLEX ) {\r
6319       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6320 \r
6321       // It takes a while for the devices to get rolling. As a result,\r
6322       // there's no guarantee that the capture and write device pointers\r
6323       // will move in lockstep.  Wait here for both devices to start\r
6324       // rolling, and then set our buffer pointers accordingly.\r
6325       // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
6326       // bytes later than the write buffer.\r
6327 \r
6328       // Stub: a serious risk of having a pre-emptive scheduling round\r
6329       // take place between the two GetCurrentPosition calls... but I'm\r
6330       // really not sure how to solve the problem.  Temporarily boost to\r
6331       // Realtime priority, maybe; but I'm not sure what priority the\r
6332       // DirectSound service threads run at. We *should* be roughly\r
6333       // within a ms or so of correct.\r
6334 \r
6335       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6336       LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6337 \r
6338       DWORD startSafeWritePointer, startSafeReadPointer;\r
6339 \r
6340       result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
6341       if ( FAILED( result ) ) {\r
6342         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6343         errorText_ = errorStream_.str();\r
6344         MUTEX_UNLOCK( &stream_.mutex );\r
6345         error( RtAudioError::SYSTEM_ERROR );\r
6346         return;\r
6347       }\r
6348       result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
6349       if ( FAILED( result ) ) {\r
6350         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6351         errorText_ = errorStream_.str();\r
6352         MUTEX_UNLOCK( &stream_.mutex );\r
6353         error( RtAudioError::SYSTEM_ERROR );\r
6354         return;\r
6355       }\r
6356       while ( true ) {\r
6357         result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
6358         if ( FAILED( result ) ) {\r
6359           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6360           errorText_ = errorStream_.str();\r
6361           MUTEX_UNLOCK( &stream_.mutex );\r
6362           error( RtAudioError::SYSTEM_ERROR );\r
6363           return;\r
6364         }\r
6365         result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
6366         if ( FAILED( result ) ) {\r
6367           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6368           errorText_ = errorStream_.str();\r
6369           MUTEX_UNLOCK( &stream_.mutex );\r
6370           error( RtAudioError::SYSTEM_ERROR );\r
6371           return;\r
6372         }\r
6373         if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
6374         Sleep( 1 );\r
6375       }\r
6376 \r
6377       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6378 \r
6379       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6380       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6381       handle->bufferPointer[1] = safeReadPointer;\r
6382     }\r
6383     else if ( stream_.mode == OUTPUT ) {\r
6384 \r
6385       // Set the proper nextWritePosition after initial startup.\r
6386       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6387       result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6388       if ( FAILED( result ) ) {\r
6389         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6390         errorText_ = errorStream_.str();\r
6391         MUTEX_UNLOCK( &stream_.mutex );\r
6392         error( RtAudioError::SYSTEM_ERROR );\r
6393         return;\r
6394       }\r
6395       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6396       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6397     }\r
6398 \r
6399     buffersRolling = true;\r
6400   }\r
6401 \r
6402   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6403     \r
6404     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6405 \r
6406     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
6407       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6408       bufferBytes *= formatBytes( stream_.userFormat );\r
6409       memset( stream_.userBuffer[0], 0, bufferBytes );\r
6410     }\r
6411 \r
6412     // Setup parameters and do buffer conversion if necessary.\r
6413     if ( stream_.doConvertBuffer[0] ) {\r
6414       buffer = stream_.deviceBuffer;\r
6415       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
6416       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
6417       bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
6418     }\r
6419     else {\r
6420       buffer = stream_.userBuffer[0];\r
6421       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6422       bufferBytes *= formatBytes( stream_.userFormat );\r
6423     }\r
6424 \r
6425     // No byte swapping necessary in DirectSound implementation.\r
6426 \r
6427     // Ahhh ... windoze.  16-bit data is signed but 8-bit data is\r
6428     // unsigned.  So, we need to convert our signed 8-bit data here to\r
6429     // unsigned.\r
6430     if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
6431       for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
6432 \r
6433     DWORD dsBufferSize = handle->dsBufferSize[0];\r
6434     nextWritePointer = handle->bufferPointer[0];\r
6435 \r
6436     DWORD endWrite, leadPointer;\r
6437     while ( true ) {\r
6438       // Find out where the read and "safe write" pointers are.\r
6439       result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6440       if ( FAILED( result ) ) {\r
6441         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6442         errorText_ = errorStream_.str();\r
6443         MUTEX_UNLOCK( &stream_.mutex );\r
6444         error( RtAudioError::SYSTEM_ERROR );\r
6445         return;\r
6446       }\r
6447 \r
6448       // We will copy our output buffer into the region between\r
6449       // safeWritePointer and leadPointer.  If leadPointer is not\r
6450       // beyond the next endWrite position, wait until it is.\r
6451       leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
6452       //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
6453       if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
6454       if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
6455       endWrite = nextWritePointer + bufferBytes;\r
6456 \r
6457       // Check whether the entire write region is behind the play pointer.\r
6458       if ( leadPointer >= endWrite ) break;\r
6459 \r
6460       // If we are here, then we must wait until the leadPointer advances\r
6461       // beyond the end of our next write region. We use the\r
6462       // Sleep() function to suspend operation until that happens.\r
6463       double millis = ( endWrite - leadPointer ) * 1000.0;\r
6464       millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
6465       if ( millis < 1.0 ) millis = 1.0;\r
6466       Sleep( (DWORD) millis );\r
6467     }\r
6468 \r
6469     if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
6470          || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
6471       // We've strayed into the forbidden zone ... resync the read pointer.\r
6472       handle->xrun[0] = true;\r
6473       nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
6474       if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
6475       handle->bufferPointer[0] = nextWritePointer;\r
6476       endWrite = nextWritePointer + bufferBytes;\r
6477     }\r
6478 \r
6479     // Lock free space in the buffer\r
6480     result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
6481                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6482     if ( FAILED( result ) ) {\r
6483       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
6484       errorText_ = errorStream_.str();\r
6485       MUTEX_UNLOCK( &stream_.mutex );\r
6486       error( RtAudioError::SYSTEM_ERROR );\r
6487       return;\r
6488     }\r
6489 \r
6490     // Copy our buffer into the DS buffer\r
6491     CopyMemory( buffer1, buffer, bufferSize1 );\r
6492     if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
6493 \r
6494     // Update our buffer offset and unlock sound buffer\r
6495     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6496     if ( FAILED( result ) ) {\r
6497       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
6498       errorText_ = errorStream_.str();\r
6499       MUTEX_UNLOCK( &stream_.mutex );\r
6500       error( RtAudioError::SYSTEM_ERROR );\r
6501       return;\r
6502     }\r
6503     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6504     handle->bufferPointer[0] = nextWritePointer;\r
6505   }\r
6506 \r
6507   // Don't bother draining input\r
6508   if ( handle->drainCounter ) {\r
6509     handle->drainCounter++;\r
6510     goto unlock;\r
6511   }\r
6512 \r
6513   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6514 \r
6515     // Setup parameters.\r
6516     if ( stream_.doConvertBuffer[1] ) {\r
6517       buffer = stream_.deviceBuffer;\r
6518       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
6519       bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
6520     }\r
6521     else {\r
6522       buffer = stream_.userBuffer[1];\r
6523       bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
6524       bufferBytes *= formatBytes( stream_.userFormat );\r
6525     }\r
6526 \r
6527     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6528     long nextReadPointer = handle->bufferPointer[1];\r
6529     DWORD dsBufferSize = handle->dsBufferSize[1];\r
6530 \r
6531     // Find out where the write and "safe read" pointers are.\r
6532     result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6533     if ( FAILED( result ) ) {\r
6534       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6535       errorText_ = errorStream_.str();\r
6536       MUTEX_UNLOCK( &stream_.mutex );\r
6537       error( RtAudioError::SYSTEM_ERROR );\r
6538       return;\r
6539     }\r
6540 \r
6541     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6542     DWORD endRead = nextReadPointer + bufferBytes;\r
6543 \r
6544     // Handling depends on whether we are INPUT or DUPLEX. \r
6545     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
6546     // then a wait here will drag the write pointers into the forbidden zone.\r
6547     // \r
6548     // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
6549     // it's in a safe position. This causes dropouts, but it seems to be the only \r
6550     // practical way to sync up the read and write pointers reliably, given the \r
6551     // the very complex relationship between phase and increment of the read and write \r
6552     // pointers.\r
6553     //\r
6554     // In order to minimize audible dropouts in DUPLEX mode, we will\r
6555     // provide a pre-roll period of 0.5 seconds in which we return\r
6556     // zeros from the read buffer while the pointers sync up.\r
6557 \r
6558     if ( stream_.mode == DUPLEX ) {\r
6559       if ( safeReadPointer < endRead ) {\r
6560         if ( duplexPrerollBytes <= 0 ) {\r
6561           // Pre-roll time over. Be more agressive.\r
6562           int adjustment = endRead-safeReadPointer;\r
6563 \r
6564           handle->xrun[1] = true;\r
6565           // Two cases:\r
6566           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
6567           //     and perform fine adjustments later.\r
6568           //   - small adjustments: back off by twice as much.\r
6569           if ( adjustment >= 2*bufferBytes )\r
6570             nextReadPointer = safeReadPointer-2*bufferBytes;\r
6571           else\r
6572             nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
6573 \r
6574           if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6575 \r
6576         }\r
6577         else {\r
6578           // In pre=roll time. Just do it.\r
6579           nextReadPointer = safeReadPointer - bufferBytes;\r
6580           while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6581         }\r
6582         endRead = nextReadPointer + bufferBytes;\r
6583       }\r
6584     }\r
6585     else { // mode == INPUT\r
6586       while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
6587         // See comments for playback.\r
6588         double millis = (endRead - safeReadPointer) * 1000.0;\r
6589         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
6590         if ( millis < 1.0 ) millis = 1.0;\r
6591         Sleep( (DWORD) millis );\r
6592 \r
6593         // Wake up and find out where we are now.\r
6594         result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6595         if ( FAILED( result ) ) {\r
6596           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6597           errorText_ = errorStream_.str();\r
6598           MUTEX_UNLOCK( &stream_.mutex );\r
6599           error( RtAudioError::SYSTEM_ERROR );\r
6600           return;\r
6601         }\r
6602       \r
6603         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6604       }\r
6605     }\r
6606 \r
6607     // Lock free space in the buffer\r
6608     result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
6609                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6610     if ( FAILED( result ) ) {\r
6611       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
6612       errorText_ = errorStream_.str();\r
6613       MUTEX_UNLOCK( &stream_.mutex );\r
6614       error( RtAudioError::SYSTEM_ERROR );\r
6615       return;\r
6616     }\r
6617 \r
6618     if ( duplexPrerollBytes <= 0 ) {\r
6619       // Copy our buffer into the DS buffer\r
6620       CopyMemory( buffer, buffer1, bufferSize1 );\r
6621       if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
6622     }\r
6623     else {\r
6624       memset( buffer, 0, bufferSize1 );\r
6625       if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
6626       duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
6627     }\r
6628 \r
6629     // Update our buffer offset and unlock sound buffer\r
6630     nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6631     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6632     if ( FAILED( result ) ) {\r
6633       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
6634       errorText_ = errorStream_.str();\r
6635       MUTEX_UNLOCK( &stream_.mutex );\r
6636       error( RtAudioError::SYSTEM_ERROR );\r
6637       return;\r
6638     }\r
6639     handle->bufferPointer[1] = nextReadPointer;\r
6640 \r
6641     // No byte swapping necessary in DirectSound implementation.\r
6642 \r
6643     // If necessary, convert 8-bit data from unsigned to signed.\r
6644     if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
6645       for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
6646 \r
6647     // Do buffer conversion if necessary.\r
6648     if ( stream_.doConvertBuffer[1] )\r
6649       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
6650   }\r
6651 \r
6652  unlock:\r
6653   MUTEX_UNLOCK( &stream_.mutex );\r
6654   RtApi::tickStreamTime();\r
6655 }\r
6656 \r
6657 // Definitions for utility functions and callbacks\r
6658 // specific to the DirectSound implementation.\r
6659 \r
6660 static unsigned __stdcall callbackHandler( void *ptr )\r
6661 {\r
6662   CallbackInfo *info = (CallbackInfo *) ptr;\r
6663   RtApiDs *object = (RtApiDs *) info->object;\r
6664   bool* isRunning = &info->isRunning;\r
6665 \r
6666   while ( *isRunning == true ) {\r
6667     object->callbackEvent();\r
6668   }\r
6669 \r
6670   _endthreadex( 0 );\r
6671   return 0;\r
6672 }\r
6673 \r
6674 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
6675                                           LPCTSTR description,\r
6676                                           LPCTSTR /*module*/,\r
6677                                           LPVOID lpContext )\r
6678 {\r
6679   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
6680   std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
6681 \r
6682   HRESULT hr;\r
6683   bool validDevice = false;\r
6684   if ( probeInfo.isInput == true ) {\r
6685     DSCCAPS caps;\r
6686     LPDIRECTSOUNDCAPTURE object;\r
6687 \r
6688     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
6689     if ( hr != DS_OK ) return TRUE;\r
6690 \r
6691     caps.dwSize = sizeof(caps);\r
6692     hr = object->GetCaps( &caps );\r
6693     if ( hr == DS_OK ) {\r
6694       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
6695         validDevice = true;\r
6696     }\r
6697     object->Release();\r
6698   }\r
6699   else {\r
6700     DSCAPS caps;\r
6701     LPDIRECTSOUND object;\r
6702     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
6703     if ( hr != DS_OK ) return TRUE;\r
6704 \r
6705     caps.dwSize = sizeof(caps);\r
6706     hr = object->GetCaps( &caps );\r
6707     if ( hr == DS_OK ) {\r
6708       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
6709         validDevice = true;\r
6710     }\r
6711     object->Release();\r
6712   }\r
6713 \r
6714   // If good device, then save its name and guid.\r
6715   std::string name = convertCharPointerToStdString( description );\r
6716   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
6717   if ( lpguid == NULL )\r
6718     name = "Default Device";\r
6719   if ( validDevice ) {\r
6720     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
6721       if ( dsDevices[i].name == name ) {\r
6722         dsDevices[i].found = true;\r
6723         if ( probeInfo.isInput ) {\r
6724           dsDevices[i].id[1] = lpguid;\r
6725           dsDevices[i].validId[1] = true;\r
6726         }\r
6727         else {\r
6728           dsDevices[i].id[0] = lpguid;\r
6729           dsDevices[i].validId[0] = true;\r
6730         }\r
6731         return TRUE;\r
6732       }\r
6733     }\r
6734 \r
6735     DsDevice device;\r
6736     device.name = name;\r
6737     device.found = true;\r
6738     if ( probeInfo.isInput ) {\r
6739       device.id[1] = lpguid;\r
6740       device.validId[1] = true;\r
6741     }\r
6742     else {\r
6743       device.id[0] = lpguid;\r
6744       device.validId[0] = true;\r
6745     }\r
6746     dsDevices.push_back( device );\r
6747   }\r
6748 \r
6749   return TRUE;\r
6750 }\r
6751 \r
6752 static const char* getErrorString( int code )\r
6753 {\r
6754   switch ( code ) {\r
6755 \r
6756   case DSERR_ALLOCATED:\r
6757     return "Already allocated";\r
6758 \r
6759   case DSERR_CONTROLUNAVAIL:\r
6760     return "Control unavailable";\r
6761 \r
6762   case DSERR_INVALIDPARAM:\r
6763     return "Invalid parameter";\r
6764 \r
6765   case DSERR_INVALIDCALL:\r
6766     return "Invalid call";\r
6767 \r
6768   case DSERR_GENERIC:\r
6769     return "Generic error";\r
6770 \r
6771   case DSERR_PRIOLEVELNEEDED:\r
6772     return "Priority level needed";\r
6773 \r
6774   case DSERR_OUTOFMEMORY:\r
6775     return "Out of memory";\r
6776 \r
6777   case DSERR_BADFORMAT:\r
6778     return "The sample rate or the channel format is not supported";\r
6779 \r
6780   case DSERR_UNSUPPORTED:\r
6781     return "Not supported";\r
6782 \r
6783   case DSERR_NODRIVER:\r
6784     return "No driver";\r
6785 \r
6786   case DSERR_ALREADYINITIALIZED:\r
6787     return "Already initialized";\r
6788 \r
6789   case DSERR_NOAGGREGATION:\r
6790     return "No aggregation";\r
6791 \r
6792   case DSERR_BUFFERLOST:\r
6793     return "Buffer lost";\r
6794 \r
6795   case DSERR_OTHERAPPHASPRIO:\r
6796     return "Another application already has priority";\r
6797 \r
6798   case DSERR_UNINITIALIZED:\r
6799     return "Uninitialized";\r
6800 \r
6801   default:\r
6802     return "DirectSound unknown error";\r
6803   }\r
6804 }\r
6805 //******************** End of __WINDOWS_DS__ *********************//\r
6806 #endif\r
6807 \r
6808 \r
6809 #if defined(__LINUX_ALSA__)\r
6810 \r
6811 #include <alsa/asoundlib.h>\r
6812 #include <unistd.h>\r
6813 \r
6814   // A structure to hold various information related to the ALSA API\r
6815   // implementation.\r
6816 struct AlsaHandle {\r
6817   snd_pcm_t *handles[2];\r
6818   bool synchronized;\r
6819   bool xrun[2];\r
6820   pthread_cond_t runnable_cv;\r
6821   bool runnable;\r
6822 \r
6823   AlsaHandle()\r
6824     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
6825 };\r
6826 \r
6827 static void *alsaCallbackHandler( void * ptr );\r
6828 \r
6829 RtApiAlsa :: RtApiAlsa()\r
6830 {\r
6831   // Nothing to do here.\r
6832 }\r
6833 \r
6834 RtApiAlsa :: ~RtApiAlsa()\r
6835 {\r
6836   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
6837 }\r
6838 \r
6839 unsigned int RtApiAlsa :: getDeviceCount( void )\r
6840 {\r
6841   unsigned nDevices = 0;\r
6842   int result, subdevice, card;\r
6843   char name[64];\r
6844   snd_ctl_t *handle;\r
6845 \r
6846   // Count cards and devices\r
6847   card = -1;\r
6848   snd_card_next( &card );\r
6849   while ( card >= 0 ) {\r
6850     sprintf( name, "hw:%d", card );\r
6851     result = snd_ctl_open( &handle, name, 0 );\r
6852     if ( result < 0 ) {\r
6853       errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6854       errorText_ = errorStream_.str();\r
6855       error( RtAudioError::WARNING );\r
6856       goto nextcard;\r
6857     }\r
6858     subdevice = -1;\r
6859     while( 1 ) {\r
6860       result = snd_ctl_pcm_next_device( handle, &subdevice );\r
6861       if ( result < 0 ) {\r
6862         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6863         errorText_ = errorStream_.str();\r
6864         error( RtAudioError::WARNING );\r
6865         break;\r
6866       }\r
6867       if ( subdevice < 0 )\r
6868         break;\r
6869       nDevices++;\r
6870     }\r
6871   nextcard:\r
6872     snd_ctl_close( handle );\r
6873     snd_card_next( &card );\r
6874   }\r
6875 \r
6876   result = snd_ctl_open( &handle, "default", 0 );\r
6877   if (result == 0) {\r
6878     nDevices++;\r
6879     snd_ctl_close( handle );\r
6880   }\r
6881 \r
6882   return nDevices;\r
6883 }\r
6884 \r
6885 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
6886 {\r
6887   RtAudio::DeviceInfo info;\r
6888   info.probed = false;\r
6889 \r
6890   unsigned nDevices = 0;\r
6891   int result, subdevice, card;\r
6892   char name[64];\r
6893   snd_ctl_t *chandle;\r
6894 \r
6895   // Count cards and devices\r
6896   card = -1;\r
6897   subdevice = -1;\r
6898   snd_card_next( &card );\r
6899   while ( card >= 0 ) {\r
6900     sprintf( name, "hw:%d", card );\r
6901     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6902     if ( result < 0 ) {\r
6903       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6904       errorText_ = errorStream_.str();\r
6905       error( RtAudioError::WARNING );\r
6906       goto nextcard;\r
6907     }\r
6908     subdevice = -1;\r
6909     while( 1 ) {\r
6910       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
6911       if ( result < 0 ) {\r
6912         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6913         errorText_ = errorStream_.str();\r
6914         error( RtAudioError::WARNING );\r
6915         break;\r
6916       }\r
6917       if ( subdevice < 0 ) break;\r
6918       if ( nDevices == device ) {\r
6919         sprintf( name, "hw:%d,%d", card, subdevice );\r
6920         goto foundDevice;\r
6921       }\r
6922       nDevices++;\r
6923     }\r
6924   nextcard:\r
6925     snd_ctl_close( chandle );\r
6926     snd_card_next( &card );\r
6927   }\r
6928 \r
6929   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
6930   if ( result == 0 ) {\r
6931     if ( nDevices == device ) {\r
6932       strcpy( name, "default" );\r
6933       goto foundDevice;\r
6934     }\r
6935     nDevices++;\r
6936   }\r
6937 \r
6938   if ( nDevices == 0 ) {\r
6939     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
6940     error( RtAudioError::INVALID_USE );\r
6941     return info;\r
6942   }\r
6943 \r
6944   if ( device >= nDevices ) {\r
6945     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
6946     error( RtAudioError::INVALID_USE );\r
6947     return info;\r
6948   }\r
6949 \r
6950  foundDevice:\r
6951 \r
6952   // If a stream is already open, we cannot probe the stream devices.\r
6953   // Thus, use the saved results.\r
6954   if ( stream_.state != STREAM_CLOSED &&\r
6955        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
6956     snd_ctl_close( chandle );\r
6957     if ( device >= devices_.size() ) {\r
6958       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
6959       error( RtAudioError::WARNING );\r
6960       return info;\r
6961     }\r
6962     return devices_[ device ];\r
6963   }\r
6964 \r
6965   int openMode = SND_PCM_ASYNC;\r
6966   snd_pcm_stream_t stream;\r
6967   snd_pcm_info_t *pcminfo;\r
6968   snd_pcm_info_alloca( &pcminfo );\r
6969   snd_pcm_t *phandle;\r
6970   snd_pcm_hw_params_t *params;\r
6971   snd_pcm_hw_params_alloca( &params );\r
6972 \r
6973   // First try for playback unless default device (which has subdev -1)\r
6974   stream = SND_PCM_STREAM_PLAYBACK;\r
6975   snd_pcm_info_set_stream( pcminfo, stream );\r
6976   if ( subdevice != -1 ) {\r
6977     snd_pcm_info_set_device( pcminfo, subdevice );\r
6978     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
6979 \r
6980     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6981     if ( result < 0 ) {\r
6982       // Device probably doesn't support playback.\r
6983       goto captureProbe;\r
6984     }\r
6985   }\r
6986 \r
6987   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
6988   if ( result < 0 ) {\r
6989     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
6990     errorText_ = errorStream_.str();\r
6991     error( RtAudioError::WARNING );\r
6992     goto captureProbe;\r
6993   }\r
6994 \r
6995   // The device is open ... fill the parameter structure.\r
6996   result = snd_pcm_hw_params_any( phandle, params );\r
6997   if ( result < 0 ) {\r
6998     snd_pcm_close( phandle );\r
6999     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7000     errorText_ = errorStream_.str();\r
7001     error( RtAudioError::WARNING );\r
7002     goto captureProbe;\r
7003   }\r
7004 \r
7005   // Get output channel information.\r
7006   unsigned int value;\r
7007   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
7008   if ( result < 0 ) {\r
7009     snd_pcm_close( phandle );\r
7010     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
7011     errorText_ = errorStream_.str();\r
7012     error( RtAudioError::WARNING );\r
7013     goto captureProbe;\r
7014   }\r
7015   info.outputChannels = value;\r
7016   snd_pcm_close( phandle );\r
7017 \r
7018  captureProbe:\r
7019   stream = SND_PCM_STREAM_CAPTURE;\r
7020   snd_pcm_info_set_stream( pcminfo, stream );\r
7021 \r
7022   // Now try for capture unless default device (with subdev = -1)\r
7023   if ( subdevice != -1 ) {\r
7024     result = snd_ctl_pcm_info( chandle, pcminfo );\r
7025     snd_ctl_close( chandle );\r
7026     if ( result < 0 ) {\r
7027       // Device probably doesn't support capture.\r
7028       if ( info.outputChannels == 0 ) return info;\r
7029       goto probeParameters;\r
7030     }\r
7031   }\r
7032   else\r
7033     snd_ctl_close( chandle );\r
7034 \r
7035   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7036   if ( result < 0 ) {\r
7037     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7038     errorText_ = errorStream_.str();\r
7039     error( RtAudioError::WARNING );\r
7040     if ( info.outputChannels == 0 ) return info;\r
7041     goto probeParameters;\r
7042   }\r
7043 \r
7044   // The device is open ... fill the parameter structure.\r
7045   result = snd_pcm_hw_params_any( phandle, params );\r
7046   if ( result < 0 ) {\r
7047     snd_pcm_close( phandle );\r
7048     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7049     errorText_ = errorStream_.str();\r
7050     error( RtAudioError::WARNING );\r
7051     if ( info.outputChannels == 0 ) return info;\r
7052     goto probeParameters;\r
7053   }\r
7054 \r
7055   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
7056   if ( result < 0 ) {\r
7057     snd_pcm_close( phandle );\r
7058     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
7059     errorText_ = errorStream_.str();\r
7060     error( RtAudioError::WARNING );\r
7061     if ( info.outputChannels == 0 ) return info;\r
7062     goto probeParameters;\r
7063   }\r
7064   info.inputChannels = value;\r
7065   snd_pcm_close( phandle );\r
7066 \r
7067   // If device opens for both playback and capture, we determine the channels.\r
7068   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
7069     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
7070 \r
7071   // ALSA doesn't provide default devices so we'll use the first available one.\r
7072   if ( device == 0 && info.outputChannels > 0 )\r
7073     info.isDefaultOutput = true;\r
7074   if ( device == 0 && info.inputChannels > 0 )\r
7075     info.isDefaultInput = true;\r
7076 \r
7077  probeParameters:\r
7078   // At this point, we just need to figure out the supported data\r
7079   // formats and sample rates.  We'll proceed by opening the device in\r
7080   // the direction with the maximum number of channels, or playback if\r
7081   // they are equal.  This might limit our sample rate options, but so\r
7082   // be it.\r
7083 \r
7084   if ( info.outputChannels >= info.inputChannels )\r
7085     stream = SND_PCM_STREAM_PLAYBACK;\r
7086   else\r
7087     stream = SND_PCM_STREAM_CAPTURE;\r
7088   snd_pcm_info_set_stream( pcminfo, stream );\r
7089 \r
7090   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7091   if ( result < 0 ) {\r
7092     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7093     errorText_ = errorStream_.str();\r
7094     error( RtAudioError::WARNING );\r
7095     return info;\r
7096   }\r
7097 \r
7098   // The device is open ... fill the parameter structure.\r
7099   result = snd_pcm_hw_params_any( phandle, params );\r
7100   if ( result < 0 ) {\r
7101     snd_pcm_close( phandle );\r
7102     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7103     errorText_ = errorStream_.str();\r
7104     error( RtAudioError::WARNING );\r
7105     return info;\r
7106   }\r
7107 \r
7108   // Test our discrete set of sample rate values.\r
7109   info.sampleRates.clear();\r
7110   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7111     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {\r
7112       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7113 \r
7114       if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
7115         info.preferredSampleRate = SAMPLE_RATES[i];\r
7116     }\r
7117   }\r
7118   if ( info.sampleRates.size() == 0 ) {\r
7119     snd_pcm_close( phandle );\r
7120     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7121     errorText_ = errorStream_.str();\r
7122     error( RtAudioError::WARNING );\r
7123     return info;\r
7124   }\r
7125 \r
7126   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7127   snd_pcm_format_t format;\r
7128   info.nativeFormats = 0;\r
7129   format = SND_PCM_FORMAT_S8;\r
7130   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7131     info.nativeFormats |= RTAUDIO_SINT8;\r
7132   format = SND_PCM_FORMAT_S16;\r
7133   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7134     info.nativeFormats |= RTAUDIO_SINT16;\r
7135   format = SND_PCM_FORMAT_S24;\r
7136   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7137     info.nativeFormats |= RTAUDIO_SINT24;\r
7138   format = SND_PCM_FORMAT_S32;\r
7139   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7140     info.nativeFormats |= RTAUDIO_SINT32;\r
7141   format = SND_PCM_FORMAT_FLOAT;\r
7142   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7143     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7144   format = SND_PCM_FORMAT_FLOAT64;\r
7145   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7146     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7147 \r
7148   // Check that we have at least one supported format\r
7149   if ( info.nativeFormats == 0 ) {\r
7150     snd_pcm_close( phandle );\r
7151     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7152     errorText_ = errorStream_.str();\r
7153     error( RtAudioError::WARNING );\r
7154     return info;\r
7155   }\r
7156 \r
7157   // Get the device name\r
7158   char *cardname;\r
7159   result = snd_card_get_name( card, &cardname );\r
7160   if ( result >= 0 ) {\r
7161     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7162     free( cardname );\r
7163   }\r
7164   info.name = name;\r
7165 \r
7166   // That's all ... close the device and return\r
7167   snd_pcm_close( phandle );\r
7168   info.probed = true;\r
7169   return info;\r
7170 }\r
7171 \r
7172 void RtApiAlsa :: saveDeviceInfo( void )\r
7173 {\r
7174   devices_.clear();\r
7175 \r
7176   unsigned int nDevices = getDeviceCount();\r
7177   devices_.resize( nDevices );\r
7178   for ( unsigned int i=0; i<nDevices; i++ )\r
7179     devices_[i] = getDeviceInfo( i );\r
7180 }\r
7181 \r
7182 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7183                                    unsigned int firstChannel, unsigned int sampleRate,\r
7184                                    RtAudioFormat format, unsigned int *bufferSize,\r
7185                                    RtAudio::StreamOptions *options )\r
7186 \r
7187 {\r
7188 #if defined(__RTAUDIO_DEBUG__)\r
7189   snd_output_t *out;\r
7190   snd_output_stdio_attach(&out, stderr, 0);\r
7191 #endif\r
7192 \r
7193   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7194 \r
7195   unsigned nDevices = 0;\r
7196   int result, subdevice, card;\r
7197   char name[64];\r
7198   snd_ctl_t *chandle;\r
7199 \r
7200   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7201     snprintf(name, sizeof(name), "%s", "default");\r
7202   else {\r
7203     // Count cards and devices\r
7204     card = -1;\r
7205     snd_card_next( &card );\r
7206     while ( card >= 0 ) {\r
7207       sprintf( name, "hw:%d", card );\r
7208       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7209       if ( result < 0 ) {\r
7210         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7211         errorText_ = errorStream_.str();\r
7212         return FAILURE;\r
7213       }\r
7214       subdevice = -1;\r
7215       while( 1 ) {\r
7216         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7217         if ( result < 0 ) break;\r
7218         if ( subdevice < 0 ) break;\r
7219         if ( nDevices == device ) {\r
7220           sprintf( name, "hw:%d,%d", card, subdevice );\r
7221           snd_ctl_close( chandle );\r
7222           goto foundDevice;\r
7223         }\r
7224         nDevices++;\r
7225       }\r
7226       snd_ctl_close( chandle );\r
7227       snd_card_next( &card );\r
7228     }\r
7229 \r
7230     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7231     if ( result == 0 ) {\r
7232       if ( nDevices == device ) {\r
7233         strcpy( name, "default" );\r
7234         goto foundDevice;\r
7235       }\r
7236       nDevices++;\r
7237     }\r
7238 \r
7239     if ( nDevices == 0 ) {\r
7240       // This should not happen because a check is made before this function is called.\r
7241       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7242       return FAILURE;\r
7243     }\r
7244 \r
7245     if ( device >= nDevices ) {\r
7246       // This should not happen because a check is made before this function is called.\r
7247       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7248       return FAILURE;\r
7249     }\r
7250   }\r
7251 \r
7252  foundDevice:\r
7253 \r
7254   // The getDeviceInfo() function will not work for a device that is\r
7255   // already open.  Thus, we'll probe the system before opening a\r
7256   // stream and save the results for use by getDeviceInfo().\r
7257   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7258     this->saveDeviceInfo();\r
7259 \r
7260   snd_pcm_stream_t stream;\r
7261   if ( mode == OUTPUT )\r
7262     stream = SND_PCM_STREAM_PLAYBACK;\r
7263   else\r
7264     stream = SND_PCM_STREAM_CAPTURE;\r
7265 \r
7266   snd_pcm_t *phandle;\r
7267   int openMode = SND_PCM_ASYNC;\r
7268   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7269   if ( result < 0 ) {\r
7270     if ( mode == OUTPUT )\r
7271       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7272     else\r
7273       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7274     errorText_ = errorStream_.str();\r
7275     return FAILURE;\r
7276   }\r
7277 \r
7278   // Fill the parameter structure.\r
7279   snd_pcm_hw_params_t *hw_params;\r
7280   snd_pcm_hw_params_alloca( &hw_params );\r
7281   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7282   if ( result < 0 ) {\r
7283     snd_pcm_close( phandle );\r
7284     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7285     errorText_ = errorStream_.str();\r
7286     return FAILURE;\r
7287   }\r
7288 \r
7289 #if defined(__RTAUDIO_DEBUG__)\r
7290   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7291   snd_pcm_hw_params_dump( hw_params, out );\r
7292 #endif\r
7293 \r
7294   // Set access ... check user preference.\r
7295   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7296     stream_.userInterleaved = false;\r
7297     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7298     if ( result < 0 ) {\r
7299       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7300       stream_.deviceInterleaved[mode] =  true;\r
7301     }\r
7302     else\r
7303       stream_.deviceInterleaved[mode] = false;\r
7304   }\r
7305   else {\r
7306     stream_.userInterleaved = true;\r
7307     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7308     if ( result < 0 ) {\r
7309       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7310       stream_.deviceInterleaved[mode] =  false;\r
7311     }\r
7312     else\r
7313       stream_.deviceInterleaved[mode] =  true;\r
7314   }\r
7315 \r
7316   if ( result < 0 ) {\r
7317     snd_pcm_close( phandle );\r
7318     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7319     errorText_ = errorStream_.str();\r
7320     return FAILURE;\r
7321   }\r
7322 \r
7323   // Determine how to set the device format.\r
7324   stream_.userFormat = format;\r
7325   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7326 \r
7327   if ( format == RTAUDIO_SINT8 )\r
7328     deviceFormat = SND_PCM_FORMAT_S8;\r
7329   else if ( format == RTAUDIO_SINT16 )\r
7330     deviceFormat = SND_PCM_FORMAT_S16;\r
7331   else if ( format == RTAUDIO_SINT24 )\r
7332     deviceFormat = SND_PCM_FORMAT_S24;\r
7333   else if ( format == RTAUDIO_SINT32 )\r
7334     deviceFormat = SND_PCM_FORMAT_S32;\r
7335   else if ( format == RTAUDIO_FLOAT32 )\r
7336     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7337   else if ( format == RTAUDIO_FLOAT64 )\r
7338     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7339 \r
7340   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7341     stream_.deviceFormat[mode] = format;\r
7342     goto setFormat;\r
7343   }\r
7344 \r
7345   // The user requested format is not natively supported by the device.\r
7346   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7347   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7348     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7349     goto setFormat;\r
7350   }\r
7351 \r
7352   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7353   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7354     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7355     goto setFormat;\r
7356   }\r
7357 \r
7358   deviceFormat = SND_PCM_FORMAT_S32;\r
7359   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7360     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7361     goto setFormat;\r
7362   }\r
7363 \r
7364   deviceFormat = SND_PCM_FORMAT_S24;\r
7365   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7366     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7367     goto setFormat;\r
7368   }\r
7369 \r
7370   deviceFormat = SND_PCM_FORMAT_S16;\r
7371   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7372     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7373     goto setFormat;\r
7374   }\r
7375 \r
7376   deviceFormat = SND_PCM_FORMAT_S8;\r
7377   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7378     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7379     goto setFormat;\r
7380   }\r
7381 \r
7382   // If we get here, no supported format was found.\r
7383   snd_pcm_close( phandle );\r
7384   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7385   errorText_ = errorStream_.str();\r
7386   return FAILURE;\r
7387 \r
7388  setFormat:\r
7389   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7390   if ( result < 0 ) {\r
7391     snd_pcm_close( phandle );\r
7392     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7393     errorText_ = errorStream_.str();\r
7394     return FAILURE;\r
7395   }\r
7396 \r
7397   // Determine whether byte-swaping is necessary.\r
7398   stream_.doByteSwap[mode] = false;\r
7399   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7400     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7401     if ( result == 0 )\r
7402       stream_.doByteSwap[mode] = true;\r
7403     else if (result < 0) {\r
7404       snd_pcm_close( phandle );\r
7405       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7406       errorText_ = errorStream_.str();\r
7407       return FAILURE;\r
7408     }\r
7409   }\r
7410 \r
7411   // Set the sample rate.\r
7412   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7413   if ( result < 0 ) {\r
7414     snd_pcm_close( phandle );\r
7415     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7416     errorText_ = errorStream_.str();\r
7417     return FAILURE;\r
7418   }\r
7419 \r
7420   // Determine the number of channels for this device.  We support a possible\r
7421   // minimum device channel number > than the value requested by the user.\r
7422   stream_.nUserChannels[mode] = channels;\r
7423   unsigned int value;\r
7424   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7425   unsigned int deviceChannels = value;\r
7426   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7427     snd_pcm_close( phandle );\r
7428     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7429     errorText_ = errorStream_.str();\r
7430     return FAILURE;\r
7431   }\r
7432 \r
7433   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7434   if ( result < 0 ) {\r
7435     snd_pcm_close( phandle );\r
7436     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7437     errorText_ = errorStream_.str();\r
7438     return FAILURE;\r
7439   }\r
7440   deviceChannels = value;\r
7441   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7442   stream_.nDeviceChannels[mode] = deviceChannels;\r
7443 \r
7444   // Set the device channels.\r
7445   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7446   if ( result < 0 ) {\r
7447     snd_pcm_close( phandle );\r
7448     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7449     errorText_ = errorStream_.str();\r
7450     return FAILURE;\r
7451   }\r
7452 \r
7453   // Set the buffer (or period) size.\r
7454   int dir = 0;\r
7455   snd_pcm_uframes_t periodSize = *bufferSize;\r
7456   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7457   if ( result < 0 ) {\r
7458     snd_pcm_close( phandle );\r
7459     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7460     errorText_ = errorStream_.str();\r
7461     return FAILURE;\r
7462   }\r
7463   *bufferSize = periodSize;\r
7464 \r
7465   // Set the buffer number, which in ALSA is referred to as the "period".\r
7466   unsigned int periods = 0;\r
7467   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7468   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7469   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7470   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7471   if ( result < 0 ) {\r
7472     snd_pcm_close( phandle );\r
7473     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7474     errorText_ = errorStream_.str();\r
7475     return FAILURE;\r
7476   }\r
7477 \r
7478   // If attempting to setup a duplex stream, the bufferSize parameter\r
7479   // MUST be the same in both directions!\r
7480   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7481     snd_pcm_close( phandle );\r
7482     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7483     errorText_ = errorStream_.str();\r
7484     return FAILURE;\r
7485   }\r
7486 \r
7487   stream_.bufferSize = *bufferSize;\r
7488 \r
7489   // Install the hardware configuration\r
7490   result = snd_pcm_hw_params( phandle, hw_params );\r
7491   if ( result < 0 ) {\r
7492     snd_pcm_close( phandle );\r
7493     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7494     errorText_ = errorStream_.str();\r
7495     return FAILURE;\r
7496   }\r
7497 \r
7498 #if defined(__RTAUDIO_DEBUG__)\r
7499   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7500   snd_pcm_hw_params_dump( hw_params, out );\r
7501 #endif\r
7502 \r
7503   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7504   snd_pcm_sw_params_t *sw_params = NULL;\r
7505   snd_pcm_sw_params_alloca( &sw_params );\r
7506   snd_pcm_sw_params_current( phandle, sw_params );\r
7507   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7508   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7509   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7510 \r
7511   // The following two settings were suggested by Theo Veenker\r
7512   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7513   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7514 \r
7515   // here are two options for a fix\r
7516   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7517   snd_pcm_uframes_t val;\r
7518   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7519   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7520 \r
7521   result = snd_pcm_sw_params( phandle, sw_params );\r
7522   if ( result < 0 ) {\r
7523     snd_pcm_close( phandle );\r
7524     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7525     errorText_ = errorStream_.str();\r
7526     return FAILURE;\r
7527   }\r
7528 \r
7529 #if defined(__RTAUDIO_DEBUG__)\r
7530   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7531   snd_pcm_sw_params_dump( sw_params, out );\r
7532 #endif\r
7533 \r
7534   // Set flags for buffer conversion\r
7535   stream_.doConvertBuffer[mode] = false;\r
7536   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7537     stream_.doConvertBuffer[mode] = true;\r
7538   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7539     stream_.doConvertBuffer[mode] = true;\r
7540   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7541        stream_.nUserChannels[mode] > 1 )\r
7542     stream_.doConvertBuffer[mode] = true;\r
7543 \r
7544   // Allocate the ApiHandle if necessary and then save.\r
7545   AlsaHandle *apiInfo = 0;\r
7546   if ( stream_.apiHandle == 0 ) {\r
7547     try {\r
7548       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7549     }\r
7550     catch ( std::bad_alloc& ) {\r
7551       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7552       goto error;\r
7553     }\r
7554 \r
7555     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7556       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7557       goto error;\r
7558     }\r
7559 \r
7560     stream_.apiHandle = (void *) apiInfo;\r
7561     apiInfo->handles[0] = 0;\r
7562     apiInfo->handles[1] = 0;\r
7563   }\r
7564   else {\r
7565     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7566   }\r
7567   apiInfo->handles[mode] = phandle;\r
7568   phandle = 0;\r
7569 \r
7570   // Allocate necessary internal buffers.\r
7571   unsigned long bufferBytes;\r
7572   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7573   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7574   if ( stream_.userBuffer[mode] == NULL ) {\r
7575     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7576     goto error;\r
7577   }\r
7578 \r
7579   if ( stream_.doConvertBuffer[mode] ) {\r
7580 \r
7581     bool makeBuffer = true;\r
7582     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7583     if ( mode == INPUT ) {\r
7584       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7585         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7586         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7587       }\r
7588     }\r
7589 \r
7590     if ( makeBuffer ) {\r
7591       bufferBytes *= *bufferSize;\r
7592       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7593       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7594       if ( stream_.deviceBuffer == NULL ) {\r
7595         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7596         goto error;\r
7597       }\r
7598     }\r
7599   }\r
7600 \r
7601   stream_.sampleRate = sampleRate;\r
7602   stream_.nBuffers = periods;\r
7603   stream_.device[mode] = device;\r
7604   stream_.state = STREAM_STOPPED;\r
7605 \r
7606   // Setup the buffer conversion information structure.\r
7607   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7608 \r
7609   // Setup thread if necessary.\r
7610   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7611     // We had already set up an output stream.\r
7612     stream_.mode = DUPLEX;\r
7613     // Link the streams if possible.\r
7614     apiInfo->synchronized = false;\r
7615     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7616       apiInfo->synchronized = true;\r
7617     else {\r
7618       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7619       error( RtAudioError::WARNING );\r
7620     }\r
7621   }\r
7622   else {\r
7623     stream_.mode = mode;\r
7624 \r
7625     // Setup callback thread.\r
7626     stream_.callbackInfo.object = (void *) this;\r
7627 \r
7628     // Set the thread attributes for joinable and realtime scheduling\r
7629     // priority (optional).  The higher priority will only take affect\r
7630     // if the program is run as root or suid. Note, under Linux\r
7631     // processes with CAP_SYS_NICE privilege, a user can change\r
7632     // scheduling policy and priority (thus need not be root). See\r
7633     // POSIX "capabilities".\r
7634     pthread_attr_t attr;\r
7635     pthread_attr_init( &attr );\r
7636     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7637 \r
7638 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7639     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7640       // We previously attempted to increase the audio callback priority\r
7641       // to SCHED_RR here via the attributes.  However, while no errors\r
7642       // were reported in doing so, it did not work.  So, now this is\r
7643       // done in the alsaCallbackHandler function.\r
7644       stream_.callbackInfo.doRealtime = true;\r
7645       int priority = options->priority;\r
7646       int min = sched_get_priority_min( SCHED_RR );\r
7647       int max = sched_get_priority_max( SCHED_RR );\r
7648       if ( priority < min ) priority = min;\r
7649       else if ( priority > max ) priority = max;\r
7650       stream_.callbackInfo.priority = priority;\r
7651     }\r
7652 #endif\r
7653 \r
7654     stream_.callbackInfo.isRunning = true;\r
7655     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7656     pthread_attr_destroy( &attr );\r
7657     if ( result ) {\r
7658       stream_.callbackInfo.isRunning = false;\r
7659       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7660       goto error;\r
7661     }\r
7662   }\r
7663 \r
7664   return SUCCESS;\r
7665 \r
7666  error:\r
7667   if ( apiInfo ) {\r
7668     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7669     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7670     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7671     delete apiInfo;\r
7672     stream_.apiHandle = 0;\r
7673   }\r
7674 \r
7675   if ( phandle) snd_pcm_close( phandle );\r
7676 \r
7677   for ( int i=0; i<2; i++ ) {\r
7678     if ( stream_.userBuffer[i] ) {\r
7679       free( stream_.userBuffer[i] );\r
7680       stream_.userBuffer[i] = 0;\r
7681     }\r
7682   }\r
7683 \r
7684   if ( stream_.deviceBuffer ) {\r
7685     free( stream_.deviceBuffer );\r
7686     stream_.deviceBuffer = 0;\r
7687   }\r
7688 \r
7689   stream_.state = STREAM_CLOSED;\r
7690   return FAILURE;\r
7691 }\r
7692 \r
7693 void RtApiAlsa :: closeStream()\r
7694 {\r
7695   if ( stream_.state == STREAM_CLOSED ) {\r
7696     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7697     error( RtAudioError::WARNING );\r
7698     return;\r
7699   }\r
7700 \r
7701   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7702   stream_.callbackInfo.isRunning = false;\r
7703   MUTEX_LOCK( &stream_.mutex );\r
7704   if ( stream_.state == STREAM_STOPPED ) {\r
7705     apiInfo->runnable = true;\r
7706     pthread_cond_signal( &apiInfo->runnable_cv );\r
7707   }\r
7708   MUTEX_UNLOCK( &stream_.mutex );\r
7709   pthread_join( stream_.callbackInfo.thread, NULL );\r
7710 \r
7711   if ( stream_.state == STREAM_RUNNING ) {\r
7712     stream_.state = STREAM_STOPPED;\r
7713     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7714       snd_pcm_drop( apiInfo->handles[0] );\r
7715     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7716       snd_pcm_drop( apiInfo->handles[1] );\r
7717   }\r
7718 \r
7719   if ( apiInfo ) {\r
7720     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7721     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7722     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7723     delete apiInfo;\r
7724     stream_.apiHandle = 0;\r
7725   }\r
7726 \r
7727   for ( int i=0; i<2; i++ ) {\r
7728     if ( stream_.userBuffer[i] ) {\r
7729       free( stream_.userBuffer[i] );\r
7730       stream_.userBuffer[i] = 0;\r
7731     }\r
7732   }\r
7733 \r
7734   if ( stream_.deviceBuffer ) {\r
7735     free( stream_.deviceBuffer );\r
7736     stream_.deviceBuffer = 0;\r
7737   }\r
7738 \r
7739   stream_.mode = UNINITIALIZED;\r
7740   stream_.state = STREAM_CLOSED;\r
7741 }\r
7742 \r
7743 void RtApiAlsa :: startStream()\r
7744 {\r
7745   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7746 \r
7747   verifyStream();\r
7748   if ( stream_.state == STREAM_RUNNING ) {\r
7749     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7750     error( RtAudioError::WARNING );\r
7751     return;\r
7752   }\r
7753 \r
7754   MUTEX_LOCK( &stream_.mutex );\r
7755 \r
7756   int result = 0;\r
7757   snd_pcm_state_t state;\r
7758   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7759   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7760   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7761     state = snd_pcm_state( handle[0] );\r
7762     if ( state != SND_PCM_STATE_PREPARED ) {\r
7763       result = snd_pcm_prepare( handle[0] );\r
7764       if ( result < 0 ) {\r
7765         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7766         errorText_ = errorStream_.str();\r
7767         goto unlock;\r
7768       }\r
7769     }\r
7770   }\r
7771 \r
7772   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7773     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7774     state = snd_pcm_state( handle[1] );\r
7775     if ( state != SND_PCM_STATE_PREPARED ) {\r
7776       result = snd_pcm_prepare( handle[1] );\r
7777       if ( result < 0 ) {\r
7778         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7779         errorText_ = errorStream_.str();\r
7780         goto unlock;\r
7781       }\r
7782     }\r
7783   }\r
7784 \r
7785   stream_.state = STREAM_RUNNING;\r
7786 \r
7787  unlock:\r
7788   apiInfo->runnable = true;\r
7789   pthread_cond_signal( &apiInfo->runnable_cv );\r
7790   MUTEX_UNLOCK( &stream_.mutex );\r
7791 \r
7792   if ( result >= 0 ) return;\r
7793   error( RtAudioError::SYSTEM_ERROR );\r
7794 }\r
7795 \r
7796 void RtApiAlsa :: stopStream()\r
7797 {\r
7798   verifyStream();\r
7799   if ( stream_.state == STREAM_STOPPED ) {\r
7800     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7801     error( RtAudioError::WARNING );\r
7802     return;\r
7803   }\r
7804 \r
7805   stream_.state = STREAM_STOPPED;\r
7806   MUTEX_LOCK( &stream_.mutex );\r
7807 \r
7808   int result = 0;\r
7809   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7810   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7811   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7812     if ( apiInfo->synchronized ) \r
7813       result = snd_pcm_drop( handle[0] );\r
7814     else\r
7815       result = snd_pcm_drain( handle[0] );\r
7816     if ( result < 0 ) {\r
7817       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7818       errorText_ = errorStream_.str();\r
7819       goto unlock;\r
7820     }\r
7821   }\r
7822 \r
7823   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7824     result = snd_pcm_drop( handle[1] );\r
7825     if ( result < 0 ) {\r
7826       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7827       errorText_ = errorStream_.str();\r
7828       goto unlock;\r
7829     }\r
7830   }\r
7831 \r
7832  unlock:\r
7833   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7834   MUTEX_UNLOCK( &stream_.mutex );\r
7835 \r
7836   if ( result >= 0 ) return;\r
7837   error( RtAudioError::SYSTEM_ERROR );\r
7838 }\r
7839 \r
7840 void RtApiAlsa :: abortStream()\r
7841 {\r
7842   verifyStream();\r
7843   if ( stream_.state == STREAM_STOPPED ) {\r
7844     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7845     error( RtAudioError::WARNING );\r
7846     return;\r
7847   }\r
7848 \r
7849   stream_.state = STREAM_STOPPED;\r
7850   MUTEX_LOCK( &stream_.mutex );\r
7851 \r
7852   int result = 0;\r
7853   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7854   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7855   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7856     result = snd_pcm_drop( handle[0] );\r
7857     if ( result < 0 ) {\r
7858       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7859       errorText_ = errorStream_.str();\r
7860       goto unlock;\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] );\r
7866     if ( result < 0 ) {\r
7867       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7868       errorText_ = errorStream_.str();\r
7869       goto unlock;\r
7870     }\r
7871   }\r
7872 \r
7873  unlock:\r
7874   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7875   MUTEX_UNLOCK( &stream_.mutex );\r
7876 \r
7877   if ( result >= 0 ) return;\r
7878   error( RtAudioError::SYSTEM_ERROR );\r
7879 }\r
7880 \r
7881 void RtApiAlsa :: callbackEvent()\r
7882 {\r
7883   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7884   if ( stream_.state == STREAM_STOPPED ) {\r
7885     MUTEX_LOCK( &stream_.mutex );\r
7886     while ( !apiInfo->runnable )\r
7887       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7888 \r
7889     if ( stream_.state != STREAM_RUNNING ) {\r
7890       MUTEX_UNLOCK( &stream_.mutex );\r
7891       return;\r
7892     }\r
7893     MUTEX_UNLOCK( &stream_.mutex );\r
7894   }\r
7895 \r
7896   if ( stream_.state == STREAM_CLOSED ) {\r
7897     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7898     error( RtAudioError::WARNING );\r
7899     return;\r
7900   }\r
7901 \r
7902   int doStopStream = 0;\r
7903   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7904   double streamTime = getStreamTime();\r
7905   RtAudioStreamStatus status = 0;\r
7906   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7907     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
7908     apiInfo->xrun[0] = false;\r
7909   }\r
7910   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
7911     status |= RTAUDIO_INPUT_OVERFLOW;\r
7912     apiInfo->xrun[1] = false;\r
7913   }\r
7914   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
7915                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
7916 \r
7917   if ( doStopStream == 2 ) {\r
7918     abortStream();\r
7919     return;\r
7920   }\r
7921 \r
7922   MUTEX_LOCK( &stream_.mutex );\r
7923 \r
7924   // The state might change while waiting on a mutex.\r
7925   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
7926 \r
7927   int result;\r
7928   char *buffer;\r
7929   int channels;\r
7930   snd_pcm_t **handle;\r
7931   snd_pcm_sframes_t frames;\r
7932   RtAudioFormat format;\r
7933   handle = (snd_pcm_t **) apiInfo->handles;\r
7934 \r
7935   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
7936 \r
7937     // Setup parameters.\r
7938     if ( stream_.doConvertBuffer[1] ) {\r
7939       buffer = stream_.deviceBuffer;\r
7940       channels = stream_.nDeviceChannels[1];\r
7941       format = stream_.deviceFormat[1];\r
7942     }\r
7943     else {\r
7944       buffer = stream_.userBuffer[1];\r
7945       channels = stream_.nUserChannels[1];\r
7946       format = stream_.userFormat;\r
7947     }\r
7948 \r
7949     // Read samples from device in interleaved/non-interleaved format.\r
7950     if ( stream_.deviceInterleaved[1] )\r
7951       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
7952     else {\r
7953       void *bufs[channels];\r
7954       size_t offset = stream_.bufferSize * formatBytes( format );\r
7955       for ( int i=0; i<channels; i++ )\r
7956         bufs[i] = (void *) (buffer + (i * offset));\r
7957       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
7958     }\r
7959 \r
7960     if ( result < (int) stream_.bufferSize ) {\r
7961       // Either an error or overrun occured.\r
7962       if ( result == -EPIPE ) {\r
7963         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
7964         if ( state == SND_PCM_STATE_XRUN ) {\r
7965           apiInfo->xrun[1] = true;\r
7966           result = snd_pcm_prepare( handle[1] );\r
7967           if ( result < 0 ) {\r
7968             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
7969             errorText_ = errorStream_.str();\r
7970           }\r
7971         }\r
7972         else {\r
7973           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7974           errorText_ = errorStream_.str();\r
7975         }\r
7976       }\r
7977       else {\r
7978         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
7979         errorText_ = errorStream_.str();\r
7980       }\r
7981       error( RtAudioError::WARNING );\r
7982       goto tryOutput;\r
7983     }\r
7984 \r
7985     // Do byte swapping if necessary.\r
7986     if ( stream_.doByteSwap[1] )\r
7987       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
7988 \r
7989     // Do buffer conversion if necessary.\r
7990     if ( stream_.doConvertBuffer[1] )\r
7991       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
7992 \r
7993     // Check stream latency\r
7994     result = snd_pcm_delay( handle[1], &frames );\r
7995     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
7996   }\r
7997 \r
7998  tryOutput:\r
7999 \r
8000   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8001 \r
8002     // Setup parameters and do buffer conversion if necessary.\r
8003     if ( stream_.doConvertBuffer[0] ) {\r
8004       buffer = stream_.deviceBuffer;\r
8005       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
8006       channels = stream_.nDeviceChannels[0];\r
8007       format = stream_.deviceFormat[0];\r
8008     }\r
8009     else {\r
8010       buffer = stream_.userBuffer[0];\r
8011       channels = stream_.nUserChannels[0];\r
8012       format = stream_.userFormat;\r
8013     }\r
8014 \r
8015     // Do byte swapping if necessary.\r
8016     if ( stream_.doByteSwap[0] )\r
8017       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
8018 \r
8019     // Write samples to device in interleaved/non-interleaved format.\r
8020     if ( stream_.deviceInterleaved[0] )\r
8021       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
8022     else {\r
8023       void *bufs[channels];\r
8024       size_t offset = stream_.bufferSize * formatBytes( format );\r
8025       for ( int i=0; i<channels; i++ )\r
8026         bufs[i] = (void *) (buffer + (i * offset));\r
8027       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
8028     }\r
8029 \r
8030     if ( result < (int) stream_.bufferSize ) {\r
8031       // Either an error or underrun occured.\r
8032       if ( result == -EPIPE ) {\r
8033         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
8034         if ( state == SND_PCM_STATE_XRUN ) {\r
8035           apiInfo->xrun[0] = true;\r
8036           result = snd_pcm_prepare( handle[0] );\r
8037           if ( result < 0 ) {\r
8038             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
8039             errorText_ = errorStream_.str();\r
8040           }\r
8041           else\r
8042             errorText_ =  "RtApiAlsa::callbackEvent: audio write error, underrun.";\r
8043         }\r
8044         else {\r
8045           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
8046           errorText_ = errorStream_.str();\r
8047         }\r
8048       }\r
8049       else {\r
8050         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
8051         errorText_ = errorStream_.str();\r
8052       }\r
8053       error( RtAudioError::WARNING );\r
8054       goto unlock;\r
8055     }\r
8056 \r
8057     // Check stream latency\r
8058     result = snd_pcm_delay( handle[0], &frames );\r
8059     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
8060   }\r
8061 \r
8062  unlock:\r
8063   MUTEX_UNLOCK( &stream_.mutex );\r
8064 \r
8065   RtApi::tickStreamTime();\r
8066   if ( doStopStream == 1 ) this->stopStream();\r
8067 }\r
8068 \r
8069 static void *alsaCallbackHandler( void *ptr )\r
8070 {\r
8071   CallbackInfo *info = (CallbackInfo *) ptr;\r
8072   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
8073   bool *isRunning = &info->isRunning;\r
8074 \r
8075 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
8076   if ( info->doRealtime ) {\r
8077     pthread_t tID = pthread_self();      // ID of this thread\r
8078     sched_param prio = { info->priority }; // scheduling priority of thread\r
8079     pthread_setschedparam( tID, SCHED_RR, &prio );\r
8080   }\r
8081 #endif\r
8082 \r
8083   while ( *isRunning == true ) {\r
8084     pthread_testcancel();\r
8085     object->callbackEvent();\r
8086   }\r
8087 \r
8088   pthread_exit( NULL );\r
8089 }\r
8090 \r
8091 //******************** End of __LINUX_ALSA__ *********************//\r
8092 #endif\r
8093 \r
8094 #if defined(__LINUX_PULSE__)\r
8095 \r
8096 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8097 // and Tristan Matthews.\r
8098 \r
8099 #include <pulse/error.h>\r
8100 #include <pulse/simple.h>\r
8101 #include <cstdio>\r
8102 \r
8103 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8104                                                       44100, 48000, 96000, 0};\r
8105 \r
8106 struct rtaudio_pa_format_mapping_t {\r
8107   RtAudioFormat rtaudio_format;\r
8108   pa_sample_format_t pa_format;\r
8109 };\r
8110 \r
8111 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8112   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8113   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8114   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8115   {0, PA_SAMPLE_INVALID}};\r
8116 \r
8117 struct PulseAudioHandle {\r
8118   pa_simple *s_play;\r
8119   pa_simple *s_rec;\r
8120   pthread_t thread;\r
8121   pthread_cond_t runnable_cv;\r
8122   bool runnable;\r
8123   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8124 };\r
8125 \r
8126 RtApiPulse::~RtApiPulse()\r
8127 {\r
8128   if ( stream_.state != STREAM_CLOSED )\r
8129     closeStream();\r
8130 }\r
8131 \r
8132 unsigned int RtApiPulse::getDeviceCount( void )\r
8133 {\r
8134   return 1;\r
8135 }\r
8136 \r
8137 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8138 {\r
8139   RtAudio::DeviceInfo info;\r
8140   info.probed = true;\r
8141   info.name = "PulseAudio";\r
8142   info.outputChannels = 2;\r
8143   info.inputChannels = 2;\r
8144   info.duplexChannels = 2;\r
8145   info.isDefaultOutput = true;\r
8146   info.isDefaultInput = true;\r
8147 \r
8148   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8149     info.sampleRates.push_back( *sr );\r
8150 \r
8151   info.preferredSampleRate = 48000;\r
8152   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8153 \r
8154   return info;\r
8155 }\r
8156 \r
8157 static void *pulseaudio_callback( void * user )\r
8158 {\r
8159   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8160   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8161   volatile bool *isRunning = &cbi->isRunning;\r
8162 \r
8163   while ( *isRunning ) {\r
8164     pthread_testcancel();\r
8165     context->callbackEvent();\r
8166   }\r
8167 \r
8168   pthread_exit( NULL );\r
8169 }\r
8170 \r
8171 void RtApiPulse::closeStream( void )\r
8172 {\r
8173   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8174 \r
8175   stream_.callbackInfo.isRunning = false;\r
8176   if ( pah ) {\r
8177     MUTEX_LOCK( &stream_.mutex );\r
8178     if ( stream_.state == STREAM_STOPPED ) {\r
8179       pah->runnable = true;\r
8180       pthread_cond_signal( &pah->runnable_cv );\r
8181     }\r
8182     MUTEX_UNLOCK( &stream_.mutex );\r
8183 \r
8184     pthread_join( pah->thread, 0 );\r
8185     if ( pah->s_play ) {\r
8186       pa_simple_flush( pah->s_play, NULL );\r
8187       pa_simple_free( pah->s_play );\r
8188     }\r
8189     if ( pah->s_rec )\r
8190       pa_simple_free( pah->s_rec );\r
8191 \r
8192     pthread_cond_destroy( &pah->runnable_cv );\r
8193     delete pah;\r
8194     stream_.apiHandle = 0;\r
8195   }\r
8196 \r
8197   if ( stream_.userBuffer[0] ) {\r
8198     free( stream_.userBuffer[0] );\r
8199     stream_.userBuffer[0] = 0;\r
8200   }\r
8201   if ( stream_.userBuffer[1] ) {\r
8202     free( stream_.userBuffer[1] );\r
8203     stream_.userBuffer[1] = 0;\r
8204   }\r
8205 \r
8206   stream_.state = STREAM_CLOSED;\r
8207   stream_.mode = UNINITIALIZED;\r
8208 }\r
8209 \r
8210 void RtApiPulse::callbackEvent( void )\r
8211 {\r
8212   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8213 \r
8214   if ( stream_.state == STREAM_STOPPED ) {\r
8215     MUTEX_LOCK( &stream_.mutex );\r
8216     while ( !pah->runnable )\r
8217       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8218 \r
8219     if ( stream_.state != STREAM_RUNNING ) {\r
8220       MUTEX_UNLOCK( &stream_.mutex );\r
8221       return;\r
8222     }\r
8223     MUTEX_UNLOCK( &stream_.mutex );\r
8224   }\r
8225 \r
8226   if ( stream_.state == STREAM_CLOSED ) {\r
8227     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8228       "this shouldn't happen!";\r
8229     error( RtAudioError::WARNING );\r
8230     return;\r
8231   }\r
8232 \r
8233   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8234   double streamTime = getStreamTime();\r
8235   RtAudioStreamStatus status = 0;\r
8236   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8237                                stream_.bufferSize, streamTime, status,\r
8238                                stream_.callbackInfo.userData );\r
8239 \r
8240   if ( doStopStream == 2 ) {\r
8241     abortStream();\r
8242     return;\r
8243   }\r
8244 \r
8245   MUTEX_LOCK( &stream_.mutex );\r
8246   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8247   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8248 \r
8249   if ( stream_.state != STREAM_RUNNING )\r
8250     goto unlock;\r
8251 \r
8252   int pa_error;\r
8253   size_t bytes;\r
8254   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8255     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8256         convertBuffer( stream_.deviceBuffer,\r
8257                        stream_.userBuffer[OUTPUT],\r
8258                        stream_.convertInfo[OUTPUT] );\r
8259         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8260                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8261     } else\r
8262         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8263                 formatBytes( stream_.userFormat );\r
8264 \r
8265     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8266       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8267         pa_strerror( pa_error ) << ".";\r
8268       errorText_ = errorStream_.str();\r
8269       error( RtAudioError::WARNING );\r
8270     }\r
8271   }\r
8272 \r
8273   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8274     if ( stream_.doConvertBuffer[INPUT] )\r
8275       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8276         formatBytes( stream_.deviceFormat[INPUT] );\r
8277     else\r
8278       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8279         formatBytes( stream_.userFormat );\r
8280             \r
8281     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8282       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8283         pa_strerror( pa_error ) << ".";\r
8284       errorText_ = errorStream_.str();\r
8285       error( RtAudioError::WARNING );\r
8286     }\r
8287     if ( stream_.doConvertBuffer[INPUT] ) {\r
8288       convertBuffer( stream_.userBuffer[INPUT],\r
8289                      stream_.deviceBuffer,\r
8290                      stream_.convertInfo[INPUT] );\r
8291     }\r
8292   }\r
8293 \r
8294  unlock:\r
8295   MUTEX_UNLOCK( &stream_.mutex );\r
8296   RtApi::tickStreamTime();\r
8297 \r
8298   if ( doStopStream == 1 )\r
8299     stopStream();\r
8300 }\r
8301 \r
8302 void RtApiPulse::startStream( void )\r
8303 {\r
8304   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8305 \r
8306   if ( stream_.state == STREAM_CLOSED ) {\r
8307     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8308     error( RtAudioError::INVALID_USE );\r
8309     return;\r
8310   }\r
8311   if ( stream_.state == STREAM_RUNNING ) {\r
8312     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8313     error( RtAudioError::WARNING );\r
8314     return;\r
8315   }\r
8316 \r
8317   MUTEX_LOCK( &stream_.mutex );\r
8318 \r
8319   stream_.state = STREAM_RUNNING;\r
8320 \r
8321   pah->runnable = true;\r
8322   pthread_cond_signal( &pah->runnable_cv );\r
8323   MUTEX_UNLOCK( &stream_.mutex );\r
8324 }\r
8325 \r
8326 void RtApiPulse::stopStream( void )\r
8327 {\r
8328   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8329 \r
8330   if ( stream_.state == STREAM_CLOSED ) {\r
8331     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8332     error( RtAudioError::INVALID_USE );\r
8333     return;\r
8334   }\r
8335   if ( stream_.state == STREAM_STOPPED ) {\r
8336     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8337     error( RtAudioError::WARNING );\r
8338     return;\r
8339   }\r
8340 \r
8341   stream_.state = STREAM_STOPPED;\r
8342   MUTEX_LOCK( &stream_.mutex );\r
8343 \r
8344   if ( pah && pah->s_play ) {\r
8345     int pa_error;\r
8346     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8347       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8348         pa_strerror( pa_error ) << ".";\r
8349       errorText_ = errorStream_.str();\r
8350       MUTEX_UNLOCK( &stream_.mutex );\r
8351       error( RtAudioError::SYSTEM_ERROR );\r
8352       return;\r
8353     }\r
8354   }\r
8355 \r
8356   stream_.state = STREAM_STOPPED;\r
8357   MUTEX_UNLOCK( &stream_.mutex );\r
8358 }\r
8359 \r
8360 void RtApiPulse::abortStream( void )\r
8361 {\r
8362   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8363 \r
8364   if ( stream_.state == STREAM_CLOSED ) {\r
8365     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8366     error( RtAudioError::INVALID_USE );\r
8367     return;\r
8368   }\r
8369   if ( stream_.state == STREAM_STOPPED ) {\r
8370     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8371     error( RtAudioError::WARNING );\r
8372     return;\r
8373   }\r
8374 \r
8375   stream_.state = STREAM_STOPPED;\r
8376   MUTEX_LOCK( &stream_.mutex );\r
8377 \r
8378   if ( pah && pah->s_play ) {\r
8379     int pa_error;\r
8380     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8381       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8382         pa_strerror( pa_error ) << ".";\r
8383       errorText_ = errorStream_.str();\r
8384       MUTEX_UNLOCK( &stream_.mutex );\r
8385       error( RtAudioError::SYSTEM_ERROR );\r
8386       return;\r
8387     }\r
8388   }\r
8389 \r
8390   stream_.state = STREAM_STOPPED;\r
8391   MUTEX_UNLOCK( &stream_.mutex );\r
8392 }\r
8393 \r
8394 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8395                                   unsigned int channels, unsigned int firstChannel,\r
8396                                   unsigned int sampleRate, RtAudioFormat format,\r
8397                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8398 {\r
8399   PulseAudioHandle *pah = 0;\r
8400   unsigned long bufferBytes = 0;\r
8401   pa_sample_spec ss;\r
8402 \r
8403   if ( device != 0 ) return false;\r
8404   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8405   if ( channels != 1 && channels != 2 ) {\r
8406     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
8407     return false;\r
8408   }\r
8409   ss.channels = channels;\r
8410 \r
8411   if ( firstChannel != 0 ) return false;\r
8412 \r
8413   bool sr_found = false;\r
8414   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8415     if ( sampleRate == *sr ) {\r
8416       sr_found = true;\r
8417       stream_.sampleRate = sampleRate;\r
8418       ss.rate = sampleRate;\r
8419       break;\r
8420     }\r
8421   }\r
8422   if ( !sr_found ) {\r
8423     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8424     return false;\r
8425   }\r
8426 \r
8427   bool sf_found = 0;\r
8428   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8429         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8430     if ( format == sf->rtaudio_format ) {\r
8431       sf_found = true;\r
8432       stream_.userFormat = sf->rtaudio_format;\r
8433       stream_.deviceFormat[mode] = stream_.userFormat;\r
8434       ss.format = sf->pa_format;\r
8435       break;\r
8436     }\r
8437   }\r
8438   if ( !sf_found ) { // Use internal data format conversion.\r
8439     stream_.userFormat = format;\r
8440     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8441     ss.format = PA_SAMPLE_FLOAT32LE;\r
8442   }\r
8443 \r
8444   // Set other stream parameters.\r
8445   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8446   else stream_.userInterleaved = true;\r
8447   stream_.deviceInterleaved[mode] = true;\r
8448   stream_.nBuffers = 1;\r
8449   stream_.doByteSwap[mode] = false;\r
8450   stream_.nUserChannels[mode] = channels;\r
8451   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8452   stream_.channelOffset[mode] = 0;\r
8453   std::string streamName = "RtAudio";\r
8454 \r
8455   // Set flags for buffer conversion.\r
8456   stream_.doConvertBuffer[mode] = false;\r
8457   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8458     stream_.doConvertBuffer[mode] = true;\r
8459   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8460     stream_.doConvertBuffer[mode] = true;\r
8461 \r
8462   // Allocate necessary internal buffers.\r
8463   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8464   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8465   if ( stream_.userBuffer[mode] == NULL ) {\r
8466     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8467     goto error;\r
8468   }\r
8469   stream_.bufferSize = *bufferSize;\r
8470 \r
8471   if ( stream_.doConvertBuffer[mode] ) {\r
8472 \r
8473     bool makeBuffer = true;\r
8474     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8475     if ( mode == INPUT ) {\r
8476       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8477         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8478         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8479       }\r
8480     }\r
8481 \r
8482     if ( makeBuffer ) {\r
8483       bufferBytes *= *bufferSize;\r
8484       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8485       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8486       if ( stream_.deviceBuffer == NULL ) {\r
8487         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8488         goto error;\r
8489       }\r
8490     }\r
8491   }\r
8492 \r
8493   stream_.device[mode] = device;\r
8494 \r
8495   // Setup the buffer conversion information structure.\r
8496   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8497 \r
8498   if ( !stream_.apiHandle ) {\r
8499     PulseAudioHandle *pah = new PulseAudioHandle;\r
8500     if ( !pah ) {\r
8501       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8502       goto error;\r
8503     }\r
8504 \r
8505     stream_.apiHandle = pah;\r
8506     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8507       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8508       goto error;\r
8509     }\r
8510   }\r
8511   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8512 \r
8513   int error;\r
8514   if ( options && !options->streamName.empty() ) streamName = options->streamName;\r
8515   switch ( mode ) {\r
8516   case INPUT:\r
8517     pa_buffer_attr buffer_attr;\r
8518     buffer_attr.fragsize = bufferBytes;\r
8519     buffer_attr.maxlength = -1;\r
8520 \r
8521     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8522     if ( !pah->s_rec ) {\r
8523       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8524       goto error;\r
8525     }\r
8526     break;\r
8527   case OUTPUT:\r
8528     pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
8529     if ( !pah->s_play ) {\r
8530       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8531       goto error;\r
8532     }\r
8533     break;\r
8534   default:\r
8535     goto error;\r
8536   }\r
8537 \r
8538   if ( stream_.mode == UNINITIALIZED )\r
8539     stream_.mode = mode;\r
8540   else if ( stream_.mode == mode )\r
8541     goto error;\r
8542   else\r
8543     stream_.mode = DUPLEX;\r
8544 \r
8545   if ( !stream_.callbackInfo.isRunning ) {\r
8546     stream_.callbackInfo.object = this;\r
8547     stream_.callbackInfo.isRunning = true;\r
8548     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8549       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8550       goto error;\r
8551     }\r
8552   }\r
8553 \r
8554   stream_.state = STREAM_STOPPED;\r
8555   return true;\r
8556  \r
8557  error:\r
8558   if ( pah && stream_.callbackInfo.isRunning ) {\r
8559     pthread_cond_destroy( &pah->runnable_cv );\r
8560     delete pah;\r
8561     stream_.apiHandle = 0;\r
8562   }\r
8563 \r
8564   for ( int i=0; i<2; i++ ) {\r
8565     if ( stream_.userBuffer[i] ) {\r
8566       free( stream_.userBuffer[i] );\r
8567       stream_.userBuffer[i] = 0;\r
8568     }\r
8569   }\r
8570 \r
8571   if ( stream_.deviceBuffer ) {\r
8572     free( stream_.deviceBuffer );\r
8573     stream_.deviceBuffer = 0;\r
8574   }\r
8575 \r
8576   return FAILURE;\r
8577 }\r
8578 \r
8579 //******************** End of __LINUX_PULSE__ *********************//\r
8580 #endif\r
8581 \r
8582 #if defined(__LINUX_OSS__)\r
8583 \r
8584 #include <unistd.h>\r
8585 #include <sys/ioctl.h>\r
8586 #include <unistd.h>\r
8587 #include <fcntl.h>\r
8588 #include <sys/soundcard.h>\r
8589 #include <errno.h>\r
8590 #include <math.h>\r
8591 \r
8592 static void *ossCallbackHandler(void * ptr);\r
8593 \r
8594 // A structure to hold various information related to the OSS API\r
8595 // implementation.\r
8596 struct OssHandle {\r
8597   int id[2];    // device ids\r
8598   bool xrun[2];\r
8599   bool triggered;\r
8600   pthread_cond_t runnable;\r
8601 \r
8602   OssHandle()\r
8603     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8604 };\r
8605 \r
8606 RtApiOss :: RtApiOss()\r
8607 {\r
8608   // Nothing to do here.\r
8609 }\r
8610 \r
8611 RtApiOss :: ~RtApiOss()\r
8612 {\r
8613   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8614 }\r
8615 \r
8616 unsigned int RtApiOss :: getDeviceCount( void )\r
8617 {\r
8618   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8619   if ( mixerfd == -1 ) {\r
8620     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8621     error( RtAudioError::WARNING );\r
8622     return 0;\r
8623   }\r
8624 \r
8625   oss_sysinfo sysinfo;\r
8626   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8627     close( mixerfd );\r
8628     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8629     error( RtAudioError::WARNING );\r
8630     return 0;\r
8631   }\r
8632 \r
8633   close( mixerfd );\r
8634   return sysinfo.numaudios;\r
8635 }\r
8636 \r
8637 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8638 {\r
8639   RtAudio::DeviceInfo info;\r
8640   info.probed = false;\r
8641 \r
8642   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8643   if ( mixerfd == -1 ) {\r
8644     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8645     error( RtAudioError::WARNING );\r
8646     return info;\r
8647   }\r
8648 \r
8649   oss_sysinfo sysinfo;\r
8650   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8651   if ( result == -1 ) {\r
8652     close( mixerfd );\r
8653     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8654     error( RtAudioError::WARNING );\r
8655     return info;\r
8656   }\r
8657 \r
8658   unsigned nDevices = sysinfo.numaudios;\r
8659   if ( nDevices == 0 ) {\r
8660     close( mixerfd );\r
8661     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8662     error( RtAudioError::INVALID_USE );\r
8663     return info;\r
8664   }\r
8665 \r
8666   if ( device >= nDevices ) {\r
8667     close( mixerfd );\r
8668     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8669     error( RtAudioError::INVALID_USE );\r
8670     return info;\r
8671   }\r
8672 \r
8673   oss_audioinfo ainfo;\r
8674   ainfo.dev = device;\r
8675   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8676   close( mixerfd );\r
8677   if ( result == -1 ) {\r
8678     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8679     errorText_ = errorStream_.str();\r
8680     error( RtAudioError::WARNING );\r
8681     return info;\r
8682   }\r
8683 \r
8684   // Probe channels\r
8685   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8686   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8687   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8688     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8689       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8690   }\r
8691 \r
8692   // Probe data formats ... do for input\r
8693   unsigned long mask = ainfo.iformats;\r
8694   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8695     info.nativeFormats |= RTAUDIO_SINT16;\r
8696   if ( mask & AFMT_S8 )\r
8697     info.nativeFormats |= RTAUDIO_SINT8;\r
8698   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8699     info.nativeFormats |= RTAUDIO_SINT32;\r
8700   if ( mask & AFMT_FLOAT )\r
8701     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8702   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8703     info.nativeFormats |= RTAUDIO_SINT24;\r
8704 \r
8705   // Check that we have at least one supported format\r
8706   if ( info.nativeFormats == 0 ) {\r
8707     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8708     errorText_ = errorStream_.str();\r
8709     error( RtAudioError::WARNING );\r
8710     return info;\r
8711   }\r
8712 \r
8713   // Probe the supported sample rates.\r
8714   info.sampleRates.clear();\r
8715   if ( ainfo.nrates ) {\r
8716     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8717       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8718         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8719           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8720 \r
8721           if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
8722             info.preferredSampleRate = SAMPLE_RATES[k];\r
8723 \r
8724           break;\r
8725         }\r
8726       }\r
8727     }\r
8728   }\r
8729   else {\r
8730     // Check min and max rate values;\r
8731     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8732       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {\r
8733         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8734 \r
8735         if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
8736           info.preferredSampleRate = SAMPLE_RATES[k];\r
8737       }\r
8738     }\r
8739   }\r
8740 \r
8741   if ( info.sampleRates.size() == 0 ) {\r
8742     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8743     errorText_ = errorStream_.str();\r
8744     error( RtAudioError::WARNING );\r
8745   }\r
8746   else {\r
8747     info.probed = true;\r
8748     info.name = ainfo.name;\r
8749   }\r
8750 \r
8751   return info;\r
8752 }\r
8753 \r
8754 \r
8755 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8756                                   unsigned int firstChannel, unsigned int sampleRate,\r
8757                                   RtAudioFormat format, unsigned int *bufferSize,\r
8758                                   RtAudio::StreamOptions *options )\r
8759 {\r
8760   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8761   if ( mixerfd == -1 ) {\r
8762     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8763     return FAILURE;\r
8764   }\r
8765 \r
8766   oss_sysinfo sysinfo;\r
8767   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8768   if ( result == -1 ) {\r
8769     close( mixerfd );\r
8770     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8771     return FAILURE;\r
8772   }\r
8773 \r
8774   unsigned nDevices = sysinfo.numaudios;\r
8775   if ( nDevices == 0 ) {\r
8776     // This should not happen because a check is made before this function is called.\r
8777     close( mixerfd );\r
8778     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8779     return FAILURE;\r
8780   }\r
8781 \r
8782   if ( device >= nDevices ) {\r
8783     // This should not happen because a check is made before this function is called.\r
8784     close( mixerfd );\r
8785     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8786     return FAILURE;\r
8787   }\r
8788 \r
8789   oss_audioinfo ainfo;\r
8790   ainfo.dev = device;\r
8791   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8792   close( mixerfd );\r
8793   if ( result == -1 ) {\r
8794     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8795     errorText_ = errorStream_.str();\r
8796     return FAILURE;\r
8797   }\r
8798 \r
8799   // Check if device supports input or output\r
8800   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8801        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8802     if ( mode == OUTPUT )\r
8803       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8804     else\r
8805       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8806     errorText_ = errorStream_.str();\r
8807     return FAILURE;\r
8808   }\r
8809 \r
8810   int flags = 0;\r
8811   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8812   if ( mode == OUTPUT )\r
8813     flags |= O_WRONLY;\r
8814   else { // mode == INPUT\r
8815     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8816       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8817       close( handle->id[0] );\r
8818       handle->id[0] = 0;\r
8819       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8820         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8821         errorText_ = errorStream_.str();\r
8822         return FAILURE;\r
8823       }\r
8824       // Check that the number previously set channels is the same.\r
8825       if ( stream_.nUserChannels[0] != channels ) {\r
8826         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8827         errorText_ = errorStream_.str();\r
8828         return FAILURE;\r
8829       }\r
8830       flags |= O_RDWR;\r
8831     }\r
8832     else\r
8833       flags |= O_RDONLY;\r
8834   }\r
8835 \r
8836   // Set exclusive access if specified.\r
8837   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8838 \r
8839   // Try to open the device.\r
8840   int fd;\r
8841   fd = open( ainfo.devnode, flags, 0 );\r
8842   if ( fd == -1 ) {\r
8843     if ( errno == EBUSY )\r
8844       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8845     else\r
8846       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8847     errorText_ = errorStream_.str();\r
8848     return FAILURE;\r
8849   }\r
8850 \r
8851   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8852   /*\r
8853     if ( flags | O_RDWR ) {\r
8854     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
8855     if ( result == -1) {\r
8856     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
8857     errorText_ = errorStream_.str();\r
8858     return FAILURE;\r
8859     }\r
8860     }\r
8861   */\r
8862 \r
8863   // Check the device channel support.\r
8864   stream_.nUserChannels[mode] = channels;\r
8865   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
8866     close( fd );\r
8867     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
8868     errorText_ = errorStream_.str();\r
8869     return FAILURE;\r
8870   }\r
8871 \r
8872   // Set the number of channels.\r
8873   int deviceChannels = channels + firstChannel;\r
8874   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
8875   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
8876     close( fd );\r
8877     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
8878     errorText_ = errorStream_.str();\r
8879     return FAILURE;\r
8880   }\r
8881   stream_.nDeviceChannels[mode] = deviceChannels;\r
8882 \r
8883   // Get the data format mask\r
8884   int mask;\r
8885   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
8886   if ( result == -1 ) {\r
8887     close( fd );\r
8888     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
8889     errorText_ = errorStream_.str();\r
8890     return FAILURE;\r
8891   }\r
8892 \r
8893   // Determine how to set the device format.\r
8894   stream_.userFormat = format;\r
8895   int deviceFormat = -1;\r
8896   stream_.doByteSwap[mode] = false;\r
8897   if ( format == RTAUDIO_SINT8 ) {\r
8898     if ( mask & AFMT_S8 ) {\r
8899       deviceFormat = AFMT_S8;\r
8900       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8901     }\r
8902   }\r
8903   else if ( format == RTAUDIO_SINT16 ) {\r
8904     if ( mask & AFMT_S16_NE ) {\r
8905       deviceFormat = AFMT_S16_NE;\r
8906       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8907     }\r
8908     else if ( mask & AFMT_S16_OE ) {\r
8909       deviceFormat = AFMT_S16_OE;\r
8910       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8911       stream_.doByteSwap[mode] = true;\r
8912     }\r
8913   }\r
8914   else if ( format == RTAUDIO_SINT24 ) {\r
8915     if ( mask & AFMT_S24_NE ) {\r
8916       deviceFormat = AFMT_S24_NE;\r
8917       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8918     }\r
8919     else if ( mask & AFMT_S24_OE ) {\r
8920       deviceFormat = AFMT_S24_OE;\r
8921       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8922       stream_.doByteSwap[mode] = true;\r
8923     }\r
8924   }\r
8925   else if ( format == RTAUDIO_SINT32 ) {\r
8926     if ( mask & AFMT_S32_NE ) {\r
8927       deviceFormat = AFMT_S32_NE;\r
8928       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8929     }\r
8930     else if ( mask & AFMT_S32_OE ) {\r
8931       deviceFormat = AFMT_S32_OE;\r
8932       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8933       stream_.doByteSwap[mode] = true;\r
8934     }\r
8935   }\r
8936 \r
8937   if ( deviceFormat == -1 ) {\r
8938     // The user requested format is not natively supported by the device.\r
8939     if ( mask & AFMT_S16_NE ) {\r
8940       deviceFormat = AFMT_S16_NE;\r
8941       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8942     }\r
8943     else if ( mask & AFMT_S32_NE ) {\r
8944       deviceFormat = AFMT_S32_NE;\r
8945       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8946     }\r
8947     else if ( mask & AFMT_S24_NE ) {\r
8948       deviceFormat = AFMT_S24_NE;\r
8949       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8950     }\r
8951     else if ( mask & AFMT_S16_OE ) {\r
8952       deviceFormat = AFMT_S16_OE;\r
8953       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
8954       stream_.doByteSwap[mode] = true;\r
8955     }\r
8956     else if ( mask & AFMT_S32_OE ) {\r
8957       deviceFormat = AFMT_S32_OE;\r
8958       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
8959       stream_.doByteSwap[mode] = true;\r
8960     }\r
8961     else if ( mask & AFMT_S24_OE ) {\r
8962       deviceFormat = AFMT_S24_OE;\r
8963       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
8964       stream_.doByteSwap[mode] = true;\r
8965     }\r
8966     else if ( mask & AFMT_S8) {\r
8967       deviceFormat = AFMT_S8;\r
8968       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
8969     }\r
8970   }\r
8971 \r
8972   if ( stream_.deviceFormat[mode] == 0 ) {\r
8973     // This really shouldn't happen ...\r
8974     close( fd );\r
8975     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8976     errorText_ = errorStream_.str();\r
8977     return FAILURE;\r
8978   }\r
8979 \r
8980   // Set the data format.\r
8981   int temp = deviceFormat;\r
8982   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
8983   if ( result == -1 || deviceFormat != temp ) {\r
8984     close( fd );\r
8985     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
8986     errorText_ = errorStream_.str();\r
8987     return FAILURE;\r
8988   }\r
8989 \r
8990   // Attempt to set the buffer size.  According to OSS, the minimum\r
8991   // number of buffers is two.  The supposed minimum buffer size is 16\r
8992   // bytes, so that will be our lower bound.  The argument to this\r
8993   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
8994   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
8995   // We'll check the actual value used near the end of the setup\r
8996   // procedure.\r
8997   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
8998   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
8999   int buffers = 0;\r
9000   if ( options ) buffers = options->numberOfBuffers;\r
9001   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
9002   if ( buffers < 2 ) buffers = 3;\r
9003   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
9004   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
9005   if ( result == -1 ) {\r
9006     close( fd );\r
9007     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
9008     errorText_ = errorStream_.str();\r
9009     return FAILURE;\r
9010   }\r
9011   stream_.nBuffers = buffers;\r
9012 \r
9013   // Save buffer size (in sample frames).\r
9014   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
9015   stream_.bufferSize = *bufferSize;\r
9016 \r
9017   // Set the sample rate.\r
9018   int srate = sampleRate;\r
9019   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
9020   if ( result == -1 ) {\r
9021     close( fd );\r
9022     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
9023     errorText_ = errorStream_.str();\r
9024     return FAILURE;\r
9025   }\r
9026 \r
9027   // Verify the sample rate setup worked.\r
9028   if ( abs( srate - sampleRate ) > 100 ) {\r
9029     close( fd );\r
9030     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
9031     errorText_ = errorStream_.str();\r
9032     return FAILURE;\r
9033   }\r
9034   stream_.sampleRate = sampleRate;\r
9035 \r
9036   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
9037     // We're doing duplex setup here.\r
9038     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
9039     stream_.nDeviceChannels[0] = deviceChannels;\r
9040   }\r
9041 \r
9042   // Set interleaving parameters.\r
9043   stream_.userInterleaved = true;\r
9044   stream_.deviceInterleaved[mode] =  true;\r
9045   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
9046     stream_.userInterleaved = false;\r
9047 \r
9048   // Set flags for buffer conversion\r
9049   stream_.doConvertBuffer[mode] = false;\r
9050   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
9051     stream_.doConvertBuffer[mode] = true;\r
9052   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
9053     stream_.doConvertBuffer[mode] = true;\r
9054   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
9055        stream_.nUserChannels[mode] > 1 )\r
9056     stream_.doConvertBuffer[mode] = true;\r
9057 \r
9058   // Allocate the stream handles if necessary and then save.\r
9059   if ( stream_.apiHandle == 0 ) {\r
9060     try {\r
9061       handle = new OssHandle;\r
9062     }\r
9063     catch ( std::bad_alloc& ) {\r
9064       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
9065       goto error;\r
9066     }\r
9067 \r
9068     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
9069       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
9070       goto error;\r
9071     }\r
9072 \r
9073     stream_.apiHandle = (void *) handle;\r
9074   }\r
9075   else {\r
9076     handle = (OssHandle *) stream_.apiHandle;\r
9077   }\r
9078   handle->id[mode] = fd;\r
9079 \r
9080   // Allocate necessary internal buffers.\r
9081   unsigned long bufferBytes;\r
9082   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
9083   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
9084   if ( stream_.userBuffer[mode] == NULL ) {\r
9085     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
9086     goto error;\r
9087   }\r
9088 \r
9089   if ( stream_.doConvertBuffer[mode] ) {\r
9090 \r
9091     bool makeBuffer = true;\r
9092     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
9093     if ( mode == INPUT ) {\r
9094       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
9095         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
9096         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
9097       }\r
9098     }\r
9099 \r
9100     if ( makeBuffer ) {\r
9101       bufferBytes *= *bufferSize;\r
9102       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9103       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9104       if ( stream_.deviceBuffer == NULL ) {\r
9105         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9106         goto error;\r
9107       }\r
9108     }\r
9109   }\r
9110 \r
9111   stream_.device[mode] = device;\r
9112   stream_.state = STREAM_STOPPED;\r
9113 \r
9114   // Setup the buffer conversion information structure.\r
9115   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9116 \r
9117   // Setup thread if necessary.\r
9118   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9119     // We had already set up an output stream.\r
9120     stream_.mode = DUPLEX;\r
9121     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9122   }\r
9123   else {\r
9124     stream_.mode = mode;\r
9125 \r
9126     // Setup callback thread.\r
9127     stream_.callbackInfo.object = (void *) this;\r
9128 \r
9129     // Set the thread attributes for joinable and realtime scheduling\r
9130     // priority.  The higher priority will only take affect if the\r
9131     // program is run as root or suid.\r
9132     pthread_attr_t attr;\r
9133     pthread_attr_init( &attr );\r
9134     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9135 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9136     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9137       struct sched_param param;\r
9138       int priority = options->priority;\r
9139       int min = sched_get_priority_min( SCHED_RR );\r
9140       int max = sched_get_priority_max( SCHED_RR );\r
9141       if ( priority < min ) priority = min;\r
9142       else if ( priority > max ) priority = max;\r
9143       param.sched_priority = priority;\r
9144       pthread_attr_setschedparam( &attr, &param );\r
9145       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9146     }\r
9147     else\r
9148       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9149 #else\r
9150     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9151 #endif\r
9152 \r
9153     stream_.callbackInfo.isRunning = true;\r
9154     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9155     pthread_attr_destroy( &attr );\r
9156     if ( result ) {\r
9157       stream_.callbackInfo.isRunning = false;\r
9158       errorText_ = "RtApiOss::error creating callback thread!";\r
9159       goto error;\r
9160     }\r
9161   }\r
9162 \r
9163   return SUCCESS;\r
9164 \r
9165  error:\r
9166   if ( handle ) {\r
9167     pthread_cond_destroy( &handle->runnable );\r
9168     if ( handle->id[0] ) close( handle->id[0] );\r
9169     if ( handle->id[1] ) close( handle->id[1] );\r
9170     delete handle;\r
9171     stream_.apiHandle = 0;\r
9172   }\r
9173 \r
9174   for ( int i=0; i<2; i++ ) {\r
9175     if ( stream_.userBuffer[i] ) {\r
9176       free( stream_.userBuffer[i] );\r
9177       stream_.userBuffer[i] = 0;\r
9178     }\r
9179   }\r
9180 \r
9181   if ( stream_.deviceBuffer ) {\r
9182     free( stream_.deviceBuffer );\r
9183     stream_.deviceBuffer = 0;\r
9184   }\r
9185 \r
9186   return FAILURE;\r
9187 }\r
9188 \r
9189 void RtApiOss :: closeStream()\r
9190 {\r
9191   if ( stream_.state == STREAM_CLOSED ) {\r
9192     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9193     error( RtAudioError::WARNING );\r
9194     return;\r
9195   }\r
9196 \r
9197   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9198   stream_.callbackInfo.isRunning = false;\r
9199   MUTEX_LOCK( &stream_.mutex );\r
9200   if ( stream_.state == STREAM_STOPPED )\r
9201     pthread_cond_signal( &handle->runnable );\r
9202   MUTEX_UNLOCK( &stream_.mutex );\r
9203   pthread_join( stream_.callbackInfo.thread, NULL );\r
9204 \r
9205   if ( stream_.state == STREAM_RUNNING ) {\r
9206     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9207       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9208     else\r
9209       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9210     stream_.state = STREAM_STOPPED;\r
9211   }\r
9212 \r
9213   if ( handle ) {\r
9214     pthread_cond_destroy( &handle->runnable );\r
9215     if ( handle->id[0] ) close( handle->id[0] );\r
9216     if ( handle->id[1] ) close( handle->id[1] );\r
9217     delete handle;\r
9218     stream_.apiHandle = 0;\r
9219   }\r
9220 \r
9221   for ( int i=0; i<2; i++ ) {\r
9222     if ( stream_.userBuffer[i] ) {\r
9223       free( stream_.userBuffer[i] );\r
9224       stream_.userBuffer[i] = 0;\r
9225     }\r
9226   }\r
9227 \r
9228   if ( stream_.deviceBuffer ) {\r
9229     free( stream_.deviceBuffer );\r
9230     stream_.deviceBuffer = 0;\r
9231   }\r
9232 \r
9233   stream_.mode = UNINITIALIZED;\r
9234   stream_.state = STREAM_CLOSED;\r
9235 }\r
9236 \r
9237 void RtApiOss :: startStream()\r
9238 {\r
9239   verifyStream();\r
9240   if ( stream_.state == STREAM_RUNNING ) {\r
9241     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9242     error( RtAudioError::WARNING );\r
9243     return;\r
9244   }\r
9245 \r
9246   MUTEX_LOCK( &stream_.mutex );\r
9247 \r
9248   stream_.state = STREAM_RUNNING;\r
9249 \r
9250   // No need to do anything else here ... OSS automatically starts\r
9251   // when fed samples.\r
9252 \r
9253   MUTEX_UNLOCK( &stream_.mutex );\r
9254 \r
9255   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9256   pthread_cond_signal( &handle->runnable );\r
9257 }\r
9258 \r
9259 void RtApiOss :: stopStream()\r
9260 {\r
9261   verifyStream();\r
9262   if ( stream_.state == STREAM_STOPPED ) {\r
9263     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9264     error( RtAudioError::WARNING );\r
9265     return;\r
9266   }\r
9267 \r
9268   MUTEX_LOCK( &stream_.mutex );\r
9269 \r
9270   // The state might change while waiting on a mutex.\r
9271   if ( stream_.state == STREAM_STOPPED ) {\r
9272     MUTEX_UNLOCK( &stream_.mutex );\r
9273     return;\r
9274   }\r
9275 \r
9276   int result = 0;\r
9277   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9278   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9279 \r
9280     // Flush the output with zeros a few times.\r
9281     char *buffer;\r
9282     int samples;\r
9283     RtAudioFormat format;\r
9284 \r
9285     if ( stream_.doConvertBuffer[0] ) {\r
9286       buffer = stream_.deviceBuffer;\r
9287       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9288       format = stream_.deviceFormat[0];\r
9289     }\r
9290     else {\r
9291       buffer = stream_.userBuffer[0];\r
9292       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9293       format = stream_.userFormat;\r
9294     }\r
9295 \r
9296     memset( buffer, 0, samples * formatBytes(format) );\r
9297     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9298       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9299       if ( result == -1 ) {\r
9300         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9301         error( RtAudioError::WARNING );\r
9302       }\r
9303     }\r
9304 \r
9305     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9306     if ( result == -1 ) {\r
9307       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9308       errorText_ = errorStream_.str();\r
9309       goto unlock;\r
9310     }\r
9311     handle->triggered = false;\r
9312   }\r
9313 \r
9314   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9315     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9316     if ( result == -1 ) {\r
9317       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9318       errorText_ = errorStream_.str();\r
9319       goto unlock;\r
9320     }\r
9321   }\r
9322 \r
9323  unlock:\r
9324   stream_.state = STREAM_STOPPED;\r
9325   MUTEX_UNLOCK( &stream_.mutex );\r
9326 \r
9327   if ( result != -1 ) return;\r
9328   error( RtAudioError::SYSTEM_ERROR );\r
9329 }\r
9330 \r
9331 void RtApiOss :: abortStream()\r
9332 {\r
9333   verifyStream();\r
9334   if ( stream_.state == STREAM_STOPPED ) {\r
9335     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9336     error( RtAudioError::WARNING );\r
9337     return;\r
9338   }\r
9339 \r
9340   MUTEX_LOCK( &stream_.mutex );\r
9341 \r
9342   // The state might change while waiting on a mutex.\r
9343   if ( stream_.state == STREAM_STOPPED ) {\r
9344     MUTEX_UNLOCK( &stream_.mutex );\r
9345     return;\r
9346   }\r
9347 \r
9348   int result = 0;\r
9349   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9350   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9351     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9352     if ( result == -1 ) {\r
9353       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9354       errorText_ = errorStream_.str();\r
9355       goto unlock;\r
9356     }\r
9357     handle->triggered = false;\r
9358   }\r
9359 \r
9360   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9361     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9362     if ( result == -1 ) {\r
9363       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9364       errorText_ = errorStream_.str();\r
9365       goto unlock;\r
9366     }\r
9367   }\r
9368 \r
9369  unlock:\r
9370   stream_.state = STREAM_STOPPED;\r
9371   MUTEX_UNLOCK( &stream_.mutex );\r
9372 \r
9373   if ( result != -1 ) return;\r
9374   error( RtAudioError::SYSTEM_ERROR );\r
9375 }\r
9376 \r
9377 void RtApiOss :: callbackEvent()\r
9378 {\r
9379   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9380   if ( stream_.state == STREAM_STOPPED ) {\r
9381     MUTEX_LOCK( &stream_.mutex );\r
9382     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9383     if ( stream_.state != STREAM_RUNNING ) {\r
9384       MUTEX_UNLOCK( &stream_.mutex );\r
9385       return;\r
9386     }\r
9387     MUTEX_UNLOCK( &stream_.mutex );\r
9388   }\r
9389 \r
9390   if ( stream_.state == STREAM_CLOSED ) {\r
9391     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9392     error( RtAudioError::WARNING );\r
9393     return;\r
9394   }\r
9395 \r
9396   // Invoke user callback to get fresh output data.\r
9397   int doStopStream = 0;\r
9398   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9399   double streamTime = getStreamTime();\r
9400   RtAudioStreamStatus status = 0;\r
9401   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9402     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9403     handle->xrun[0] = false;\r
9404   }\r
9405   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9406     status |= RTAUDIO_INPUT_OVERFLOW;\r
9407     handle->xrun[1] = false;\r
9408   }\r
9409   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9410                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9411   if ( doStopStream == 2 ) {\r
9412     this->abortStream();\r
9413     return;\r
9414   }\r
9415 \r
9416   MUTEX_LOCK( &stream_.mutex );\r
9417 \r
9418   // The state might change while waiting on a mutex.\r
9419   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9420 \r
9421   int result;\r
9422   char *buffer;\r
9423   int samples;\r
9424   RtAudioFormat format;\r
9425 \r
9426   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9427 \r
9428     // Setup parameters and do buffer conversion if necessary.\r
9429     if ( stream_.doConvertBuffer[0] ) {\r
9430       buffer = stream_.deviceBuffer;\r
9431       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9432       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9433       format = stream_.deviceFormat[0];\r
9434     }\r
9435     else {\r
9436       buffer = stream_.userBuffer[0];\r
9437       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9438       format = stream_.userFormat;\r
9439     }\r
9440 \r
9441     // Do byte swapping if necessary.\r
9442     if ( stream_.doByteSwap[0] )\r
9443       byteSwapBuffer( buffer, samples, format );\r
9444 \r
9445     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9446       int trig = 0;\r
9447       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9448       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9449       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9450       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9451       handle->triggered = true;\r
9452     }\r
9453     else\r
9454       // Write samples to device.\r
9455       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9456 \r
9457     if ( result == -1 ) {\r
9458       // We'll assume this is an underrun, though there isn't a\r
9459       // specific means for determining that.\r
9460       handle->xrun[0] = true;\r
9461       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9462       error( RtAudioError::WARNING );\r
9463       // Continue on to input section.\r
9464     }\r
9465   }\r
9466 \r
9467   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9468 \r
9469     // Setup parameters.\r
9470     if ( stream_.doConvertBuffer[1] ) {\r
9471       buffer = stream_.deviceBuffer;\r
9472       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9473       format = stream_.deviceFormat[1];\r
9474     }\r
9475     else {\r
9476       buffer = stream_.userBuffer[1];\r
9477       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9478       format = stream_.userFormat;\r
9479     }\r
9480 \r
9481     // Read samples from device.\r
9482     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9483 \r
9484     if ( result == -1 ) {\r
9485       // We'll assume this is an overrun, though there isn't a\r
9486       // specific means for determining that.\r
9487       handle->xrun[1] = true;\r
9488       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9489       error( RtAudioError::WARNING );\r
9490       goto unlock;\r
9491     }\r
9492 \r
9493     // Do byte swapping if necessary.\r
9494     if ( stream_.doByteSwap[1] )\r
9495       byteSwapBuffer( buffer, samples, format );\r
9496 \r
9497     // Do buffer conversion if necessary.\r
9498     if ( stream_.doConvertBuffer[1] )\r
9499       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9500   }\r
9501 \r
9502  unlock:\r
9503   MUTEX_UNLOCK( &stream_.mutex );\r
9504 \r
9505   RtApi::tickStreamTime();\r
9506   if ( doStopStream == 1 ) this->stopStream();\r
9507 }\r
9508 \r
9509 static void *ossCallbackHandler( void *ptr )\r
9510 {\r
9511   CallbackInfo *info = (CallbackInfo *) ptr;\r
9512   RtApiOss *object = (RtApiOss *) info->object;\r
9513   bool *isRunning = &info->isRunning;\r
9514 \r
9515   while ( *isRunning == true ) {\r
9516     pthread_testcancel();\r
9517     object->callbackEvent();\r
9518   }\r
9519 \r
9520   pthread_exit( NULL );\r
9521 }\r
9522 \r
9523 //******************** End of __LINUX_OSS__ *********************//\r
9524 #endif\r
9525 \r
9526 \r
9527 // *************************************************** //\r
9528 //\r
9529 // Protected common (OS-independent) RtAudio methods.\r
9530 //\r
9531 // *************************************************** //\r
9532 \r
9533 // This method can be modified to control the behavior of error\r
9534 // message printing.\r
9535 void RtApi :: error( RtAudioError::Type type )\r
9536 {\r
9537   errorStream_.str(""); // clear the ostringstream\r
9538 \r
9539   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9540   if ( errorCallback ) {\r
9541     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9542 \r
9543     if ( firstErrorOccurred_ )\r
9544       return;\r
9545 \r
9546     firstErrorOccurred_ = true;\r
9547     const std::string errorMessage = errorText_;\r
9548 \r
9549     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9550       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9551       abortStream();\r
9552     }\r
9553 \r
9554     errorCallback( type, errorMessage );\r
9555     firstErrorOccurred_ = false;\r
9556     return;\r
9557   }\r
9558 \r
9559   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9560     std::cerr << '\n' << errorText_ << "\n\n";\r
9561   else if ( type != RtAudioError::WARNING )\r
9562     throw( RtAudioError( errorText_, type ) );\r
9563 }\r
9564 \r
9565 void RtApi :: verifyStream()\r
9566 {\r
9567   if ( stream_.state == STREAM_CLOSED ) {\r
9568     errorText_ = "RtApi:: a stream is not open!";\r
9569     error( RtAudioError::INVALID_USE );\r
9570   }\r
9571 }\r
9572 \r
9573 void RtApi :: clearStreamInfo()\r
9574 {\r
9575   stream_.mode = UNINITIALIZED;\r
9576   stream_.state = STREAM_CLOSED;\r
9577   stream_.sampleRate = 0;\r
9578   stream_.bufferSize = 0;\r
9579   stream_.nBuffers = 0;\r
9580   stream_.userFormat = 0;\r
9581   stream_.userInterleaved = true;\r
9582   stream_.streamTime = 0.0;\r
9583   stream_.apiHandle = 0;\r
9584   stream_.deviceBuffer = 0;\r
9585   stream_.callbackInfo.callback = 0;\r
9586   stream_.callbackInfo.userData = 0;\r
9587   stream_.callbackInfo.isRunning = false;\r
9588   stream_.callbackInfo.errorCallback = 0;\r
9589   for ( int i=0; i<2; i++ ) {\r
9590     stream_.device[i] = 11111;\r
9591     stream_.doConvertBuffer[i] = false;\r
9592     stream_.deviceInterleaved[i] = true;\r
9593     stream_.doByteSwap[i] = false;\r
9594     stream_.nUserChannels[i] = 0;\r
9595     stream_.nDeviceChannels[i] = 0;\r
9596     stream_.channelOffset[i] = 0;\r
9597     stream_.deviceFormat[i] = 0;\r
9598     stream_.latency[i] = 0;\r
9599     stream_.userBuffer[i] = 0;\r
9600     stream_.convertInfo[i].channels = 0;\r
9601     stream_.convertInfo[i].inJump = 0;\r
9602     stream_.convertInfo[i].outJump = 0;\r
9603     stream_.convertInfo[i].inFormat = 0;\r
9604     stream_.convertInfo[i].outFormat = 0;\r
9605     stream_.convertInfo[i].inOffset.clear();\r
9606     stream_.convertInfo[i].outOffset.clear();\r
9607   }\r
9608 }\r
9609 \r
9610 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9611 {\r
9612   if ( format == RTAUDIO_SINT16 )\r
9613     return 2;\r
9614   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9615     return 4;\r
9616   else if ( format == RTAUDIO_FLOAT64 )\r
9617     return 8;\r
9618   else if ( format == RTAUDIO_SINT24 )\r
9619     return 3;\r
9620   else if ( format == RTAUDIO_SINT8 )\r
9621     return 1;\r
9622 \r
9623   errorText_ = "RtApi::formatBytes: undefined format.";\r
9624   error( RtAudioError::WARNING );\r
9625 \r
9626   return 0;\r
9627 }\r
9628 \r
9629 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9630 {\r
9631   if ( mode == INPUT ) { // convert device to user buffer\r
9632     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9633     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9634     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9635     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9636   }\r
9637   else { // convert user to device buffer\r
9638     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9639     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9640     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9641     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9642   }\r
9643 \r
9644   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9645     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9646   else\r
9647     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9648 \r
9649   // Set up the interleave/deinterleave offsets.\r
9650   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9651     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9652          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9653       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9654         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9655         stream_.convertInfo[mode].outOffset.push_back( k );\r
9656         stream_.convertInfo[mode].inJump = 1;\r
9657       }\r
9658     }\r
9659     else {\r
9660       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9661         stream_.convertInfo[mode].inOffset.push_back( k );\r
9662         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9663         stream_.convertInfo[mode].outJump = 1;\r
9664       }\r
9665     }\r
9666   }\r
9667   else { // no (de)interleaving\r
9668     if ( stream_.userInterleaved ) {\r
9669       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9670         stream_.convertInfo[mode].inOffset.push_back( k );\r
9671         stream_.convertInfo[mode].outOffset.push_back( k );\r
9672       }\r
9673     }\r
9674     else {\r
9675       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9676         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9677         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9678         stream_.convertInfo[mode].inJump = 1;\r
9679         stream_.convertInfo[mode].outJump = 1;\r
9680       }\r
9681     }\r
9682   }\r
9683 \r
9684   // Add channel offset.\r
9685   if ( firstChannel > 0 ) {\r
9686     if ( stream_.deviceInterleaved[mode] ) {\r
9687       if ( mode == OUTPUT ) {\r
9688         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9689           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9690       }\r
9691       else {\r
9692         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9693           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9694       }\r
9695     }\r
9696     else {\r
9697       if ( mode == OUTPUT ) {\r
9698         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9699           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9700       }\r
9701       else {\r
9702         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9703           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9704       }\r
9705     }\r
9706   }\r
9707 }\r
9708 \r
9709 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9710 {\r
9711   // This function does format conversion, input/output channel compensation, and\r
9712   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9713   // the lower three bytes of a 32-bit integer.\r
9714 \r
9715   // Clear our device buffer when in/out duplex device channels are different\r
9716   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9717        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9718     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9719 \r
9720   int j;\r
9721   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9722     Float64 scale;\r
9723     Float64 *out = (Float64 *)outBuffer;\r
9724 \r
9725     if (info.inFormat == RTAUDIO_SINT8) {\r
9726       signed char *in = (signed char *)inBuffer;\r
9727       scale = 1.0 / 127.5;\r
9728       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9729         for (j=0; j<info.channels; j++) {\r
9730           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9731           out[info.outOffset[j]] += 0.5;\r
9732           out[info.outOffset[j]] *= scale;\r
9733         }\r
9734         in += info.inJump;\r
9735         out += info.outJump;\r
9736       }\r
9737     }\r
9738     else if (info.inFormat == RTAUDIO_SINT16) {\r
9739       Int16 *in = (Int16 *)inBuffer;\r
9740       scale = 1.0 / 32767.5;\r
9741       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9742         for (j=0; j<info.channels; j++) {\r
9743           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9744           out[info.outOffset[j]] += 0.5;\r
9745           out[info.outOffset[j]] *= scale;\r
9746         }\r
9747         in += info.inJump;\r
9748         out += info.outJump;\r
9749       }\r
9750     }\r
9751     else if (info.inFormat == RTAUDIO_SINT24) {\r
9752       Int24 *in = (Int24 *)inBuffer;\r
9753       scale = 1.0 / 8388607.5;\r
9754       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9755         for (j=0; j<info.channels; j++) {\r
9756           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9757           out[info.outOffset[j]] += 0.5;\r
9758           out[info.outOffset[j]] *= scale;\r
9759         }\r
9760         in += info.inJump;\r
9761         out += info.outJump;\r
9762       }\r
9763     }\r
9764     else if (info.inFormat == RTAUDIO_SINT32) {\r
9765       Int32 *in = (Int32 *)inBuffer;\r
9766       scale = 1.0 / 2147483647.5;\r
9767       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9768         for (j=0; j<info.channels; j++) {\r
9769           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9770           out[info.outOffset[j]] += 0.5;\r
9771           out[info.outOffset[j]] *= scale;\r
9772         }\r
9773         in += info.inJump;\r
9774         out += info.outJump;\r
9775       }\r
9776     }\r
9777     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9778       Float32 *in = (Float32 *)inBuffer;\r
9779       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9780         for (j=0; j<info.channels; j++) {\r
9781           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9782         }\r
9783         in += info.inJump;\r
9784         out += info.outJump;\r
9785       }\r
9786     }\r
9787     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9788       // Channel compensation and/or (de)interleaving only.\r
9789       Float64 *in = (Float64 *)inBuffer;\r
9790       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9791         for (j=0; j<info.channels; j++) {\r
9792           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9793         }\r
9794         in += info.inJump;\r
9795         out += info.outJump;\r
9796       }\r
9797     }\r
9798   }\r
9799   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9800     Float32 scale;\r
9801     Float32 *out = (Float32 *)outBuffer;\r
9802 \r
9803     if (info.inFormat == RTAUDIO_SINT8) {\r
9804       signed char *in = (signed char *)inBuffer;\r
9805       scale = (Float32) ( 1.0 / 127.5 );\r
9806       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9807         for (j=0; j<info.channels; j++) {\r
9808           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9809           out[info.outOffset[j]] += 0.5;\r
9810           out[info.outOffset[j]] *= scale;\r
9811         }\r
9812         in += info.inJump;\r
9813         out += info.outJump;\r
9814       }\r
9815     }\r
9816     else if (info.inFormat == RTAUDIO_SINT16) {\r
9817       Int16 *in = (Int16 *)inBuffer;\r
9818       scale = (Float32) ( 1.0 / 32767.5 );\r
9819       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9820         for (j=0; j<info.channels; j++) {\r
9821           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9822           out[info.outOffset[j]] += 0.5;\r
9823           out[info.outOffset[j]] *= scale;\r
9824         }\r
9825         in += info.inJump;\r
9826         out += info.outJump;\r
9827       }\r
9828     }\r
9829     else if (info.inFormat == RTAUDIO_SINT24) {\r
9830       Int24 *in = (Int24 *)inBuffer;\r
9831       scale = (Float32) ( 1.0 / 8388607.5 );\r
9832       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9833         for (j=0; j<info.channels; j++) {\r
9834           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9835           out[info.outOffset[j]] += 0.5;\r
9836           out[info.outOffset[j]] *= scale;\r
9837         }\r
9838         in += info.inJump;\r
9839         out += info.outJump;\r
9840       }\r
9841     }\r
9842     else if (info.inFormat == RTAUDIO_SINT32) {\r
9843       Int32 *in = (Int32 *)inBuffer;\r
9844       scale = (Float32) ( 1.0 / 2147483647.5 );\r
9845       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9846         for (j=0; j<info.channels; j++) {\r
9847           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9848           out[info.outOffset[j]] += 0.5;\r
9849           out[info.outOffset[j]] *= scale;\r
9850         }\r
9851         in += info.inJump;\r
9852         out += info.outJump;\r
9853       }\r
9854     }\r
9855     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9856       // Channel compensation and/or (de)interleaving only.\r
9857       Float32 *in = (Float32 *)inBuffer;\r
9858       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9859         for (j=0; j<info.channels; j++) {\r
9860           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9861         }\r
9862         in += info.inJump;\r
9863         out += info.outJump;\r
9864       }\r
9865     }\r
9866     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9867       Float64 *in = (Float64 *)inBuffer;\r
9868       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9869         for (j=0; j<info.channels; j++) {\r
9870           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9871         }\r
9872         in += info.inJump;\r
9873         out += info.outJump;\r
9874       }\r
9875     }\r
9876   }\r
9877   else if (info.outFormat == RTAUDIO_SINT32) {\r
9878     Int32 *out = (Int32 *)outBuffer;\r
9879     if (info.inFormat == RTAUDIO_SINT8) {\r
9880       signed char *in = (signed char *)inBuffer;\r
9881       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9882         for (j=0; j<info.channels; j++) {\r
9883           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9884           out[info.outOffset[j]] <<= 24;\r
9885         }\r
9886         in += info.inJump;\r
9887         out += info.outJump;\r
9888       }\r
9889     }\r
9890     else if (info.inFormat == RTAUDIO_SINT16) {\r
9891       Int16 *in = (Int16 *)inBuffer;\r
9892       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9893         for (j=0; j<info.channels; j++) {\r
9894           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
9895           out[info.outOffset[j]] <<= 16;\r
9896         }\r
9897         in += info.inJump;\r
9898         out += info.outJump;\r
9899       }\r
9900     }\r
9901     else if (info.inFormat == RTAUDIO_SINT24) {\r
9902       Int24 *in = (Int24 *)inBuffer;\r
9903       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9904         for (j=0; j<info.channels; j++) {\r
9905           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
9906           out[info.outOffset[j]] <<= 8;\r
9907         }\r
9908         in += info.inJump;\r
9909         out += info.outJump;\r
9910       }\r
9911     }\r
9912     else if (info.inFormat == RTAUDIO_SINT32) {\r
9913       // Channel compensation and/or (de)interleaving only.\r
9914       Int32 *in = (Int32 *)inBuffer;\r
9915       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9916         for (j=0; j<info.channels; j++) {\r
9917           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9918         }\r
9919         in += info.inJump;\r
9920         out += info.outJump;\r
9921       }\r
9922     }\r
9923     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9924       Float32 *in = (Float32 *)inBuffer;\r
9925       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9926         for (j=0; j<info.channels; j++) {\r
9927           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9928         }\r
9929         in += info.inJump;\r
9930         out += info.outJump;\r
9931       }\r
9932     }\r
9933     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9934       Float64 *in = (Float64 *)inBuffer;\r
9935       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9936         for (j=0; j<info.channels; j++) {\r
9937           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
9938         }\r
9939         in += info.inJump;\r
9940         out += info.outJump;\r
9941       }\r
9942     }\r
9943   }\r
9944   else if (info.outFormat == RTAUDIO_SINT24) {\r
9945     Int24 *out = (Int24 *)outBuffer;\r
9946     if (info.inFormat == RTAUDIO_SINT8) {\r
9947       signed char *in = (signed char *)inBuffer;\r
9948       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9949         for (j=0; j<info.channels; j++) {\r
9950           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
9951           //out[info.outOffset[j]] <<= 16;\r
9952         }\r
9953         in += info.inJump;\r
9954         out += info.outJump;\r
9955       }\r
9956     }\r
9957     else if (info.inFormat == RTAUDIO_SINT16) {\r
9958       Int16 *in = (Int16 *)inBuffer;\r
9959       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9960         for (j=0; j<info.channels; j++) {\r
9961           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
9962           //out[info.outOffset[j]] <<= 8;\r
9963         }\r
9964         in += info.inJump;\r
9965         out += info.outJump;\r
9966       }\r
9967     }\r
9968     else if (info.inFormat == RTAUDIO_SINT24) {\r
9969       // Channel compensation and/or (de)interleaving only.\r
9970       Int24 *in = (Int24 *)inBuffer;\r
9971       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9972         for (j=0; j<info.channels; j++) {\r
9973           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9974         }\r
9975         in += info.inJump;\r
9976         out += info.outJump;\r
9977       }\r
9978     }\r
9979     else if (info.inFormat == RTAUDIO_SINT32) {\r
9980       Int32 *in = (Int32 *)inBuffer;\r
9981       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9982         for (j=0; j<info.channels; j++) {\r
9983           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
9984           //out[info.outOffset[j]] >>= 8;\r
9985         }\r
9986         in += info.inJump;\r
9987         out += info.outJump;\r
9988       }\r
9989     }\r
9990     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9991       Float32 *in = (Float32 *)inBuffer;\r
9992       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9993         for (j=0; j<info.channels; j++) {\r
9994           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
9995         }\r
9996         in += info.inJump;\r
9997         out += info.outJump;\r
9998       }\r
9999     }\r
10000     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10001       Float64 *in = (Float64 *)inBuffer;\r
10002       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10003         for (j=0; j<info.channels; j++) {\r
10004           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
10005         }\r
10006         in += info.inJump;\r
10007         out += info.outJump;\r
10008       }\r
10009     }\r
10010   }\r
10011   else if (info.outFormat == RTAUDIO_SINT16) {\r
10012     Int16 *out = (Int16 *)outBuffer;\r
10013     if (info.inFormat == RTAUDIO_SINT8) {\r
10014       signed char *in = (signed char *)inBuffer;\r
10015       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10016         for (j=0; j<info.channels; j++) {\r
10017           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
10018           out[info.outOffset[j]] <<= 8;\r
10019         }\r
10020         in += info.inJump;\r
10021         out += info.outJump;\r
10022       }\r
10023     }\r
10024     else if (info.inFormat == RTAUDIO_SINT16) {\r
10025       // Channel compensation and/or (de)interleaving only.\r
10026       Int16 *in = (Int16 *)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]] = in[info.inOffset[j]];\r
10030         }\r
10031         in += info.inJump;\r
10032         out += info.outJump;\r
10033       }\r
10034     }\r
10035     else if (info.inFormat == RTAUDIO_SINT24) {\r
10036       Int24 *in = (Int24 *)inBuffer;\r
10037       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10038         for (j=0; j<info.channels; j++) {\r
10039           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
10040         }\r
10041         in += info.inJump;\r
10042         out += info.outJump;\r
10043       }\r
10044     }\r
10045     else if (info.inFormat == RTAUDIO_SINT32) {\r
10046       Int32 *in = (Int32 *)inBuffer;\r
10047       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10048         for (j=0; j<info.channels; j++) {\r
10049           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
10050         }\r
10051         in += info.inJump;\r
10052         out += info.outJump;\r
10053       }\r
10054     }\r
10055     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10056       Float32 *in = (Float32 *)inBuffer;\r
10057       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10058         for (j=0; j<info.channels; j++) {\r
10059           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
10060         }\r
10061         in += info.inJump;\r
10062         out += info.outJump;\r
10063       }\r
10064     }\r
10065     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10066       Float64 *in = (Float64 *)inBuffer;\r
10067       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10068         for (j=0; j<info.channels; j++) {\r
10069           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
10070         }\r
10071         in += info.inJump;\r
10072         out += info.outJump;\r
10073       }\r
10074     }\r
10075   }\r
10076   else if (info.outFormat == RTAUDIO_SINT8) {\r
10077     signed char *out = (signed char *)outBuffer;\r
10078     if (info.inFormat == RTAUDIO_SINT8) {\r
10079       // Channel compensation and/or (de)interleaving only.\r
10080       signed char *in = (signed char *)inBuffer;\r
10081       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10082         for (j=0; j<info.channels; j++) {\r
10083           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10084         }\r
10085         in += info.inJump;\r
10086         out += info.outJump;\r
10087       }\r
10088     }\r
10089     if (info.inFormat == RTAUDIO_SINT16) {\r
10090       Int16 *in = (Int16 *)inBuffer;\r
10091       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10092         for (j=0; j<info.channels; j++) {\r
10093           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
10094         }\r
10095         in += info.inJump;\r
10096         out += info.outJump;\r
10097       }\r
10098     }\r
10099     else if (info.inFormat == RTAUDIO_SINT24) {\r
10100       Int24 *in = (Int24 *)inBuffer;\r
10101       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10102         for (j=0; j<info.channels; j++) {\r
10103           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10104         }\r
10105         in += info.inJump;\r
10106         out += info.outJump;\r
10107       }\r
10108     }\r
10109     else if (info.inFormat == RTAUDIO_SINT32) {\r
10110       Int32 *in = (Int32 *)inBuffer;\r
10111       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10112         for (j=0; j<info.channels; j++) {\r
10113           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10114         }\r
10115         in += info.inJump;\r
10116         out += info.outJump;\r
10117       }\r
10118     }\r
10119     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10120       Float32 *in = (Float32 *)inBuffer;\r
10121       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10122         for (j=0; j<info.channels; j++) {\r
10123           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10124         }\r
10125         in += info.inJump;\r
10126         out += info.outJump;\r
10127       }\r
10128     }\r
10129     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10130       Float64 *in = (Float64 *)inBuffer;\r
10131       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10132         for (j=0; j<info.channels; j++) {\r
10133           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10134         }\r
10135         in += info.inJump;\r
10136         out += info.outJump;\r
10137       }\r
10138     }\r
10139   }\r
10140 }\r
10141 \r
10142 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10143 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10144 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10145 \r
10146 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10147 {\r
10148   char val;\r
10149   char *ptr;\r
10150 \r
10151   ptr = buffer;\r
10152   if ( format == RTAUDIO_SINT16 ) {\r
10153     for ( unsigned int i=0; i<samples; i++ ) {\r
10154       // Swap 1st and 2nd bytes.\r
10155       val = *(ptr);\r
10156       *(ptr) = *(ptr+1);\r
10157       *(ptr+1) = val;\r
10158 \r
10159       // Increment 2 bytes.\r
10160       ptr += 2;\r
10161     }\r
10162   }\r
10163   else if ( format == RTAUDIO_SINT32 ||\r
10164             format == RTAUDIO_FLOAT32 ) {\r
10165     for ( unsigned int i=0; i<samples; i++ ) {\r
10166       // Swap 1st and 4th bytes.\r
10167       val = *(ptr);\r
10168       *(ptr) = *(ptr+3);\r
10169       *(ptr+3) = val;\r
10170 \r
10171       // Swap 2nd and 3rd bytes.\r
10172       ptr += 1;\r
10173       val = *(ptr);\r
10174       *(ptr) = *(ptr+1);\r
10175       *(ptr+1) = val;\r
10176 \r
10177       // Increment 3 more bytes.\r
10178       ptr += 3;\r
10179     }\r
10180   }\r
10181   else if ( format == RTAUDIO_SINT24 ) {\r
10182     for ( unsigned int i=0; i<samples; i++ ) {\r
10183       // Swap 1st and 3rd bytes.\r
10184       val = *(ptr);\r
10185       *(ptr) = *(ptr+2);\r
10186       *(ptr+2) = val;\r
10187 \r
10188       // Increment 2 more bytes.\r
10189       ptr += 2;\r
10190     }\r
10191   }\r
10192   else if ( format == RTAUDIO_FLOAT64 ) {\r
10193     for ( unsigned int i=0; i<samples; i++ ) {\r
10194       // Swap 1st and 8th bytes\r
10195       val = *(ptr);\r
10196       *(ptr) = *(ptr+7);\r
10197       *(ptr+7) = val;\r
10198 \r
10199       // Swap 2nd and 7th bytes\r
10200       ptr += 1;\r
10201       val = *(ptr);\r
10202       *(ptr) = *(ptr+5);\r
10203       *(ptr+5) = val;\r
10204 \r
10205       // Swap 3rd and 6th bytes\r
10206       ptr += 1;\r
10207       val = *(ptr);\r
10208       *(ptr) = *(ptr+3);\r
10209       *(ptr+3) = val;\r
10210 \r
10211       // Swap 4th and 5th bytes\r
10212       ptr += 1;\r
10213       val = *(ptr);\r
10214       *(ptr) = *(ptr+1);\r
10215       *(ptr+1) = val;\r
10216 \r
10217       // Increment 5 more bytes.\r
10218       ptr += 5;\r
10219     }\r
10220   }\r
10221 }\r
10222 \r
10223   // Indentation settings for Vim and Emacs\r
10224   //\r
10225   // Local Variables:\r
10226   // c-basic-offset: 2\r
10227   // indent-tabs-mode: nil\r
10228   // End:\r
10229   //\r
10230   // vim: et sts=2 sw=2\r
10231 \r