Fix typo in previous.
[rtaudio-cdist.git] / RtAudio.cpp
1 /************************************************************************/\r
2 /*! \class RtAudio\r
3     \brief Realtime audio i/o C++ classes.\r
4 \r
5     RtAudio provides a common API (Application Programming Interface)\r
6     for realtime audio input/output across Linux (native ALSA, Jack,\r
7     and OSS), Macintosh OS X (CoreAudio and Jack), and Windows\r
8     (DirectSound, ASIO and WASAPI) operating systems.\r
9 \r
10     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
11 \r
12     RtAudio: realtime audio i/o C++ classes\r
13     Copyright (c) 2001-2016 Gary P. Scavone\r
14 \r
15     Permission is hereby granted, free of charge, to any person\r
16     obtaining a copy of this software and associated documentation files\r
17     (the "Software"), to deal in the Software without restriction,\r
18     including without limitation the rights to use, copy, modify, merge,\r
19     publish, distribute, sublicense, and/or sell copies of the Software,\r
20     and to permit persons to whom the Software is furnished to do so,\r
21     subject to the following conditions:\r
22 \r
23     The above copyright notice and this permission notice shall be\r
24     included in all copies or substantial portions of the Software.\r
25 \r
26     Any person wishing to distribute modifications to the Software is\r
27     asked to send the modifications to the original developer so that\r
28     they can be incorporated into the canonical version.  This is,\r
29     however, not a binding provision of this license.\r
30 \r
31     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
32     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
33     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
34     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
35     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
36     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
37     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
38 */\r
39 /************************************************************************/\r
40 \r
41 // RtAudio: Version 4.1.2\r
42 \r
43 #include "RtAudio.h"\r
44 #include <iostream>\r
45 #include <cstdlib>\r
46 #include <cstring>\r
47 #include <climits>\r
48 #include <algorithm>\r
49 #include <cmath>\r
50 \r
51 // Static variable definitions.\r
52 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
53 const unsigned int RtApi::SAMPLE_RATES[] = {\r
54   4000, 5512, 8000, 9600, 11025, 16000, 22050,\r
55   32000, 44100, 48000, 88200, 96000, 176400, 192000\r
56 };\r
57 \r
58 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)\r
59   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
60   #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)\r
61   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
62   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)\r
63 \r
64   #include "tchar.h"\r
65 \r
66   static std::string convertCharPointerToStdString(const char *text)\r
67   {\r
68     return std::string(text);\r
69   }\r
70 \r
71   static std::string convertCharPointerToStdString(const wchar_t *text)\r
72   {\r
73     int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);\r
74     std::string s( length-1, '\0' );\r
75     WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);\r
76     return s;\r
77   }\r
78 \r
79 #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
80   // pthread API\r
81   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
82   #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)\r
83   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)\r
84   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)\r
85 #else\r
86   #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions\r
87   #define MUTEX_DESTROY(A)    abs(*A) // dummy definitions\r
88 #endif\r
89 \r
90 // *************************************************** //\r
91 //\r
92 // RtAudio definitions.\r
93 //\r
94 // *************************************************** //\r
95 \r
96 std::string RtAudio :: getVersion( void ) throw()\r
97 {\r
98   return RTAUDIO_VERSION;\r
99 }\r
100 \r
101 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()\r
102 {\r
103   apis.clear();\r
104 \r
105   // The order here will control the order of RtAudio's API search in\r
106   // the constructor.\r
107 #if defined(__UNIX_JACK__)\r
108   apis.push_back( UNIX_JACK );\r
109 #endif\r
110 #if defined(__LINUX_ALSA__)\r
111   apis.push_back( LINUX_ALSA );\r
112 #endif\r
113 #if defined(__LINUX_PULSE__)\r
114   apis.push_back( LINUX_PULSE );\r
115 #endif\r
116 #if defined(__LINUX_OSS__)\r
117   apis.push_back( LINUX_OSS );\r
118 #endif\r
119 #if defined(__WINDOWS_ASIO__)\r
120   apis.push_back( WINDOWS_ASIO );\r
121 #endif\r
122 #if defined(__WINDOWS_WASAPI__)\r
123   apis.push_back( WINDOWS_WASAPI );\r
124 #endif\r
125 #if defined(__WINDOWS_DS__)\r
126   apis.push_back( WINDOWS_DS );\r
127 #endif\r
128 #if defined(__MACOSX_CORE__)\r
129   apis.push_back( MACOSX_CORE );\r
130 #endif\r
131 #if defined(__RTAUDIO_DUMMY__)\r
132   apis.push_back( RTAUDIO_DUMMY );\r
133 #endif\r
134 }\r
135 \r
136 void RtAudio :: openRtApi( RtAudio::Api api )\r
137 {\r
138   if ( rtapi_ )\r
139     delete rtapi_;\r
140   rtapi_ = 0;\r
141 \r
142 #if defined(__UNIX_JACK__)\r
143   if ( api == UNIX_JACK )\r
144     rtapi_ = new RtApiJack();\r
145 #endif\r
146 #if defined(__LINUX_ALSA__)\r
147   if ( api == LINUX_ALSA )\r
148     rtapi_ = new RtApiAlsa();\r
149 #endif\r
150 #if defined(__LINUX_PULSE__)\r
151   if ( api == LINUX_PULSE )\r
152     rtapi_ = new RtApiPulse();\r
153 #endif\r
154 #if defined(__LINUX_OSS__)\r
155   if ( api == LINUX_OSS )\r
156     rtapi_ = new RtApiOss();\r
157 #endif\r
158 #if defined(__WINDOWS_ASIO__)\r
159   if ( api == WINDOWS_ASIO )\r
160     rtapi_ = new RtApiAsio();\r
161 #endif\r
162 #if defined(__WINDOWS_WASAPI__)\r
163   if ( api == WINDOWS_WASAPI )\r
164     rtapi_ = new RtApiWasapi();\r
165 #endif\r
166 #if defined(__WINDOWS_DS__)\r
167   if ( api == WINDOWS_DS )\r
168     rtapi_ = new RtApiDs();\r
169 #endif\r
170 #if defined(__MACOSX_CORE__)\r
171   if ( api == MACOSX_CORE )\r
172     rtapi_ = new RtApiCore();\r
173 #endif\r
174 #if defined(__RTAUDIO_DUMMY__)\r
175   if ( api == RTAUDIO_DUMMY )\r
176     rtapi_ = new RtApiDummy();\r
177 #endif\r
178 }\r
179 \r
180 RtAudio :: RtAudio( RtAudio::Api api )\r
181 {\r
182   rtapi_ = 0;\r
183 \r
184   if ( api != UNSPECIFIED ) {\r
185     // Attempt to open the specified API.\r
186     openRtApi( api );\r
187     if ( rtapi_ ) return;\r
188 \r
189     // No compiled support for specified API value.  Issue a debug\r
190     // warning and continue as if no API was specified.\r
191     std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;\r
192   }\r
193 \r
194   // Iterate through the compiled APIs and return as soon as we find\r
195   // one with at least one device or we reach the end of the list.\r
196   std::vector< RtAudio::Api > apis;\r
197   getCompiledApi( apis );\r
198   for ( unsigned int i=0; i<apis.size(); i++ ) {\r
199     openRtApi( apis[i] );\r
200     if ( rtapi_ && rtapi_->getDeviceCount() ) break;\r
201   }\r
202 \r
203   if ( rtapi_ ) return;\r
204 \r
205   // It should not be possible to get here because the preprocessor\r
206   // definition __RTAUDIO_DUMMY__ is automatically defined if no\r
207   // API-specific definitions are passed to the compiler. But just in\r
208   // case something weird happens, we'll thow an error.\r
209   std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
210   throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );\r
211 }\r
212 \r
213 RtAudio :: ~RtAudio() throw()\r
214 {\r
215   if ( rtapi_ )\r
216     delete rtapi_;\r
217 }\r
218 \r
219 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,\r
220                             RtAudio::StreamParameters *inputParameters,\r
221                             RtAudioFormat format, unsigned int sampleRate,\r
222                             unsigned int *bufferFrames,\r
223                             RtAudioCallback callback, void *userData,\r
224                             RtAudio::StreamOptions *options,\r
225                             RtAudioErrorCallback errorCallback )\r
226 {\r
227   return rtapi_->openStream( outputParameters, inputParameters, format,\r
228                              sampleRate, bufferFrames, callback,\r
229                              userData, options, errorCallback );\r
230 }\r
231 \r
232 // *************************************************** //\r
233 //\r
234 // Public RtApi definitions (see end of file for\r
235 // private or protected utility functions).\r
236 //\r
237 // *************************************************** //\r
238 \r
239 RtApi :: RtApi()\r
240 {\r
241   stream_.state = STREAM_CLOSED;\r
242   stream_.mode = UNINITIALIZED;\r
243   stream_.apiHandle = 0;\r
244   stream_.userBuffer[0] = 0;\r
245   stream_.userBuffer[1] = 0;\r
246   MUTEX_INITIALIZE( &stream_.mutex );\r
247   showWarnings_ = true;\r
248   firstErrorOccurred_ = false;\r
249 }\r
250 \r
251 RtApi :: ~RtApi()\r
252 {\r
253   MUTEX_DESTROY( &stream_.mutex );\r
254 }\r
255 \r
256 void RtApi :: openStream( RtAudio::StreamParameters *oParams,\r
257                           RtAudio::StreamParameters *iParams,\r
258                           RtAudioFormat format, unsigned int sampleRate,\r
259                           unsigned int *bufferFrames,\r
260                           RtAudioCallback callback, void *userData,\r
261                           RtAudio::StreamOptions *options,\r
262                           RtAudioErrorCallback errorCallback )\r
263 {\r
264   if ( stream_.state != STREAM_CLOSED ) {\r
265     errorText_ = "RtApi::openStream: a stream is already open!";\r
266     error( RtAudioError::INVALID_USE );\r
267     return;\r
268   }\r
269 \r
270   // Clear stream information potentially left from a previously open stream.\r
271   clearStreamInfo();\r
272 \r
273   if ( oParams && oParams->nChannels < 1 ) {\r
274     errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";\r
275     error( RtAudioError::INVALID_USE );\r
276     return;\r
277   }\r
278 \r
279   if ( iParams && iParams->nChannels < 1 ) {\r
280     errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";\r
281     error( RtAudioError::INVALID_USE );\r
282     return;\r
283   }\r
284 \r
285   if ( oParams == NULL && iParams == NULL ) {\r
286     errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";\r
287     error( RtAudioError::INVALID_USE );\r
288     return;\r
289   }\r
290 \r
291   if ( formatBytes(format) == 0 ) {\r
292     errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";\r
293     error( RtAudioError::INVALID_USE );\r
294     return;\r
295   }\r
296 \r
297   unsigned int nDevices = getDeviceCount();\r
298   unsigned int oChannels = 0;\r
299   if ( oParams ) {\r
300     oChannels = oParams->nChannels;\r
301     if ( oParams->deviceId >= nDevices ) {\r
302       errorText_ = "RtApi::openStream: output device parameter value is invalid.";\r
303       error( RtAudioError::INVALID_USE );\r
304       return;\r
305     }\r
306   }\r
307 \r
308   unsigned int iChannels = 0;\r
309   if ( iParams ) {\r
310     iChannels = iParams->nChannels;\r
311     if ( iParams->deviceId >= nDevices ) {\r
312       errorText_ = "RtApi::openStream: input device parameter value is invalid.";\r
313       error( RtAudioError::INVALID_USE );\r
314       return;\r
315     }\r
316   }\r
317 \r
318   bool result;\r
319 \r
320   if ( oChannels > 0 ) {\r
321 \r
322     result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,\r
323                               sampleRate, format, bufferFrames, options );\r
324     if ( result == false ) {\r
325       error( RtAudioError::SYSTEM_ERROR );\r
326       return;\r
327     }\r
328   }\r
329 \r
330   if ( iChannels > 0 ) {\r
331 \r
332     result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,\r
333                               sampleRate, format, bufferFrames, options );\r
334     if ( result == false ) {\r
335       if ( oChannels > 0 ) closeStream();\r
336       error( RtAudioError::SYSTEM_ERROR );\r
337       return;\r
338     }\r
339   }\r
340 \r
341   stream_.callbackInfo.callback = (void *) callback;\r
342   stream_.callbackInfo.userData = userData;\r
343   stream_.callbackInfo.errorCallback = (void *) errorCallback;\r
344 \r
345   if ( options ) options->numberOfBuffers = stream_.nBuffers;\r
346   stream_.state = STREAM_STOPPED;\r
347 }\r
348 \r
349 unsigned int RtApi :: getDefaultInputDevice( void )\r
350 {\r
351   // Should be implemented in subclasses if possible.\r
352   return 0;\r
353 }\r
354 \r
355 unsigned int RtApi :: getDefaultOutputDevice( void )\r
356 {\r
357   // Should be implemented in subclasses if possible.\r
358   return 0;\r
359 }\r
360 \r
361 void RtApi :: closeStream( void )\r
362 {\r
363   // MUST be implemented in subclasses!\r
364   return;\r
365 }\r
366 \r
367 bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,\r
368                                unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,\r
369                                RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,\r
370                                RtAudio::StreamOptions * /*options*/ )\r
371 {\r
372   // MUST be implemented in subclasses!\r
373   return FAILURE;\r
374 }\r
375 \r
376 void RtApi :: tickStreamTime( void )\r
377 {\r
378   // Subclasses that do not provide their own implementation of\r
379   // getStreamTime should call this function once per buffer I/O to\r
380   // provide basic stream time support.\r
381 \r
382   stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );\r
383 \r
384 #if defined( HAVE_GETTIMEOFDAY )\r
385   gettimeofday( &stream_.lastTickTimestamp, NULL );\r
386 #endif\r
387 }\r
388 \r
389 long RtApi :: getStreamLatency( void )\r
390 {\r
391   verifyStream();\r
392 \r
393   long totalLatency = 0;\r
394   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
395     totalLatency = stream_.latency[0];\r
396   if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
397     totalLatency += stream_.latency[1];\r
398 \r
399   return totalLatency;\r
400 }\r
401 \r
402 double RtApi :: getStreamTime( void )\r
403 {\r
404   verifyStream();\r
405 \r
406 #if defined( HAVE_GETTIMEOFDAY )\r
407   // Return a very accurate estimate of the stream time by\r
408   // adding in the elapsed time since the last tick.\r
409   struct timeval then;\r
410   struct timeval now;\r
411 \r
412   // If lastTickTimestamp is 0 it means we haven't had a "last tick" since\r
413   // we started the stream.\r
414   if ( stream_.state != STREAM_RUNNING || (stream_.lastTickTimestamp.tv_sec == 0 && stream_.lastTickTimestamp.tv_usec == 0) )\r
415     return stream_.streamTime;\r
416 \r
417   gettimeofday( &now, NULL );\r
418   then = stream_.lastTickTimestamp;\r
419   return stream_.streamTime +\r
420     ((now.tv_sec + 0.000001 * now.tv_usec) -\r
421      (then.tv_sec + 0.000001 * then.tv_usec));\r
422 #else\r
423   return stream_.streamTime;\r
424 #endif\r
425 }\r
426 \r
427 void RtApi :: setStreamTime( double time )\r
428 {\r
429   verifyStream();\r
430 \r
431   if ( time >= 0.0 )\r
432     stream_.streamTime = time;\r
433 }\r
434 \r
435 unsigned int RtApi :: getStreamSampleRate( void )\r
436 {\r
437  verifyStream();\r
438 \r
439  return stream_.sampleRate;\r
440 }\r
441 \r
442 void RtApi :: startStream( void )\r
443 {\r
444 #if defined( HAVE_GETTIMEOFDAY )\r
445   stream_.lastTickTimestamp.tv_sec = 0;\r
446   stream_.lastTickTimestamp.tv_usec = 0;\r
447 #endif\r
448 }\r
449 \r
450 \r
451 // *************************************************** //\r
452 //\r
453 // OS/API-specific methods.\r
454 //\r
455 // *************************************************** //\r
456 \r
457 #if defined(__MACOSX_CORE__)\r
458 \r
459 // The OS X CoreAudio API is designed to use a separate callback\r
460 // procedure for each of its audio devices.  A single RtAudio duplex\r
461 // stream using two different devices is supported here, though it\r
462 // cannot be guaranteed to always behave correctly because we cannot\r
463 // synchronize these two callbacks.\r
464 //\r
465 // A property listener is installed for over/underrun information.\r
466 // However, no functionality is currently provided to allow property\r
467 // listeners to trigger user handlers because it is unclear what could\r
468 // be done if a critical stream parameter (buffer size, sample rate,\r
469 // device disconnect) notification arrived.  The listeners entail\r
470 // quite a bit of extra code and most likely, a user program wouldn't\r
471 // be prepared for the result anyway.  However, we do provide a flag\r
472 // to the client callback function to inform of an over/underrun.\r
473 \r
474 // A structure to hold various information related to the CoreAudio API\r
475 // implementation.\r
476 struct CoreHandle {\r
477   AudioDeviceID id[2];    // device ids\r
478 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
479   AudioDeviceIOProcID procId[2];\r
480 #endif\r
481   UInt32 iStream[2];      // device stream index (or first if using multiple)\r
482   UInt32 nStreams[2];     // number of streams to use\r
483   bool xrun[2];\r
484   char *deviceBuffer;\r
485   pthread_cond_t condition;\r
486   int drainCounter;       // Tracks callback counts when draining\r
487   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
488 \r
489   CoreHandle()\r
490     :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
491 };\r
492 \r
493 RtApiCore:: RtApiCore()\r
494 {\r
495 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
496   // This is a largely undocumented but absolutely necessary\r
497   // requirement starting with OS-X 10.6.  If not called, queries and\r
498   // updates to various audio device properties are not handled\r
499   // correctly.\r
500   CFRunLoopRef theRunLoop = NULL;\r
501   AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
502                                           kAudioObjectPropertyScopeGlobal,\r
503                                           kAudioObjectPropertyElementMaster };\r
504   OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
505   if ( result != noErr ) {\r
506     errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
507     error( RtAudioError::WARNING );\r
508   }\r
509 #endif\r
510 }\r
511 \r
512 RtApiCore :: ~RtApiCore()\r
513 {\r
514   // The subclass destructor gets called before the base class\r
515   // destructor, so close an existing stream before deallocating\r
516   // apiDeviceId memory.\r
517   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
518 }\r
519 \r
520 unsigned int RtApiCore :: getDeviceCount( void )\r
521 {\r
522   // Find out how many audio devices there are, if any.\r
523   UInt32 dataSize;\r
524   AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
525   OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );\r
526   if ( result != noErr ) {\r
527     errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";\r
528     error( RtAudioError::WARNING );\r
529     return 0;\r
530   }\r
531 \r
532   return dataSize / sizeof( AudioDeviceID );\r
533 }\r
534 \r
535 unsigned int RtApiCore :: getDefaultInputDevice( void )\r
536 {\r
537   unsigned int nDevices = getDeviceCount();\r
538   if ( nDevices <= 1 ) return 0;\r
539 \r
540   AudioDeviceID id;\r
541   UInt32 dataSize = sizeof( AudioDeviceID );\r
542   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
543   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
544   if ( result != noErr ) {\r
545     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";\r
546     error( RtAudioError::WARNING );\r
547     return 0;\r
548   }\r
549 \r
550   dataSize *= nDevices;\r
551   AudioDeviceID deviceList[ nDevices ];\r
552   property.mSelector = kAudioHardwarePropertyDevices;\r
553   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
554   if ( result != noErr ) {\r
555     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";\r
556     error( RtAudioError::WARNING );\r
557     return 0;\r
558   }\r
559 \r
560   for ( unsigned int i=0; i<nDevices; i++ )\r
561     if ( id == deviceList[i] ) return i;\r
562 \r
563   errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";\r
564   error( RtAudioError::WARNING );\r
565   return 0;\r
566 }\r
567 \r
568 unsigned int RtApiCore :: getDefaultOutputDevice( void )\r
569 {\r
570   unsigned int nDevices = getDeviceCount();\r
571   if ( nDevices <= 1 ) return 0;\r
572 \r
573   AudioDeviceID id;\r
574   UInt32 dataSize = sizeof( AudioDeviceID );\r
575   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
576   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
577   if ( result != noErr ) {\r
578     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";\r
579     error( RtAudioError::WARNING );\r
580     return 0;\r
581   }\r
582 \r
583   dataSize = sizeof( AudioDeviceID ) * nDevices;\r
584   AudioDeviceID deviceList[ nDevices ];\r
585   property.mSelector = kAudioHardwarePropertyDevices;\r
586   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
587   if ( result != noErr ) {\r
588     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";\r
589     error( RtAudioError::WARNING );\r
590     return 0;\r
591   }\r
592 \r
593   for ( unsigned int i=0; i<nDevices; i++ )\r
594     if ( id == deviceList[i] ) return i;\r
595 \r
596   errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";\r
597   error( RtAudioError::WARNING );\r
598   return 0;\r
599 }\r
600 \r
601 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )\r
602 {\r
603   RtAudio::DeviceInfo info;\r
604   info.probed = false;\r
605 \r
606   // Get device ID\r
607   unsigned int nDevices = getDeviceCount();\r
608   if ( nDevices == 0 ) {\r
609     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
610     error( RtAudioError::INVALID_USE );\r
611     return info;\r
612   }\r
613 \r
614   if ( device >= nDevices ) {\r
615     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
616     error( RtAudioError::INVALID_USE );\r
617     return info;\r
618   }\r
619 \r
620   AudioDeviceID deviceList[ nDevices ];\r
621   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
622   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
623                                           kAudioObjectPropertyScopeGlobal,\r
624                                           kAudioObjectPropertyElementMaster };\r
625   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
626                                                 0, NULL, &dataSize, (void *) &deviceList );\r
627   if ( result != noErr ) {\r
628     errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";\r
629     error( RtAudioError::WARNING );\r
630     return info;\r
631   }\r
632 \r
633   AudioDeviceID id = deviceList[ device ];\r
634 \r
635   // Get the device name.\r
636   info.name.erase();\r
637   CFStringRef cfname;\r
638   dataSize = sizeof( CFStringRef );\r
639   property.mSelector = kAudioObjectPropertyManufacturer;\r
640   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
641   if ( result != noErr ) {\r
642     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";\r
643     errorText_ = errorStream_.str();\r
644     error( RtAudioError::WARNING );\r
645     return info;\r
646   }\r
647 \r
648   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
649   int length = CFStringGetLength(cfname);\r
650   char *mname = (char *)malloc(length * 3 + 1);\r
651 #if defined( UNICODE ) || defined( _UNICODE )\r
652   CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
653 #else\r
654   CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
655 #endif\r
656   info.name.append( (const char *)mname, strlen(mname) );\r
657   info.name.append( ": " );\r
658   CFRelease( cfname );\r
659   free(mname);\r
660 \r
661   property.mSelector = kAudioObjectPropertyName;\r
662   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
663   if ( result != noErr ) {\r
664     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";\r
665     errorText_ = errorStream_.str();\r
666     error( RtAudioError::WARNING );\r
667     return info;\r
668   }\r
669 \r
670   //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
671   length = CFStringGetLength(cfname);\r
672   char *name = (char *)malloc(length * 3 + 1);\r
673 #if defined( UNICODE ) || defined( _UNICODE )\r
674   CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
675 #else\r
676   CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
677 #endif\r
678   info.name.append( (const char *)name, strlen(name) );\r
679   CFRelease( cfname );\r
680   free(name);\r
681 \r
682   // Get the output stream "configuration".\r
683   AudioBufferList       *bufferList = nil;\r
684   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
685   property.mScope = kAudioDevicePropertyScopeOutput;\r
686   //  property.mElement = kAudioObjectPropertyElementWildcard;\r
687   dataSize = 0;\r
688   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
689   if ( result != noErr || dataSize == 0 ) {\r
690     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";\r
691     errorText_ = errorStream_.str();\r
692     error( RtAudioError::WARNING );\r
693     return info;\r
694   }\r
695 \r
696   // Allocate the AudioBufferList.\r
697   bufferList = (AudioBufferList *) malloc( dataSize );\r
698   if ( bufferList == NULL ) {\r
699     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";\r
700     error( RtAudioError::WARNING );\r
701     return info;\r
702   }\r
703 \r
704   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
705   if ( result != noErr || dataSize == 0 ) {\r
706     free( bufferList );\r
707     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";\r
708     errorText_ = errorStream_.str();\r
709     error( RtAudioError::WARNING );\r
710     return info;\r
711   }\r
712 \r
713   // Get output channel information.\r
714   unsigned int i, nStreams = bufferList->mNumberBuffers;\r
715   for ( i=0; i<nStreams; i++ )\r
716     info.outputChannels += bufferList->mBuffers[i].mNumberChannels;\r
717   free( bufferList );\r
718 \r
719   // Get the input stream "configuration".\r
720   property.mScope = kAudioDevicePropertyScopeInput;\r
721   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
722   if ( result != noErr || dataSize == 0 ) {\r
723     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";\r
724     errorText_ = errorStream_.str();\r
725     error( RtAudioError::WARNING );\r
726     return info;\r
727   }\r
728 \r
729   // Allocate the AudioBufferList.\r
730   bufferList = (AudioBufferList *) malloc( dataSize );\r
731   if ( bufferList == NULL ) {\r
732     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";\r
733     error( RtAudioError::WARNING );\r
734     return info;\r
735   }\r
736 \r
737   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
738   if (result != noErr || dataSize == 0) {\r
739     free( bufferList );\r
740     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";\r
741     errorText_ = errorStream_.str();\r
742     error( RtAudioError::WARNING );\r
743     return info;\r
744   }\r
745 \r
746   // Get input channel information.\r
747   nStreams = bufferList->mNumberBuffers;\r
748   for ( i=0; i<nStreams; i++ )\r
749     info.inputChannels += bufferList->mBuffers[i].mNumberChannels;\r
750   free( bufferList );\r
751 \r
752   // If device opens for both playback and capture, we determine the channels.\r
753   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
754     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
755 \r
756   // Probe the device sample rates.\r
757   bool isInput = false;\r
758   if ( info.outputChannels == 0 ) isInput = true;\r
759 \r
760   // Determine the supported sample rates.\r
761   property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\r
762   if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;\r
763   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
764   if ( result != kAudioHardwareNoError || dataSize == 0 ) {\r
765     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";\r
766     errorText_ = errorStream_.str();\r
767     error( RtAudioError::WARNING );\r
768     return info;\r
769   }\r
770 \r
771   UInt32 nRanges = dataSize / sizeof( AudioValueRange );\r
772   AudioValueRange rangeList[ nRanges ];\r
773   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );\r
774   if ( result != kAudioHardwareNoError ) {\r
775     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";\r
776     errorText_ = errorStream_.str();\r
777     error( RtAudioError::WARNING );\r
778     return info;\r
779   }\r
780 \r
781   // The sample rate reporting mechanism is a bit of a mystery.  It\r
782   // seems that it can either return individual rates or a range of\r
783   // rates.  I assume that if the min / max range values are the same,\r
784   // then that represents a single supported rate and if the min / max\r
785   // range values are different, the device supports an arbitrary\r
786   // range of values (though there might be multiple ranges, so we'll\r
787   // use the most conservative range).\r
788   Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
789   bool haveValueRange = false;\r
790   info.sampleRates.clear();\r
791   for ( UInt32 i=0; i<nRanges; i++ ) {\r
792     if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {\r
793       unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;\r
794       info.sampleRates.push_back( tmpSr );\r
795 \r
796       if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )\r
797         info.preferredSampleRate = tmpSr;\r
798 \r
799     } else {\r
800       haveValueRange = true;\r
801       if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
802       if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
803     }\r
804   }\r
805 \r
806   if ( haveValueRange ) {\r
807     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
808       if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {\r
809         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
810 \r
811         if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
812           info.preferredSampleRate = SAMPLE_RATES[k];\r
813       }\r
814     }\r
815   }\r
816 \r
817   // Sort and remove any redundant values\r
818   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
819   info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
820 \r
821   if ( info.sampleRates.size() == 0 ) {\r
822     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
823     errorText_ = errorStream_.str();\r
824     error( RtAudioError::WARNING );\r
825     return info;\r
826   }\r
827 \r
828   // CoreAudio always uses 32-bit floating point data for PCM streams.\r
829   // Thus, any other "physical" formats supported by the device are of\r
830   // no interest to the client.\r
831   info.nativeFormats = RTAUDIO_FLOAT32;\r
832 \r
833   if ( info.outputChannels > 0 )\r
834     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
835   if ( info.inputChannels > 0 )\r
836     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
837 \r
838   info.probed = true;\r
839   return info;\r
840 }\r
841 \r
842 static OSStatus callbackHandler( AudioDeviceID inDevice,\r
843                                  const AudioTimeStamp* /*inNow*/,\r
844                                  const AudioBufferList* inInputData,\r
845                                  const AudioTimeStamp* /*inInputTime*/,\r
846                                  AudioBufferList* outOutputData,\r
847                                  const AudioTimeStamp* /*inOutputTime*/,\r
848                                  void* infoPointer )\r
849 {\r
850   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
851 \r
852   RtApiCore *object = (RtApiCore *) info->object;\r
853   if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )\r
854     return kAudioHardwareUnspecifiedError;\r
855   else\r
856     return kAudioHardwareNoError;\r
857 }\r
858 \r
859 static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
860                               UInt32 nAddresses,\r
861                               const AudioObjectPropertyAddress properties[],\r
862                               void* handlePointer )\r
863 {\r
864   CoreHandle *handle = (CoreHandle *) handlePointer;\r
865   for ( UInt32 i=0; i<nAddresses; i++ ) {\r
866     if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {\r
867       if ( properties[i].mScope == kAudioDevicePropertyScopeInput )\r
868         handle->xrun[1] = true;\r
869       else\r
870         handle->xrun[0] = true;\r
871     }\r
872   }\r
873 \r
874   return kAudioHardwareNoError;\r
875 }\r
876 \r
877 static OSStatus rateListener( AudioObjectID inDevice,\r
878                               UInt32 /*nAddresses*/,\r
879                               const AudioObjectPropertyAddress /*properties*/[],\r
880                               void* ratePointer )\r
881 {\r
882   Float64 *rate = (Float64 *) ratePointer;\r
883   UInt32 dataSize = sizeof( Float64 );\r
884   AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
885                                           kAudioObjectPropertyScopeGlobal,\r
886                                           kAudioObjectPropertyElementMaster };\r
887   AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
888   return kAudioHardwareNoError;\r
889 }\r
890 \r
891 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
892                                    unsigned int firstChannel, unsigned int sampleRate,\r
893                                    RtAudioFormat format, unsigned int *bufferSize,\r
894                                    RtAudio::StreamOptions *options )\r
895 {\r
896   // Get device ID\r
897   unsigned int nDevices = getDeviceCount();\r
898   if ( nDevices == 0 ) {\r
899     // This should not happen because a check is made before this function is called.\r
900     errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";\r
901     return FAILURE;\r
902   }\r
903 \r
904   if ( device >= nDevices ) {\r
905     // This should not happen because a check is made before this function is called.\r
906     errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";\r
907     return FAILURE;\r
908   }\r
909 \r
910   AudioDeviceID deviceList[ nDevices ];\r
911   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
912   AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
913                                           kAudioObjectPropertyScopeGlobal,\r
914                                           kAudioObjectPropertyElementMaster };\r
915   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
916                                                 0, NULL, &dataSize, (void *) &deviceList );\r
917   if ( result != noErr ) {\r
918     errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";\r
919     return FAILURE;\r
920   }\r
921 \r
922   AudioDeviceID id = deviceList[ device ];\r
923 \r
924   // Setup for stream mode.\r
925   bool isInput = false;\r
926   if ( mode == INPUT ) {\r
927     isInput = true;\r
928     property.mScope = kAudioDevicePropertyScopeInput;\r
929   }\r
930   else\r
931     property.mScope = kAudioDevicePropertyScopeOutput;\r
932 \r
933   // Get the stream "configuration".\r
934   AudioBufferList       *bufferList = nil;\r
935   dataSize = 0;\r
936   property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
937   result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
938   if ( result != noErr || dataSize == 0 ) {\r
939     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";\r
940     errorText_ = errorStream_.str();\r
941     return FAILURE;\r
942   }\r
943 \r
944   // Allocate the AudioBufferList.\r
945   bufferList = (AudioBufferList *) malloc( dataSize );\r
946   if ( bufferList == NULL ) {\r
947     errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";\r
948     return FAILURE;\r
949   }\r
950 \r
951   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
952   if (result != noErr || dataSize == 0) {\r
953     free( bufferList );\r
954     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
955     errorText_ = errorStream_.str();\r
956     return FAILURE;\r
957   }\r
958 \r
959   // Search for one or more streams that contain the desired number of\r
960   // channels. CoreAudio devices can have an arbitrary number of\r
961   // streams and each stream can have an arbitrary number of channels.\r
962   // For each stream, a single buffer of interleaved samples is\r
963   // provided.  RtAudio prefers the use of one stream of interleaved\r
964   // data or multiple consecutive single-channel streams.  However, we\r
965   // now support multiple consecutive multi-channel streams of\r
966   // interleaved data as well.\r
967   UInt32 iStream, offsetCounter = firstChannel;\r
968   UInt32 nStreams = bufferList->mNumberBuffers;\r
969   bool monoMode = false;\r
970   bool foundStream = false;\r
971 \r
972   // First check that the device supports the requested number of\r
973   // channels.\r
974   UInt32 deviceChannels = 0;\r
975   for ( iStream=0; iStream<nStreams; iStream++ )\r
976     deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;\r
977 \r
978   if ( deviceChannels < ( channels + firstChannel ) ) {\r
979     free( bufferList );\r
980     errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";\r
981     errorText_ = errorStream_.str();\r
982     return FAILURE;\r
983   }\r
984 \r
985   // Look for a single stream meeting our needs.\r
986   UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;\r
987   for ( iStream=0; iStream<nStreams; iStream++ ) {\r
988     streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
989     if ( streamChannels >= channels + offsetCounter ) {\r
990       firstStream = iStream;\r
991       channelOffset = offsetCounter;\r
992       foundStream = true;\r
993       break;\r
994     }\r
995     if ( streamChannels > offsetCounter ) break;\r
996     offsetCounter -= streamChannels;\r
997   }\r
998 \r
999   // If we didn't find a single stream above, then we should be able\r
1000   // to meet the channel specification with multiple streams.\r
1001   if ( foundStream == false ) {\r
1002     monoMode = true;\r
1003     offsetCounter = firstChannel;\r
1004     for ( iStream=0; iStream<nStreams; iStream++ ) {\r
1005       streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
1006       if ( streamChannels > offsetCounter ) break;\r
1007       offsetCounter -= streamChannels;\r
1008     }\r
1009 \r
1010     firstStream = iStream;\r
1011     channelOffset = offsetCounter;\r
1012     Int32 channelCounter = channels + offsetCounter - streamChannels;\r
1013 \r
1014     if ( streamChannels > 1 ) monoMode = false;\r
1015     while ( channelCounter > 0 ) {\r
1016       streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;\r
1017       if ( streamChannels > 1 ) monoMode = false;\r
1018       channelCounter -= streamChannels;\r
1019       streamCount++;\r
1020     }\r
1021   }\r
1022 \r
1023   free( bufferList );\r
1024 \r
1025   // Determine the buffer size.\r
1026   AudioValueRange       bufferRange;\r
1027   dataSize = sizeof( AudioValueRange );\r
1028   property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\r
1029   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );\r
1030 \r
1031   if ( result != noErr ) {\r
1032     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";\r
1033     errorText_ = errorStream_.str();\r
1034     return FAILURE;\r
1035   }\r
1036 \r
1037   if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
1038   else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;\r
1039   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
1040 \r
1041   // Set the buffer size.  For multiple streams, I'm assuming we only\r
1042   // need to make this setting for the master channel.\r
1043   UInt32 theSize = (UInt32) *bufferSize;\r
1044   dataSize = sizeof( UInt32 );\r
1045   property.mSelector = kAudioDevicePropertyBufferFrameSize;\r
1046   result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );\r
1047 \r
1048   if ( result != noErr ) {\r
1049     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";\r
1050     errorText_ = errorStream_.str();\r
1051     return FAILURE;\r
1052   }\r
1053 \r
1054   // If attempting to setup a duplex stream, the bufferSize parameter\r
1055   // MUST be the same in both directions!\r
1056   *bufferSize = theSize;\r
1057   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
1058     errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";\r
1059     errorText_ = errorStream_.str();\r
1060     return FAILURE;\r
1061   }\r
1062 \r
1063   stream_.bufferSize = *bufferSize;\r
1064   stream_.nBuffers = 1;\r
1065 \r
1066   // Try to set "hog" mode ... it's not clear to me this is working.\r
1067   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
1068     pid_t hog_pid;\r
1069     dataSize = sizeof( hog_pid );\r
1070     property.mSelector = kAudioDevicePropertyHogMode;\r
1071     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );\r
1072     if ( result != noErr ) {\r
1073       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";\r
1074       errorText_ = errorStream_.str();\r
1075       return FAILURE;\r
1076     }\r
1077 \r
1078     if ( hog_pid != getpid() ) {\r
1079       hog_pid = getpid();\r
1080       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );\r
1081       if ( result != noErr ) {\r
1082         errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";\r
1083         errorText_ = errorStream_.str();\r
1084         return FAILURE;\r
1085       }\r
1086     }\r
1087   }\r
1088 \r
1089   // Check and if necessary, change the sample rate for the device.\r
1090   Float64 nominalRate;\r
1091   dataSize = sizeof( Float64 );\r
1092   property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
1093   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
1094   if ( result != noErr ) {\r
1095     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
1096     errorText_ = errorStream_.str();\r
1097     return FAILURE;\r
1098   }\r
1099 \r
1100   // Only change the sample rate if off by more than 1 Hz.\r
1101   if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
1102 \r
1103     // Set a property listener for the sample rate change\r
1104     Float64 reportedRate = 0.0;\r
1105     AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
1106     result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1107     if ( result != noErr ) {\r
1108       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
1109       errorText_ = errorStream_.str();\r
1110       return FAILURE;\r
1111     }\r
1112 \r
1113     nominalRate = (Float64) sampleRate;\r
1114     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
1115     if ( result != noErr ) {\r
1116       AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1117       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
1118       errorText_ = errorStream_.str();\r
1119       return FAILURE;\r
1120     }\r
1121 \r
1122     // Now wait until the reported nominal rate is what we just set.\r
1123     UInt32 microCounter = 0;\r
1124     while ( reportedRate != nominalRate ) {\r
1125       microCounter += 5000;\r
1126       if ( microCounter > 5000000 ) break;\r
1127       usleep( 5000 );\r
1128     }\r
1129 \r
1130     // Remove the property listener.\r
1131     AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
1132 \r
1133     if ( microCounter > 5000000 ) {\r
1134       errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
1135       errorText_ = errorStream_.str();\r
1136       return FAILURE;\r
1137     }\r
1138   }\r
1139 \r
1140   // Now set the stream format for all streams.  Also, check the\r
1141   // physical format of the device and change that if necessary.\r
1142   AudioStreamBasicDescription   description;\r
1143   dataSize = sizeof( AudioStreamBasicDescription );\r
1144   property.mSelector = kAudioStreamPropertyVirtualFormat;\r
1145   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
1146   if ( result != noErr ) {\r
1147     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
1148     errorText_ = errorStream_.str();\r
1149     return FAILURE;\r
1150   }\r
1151 \r
1152   // Set the sample rate and data format id.  However, only make the\r
1153   // change if the sample rate is not within 1.0 of the desired\r
1154   // rate and the format is not linear pcm.\r
1155   bool updateFormat = false;\r
1156   if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
1157     description.mSampleRate = (Float64) sampleRate;\r
1158     updateFormat = true;\r
1159   }\r
1160 \r
1161   if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
1162     description.mFormatID = kAudioFormatLinearPCM;\r
1163     updateFormat = true;\r
1164   }\r
1165 \r
1166   if ( updateFormat ) {\r
1167     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
1168     if ( result != noErr ) {\r
1169       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
1170       errorText_ = errorStream_.str();\r
1171       return FAILURE;\r
1172     }\r
1173   }\r
1174 \r
1175   // Now check the physical format.\r
1176   property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
1177   result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );\r
1178   if ( result != noErr ) {\r
1179     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
1180     errorText_ = errorStream_.str();\r
1181     return FAILURE;\r
1182   }\r
1183 \r
1184   //std::cout << "Current physical stream format:" << std::endl;\r
1185   //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
1186   //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1187   //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
1188   //std::cout << "   sample rate = " << description.mSampleRate << std::endl;\r
1189 \r
1190   if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
1191     description.mFormatID = kAudioFormatLinearPCM;\r
1192     //description.mSampleRate = (Float64) sampleRate;\r
1193     AudioStreamBasicDescription testDescription = description;\r
1194     UInt32 formatFlags;\r
1195 \r
1196     // We'll try higher bit rates first and then work our way down.\r
1197     std::vector< std::pair<UInt32, UInt32>  > physicalFormats;\r
1198     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;\r
1199     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1200     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1201     physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
1202     physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed\r
1203     formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
1204     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
1205     formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
1206     physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
1207     formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
1208     physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
1209     physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
1210 \r
1211     bool setPhysicalFormat = false;\r
1212     for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
1213       testDescription = description;\r
1214       testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
1215       testDescription.mFormatFlags = physicalFormats[i].second;\r
1216       if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
1217         testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;\r
1218       else\r
1219         testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
1220       testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
1221       result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
1222       if ( result == noErr ) {\r
1223         setPhysicalFormat = true;\r
1224         //std::cout << "Updated physical stream format:" << std::endl;\r
1225         //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
1226         //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
1227         //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
1228         //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;\r
1229         break;\r
1230       }\r
1231     }\r
1232 \r
1233     if ( !setPhysicalFormat ) {\r
1234       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
1235       errorText_ = errorStream_.str();\r
1236       return FAILURE;\r
1237     }\r
1238   } // done setting virtual/physical formats.\r
1239 \r
1240   // Get the stream / device latency.\r
1241   UInt32 latency;\r
1242   dataSize = sizeof( UInt32 );\r
1243   property.mSelector = kAudioDevicePropertyLatency;\r
1244   if ( AudioObjectHasProperty( id, &property ) == true ) {\r
1245     result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );\r
1246     if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;\r
1247     else {\r
1248       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";\r
1249       errorText_ = errorStream_.str();\r
1250       error( RtAudioError::WARNING );\r
1251     }\r
1252   }\r
1253 \r
1254   // Byte-swapping: According to AudioHardware.h, the stream data will\r
1255   // always be presented in native-endian format, so we should never\r
1256   // need to byte swap.\r
1257   stream_.doByteSwap[mode] = false;\r
1258 \r
1259   // From the CoreAudio documentation, PCM data must be supplied as\r
1260   // 32-bit floats.\r
1261   stream_.userFormat = format;\r
1262   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
1263 \r
1264   if ( streamCount == 1 )\r
1265     stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;\r
1266   else // multiple streams\r
1267     stream_.nDeviceChannels[mode] = channels;\r
1268   stream_.nUserChannels[mode] = channels;\r
1269   stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream\r
1270   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
1271   else stream_.userInterleaved = true;\r
1272   stream_.deviceInterleaved[mode] = true;\r
1273   if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;\r
1274 \r
1275   // Set flags for buffer conversion.\r
1276   stream_.doConvertBuffer[mode] = false;\r
1277   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
1278     stream_.doConvertBuffer[mode] = true;\r
1279   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
1280     stream_.doConvertBuffer[mode] = true;\r
1281   if ( streamCount == 1 ) {\r
1282     if ( stream_.nUserChannels[mode] > 1 &&\r
1283          stream_.userInterleaved != stream_.deviceInterleaved[mode] )\r
1284       stream_.doConvertBuffer[mode] = true;\r
1285   }\r
1286   else if ( monoMode && stream_.userInterleaved )\r
1287     stream_.doConvertBuffer[mode] = true;\r
1288 \r
1289   // Allocate our CoreHandle structure for the stream.\r
1290   CoreHandle *handle = 0;\r
1291   if ( stream_.apiHandle == 0 ) {\r
1292     try {\r
1293       handle = new CoreHandle;\r
1294     }\r
1295     catch ( std::bad_alloc& ) {\r
1296       errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";\r
1297       goto error;\r
1298     }\r
1299 \r
1300     if ( pthread_cond_init( &handle->condition, NULL ) ) {\r
1301       errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";\r
1302       goto error;\r
1303     }\r
1304     stream_.apiHandle = (void *) handle;\r
1305   }\r
1306   else\r
1307     handle = (CoreHandle *) stream_.apiHandle;\r
1308   handle->iStream[mode] = firstStream;\r
1309   handle->nStreams[mode] = streamCount;\r
1310   handle->id[mode] = id;\r
1311 \r
1312   // Allocate necessary internal buffers.\r
1313   unsigned long bufferBytes;\r
1314   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
1315   //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
1316   stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
1317   memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
1318   if ( stream_.userBuffer[mode] == NULL ) {\r
1319     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
1320     goto error;\r
1321   }\r
1322 \r
1323   // If possible, we will make use of the CoreAudio stream buffers as\r
1324   // "device buffers".  However, we can't do this if using multiple\r
1325   // streams.\r
1326   if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {\r
1327 \r
1328     bool makeBuffer = true;\r
1329     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
1330     if ( mode == INPUT ) {\r
1331       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
1332         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
1333         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
1334       }\r
1335     }\r
1336 \r
1337     if ( makeBuffer ) {\r
1338       bufferBytes *= *bufferSize;\r
1339       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
1340       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
1341       if ( stream_.deviceBuffer == NULL ) {\r
1342         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";\r
1343         goto error;\r
1344       }\r
1345     }\r
1346   }\r
1347 \r
1348   stream_.sampleRate = sampleRate;\r
1349   stream_.device[mode] = device;\r
1350   stream_.state = STREAM_STOPPED;\r
1351   stream_.callbackInfo.object = (void *) this;\r
1352 \r
1353   // Setup the buffer conversion information structure.\r
1354   if ( stream_.doConvertBuffer[mode] ) {\r
1355     if ( streamCount > 1 ) setConvertInfo( mode, 0 );\r
1356     else setConvertInfo( mode, channelOffset );\r
1357   }\r
1358 \r
1359   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )\r
1360     // Only one callback procedure per device.\r
1361     stream_.mode = DUPLEX;\r
1362   else {\r
1363 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1364     result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );\r
1365 #else\r
1366     // deprecated in favor of AudioDeviceCreateIOProcID()\r
1367     result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );\r
1368 #endif\r
1369     if ( result != noErr ) {\r
1370       errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";\r
1371       errorText_ = errorStream_.str();\r
1372       goto error;\r
1373     }\r
1374     if ( stream_.mode == OUTPUT && mode == INPUT )\r
1375       stream_.mode = DUPLEX;\r
1376     else\r
1377       stream_.mode = mode;\r
1378   }\r
1379 \r
1380   // Setup the device property listener for over/underload.\r
1381   property.mSelector = kAudioDeviceProcessorOverload;\r
1382   property.mScope = kAudioObjectPropertyScopeGlobal;\r
1383   result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
1384 \r
1385   return SUCCESS;\r
1386 \r
1387  error:\r
1388   if ( handle ) {\r
1389     pthread_cond_destroy( &handle->condition );\r
1390     delete handle;\r
1391     stream_.apiHandle = 0;\r
1392   }\r
1393 \r
1394   for ( int i=0; i<2; i++ ) {\r
1395     if ( stream_.userBuffer[i] ) {\r
1396       free( stream_.userBuffer[i] );\r
1397       stream_.userBuffer[i] = 0;\r
1398     }\r
1399   }\r
1400 \r
1401   if ( stream_.deviceBuffer ) {\r
1402     free( stream_.deviceBuffer );\r
1403     stream_.deviceBuffer = 0;\r
1404   }\r
1405 \r
1406   stream_.state = STREAM_CLOSED;\r
1407   return FAILURE;\r
1408 }\r
1409 \r
1410 void RtApiCore :: closeStream( void )\r
1411 {\r
1412   if ( stream_.state == STREAM_CLOSED ) {\r
1413     errorText_ = "RtApiCore::closeStream(): no open stream to close!";\r
1414     error( RtAudioError::WARNING );\r
1415     return;\r
1416   }\r
1417 \r
1418   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1419   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1420     if (handle) {\r
1421       AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
1422         kAudioObjectPropertyScopeGlobal,\r
1423         kAudioObjectPropertyElementMaster };\r
1424 \r
1425       property.mSelector = kAudioDeviceProcessorOverload;\r
1426       property.mScope = kAudioObjectPropertyScopeGlobal;\r
1427       if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {\r
1428         errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
1429         error( RtAudioError::WARNING );\r
1430       }\r
1431     }\r
1432     if ( stream_.state == STREAM_RUNNING )\r
1433       AudioDeviceStop( handle->id[0], callbackHandler );\r
1434 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1435     AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );\r
1436 #else\r
1437     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1438     AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );\r
1439 #endif\r
1440   }\r
1441 \r
1442   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1443     if (handle) {\r
1444       AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
1445         kAudioObjectPropertyScopeGlobal,\r
1446         kAudioObjectPropertyElementMaster };\r
1447 \r
1448       property.mSelector = kAudioDeviceProcessorOverload;\r
1449       property.mScope = kAudioObjectPropertyScopeGlobal;\r
1450       if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {\r
1451         errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
1452         error( RtAudioError::WARNING );\r
1453       }\r
1454     }\r
1455     if ( stream_.state == STREAM_RUNNING )\r
1456       AudioDeviceStop( handle->id[1], callbackHandler );\r
1457 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
1458     AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );\r
1459 #else\r
1460     // deprecated in favor of AudioDeviceDestroyIOProcID()\r
1461     AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );\r
1462 #endif\r
1463   }\r
1464 \r
1465   for ( int i=0; i<2; i++ ) {\r
1466     if ( stream_.userBuffer[i] ) {\r
1467       free( stream_.userBuffer[i] );\r
1468       stream_.userBuffer[i] = 0;\r
1469     }\r
1470   }\r
1471 \r
1472   if ( stream_.deviceBuffer ) {\r
1473     free( stream_.deviceBuffer );\r
1474     stream_.deviceBuffer = 0;\r
1475   }\r
1476 \r
1477   // Destroy pthread condition variable.\r
1478   pthread_cond_destroy( &handle->condition );\r
1479   delete handle;\r
1480   stream_.apiHandle = 0;\r
1481 \r
1482   stream_.mode = UNINITIALIZED;\r
1483   stream_.state = STREAM_CLOSED;\r
1484 }\r
1485 \r
1486 void RtApiCore :: startStream( void )\r
1487 {\r
1488   verifyStream();\r
1489   RtApi::startStream();\r
1490   if ( stream_.state == STREAM_RUNNING ) {\r
1491     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
1492     error( RtAudioError::WARNING );\r
1493     return;\r
1494   }\r
1495 \r
1496   OSStatus result = noErr;\r
1497   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1498   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1499 \r
1500     result = AudioDeviceStart( handle->id[0], callbackHandler );\r
1501     if ( result != noErr ) {\r
1502       errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";\r
1503       errorText_ = errorStream_.str();\r
1504       goto unlock;\r
1505     }\r
1506   }\r
1507 \r
1508   if ( stream_.mode == INPUT ||\r
1509        ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1510 \r
1511     result = AudioDeviceStart( handle->id[1], callbackHandler );\r
1512     if ( result != noErr ) {\r
1513       errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";\r
1514       errorText_ = errorStream_.str();\r
1515       goto unlock;\r
1516     }\r
1517   }\r
1518 \r
1519   handle->drainCounter = 0;\r
1520   handle->internalDrain = false;\r
1521   stream_.state = STREAM_RUNNING;\r
1522 \r
1523  unlock:\r
1524   if ( result == noErr ) return;\r
1525   error( RtAudioError::SYSTEM_ERROR );\r
1526 }\r
1527 \r
1528 void RtApiCore :: stopStream( void )\r
1529 {\r
1530   verifyStream();\r
1531   if ( stream_.state == STREAM_STOPPED ) {\r
1532     errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";\r
1533     error( RtAudioError::WARNING );\r
1534     return;\r
1535   }\r
1536 \r
1537   OSStatus result = noErr;\r
1538   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1539   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
1540 \r
1541     if ( handle->drainCounter == 0 ) {\r
1542       handle->drainCounter = 2;\r
1543       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
1544     }\r
1545 \r
1546     result = AudioDeviceStop( handle->id[0], callbackHandler );\r
1547     if ( result != noErr ) {\r
1548       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
1549       errorText_ = errorStream_.str();\r
1550       goto unlock;\r
1551     }\r
1552   }\r
1553 \r
1554   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
1555 \r
1556     result = AudioDeviceStop( handle->id[1], callbackHandler );\r
1557     if ( result != noErr ) {\r
1558       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
1559       errorText_ = errorStream_.str();\r
1560       goto unlock;\r
1561     }\r
1562   }\r
1563 \r
1564   stream_.state = STREAM_STOPPED;\r
1565 \r
1566  unlock:\r
1567   if ( result == noErr ) return;\r
1568   error( RtAudioError::SYSTEM_ERROR );\r
1569 }\r
1570 \r
1571 void RtApiCore :: abortStream( void )\r
1572 {\r
1573   verifyStream();\r
1574   if ( stream_.state == STREAM_STOPPED ) {\r
1575     errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";\r
1576     error( RtAudioError::WARNING );\r
1577     return;\r
1578   }\r
1579 \r
1580   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1581   handle->drainCounter = 2;\r
1582 \r
1583   stopStream();\r
1584 }\r
1585 \r
1586 // This function will be called by a spawned thread when the user\r
1587 // callback function signals that the stream should be stopped or\r
1588 // aborted.  It is better to handle it this way because the\r
1589 // callbackEvent() function probably should return before the AudioDeviceStop()\r
1590 // function is called.\r
1591 static void *coreStopStream( void *ptr )\r
1592 {\r
1593   CallbackInfo *info = (CallbackInfo *) ptr;\r
1594   RtApiCore *object = (RtApiCore *) info->object;\r
1595 \r
1596   object->stopStream();\r
1597   pthread_exit( NULL );\r
1598 }\r
1599 \r
1600 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,\r
1601                                  const AudioBufferList *inBufferList,\r
1602                                  const AudioBufferList *outBufferList )\r
1603 {\r
1604   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
1605   if ( stream_.state == STREAM_CLOSED ) {\r
1606     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
1607     error( RtAudioError::WARNING );\r
1608     return FAILURE;\r
1609   }\r
1610 \r
1611   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
1612   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
1613 \r
1614   // Check if we were draining the stream and signal is finished.\r
1615   if ( handle->drainCounter > 3 ) {\r
1616     ThreadHandle threadId;\r
1617 \r
1618     stream_.state = STREAM_STOPPING;\r
1619     if ( handle->internalDrain == true )\r
1620       pthread_create( &threadId, NULL, coreStopStream, info );\r
1621     else // external call to stopStream()\r
1622       pthread_cond_signal( &handle->condition );\r
1623     return SUCCESS;\r
1624   }\r
1625 \r
1626   AudioDeviceID outputDevice = handle->id[0];\r
1627 \r
1628   // Invoke user callback to get fresh output data UNLESS we are\r
1629   // draining stream or duplex mode AND the input/output devices are\r
1630   // different AND this function is called for the input device.\r
1631   if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {\r
1632     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
1633     double streamTime = getStreamTime();\r
1634     RtAudioStreamStatus status = 0;\r
1635     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
1636       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
1637       handle->xrun[0] = false;\r
1638     }\r
1639     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
1640       status |= RTAUDIO_INPUT_OVERFLOW;\r
1641       handle->xrun[1] = false;\r
1642     }\r
1643 \r
1644     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
1645                                   stream_.bufferSize, streamTime, status, info->userData );\r
1646     if ( cbReturnValue == 2 ) {\r
1647       stream_.state = STREAM_STOPPING;\r
1648       handle->drainCounter = 2;\r
1649       abortStream();\r
1650       return SUCCESS;\r
1651     }\r
1652     else if ( cbReturnValue == 1 ) {\r
1653       handle->drainCounter = 1;\r
1654       handle->internalDrain = true;\r
1655     }\r
1656   }\r
1657 \r
1658   if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {\r
1659 \r
1660     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
1661 \r
1662       if ( handle->nStreams[0] == 1 ) {\r
1663         memset( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1664                 0,\r
1665                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1666       }\r
1667       else { // fill multiple streams with zeros\r
1668         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1669           memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1670                   0,\r
1671                   outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );\r
1672         }\r
1673       }\r
1674     }\r
1675     else if ( handle->nStreams[0] == 1 ) {\r
1676       if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer\r
1677         convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,\r
1678                        stream_.userBuffer[0], stream_.convertInfo[0] );\r
1679       }\r
1680       else { // copy from user buffer\r
1681         memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,\r
1682                 stream_.userBuffer[0],\r
1683                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
1684       }\r
1685     }\r
1686     else { // fill multiple streams\r
1687       Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];\r
1688       if ( stream_.doConvertBuffer[0] ) {\r
1689         convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
1690         inBuffer = (Float32 *) stream_.deviceBuffer;\r
1691       }\r
1692 \r
1693       if ( stream_.deviceInterleaved[0] == false ) { // mono mode\r
1694         UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;\r
1695         for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
1696           memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
1697                   (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );\r
1698         }\r
1699       }\r
1700       else { // fill multiple multi-channel streams with interleaved data\r
1701         UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;\r
1702         Float32 *out, *in;\r
1703 \r
1704         bool inInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1705         UInt32 inChannels = stream_.nUserChannels[0];\r
1706         if ( stream_.doConvertBuffer[0] ) {\r
1707           inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1708           inChannels = stream_.nDeviceChannels[0];\r
1709         }\r
1710 \r
1711         if ( inInterleaved ) inOffset = 1;\r
1712         else inOffset = stream_.bufferSize;\r
1713 \r
1714         channelsLeft = inChannels;\r
1715         for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
1716           in = inBuffer;\r
1717           out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;\r
1718           streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;\r
1719 \r
1720           outJump = 0;\r
1721           // Account for possible channel offset in first stream\r
1722           if ( i == 0 && stream_.channelOffset[0] > 0 ) {\r
1723             streamChannels -= stream_.channelOffset[0];\r
1724             outJump = stream_.channelOffset[0];\r
1725             out += outJump;\r
1726           }\r
1727 \r
1728           // Account for possible unfilled channels at end of the last stream\r
1729           if ( streamChannels > channelsLeft ) {\r
1730             outJump = streamChannels - channelsLeft;\r
1731             streamChannels = channelsLeft;\r
1732           }\r
1733 \r
1734           // Determine input buffer offsets and skips\r
1735           if ( inInterleaved ) {\r
1736             inJump = inChannels;\r
1737             in += inChannels - channelsLeft;\r
1738           }\r
1739           else {\r
1740             inJump = 1;\r
1741             in += (inChannels - channelsLeft) * inOffset;\r
1742           }\r
1743 \r
1744           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1745             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1746               *out++ = in[j*inOffset];\r
1747             }\r
1748             out += outJump;\r
1749             in += inJump;\r
1750           }\r
1751           channelsLeft -= streamChannels;\r
1752         }\r
1753       }\r
1754     }\r
1755   }\r
1756 \r
1757   // Don't bother draining input\r
1758   if ( handle->drainCounter ) {\r
1759     handle->drainCounter++;\r
1760     goto unlock;\r
1761   }\r
1762 \r
1763   AudioDeviceID inputDevice;\r
1764   inputDevice = handle->id[1];\r
1765   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {\r
1766 \r
1767     if ( handle->nStreams[1] == 1 ) {\r
1768       if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer\r
1769         convertBuffer( stream_.userBuffer[1],\r
1770                        (char *) inBufferList->mBuffers[handle->iStream[1]].mData,\r
1771                        stream_.convertInfo[1] );\r
1772       }\r
1773       else { // copy to user buffer\r
1774         memcpy( stream_.userBuffer[1],\r
1775                 inBufferList->mBuffers[handle->iStream[1]].mData,\r
1776                 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );\r
1777       }\r
1778     }\r
1779     else { // read from multiple streams\r
1780       Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];\r
1781       if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;\r
1782 \r
1783       if ( stream_.deviceInterleaved[1] == false ) { // mono mode\r
1784         UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;\r
1785         for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
1786           memcpy( (void *)&outBuffer[i*stream_.bufferSize],\r
1787                   inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );\r
1788         }\r
1789       }\r
1790       else { // read from multiple multi-channel streams\r
1791         UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;\r
1792         Float32 *out, *in;\r
1793 \r
1794         bool outInterleaved = ( stream_.userInterleaved ) ? true : false;\r
1795         UInt32 outChannels = stream_.nUserChannels[1];\r
1796         if ( stream_.doConvertBuffer[1] ) {\r
1797           outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
1798           outChannels = stream_.nDeviceChannels[1];\r
1799         }\r
1800 \r
1801         if ( outInterleaved ) outOffset = 1;\r
1802         else outOffset = stream_.bufferSize;\r
1803 \r
1804         channelsLeft = outChannels;\r
1805         for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {\r
1806           out = outBuffer;\r
1807           in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;\r
1808           streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;\r
1809 \r
1810           inJump = 0;\r
1811           // Account for possible channel offset in first stream\r
1812           if ( i == 0 && stream_.channelOffset[1] > 0 ) {\r
1813             streamChannels -= stream_.channelOffset[1];\r
1814             inJump = stream_.channelOffset[1];\r
1815             in += inJump;\r
1816           }\r
1817 \r
1818           // Account for possible unread channels at end of the last stream\r
1819           if ( streamChannels > channelsLeft ) {\r
1820             inJump = streamChannels - channelsLeft;\r
1821             streamChannels = channelsLeft;\r
1822           }\r
1823 \r
1824           // Determine output buffer offsets and skips\r
1825           if ( outInterleaved ) {\r
1826             outJump = outChannels;\r
1827             out += outChannels - channelsLeft;\r
1828           }\r
1829           else {\r
1830             outJump = 1;\r
1831             out += (outChannels - channelsLeft) * outOffset;\r
1832           }\r
1833 \r
1834           for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
1835             for ( unsigned int j=0; j<streamChannels; j++ ) {\r
1836               out[j*outOffset] = *in++;\r
1837             }\r
1838             out += outJump;\r
1839             in += inJump;\r
1840           }\r
1841           channelsLeft -= streamChannels;\r
1842         }\r
1843       }\r
1844 \r
1845       if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
1846         convertBuffer( stream_.userBuffer[1],\r
1847                        stream_.deviceBuffer,\r
1848                        stream_.convertInfo[1] );\r
1849       }\r
1850     }\r
1851   }\r
1852 \r
1853  unlock:\r
1854   //MUTEX_UNLOCK( &stream_.mutex );\r
1855 \r
1856   RtApi::tickStreamTime();\r
1857   return SUCCESS;\r
1858 }\r
1859 \r
1860 const char* RtApiCore :: getErrorCode( OSStatus code )\r
1861 {\r
1862   switch( code ) {\r
1863 \r
1864   case kAudioHardwareNotRunningError:\r
1865     return "kAudioHardwareNotRunningError";\r
1866 \r
1867   case kAudioHardwareUnspecifiedError:\r
1868     return "kAudioHardwareUnspecifiedError";\r
1869 \r
1870   case kAudioHardwareUnknownPropertyError:\r
1871     return "kAudioHardwareUnknownPropertyError";\r
1872 \r
1873   case kAudioHardwareBadPropertySizeError:\r
1874     return "kAudioHardwareBadPropertySizeError";\r
1875 \r
1876   case kAudioHardwareIllegalOperationError:\r
1877     return "kAudioHardwareIllegalOperationError";\r
1878 \r
1879   case kAudioHardwareBadObjectError:\r
1880     return "kAudioHardwareBadObjectError";\r
1881 \r
1882   case kAudioHardwareBadDeviceError:\r
1883     return "kAudioHardwareBadDeviceError";\r
1884 \r
1885   case kAudioHardwareBadStreamError:\r
1886     return "kAudioHardwareBadStreamError";\r
1887 \r
1888   case kAudioHardwareUnsupportedOperationError:\r
1889     return "kAudioHardwareUnsupportedOperationError";\r
1890 \r
1891   case kAudioDeviceUnsupportedFormatError:\r
1892     return "kAudioDeviceUnsupportedFormatError";\r
1893 \r
1894   case kAudioDevicePermissionsError:\r
1895     return "kAudioDevicePermissionsError";\r
1896 \r
1897   default:\r
1898     return "CoreAudio unknown error";\r
1899   }\r
1900 }\r
1901 \r
1902   //******************** End of __MACOSX_CORE__ *********************//\r
1903 #endif\r
1904 \r
1905 #if defined(__UNIX_JACK__)\r
1906 \r
1907 // JACK is a low-latency audio server, originally written for the\r
1908 // GNU/Linux operating system and now also ported to OS-X. It can\r
1909 // connect a number of different applications to an audio device, as\r
1910 // well as allowing them to share audio between themselves.\r
1911 //\r
1912 // When using JACK with RtAudio, "devices" refer to JACK clients that\r
1913 // have ports connected to the server.  The JACK server is typically\r
1914 // started in a terminal as follows:\r
1915 //\r
1916 // .jackd -d alsa -d hw:0\r
1917 //\r
1918 // or through an interface program such as qjackctl.  Many of the\r
1919 // parameters normally set for a stream are fixed by the JACK server\r
1920 // and can be specified when the JACK server is started.  In\r
1921 // particular,\r
1922 //\r
1923 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4\r
1924 //\r
1925 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample\r
1926 // frames, and number of buffers = 4.  Once the server is running, it\r
1927 // is not possible to override these values.  If the values are not\r
1928 // specified in the command-line, the JACK server uses default values.\r
1929 //\r
1930 // The JACK server does not have to be running when an instance of\r
1931 // RtApiJack is created, though the function getDeviceCount() will\r
1932 // report 0 devices found until JACK has been started.  When no\r
1933 // devices are available (i.e., the JACK server is not running), a\r
1934 // stream cannot be opened.\r
1935 \r
1936 #include <jack/jack.h>\r
1937 #include <unistd.h>\r
1938 #include <cstdio>\r
1939 \r
1940 // A structure to hold various information related to the Jack API\r
1941 // implementation.\r
1942 struct JackHandle {\r
1943   jack_client_t *client;\r
1944   jack_port_t **ports[2];\r
1945   std::string deviceName[2];\r
1946   bool xrun[2];\r
1947   pthread_cond_t condition;\r
1948   int drainCounter;       // Tracks callback counts when draining\r
1949   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
1950 \r
1951   JackHandle()\r
1952     :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
1953 };\r
1954 \r
1955 static void jackSilentError( const char * ) {};\r
1956 \r
1957 RtApiJack :: RtApiJack()\r
1958 {\r
1959   // Nothing to do here.\r
1960 #if !defined(__RTAUDIO_DEBUG__)\r
1961   // Turn off Jack's internal error reporting.\r
1962   jack_set_error_function( &jackSilentError );\r
1963 #endif\r
1964 }\r
1965 \r
1966 RtApiJack :: ~RtApiJack()\r
1967 {\r
1968   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
1969 }\r
1970 \r
1971 unsigned int RtApiJack :: getDeviceCount( void )\r
1972 {\r
1973   // See if we can become a jack client.\r
1974   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
1975   jack_status_t *status = NULL;\r
1976   jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );\r
1977   if ( client == 0 ) return 0;\r
1978 \r
1979   const char **ports;\r
1980   std::string port, previousPort;\r
1981   unsigned int nChannels = 0, nDevices = 0;\r
1982   ports = jack_get_ports( client, NULL, NULL, 0 );\r
1983   if ( ports ) {\r
1984     // Parse the port names up to the first colon (:).\r
1985     size_t iColon = 0;\r
1986     do {\r
1987       port = (char *) ports[ nChannels ];\r
1988       iColon = port.find(":");\r
1989       if ( iColon != std::string::npos ) {\r
1990         port = port.substr( 0, iColon + 1 );\r
1991         if ( port != previousPort ) {\r
1992           nDevices++;\r
1993           previousPort = port;\r
1994         }\r
1995       }\r
1996     } while ( ports[++nChannels] );\r
1997     free( ports );\r
1998   }\r
1999 \r
2000   jack_client_close( client );\r
2001   return nDevices;\r
2002 }\r
2003 \r
2004 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )\r
2005 {\r
2006   RtAudio::DeviceInfo info;\r
2007   info.probed = false;\r
2008 \r
2009   jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption\r
2010   jack_status_t *status = NULL;\r
2011   jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );\r
2012   if ( client == 0 ) {\r
2013     errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";\r
2014     error( RtAudioError::WARNING );\r
2015     return info;\r
2016   }\r
2017 \r
2018   const char **ports;\r
2019   std::string port, previousPort;\r
2020   unsigned int nPorts = 0, nDevices = 0;\r
2021   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2022   if ( ports ) {\r
2023     // Parse the port names up to the first colon (:).\r
2024     size_t iColon = 0;\r
2025     do {\r
2026       port = (char *) ports[ nPorts ];\r
2027       iColon = port.find(":");\r
2028       if ( iColon != std::string::npos ) {\r
2029         port = port.substr( 0, iColon );\r
2030         if ( port != previousPort ) {\r
2031           if ( nDevices == device ) info.name = port;\r
2032           nDevices++;\r
2033           previousPort = port;\r
2034         }\r
2035       }\r
2036     } while ( ports[++nPorts] );\r
2037     free( ports );\r
2038   }\r
2039 \r
2040   if ( device >= nDevices ) {\r
2041     jack_client_close( client );\r
2042     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
2043     error( RtAudioError::INVALID_USE );\r
2044     return info;\r
2045   }\r
2046 \r
2047   // Get the current jack server sample rate.\r
2048   info.sampleRates.clear();\r
2049 \r
2050   info.preferredSampleRate = jack_get_sample_rate( client );\r
2051   info.sampleRates.push_back( info.preferredSampleRate );\r
2052 \r
2053   // Count the available ports containing the client name as device\r
2054   // channels.  Jack "input ports" equal RtAudio output channels.\r
2055   unsigned int nChannels = 0;\r
2056   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );\r
2057   if ( ports ) {\r
2058     while ( ports[ nChannels ] ) nChannels++;\r
2059     free( ports );\r
2060     info.outputChannels = nChannels;\r
2061   }\r
2062 \r
2063   // Jack "output ports" equal RtAudio input channels.\r
2064   nChannels = 0;\r
2065   ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );\r
2066   if ( ports ) {\r
2067     while ( ports[ nChannels ] ) nChannels++;\r
2068     free( ports );\r
2069     info.inputChannels = nChannels;\r
2070   }\r
2071 \r
2072   if ( info.outputChannels == 0 && info.inputChannels == 0 ) {\r
2073     jack_client_close(client);\r
2074     errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";\r
2075     error( RtAudioError::WARNING );\r
2076     return info;\r
2077   }\r
2078 \r
2079   // If device opens for both playback and capture, we determine the channels.\r
2080   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2081     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2082 \r
2083   // Jack always uses 32-bit floats.\r
2084   info.nativeFormats = RTAUDIO_FLOAT32;\r
2085 \r
2086   // Jack doesn't provide default devices so we'll use the first available one.\r
2087   if ( device == 0 && info.outputChannels > 0 )\r
2088     info.isDefaultOutput = true;\r
2089   if ( device == 0 && info.inputChannels > 0 )\r
2090     info.isDefaultInput = true;\r
2091 \r
2092   jack_client_close(client);\r
2093   info.probed = true;\r
2094   return info;\r
2095 }\r
2096 \r
2097 static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
2098 {\r
2099   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2100 \r
2101   RtApiJack *object = (RtApiJack *) info->object;\r
2102   if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;\r
2103 \r
2104   return 0;\r
2105 }\r
2106 \r
2107 // This function will be called by a spawned thread when the Jack\r
2108 // server signals that it is shutting down.  It is necessary to handle\r
2109 // it this way because the jackShutdown() function must return before\r
2110 // the jack_deactivate() function (in closeStream()) will return.\r
2111 static void *jackCloseStream( void *ptr )\r
2112 {\r
2113   CallbackInfo *info = (CallbackInfo *) ptr;\r
2114   RtApiJack *object = (RtApiJack *) info->object;\r
2115 \r
2116   object->closeStream();\r
2117 \r
2118   pthread_exit( NULL );\r
2119 }\r
2120 static void jackShutdown( void *infoPointer )\r
2121 {\r
2122   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
2123   RtApiJack *object = (RtApiJack *) info->object;\r
2124 \r
2125   // Check current stream state.  If stopped, then we'll assume this\r
2126   // was called as a result of a call to RtApiJack::stopStream (the\r
2127   // deactivation of a client handle causes this function to be called).\r
2128   // If not, we'll assume the Jack server is shutting down or some\r
2129   // other problem occurred and we should close the stream.\r
2130   if ( object->isStreamRunning() == false ) return;\r
2131 \r
2132   ThreadHandle threadId;\r
2133   pthread_create( &threadId, NULL, jackCloseStream, info );\r
2134   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
2135 }\r
2136 \r
2137 static int jackXrun( void *infoPointer )\r
2138 {\r
2139   JackHandle *handle = (JackHandle *) infoPointer;\r
2140 \r
2141   if ( handle->ports[0] ) handle->xrun[0] = true;\r
2142   if ( handle->ports[1] ) handle->xrun[1] = true;\r
2143 \r
2144   return 0;\r
2145 }\r
2146 \r
2147 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2148                                    unsigned int firstChannel, unsigned int sampleRate,\r
2149                                    RtAudioFormat format, unsigned int *bufferSize,\r
2150                                    RtAudio::StreamOptions *options )\r
2151 {\r
2152   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2153 \r
2154   // Look for jack server and try to become a client (only do once per stream).\r
2155   jack_client_t *client = 0;\r
2156   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {\r
2157     jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
2158     jack_status_t *status = NULL;\r
2159     if ( options && !options->streamName.empty() )\r
2160       client = jack_client_open( options->streamName.c_str(), jackoptions, status );\r
2161     else\r
2162       client = jack_client_open( "RtApiJack", jackoptions, status );\r
2163     if ( client == 0 ) {\r
2164       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";\r
2165       error( RtAudioError::WARNING );\r
2166       return FAILURE;\r
2167     }\r
2168   }\r
2169   else {\r
2170     // The handle must have been created on an earlier pass.\r
2171     client = handle->client;\r
2172   }\r
2173 \r
2174   const char **ports;\r
2175   std::string port, previousPort, deviceName;\r
2176   unsigned int nPorts = 0, nDevices = 0;\r
2177   ports = jack_get_ports( client, NULL, NULL, 0 );\r
2178   if ( ports ) {\r
2179     // Parse the port names up to the first colon (:).\r
2180     size_t iColon = 0;\r
2181     do {\r
2182       port = (char *) ports[ nPorts ];\r
2183       iColon = port.find(":");\r
2184       if ( iColon != std::string::npos ) {\r
2185         port = port.substr( 0, iColon );\r
2186         if ( port != previousPort ) {\r
2187           if ( nDevices == device ) deviceName = port;\r
2188           nDevices++;\r
2189           previousPort = port;\r
2190         }\r
2191       }\r
2192     } while ( ports[++nPorts] );\r
2193     free( ports );\r
2194   }\r
2195 \r
2196   if ( device >= nDevices ) {\r
2197     errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";\r
2198     return FAILURE;\r
2199   }\r
2200 \r
2201   // Count the available ports containing the client name as device\r
2202   // channels.  Jack "input ports" equal RtAudio output channels.\r
2203   unsigned int nChannels = 0;\r
2204   unsigned long flag = JackPortIsInput;\r
2205   if ( mode == INPUT ) flag = JackPortIsOutput;\r
2206   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2207   if ( ports ) {\r
2208     while ( ports[ nChannels ] ) nChannels++;\r
2209     free( ports );\r
2210   }\r
2211 \r
2212   // Compare the jack ports for specified client to the requested number of channels.\r
2213   if ( nChannels < (channels + firstChannel) ) {\r
2214     errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";\r
2215     errorText_ = errorStream_.str();\r
2216     return FAILURE;\r
2217   }\r
2218 \r
2219   // Check the jack server sample rate.\r
2220   unsigned int jackRate = jack_get_sample_rate( client );\r
2221   if ( sampleRate != jackRate ) {\r
2222     jack_client_close( client );\r
2223     errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";\r
2224     errorText_ = errorStream_.str();\r
2225     return FAILURE;\r
2226   }\r
2227   stream_.sampleRate = jackRate;\r
2228 \r
2229   // Get the latency of the JACK port.\r
2230   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
2231   if ( ports[ firstChannel ] ) {\r
2232     // Added by Ge Wang\r
2233     jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
2234     // the range (usually the min and max are equal)\r
2235     jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
2236     // get the latency range\r
2237     jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
2238     // be optimistic, use the min!\r
2239     stream_.latency[mode] = latrange.min;\r
2240     //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
2241   }\r
2242   free( ports );\r
2243 \r
2244   // The jack server always uses 32-bit floating-point data.\r
2245   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
2246   stream_.userFormat = format;\r
2247 \r
2248   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
2249   else stream_.userInterleaved = true;\r
2250 \r
2251   // Jack always uses non-interleaved buffers.\r
2252   stream_.deviceInterleaved[mode] = false;\r
2253 \r
2254   // Jack always provides host byte-ordered data.\r
2255   stream_.doByteSwap[mode] = false;\r
2256 \r
2257   // Get the buffer size.  The buffer size and number of buffers\r
2258   // (periods) is set when the jack server is started.\r
2259   stream_.bufferSize = (int) jack_get_buffer_size( client );\r
2260   *bufferSize = stream_.bufferSize;\r
2261 \r
2262   stream_.nDeviceChannels[mode] = channels;\r
2263   stream_.nUserChannels[mode] = channels;\r
2264 \r
2265   // Set flags for buffer conversion.\r
2266   stream_.doConvertBuffer[mode] = false;\r
2267   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
2268     stream_.doConvertBuffer[mode] = true;\r
2269   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
2270        stream_.nUserChannels[mode] > 1 )\r
2271     stream_.doConvertBuffer[mode] = true;\r
2272 \r
2273   // Allocate our JackHandle structure for the stream.\r
2274   if ( handle == 0 ) {\r
2275     try {\r
2276       handle = new JackHandle;\r
2277     }\r
2278     catch ( std::bad_alloc& ) {\r
2279       errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";\r
2280       goto error;\r
2281     }\r
2282 \r
2283     if ( pthread_cond_init(&handle->condition, NULL) ) {\r
2284       errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";\r
2285       goto error;\r
2286     }\r
2287     stream_.apiHandle = (void *) handle;\r
2288     handle->client = client;\r
2289   }\r
2290   handle->deviceName[mode] = deviceName;\r
2291 \r
2292   // Allocate necessary internal buffers.\r
2293   unsigned long bufferBytes;\r
2294   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
2295   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
2296   if ( stream_.userBuffer[mode] == NULL ) {\r
2297     errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";\r
2298     goto error;\r
2299   }\r
2300 \r
2301   if ( stream_.doConvertBuffer[mode] ) {\r
2302 \r
2303     bool makeBuffer = true;\r
2304     if ( mode == OUTPUT )\r
2305       bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
2306     else { // mode == INPUT\r
2307       bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );\r
2308       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
2309         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);\r
2310         if ( bufferBytes < bytesOut ) makeBuffer = false;\r
2311       }\r
2312     }\r
2313 \r
2314     if ( makeBuffer ) {\r
2315       bufferBytes *= *bufferSize;\r
2316       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
2317       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
2318       if ( stream_.deviceBuffer == NULL ) {\r
2319         errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";\r
2320         goto error;\r
2321       }\r
2322     }\r
2323   }\r
2324 \r
2325   // Allocate memory for the Jack ports (channels) identifiers.\r
2326   handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );\r
2327   if ( handle->ports[mode] == NULL )  {\r
2328     errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";\r
2329     goto error;\r
2330   }\r
2331 \r
2332   stream_.device[mode] = device;\r
2333   stream_.channelOffset[mode] = firstChannel;\r
2334   stream_.state = STREAM_STOPPED;\r
2335   stream_.callbackInfo.object = (void *) this;\r
2336 \r
2337   if ( stream_.mode == OUTPUT && mode == INPUT )\r
2338     // We had already set up the stream for output.\r
2339     stream_.mode = DUPLEX;\r
2340   else {\r
2341     stream_.mode = mode;\r
2342     jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );\r
2343     jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );\r
2344     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );\r
2345   }\r
2346 \r
2347   // Register our ports.\r
2348   char label[64];\r
2349   if ( mode == OUTPUT ) {\r
2350     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2351       snprintf( label, 64, "outport %d", i );\r
2352       handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,\r
2353                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );\r
2354     }\r
2355   }\r
2356   else {\r
2357     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2358       snprintf( label, 64, "inport %d", i );\r
2359       handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,\r
2360                                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );\r
2361     }\r
2362   }\r
2363 \r
2364   // Setup the buffer conversion information structure.  We don't use\r
2365   // buffers to do channel offsets, so we override that parameter\r
2366   // here.\r
2367   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
2368 \r
2369   return SUCCESS;\r
2370 \r
2371  error:\r
2372   if ( handle ) {\r
2373     pthread_cond_destroy( &handle->condition );\r
2374     jack_client_close( handle->client );\r
2375 \r
2376     if ( handle->ports[0] ) free( handle->ports[0] );\r
2377     if ( handle->ports[1] ) free( handle->ports[1] );\r
2378 \r
2379     delete handle;\r
2380     stream_.apiHandle = 0;\r
2381   }\r
2382 \r
2383   for ( int i=0; i<2; i++ ) {\r
2384     if ( stream_.userBuffer[i] ) {\r
2385       free( stream_.userBuffer[i] );\r
2386       stream_.userBuffer[i] = 0;\r
2387     }\r
2388   }\r
2389 \r
2390   if ( stream_.deviceBuffer ) {\r
2391     free( stream_.deviceBuffer );\r
2392     stream_.deviceBuffer = 0;\r
2393   }\r
2394 \r
2395   return FAILURE;\r
2396 }\r
2397 \r
2398 void RtApiJack :: closeStream( void )\r
2399 {\r
2400   if ( stream_.state == STREAM_CLOSED ) {\r
2401     errorText_ = "RtApiJack::closeStream(): no open stream to close!";\r
2402     error( RtAudioError::WARNING );\r
2403     return;\r
2404   }\r
2405 \r
2406   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2407   if ( handle ) {\r
2408 \r
2409     if ( stream_.state == STREAM_RUNNING )\r
2410       jack_deactivate( handle->client );\r
2411 \r
2412     jack_client_close( handle->client );\r
2413   }\r
2414 \r
2415   if ( handle ) {\r
2416     if ( handle->ports[0] ) free( handle->ports[0] );\r
2417     if ( handle->ports[1] ) free( handle->ports[1] );\r
2418     pthread_cond_destroy( &handle->condition );\r
2419     delete handle;\r
2420     stream_.apiHandle = 0;\r
2421   }\r
2422 \r
2423   for ( int i=0; i<2; i++ ) {\r
2424     if ( stream_.userBuffer[i] ) {\r
2425       free( stream_.userBuffer[i] );\r
2426       stream_.userBuffer[i] = 0;\r
2427     }\r
2428   }\r
2429 \r
2430   if ( stream_.deviceBuffer ) {\r
2431     free( stream_.deviceBuffer );\r
2432     stream_.deviceBuffer = 0;\r
2433   }\r
2434 \r
2435   stream_.mode = UNINITIALIZED;\r
2436   stream_.state = STREAM_CLOSED;\r
2437 }\r
2438 \r
2439 void RtApiJack :: startStream( void )\r
2440 {\r
2441   verifyStream();\r
2442   RtApi::startStream();\r
2443   if ( stream_.state == STREAM_RUNNING ) {\r
2444     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
2445     error( RtAudioError::WARNING );\r
2446     return;\r
2447   }\r
2448 \r
2449   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2450   int result = jack_activate( handle->client );\r
2451   if ( result ) {\r
2452     errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";\r
2453     goto unlock;\r
2454   }\r
2455 \r
2456   const char **ports;\r
2457 \r
2458   // Get the list of available ports.\r
2459   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2460     result = 1;\r
2461     ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);\r
2462     if ( ports == NULL) {\r
2463       errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";\r
2464       goto unlock;\r
2465     }\r
2466 \r
2467     // Now make the port connections.  Since RtAudio wasn't designed to\r
2468     // allow the user to select particular channels of a device, we'll\r
2469     // just open the first "nChannels" ports with offset.\r
2470     for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2471       result = 1;\r
2472       if ( ports[ stream_.channelOffset[0] + i ] )\r
2473         result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );\r
2474       if ( result ) {\r
2475         free( ports );\r
2476         errorText_ = "RtApiJack::startStream(): error connecting output ports!";\r
2477         goto unlock;\r
2478       }\r
2479     }\r
2480     free(ports);\r
2481   }\r
2482 \r
2483   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2484     result = 1;\r
2485     ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );\r
2486     if ( ports == NULL) {\r
2487       errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";\r
2488       goto unlock;\r
2489     }\r
2490 \r
2491     // Now make the port connections.  See note above.\r
2492     for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2493       result = 1;\r
2494       if ( ports[ stream_.channelOffset[1] + i ] )\r
2495         result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );\r
2496       if ( result ) {\r
2497         free( ports );\r
2498         errorText_ = "RtApiJack::startStream(): error connecting input ports!";\r
2499         goto unlock;\r
2500       }\r
2501     }\r
2502     free(ports);\r
2503   }\r
2504 \r
2505   handle->drainCounter = 0;\r
2506   handle->internalDrain = false;\r
2507   stream_.state = STREAM_RUNNING;\r
2508 \r
2509  unlock:\r
2510   if ( result == 0 ) return;\r
2511   error( RtAudioError::SYSTEM_ERROR );\r
2512 }\r
2513 \r
2514 void RtApiJack :: stopStream( void )\r
2515 {\r
2516   verifyStream();\r
2517   if ( stream_.state == STREAM_STOPPED ) {\r
2518     errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";\r
2519     error( RtAudioError::WARNING );\r
2520     return;\r
2521   }\r
2522 \r
2523   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2524   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2525 \r
2526     if ( handle->drainCounter == 0 ) {\r
2527       handle->drainCounter = 2;\r
2528       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
2529     }\r
2530   }\r
2531 \r
2532   jack_deactivate( handle->client );\r
2533   stream_.state = STREAM_STOPPED;\r
2534 }\r
2535 \r
2536 void RtApiJack :: abortStream( void )\r
2537 {\r
2538   verifyStream();\r
2539   if ( stream_.state == STREAM_STOPPED ) {\r
2540     errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";\r
2541     error( RtAudioError::WARNING );\r
2542     return;\r
2543   }\r
2544 \r
2545   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2546   handle->drainCounter = 2;\r
2547 \r
2548   stopStream();\r
2549 }\r
2550 \r
2551 // This function will be called by a spawned thread when the user\r
2552 // callback function signals that the stream should be stopped or\r
2553 // aborted.  It is necessary to handle it this way because the\r
2554 // callbackEvent() function must return before the jack_deactivate()\r
2555 // function will return.\r
2556 static void *jackStopStream( void *ptr )\r
2557 {\r
2558   CallbackInfo *info = (CallbackInfo *) ptr;\r
2559   RtApiJack *object = (RtApiJack *) info->object;\r
2560 \r
2561   object->stopStream();\r
2562   pthread_exit( NULL );\r
2563 }\r
2564 \r
2565 bool RtApiJack :: callbackEvent( unsigned long nframes )\r
2566 {\r
2567   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
2568   if ( stream_.state == STREAM_CLOSED ) {\r
2569     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
2570     error( RtAudioError::WARNING );\r
2571     return FAILURE;\r
2572   }\r
2573   if ( stream_.bufferSize != nframes ) {\r
2574     errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";\r
2575     error( RtAudioError::WARNING );\r
2576     return FAILURE;\r
2577   }\r
2578 \r
2579   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
2580   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
2581 \r
2582   // Check if we were draining the stream and signal is finished.\r
2583   if ( handle->drainCounter > 3 ) {\r
2584     ThreadHandle threadId;\r
2585 \r
2586     stream_.state = STREAM_STOPPING;\r
2587     if ( handle->internalDrain == true )\r
2588       pthread_create( &threadId, NULL, jackStopStream, info );\r
2589     else\r
2590       pthread_cond_signal( &handle->condition );\r
2591     return SUCCESS;\r
2592   }\r
2593 \r
2594   // Invoke user callback first, to get fresh output data.\r
2595   if ( handle->drainCounter == 0 ) {\r
2596     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
2597     double streamTime = getStreamTime();\r
2598     RtAudioStreamStatus status = 0;\r
2599     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
2600       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
2601       handle->xrun[0] = false;\r
2602     }\r
2603     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
2604       status |= RTAUDIO_INPUT_OVERFLOW;\r
2605       handle->xrun[1] = false;\r
2606     }\r
2607     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
2608                                   stream_.bufferSize, streamTime, status, info->userData );\r
2609     if ( cbReturnValue == 2 ) {\r
2610       stream_.state = STREAM_STOPPING;\r
2611       handle->drainCounter = 2;\r
2612       ThreadHandle id;\r
2613       pthread_create( &id, NULL, jackStopStream, info );\r
2614       return SUCCESS;\r
2615     }\r
2616     else if ( cbReturnValue == 1 ) {\r
2617       handle->drainCounter = 1;\r
2618       handle->internalDrain = true;\r
2619     }\r
2620   }\r
2621 \r
2622   jack_default_audio_sample_t *jackbuffer;\r
2623   unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );\r
2624   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
2625 \r
2626     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
2627 \r
2628       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2629         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2630         memset( jackbuffer, 0, bufferBytes );\r
2631       }\r
2632 \r
2633     }\r
2634     else if ( stream_.doConvertBuffer[0] ) {\r
2635 \r
2636       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
2637 \r
2638       for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
2639         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2640         memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );\r
2641       }\r
2642     }\r
2643     else { // no buffer conversion\r
2644       for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
2645         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
2646         memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
2647       }\r
2648     }\r
2649   }\r
2650 \r
2651   // Don't bother draining input\r
2652   if ( handle->drainCounter ) {\r
2653     handle->drainCounter++;\r
2654     goto unlock;\r
2655   }\r
2656 \r
2657   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
2658 \r
2659     if ( stream_.doConvertBuffer[1] ) {\r
2660       for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {\r
2661         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2662         memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );\r
2663       }\r
2664       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
2665     }\r
2666     else { // no buffer conversion\r
2667       for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
2668         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
2669         memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );\r
2670       }\r
2671     }\r
2672   }\r
2673 \r
2674  unlock:\r
2675   RtApi::tickStreamTime();\r
2676   return SUCCESS;\r
2677 }\r
2678   //******************** End of __UNIX_JACK__ *********************//\r
2679 #endif\r
2680 \r
2681 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows\r
2682 \r
2683 // The ASIO API is designed around a callback scheme, so this\r
2684 // implementation is similar to that used for OS-X CoreAudio and Linux\r
2685 // Jack.  The primary constraint with ASIO is that it only allows\r
2686 // access to a single driver at a time.  Thus, it is not possible to\r
2687 // have more than one simultaneous RtAudio stream.\r
2688 //\r
2689 // This implementation also requires a number of external ASIO files\r
2690 // and a few global variables.  The ASIO callback scheme does not\r
2691 // allow for the passing of user data, so we must create a global\r
2692 // pointer to our callbackInfo structure.\r
2693 //\r
2694 // On unix systems, we make use of a pthread condition variable.\r
2695 // Since there is no equivalent in Windows, I hacked something based\r
2696 // on information found in\r
2697 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r
2698 \r
2699 #include "asiosys.h"\r
2700 #include "asio.h"\r
2701 #include "iasiothiscallresolver.h"\r
2702 #include "asiodrivers.h"\r
2703 #include <cmath>\r
2704 \r
2705 static AsioDrivers drivers;\r
2706 static ASIOCallbacks asioCallbacks;\r
2707 static ASIODriverInfo driverInfo;\r
2708 static CallbackInfo *asioCallbackInfo;\r
2709 static bool asioXRun;\r
2710 \r
2711 struct AsioHandle {\r
2712   int drainCounter;       // Tracks callback counts when draining\r
2713   bool internalDrain;     // Indicates if stop is initiated from callback or not.\r
2714   ASIOBufferInfo *bufferInfos;\r
2715   HANDLE condition;\r
2716 \r
2717   AsioHandle()\r
2718     :drainCounter(0), internalDrain(false), bufferInfos(0) {}\r
2719 };\r
2720 \r
2721 // Function declarations (definitions at end of section)\r
2722 static const char* getAsioErrorString( ASIOError result );\r
2723 static void sampleRateChanged( ASIOSampleRate sRate );\r
2724 static long asioMessages( long selector, long value, void* message, double* opt );\r
2725 \r
2726 RtApiAsio :: RtApiAsio()\r
2727 {\r
2728   // ASIO cannot run on a multi-threaded appartment. You can call\r
2729   // CoInitialize beforehand, but it must be for appartment threading\r
2730   // (in which case, CoInitilialize will return S_FALSE here).\r
2731   coInitialized_ = false;\r
2732   HRESULT hr = CoInitialize( NULL );\r
2733   if ( FAILED(hr) ) {\r
2734     errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
2735     error( RtAudioError::WARNING );\r
2736   }\r
2737   coInitialized_ = true;\r
2738 \r
2739   drivers.removeCurrentDriver();\r
2740   driverInfo.asioVersion = 2;\r
2741 \r
2742   // See note in DirectSound implementation about GetDesktopWindow().\r
2743   driverInfo.sysRef = GetForegroundWindow();\r
2744 }\r
2745 \r
2746 RtApiAsio :: ~RtApiAsio()\r
2747 {\r
2748   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
2749   if ( coInitialized_ ) CoUninitialize();\r
2750 }\r
2751 \r
2752 unsigned int RtApiAsio :: getDeviceCount( void )\r
2753 {\r
2754   return (unsigned int) drivers.asioGetNumDev();\r
2755 }\r
2756 \r
2757 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )\r
2758 {\r
2759   RtAudio::DeviceInfo info;\r
2760   info.probed = false;\r
2761 \r
2762   // Get device ID\r
2763   unsigned int nDevices = getDeviceCount();\r
2764   if ( nDevices == 0 ) {\r
2765     errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
2766     error( RtAudioError::INVALID_USE );\r
2767     return info;\r
2768   }\r
2769 \r
2770   if ( device >= nDevices ) {\r
2771     errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
2772     error( RtAudioError::INVALID_USE );\r
2773     return info;\r
2774   }\r
2775 \r
2776   // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.\r
2777   if ( stream_.state != STREAM_CLOSED ) {\r
2778     if ( device >= devices_.size() ) {\r
2779       errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";\r
2780       error( RtAudioError::WARNING );\r
2781       return info;\r
2782     }\r
2783     return devices_[ device ];\r
2784   }\r
2785 \r
2786   char driverName[32];\r
2787   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2788   if ( result != ASE_OK ) {\r
2789     errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2790     errorText_ = errorStream_.str();\r
2791     error( RtAudioError::WARNING );\r
2792     return info;\r
2793   }\r
2794 \r
2795   info.name = driverName;\r
2796 \r
2797   if ( !drivers.loadDriver( driverName ) ) {\r
2798     errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";\r
2799     errorText_ = errorStream_.str();\r
2800     error( RtAudioError::WARNING );\r
2801     return info;\r
2802   }\r
2803 \r
2804   result = ASIOInit( &driverInfo );\r
2805   if ( result != ASE_OK ) {\r
2806     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2807     errorText_ = errorStream_.str();\r
2808     error( RtAudioError::WARNING );\r
2809     return info;\r
2810   }\r
2811 \r
2812   // Determine the device channel information.\r
2813   long inputChannels, outputChannels;\r
2814   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2815   if ( result != ASE_OK ) {\r
2816     drivers.removeCurrentDriver();\r
2817     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2818     errorText_ = errorStream_.str();\r
2819     error( RtAudioError::WARNING );\r
2820     return info;\r
2821   }\r
2822 \r
2823   info.outputChannels = outputChannels;\r
2824   info.inputChannels = inputChannels;\r
2825   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
2826     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
2827 \r
2828   // Determine the supported sample rates.\r
2829   info.sampleRates.clear();\r
2830   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
2831     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
2832     if ( result == ASE_OK ) {\r
2833       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
2834 \r
2835       if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
2836         info.preferredSampleRate = SAMPLE_RATES[i];\r
2837     }\r
2838   }\r
2839 \r
2840   // Determine supported data types ... just check first channel and assume rest are the same.\r
2841   ASIOChannelInfo channelInfo;\r
2842   channelInfo.channel = 0;\r
2843   channelInfo.isInput = true;\r
2844   if ( info.inputChannels <= 0 ) channelInfo.isInput = false;\r
2845   result = ASIOGetChannelInfo( &channelInfo );\r
2846   if ( result != ASE_OK ) {\r
2847     drivers.removeCurrentDriver();\r
2848     errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";\r
2849     errorText_ = errorStream_.str();\r
2850     error( RtAudioError::WARNING );\r
2851     return info;\r
2852   }\r
2853 \r
2854   info.nativeFormats = 0;\r
2855   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )\r
2856     info.nativeFormats |= RTAUDIO_SINT16;\r
2857   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )\r
2858     info.nativeFormats |= RTAUDIO_SINT32;\r
2859   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )\r
2860     info.nativeFormats |= RTAUDIO_FLOAT32;\r
2861   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
2862     info.nativeFormats |= RTAUDIO_FLOAT64;\r
2863   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
2864     info.nativeFormats |= RTAUDIO_SINT24;\r
2865 \r
2866   if ( info.outputChannels > 0 )\r
2867     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
2868   if ( info.inputChannels > 0 )\r
2869     if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
2870 \r
2871   info.probed = true;\r
2872   drivers.removeCurrentDriver();\r
2873   return info;\r
2874 }\r
2875 \r
2876 static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
2877 {\r
2878   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
2879   object->callbackEvent( index );\r
2880 }\r
2881 \r
2882 void RtApiAsio :: saveDeviceInfo( void )\r
2883 {\r
2884   devices_.clear();\r
2885 \r
2886   unsigned int nDevices = getDeviceCount();\r
2887   devices_.resize( nDevices );\r
2888   for ( unsigned int i=0; i<nDevices; i++ )\r
2889     devices_[i] = getDeviceInfo( i );\r
2890 }\r
2891 \r
2892 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
2893                                    unsigned int firstChannel, unsigned int sampleRate,\r
2894                                    RtAudioFormat format, unsigned int *bufferSize,\r
2895                                    RtAudio::StreamOptions *options )\r
2896 {////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
2897 \r
2898   bool isDuplexInput =  mode == INPUT && stream_.mode == OUTPUT;\r
2899 \r
2900   // For ASIO, a duplex stream MUST use the same driver.\r
2901   if ( isDuplexInput && stream_.device[0] != device ) {\r
2902     errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
2903     return FAILURE;\r
2904   }\r
2905 \r
2906   char driverName[32];\r
2907   ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
2908   if ( result != ASE_OK ) {\r
2909     errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
2910     errorText_ = errorStream_.str();\r
2911     return FAILURE;\r
2912   }\r
2913 \r
2914   // Only load the driver once for duplex stream.\r
2915   if ( !isDuplexInput ) {\r
2916     // The getDeviceInfo() function will not work when a stream is open\r
2917     // because ASIO does not allow multiple devices to run at the same\r
2918     // time.  Thus, we'll probe the system before opening a stream and\r
2919     // save the results for use by getDeviceInfo().\r
2920     this->saveDeviceInfo();\r
2921 \r
2922     if ( !drivers.loadDriver( driverName ) ) {\r
2923       errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
2924       errorText_ = errorStream_.str();\r
2925       return FAILURE;\r
2926     }\r
2927 \r
2928     result = ASIOInit( &driverInfo );\r
2929     if ( result != ASE_OK ) {\r
2930       errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
2931       errorText_ = errorStream_.str();\r
2932       return FAILURE;\r
2933     }\r
2934   }\r
2935 \r
2936   // keep them before any "goto error", they are used for error cleanup + goto device boundary checks\r
2937   bool buffersAllocated = false;\r
2938   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
2939   unsigned int nChannels;\r
2940 \r
2941 \r
2942   // Check the device channel count.\r
2943   long inputChannels, outputChannels;\r
2944   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
2945   if ( result != ASE_OK ) {\r
2946     errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
2947     errorText_ = errorStream_.str();\r
2948     goto error;\r
2949   }\r
2950 \r
2951   if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
2952        ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
2953     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
2954     errorText_ = errorStream_.str();\r
2955     goto error;\r
2956   }\r
2957   stream_.nDeviceChannels[mode] = channels;\r
2958   stream_.nUserChannels[mode] = channels;\r
2959   stream_.channelOffset[mode] = firstChannel;\r
2960 \r
2961   // Verify the sample rate is supported.\r
2962   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
2963   if ( result != ASE_OK ) {\r
2964     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
2965     errorText_ = errorStream_.str();\r
2966     goto error;\r
2967   }\r
2968 \r
2969   // Get the current sample rate\r
2970   ASIOSampleRate currentRate;\r
2971   result = ASIOGetSampleRate( &currentRate );\r
2972   if ( result != ASE_OK ) {\r
2973     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
2974     errorText_ = errorStream_.str();\r
2975     goto error;\r
2976   }\r
2977 \r
2978   // Set the sample rate only if necessary\r
2979   if ( currentRate != sampleRate ) {\r
2980     result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
2981     if ( result != ASE_OK ) {\r
2982       errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
2983       errorText_ = errorStream_.str();\r
2984       goto error;\r
2985     }\r
2986   }\r
2987 \r
2988   // Determine the driver data type.\r
2989   ASIOChannelInfo channelInfo;\r
2990   channelInfo.channel = 0;\r
2991   if ( mode == OUTPUT ) channelInfo.isInput = false;\r
2992   else channelInfo.isInput = true;\r
2993   result = ASIOGetChannelInfo( &channelInfo );\r
2994   if ( result != ASE_OK ) {\r
2995     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
2996     errorText_ = errorStream_.str();\r
2997     goto error;\r
2998   }\r
2999 \r
3000   // Assuming WINDOWS host is always little-endian.\r
3001   stream_.doByteSwap[mode] = false;\r
3002   stream_.userFormat = format;\r
3003   stream_.deviceFormat[mode] = 0;\r
3004   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {\r
3005     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
3006     if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;\r
3007   }\r
3008   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {\r
3009     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
3010     if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;\r
3011   }\r
3012   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {\r
3013     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
3014     if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;\r
3015   }\r
3016   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {\r
3017     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
3018     if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
3019   }\r
3020   else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
3021     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
3022     if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
3023   }\r
3024 \r
3025   if ( stream_.deviceFormat[mode] == 0 ) {\r
3026     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
3027     errorText_ = errorStream_.str();\r
3028     goto error;\r
3029   }\r
3030 \r
3031   // Set the buffer size.  For a duplex stream, this will end up\r
3032   // setting the buffer size based on the input constraints, which\r
3033   // should be ok.\r
3034   long minSize, maxSize, preferSize, granularity;\r
3035   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
3036   if ( result != ASE_OK ) {\r
3037     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
3038     errorText_ = errorStream_.str();\r
3039     goto error;\r
3040   }\r
3041 \r
3042   if ( isDuplexInput ) {\r
3043     // When this is the duplex input (output was opened before), then we have to use the same\r
3044     // buffersize as the output, because it might use the preferred buffer size, which most\r
3045     // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,\r
3046     // So instead of throwing an error, make them equal. The caller uses the reference\r
3047     // to the "bufferSize" param as usual to set up processing buffers.\r
3048 \r
3049     *bufferSize = stream_.bufferSize;\r
3050 \r
3051   } else {\r
3052     if ( *bufferSize == 0 ) *bufferSize = preferSize;\r
3053     else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
3054     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
3055     else if ( granularity == -1 ) {\r
3056       // Make sure bufferSize is a power of two.\r
3057       int log2_of_min_size = 0;\r
3058       int log2_of_max_size = 0;\r
3059 \r
3060       for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
3061         if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
3062         if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
3063       }\r
3064 \r
3065       long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
3066       int min_delta_num = log2_of_min_size;\r
3067 \r
3068       for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
3069         long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
3070         if (current_delta < min_delta) {\r
3071           min_delta = current_delta;\r
3072           min_delta_num = i;\r
3073         }\r
3074       }\r
3075 \r
3076       *bufferSize = ( (unsigned int)1 << min_delta_num );\r
3077       if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
3078       else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
3079     }\r
3080     else if ( granularity != 0 ) {\r
3081       // Set to an even multiple of granularity, rounding up.\r
3082       *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
3083     }\r
3084   }\r
3085 \r
3086   /*\r
3087   // we don't use it anymore, see above!\r
3088   // Just left it here for the case...\r
3089   if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {\r
3090     errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
3091     goto error;\r
3092   }\r
3093   */\r
3094 \r
3095   stream_.bufferSize = *bufferSize;\r
3096   stream_.nBuffers = 2;\r
3097 \r
3098   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
3099   else stream_.userInterleaved = true;\r
3100 \r
3101   // ASIO always uses non-interleaved buffers.\r
3102   stream_.deviceInterleaved[mode] = false;\r
3103 \r
3104   // Allocate, if necessary, our AsioHandle structure for the stream.\r
3105   if ( handle == 0 ) {\r
3106     try {\r
3107       handle = new AsioHandle;\r
3108     }\r
3109     catch ( std::bad_alloc& ) {\r
3110       errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
3111       goto error;\r
3112     }\r
3113     handle->bufferInfos = 0;\r
3114 \r
3115     // Create a manual-reset event.\r
3116     handle->condition = CreateEvent( NULL,   // no security\r
3117                                      TRUE,   // manual-reset\r
3118                                      FALSE,  // non-signaled initially\r
3119                                      NULL ); // unnamed\r
3120     stream_.apiHandle = (void *) handle;\r
3121   }\r
3122 \r
3123   // Create the ASIO internal buffers.  Since RtAudio sets up input\r
3124   // and output separately, we'll have to dispose of previously\r
3125   // created output buffers for a duplex stream.\r
3126   if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
3127     ASIODisposeBuffers();\r
3128     if ( handle->bufferInfos ) free( handle->bufferInfos );\r
3129   }\r
3130 \r
3131   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
3132   unsigned int i;\r
3133   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3134   handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
3135   if ( handle->bufferInfos == NULL ) {\r
3136     errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
3137     errorText_ = errorStream_.str();\r
3138     goto error;\r
3139   }\r
3140 \r
3141   ASIOBufferInfo *infos;\r
3142   infos = handle->bufferInfos;\r
3143   for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {\r
3144     infos->isInput = ASIOFalse;\r
3145     infos->channelNum = i + stream_.channelOffset[0];\r
3146     infos->buffers[0] = infos->buffers[1] = 0;\r
3147   }\r
3148   for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {\r
3149     infos->isInput = ASIOTrue;\r
3150     infos->channelNum = i + stream_.channelOffset[1];\r
3151     infos->buffers[0] = infos->buffers[1] = 0;\r
3152   }\r
3153 \r
3154   // prepare for callbacks\r
3155   stream_.sampleRate = sampleRate;\r
3156   stream_.device[mode] = device;\r
3157   stream_.mode = isDuplexInput ? DUPLEX : mode;\r
3158 \r
3159   // store this class instance before registering callbacks, that are going to use it\r
3160   asioCallbackInfo = &stream_.callbackInfo;\r
3161   stream_.callbackInfo.object = (void *) this;\r
3162 \r
3163   // Set up the ASIO callback structure and create the ASIO data buffers.\r
3164   asioCallbacks.bufferSwitch = &bufferSwitch;\r
3165   asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
3166   asioCallbacks.asioMessage = &asioMessages;\r
3167   asioCallbacks.bufferSwitchTimeInfo = NULL;\r
3168   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3169   if ( result != ASE_OK ) {\r
3170     // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges\r
3171     // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver\r
3172     // in that case, let's be naïve and try that instead\r
3173     *bufferSize = preferSize;\r
3174     stream_.bufferSize = *bufferSize;\r
3175     result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
3176   }\r
3177 \r
3178   if ( result != ASE_OK ) {\r
3179     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
3180     errorText_ = errorStream_.str();\r
3181     goto error;\r
3182   }\r
3183   buffersAllocated = true;\r
3184   stream_.state = STREAM_STOPPED;\r
3185 \r
3186   // Set flags for buffer conversion.\r
3187   stream_.doConvertBuffer[mode] = false;\r
3188   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
3189     stream_.doConvertBuffer[mode] = true;\r
3190   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
3191        stream_.nUserChannels[mode] > 1 )\r
3192     stream_.doConvertBuffer[mode] = true;\r
3193 \r
3194   // Allocate necessary internal buffers\r
3195   unsigned long bufferBytes;\r
3196   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
3197   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
3198   if ( stream_.userBuffer[mode] == NULL ) {\r
3199     errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";\r
3200     goto error;\r
3201   }\r
3202 \r
3203   if ( stream_.doConvertBuffer[mode] ) {\r
3204 \r
3205     bool makeBuffer = true;\r
3206     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
3207     if ( isDuplexInput && stream_.deviceBuffer ) {\r
3208       unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
3209       if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
3210     }\r
3211 \r
3212     if ( makeBuffer ) {\r
3213       bufferBytes *= *bufferSize;\r
3214       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
3215       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
3216       if ( stream_.deviceBuffer == NULL ) {\r
3217         errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";\r
3218         goto error;\r
3219       }\r
3220     }\r
3221   }\r
3222 \r
3223   // Determine device latencies\r
3224   long inputLatency, outputLatency;\r
3225   result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
3226   if ( result != ASE_OK ) {\r
3227     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
3228     errorText_ = errorStream_.str();\r
3229     error( RtAudioError::WARNING); // warn but don't fail\r
3230   }\r
3231   else {\r
3232     stream_.latency[0] = outputLatency;\r
3233     stream_.latency[1] = inputLatency;\r
3234   }\r
3235 \r
3236   // Setup the buffer conversion information structure.  We don't use\r
3237   // buffers to do channel offsets, so we override that parameter\r
3238   // here.\r
3239   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
3240 \r
3241   return SUCCESS;\r
3242 \r
3243  error:\r
3244   if ( !isDuplexInput ) {\r
3245     // the cleanup for error in the duplex input, is done by RtApi::openStream\r
3246     // So we clean up for single channel only\r
3247 \r
3248     if ( buffersAllocated )\r
3249       ASIODisposeBuffers();\r
3250 \r
3251     drivers.removeCurrentDriver();\r
3252 \r
3253     if ( handle ) {\r
3254       CloseHandle( handle->condition );\r
3255       if ( handle->bufferInfos )\r
3256         free( handle->bufferInfos );\r
3257 \r
3258       delete handle;\r
3259       stream_.apiHandle = 0;\r
3260     }\r
3261 \r
3262 \r
3263     if ( stream_.userBuffer[mode] ) {\r
3264       free( stream_.userBuffer[mode] );\r
3265       stream_.userBuffer[mode] = 0;\r
3266     }\r
3267 \r
3268     if ( stream_.deviceBuffer ) {\r
3269       free( stream_.deviceBuffer );\r
3270       stream_.deviceBuffer = 0;\r
3271     }\r
3272   }\r
3273 \r
3274   return FAILURE;\r
3275 }////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
3276 \r
3277 void RtApiAsio :: closeStream()\r
3278 {\r
3279   if ( stream_.state == STREAM_CLOSED ) {\r
3280     errorText_ = "RtApiAsio::closeStream(): no open stream to close!";\r
3281     error( RtAudioError::WARNING );\r
3282     return;\r
3283   }\r
3284 \r
3285   if ( stream_.state == STREAM_RUNNING ) {\r
3286     stream_.state = STREAM_STOPPED;\r
3287     ASIOStop();\r
3288   }\r
3289   ASIODisposeBuffers();\r
3290   drivers.removeCurrentDriver();\r
3291 \r
3292   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3293   if ( handle ) {\r
3294     CloseHandle( handle->condition );\r
3295     if ( handle->bufferInfos )\r
3296       free( handle->bufferInfos );\r
3297     delete handle;\r
3298     stream_.apiHandle = 0;\r
3299   }\r
3300 \r
3301   for ( int i=0; i<2; i++ ) {\r
3302     if ( stream_.userBuffer[i] ) {\r
3303       free( stream_.userBuffer[i] );\r
3304       stream_.userBuffer[i] = 0;\r
3305     }\r
3306   }\r
3307 \r
3308   if ( stream_.deviceBuffer ) {\r
3309     free( stream_.deviceBuffer );\r
3310     stream_.deviceBuffer = 0;\r
3311   }\r
3312 \r
3313   stream_.mode = UNINITIALIZED;\r
3314   stream_.state = STREAM_CLOSED;\r
3315 }\r
3316 \r
3317 bool stopThreadCalled = false;\r
3318 \r
3319 void RtApiAsio :: startStream()\r
3320 {\r
3321   verifyStream();\r
3322   RtApi::startStream();\r
3323   if ( stream_.state == STREAM_RUNNING ) {\r
3324     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
3325     error( RtAudioError::WARNING );\r
3326     return;\r
3327   }\r
3328 \r
3329   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3330   ASIOError result = ASIOStart();\r
3331   if ( result != ASE_OK ) {\r
3332     errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";\r
3333     errorText_ = errorStream_.str();\r
3334     goto unlock;\r
3335   }\r
3336 \r
3337   handle->drainCounter = 0;\r
3338   handle->internalDrain = false;\r
3339   ResetEvent( handle->condition );\r
3340   stream_.state = STREAM_RUNNING;\r
3341   asioXRun = false;\r
3342 \r
3343  unlock:\r
3344   stopThreadCalled = false;\r
3345 \r
3346   if ( result == ASE_OK ) return;\r
3347   error( RtAudioError::SYSTEM_ERROR );\r
3348 }\r
3349 \r
3350 void RtApiAsio :: stopStream()\r
3351 {\r
3352   verifyStream();\r
3353   if ( stream_.state == STREAM_STOPPED ) {\r
3354     errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";\r
3355     error( RtAudioError::WARNING );\r
3356     return;\r
3357   }\r
3358 \r
3359   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3360   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3361     if ( handle->drainCounter == 0 ) {\r
3362       handle->drainCounter = 2;\r
3363       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
3364     }\r
3365   }\r
3366 \r
3367   stream_.state = STREAM_STOPPED;\r
3368 \r
3369   ASIOError result = ASIOStop();\r
3370   if ( result != ASE_OK ) {\r
3371     errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";\r
3372     errorText_ = errorStream_.str();\r
3373   }\r
3374 \r
3375   if ( result == ASE_OK ) return;\r
3376   error( RtAudioError::SYSTEM_ERROR );\r
3377 }\r
3378 \r
3379 void RtApiAsio :: abortStream()\r
3380 {\r
3381   verifyStream();\r
3382   if ( stream_.state == STREAM_STOPPED ) {\r
3383     errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";\r
3384     error( RtAudioError::WARNING );\r
3385     return;\r
3386   }\r
3387 \r
3388   // The following lines were commented-out because some behavior was\r
3389   // noted where the device buffers need to be zeroed to avoid\r
3390   // continuing sound, even when the device buffers are completely\r
3391   // disposed.  So now, calling abort is the same as calling stop.\r
3392   // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3393   // handle->drainCounter = 2;\r
3394   stopStream();\r
3395 }\r
3396 \r
3397 // This function will be called by a spawned thread when the user\r
3398 // callback function signals that the stream should be stopped or\r
3399 // aborted.  It is necessary to handle it this way because the\r
3400 // callbackEvent() function must return before the ASIOStop()\r
3401 // function will return.\r
3402 static unsigned __stdcall asioStopStream( void *ptr )\r
3403 {\r
3404   CallbackInfo *info = (CallbackInfo *) ptr;\r
3405   RtApiAsio *object = (RtApiAsio *) info->object;\r
3406 \r
3407   object->stopStream();\r
3408   _endthreadex( 0 );\r
3409   return 0;\r
3410 }\r
3411 \r
3412 bool RtApiAsio :: callbackEvent( long bufferIndex )\r
3413 {\r
3414   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
3415   if ( stream_.state == STREAM_CLOSED ) {\r
3416     errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
3417     error( RtAudioError::WARNING );\r
3418     return FAILURE;\r
3419   }\r
3420 \r
3421   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
3422   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
3423 \r
3424   // Check if we were draining the stream and signal if finished.\r
3425   if ( handle->drainCounter > 3 ) {\r
3426 \r
3427     stream_.state = STREAM_STOPPING;\r
3428     if ( handle->internalDrain == false )\r
3429       SetEvent( handle->condition );\r
3430     else { // spawn a thread to stop the stream\r
3431       unsigned threadId;\r
3432       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3433                                                     &stream_.callbackInfo, 0, &threadId );\r
3434     }\r
3435     return SUCCESS;\r
3436   }\r
3437 \r
3438   // Invoke user callback to get fresh output data UNLESS we are\r
3439   // draining stream.\r
3440   if ( handle->drainCounter == 0 ) {\r
3441     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
3442     double streamTime = getStreamTime();\r
3443     RtAudioStreamStatus status = 0;\r
3444     if ( stream_.mode != INPUT && asioXRun == true ) {\r
3445       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
3446       asioXRun = false;\r
3447     }\r
3448     if ( stream_.mode != OUTPUT && asioXRun == true ) {\r
3449       status |= RTAUDIO_INPUT_OVERFLOW;\r
3450       asioXRun = false;\r
3451     }\r
3452     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
3453                                      stream_.bufferSize, streamTime, status, info->userData );\r
3454     if ( cbReturnValue == 2 ) {\r
3455       stream_.state = STREAM_STOPPING;\r
3456       handle->drainCounter = 2;\r
3457       unsigned threadId;\r
3458       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
3459                                                     &stream_.callbackInfo, 0, &threadId );\r
3460       return SUCCESS;\r
3461     }\r
3462     else if ( cbReturnValue == 1 ) {\r
3463       handle->drainCounter = 1;\r
3464       handle->internalDrain = true;\r
3465     }\r
3466   }\r
3467 \r
3468   unsigned int nChannels, bufferBytes, i, j;\r
3469   nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
3470   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
3471 \r
3472     bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );\r
3473 \r
3474     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
3475 \r
3476       for ( i=0, j=0; i<nChannels; i++ ) {\r
3477         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3478           memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );\r
3479       }\r
3480 \r
3481     }\r
3482     else if ( stream_.doConvertBuffer[0] ) {\r
3483 \r
3484       convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
3485       if ( stream_.doByteSwap[0] )\r
3486         byteSwapBuffer( stream_.deviceBuffer,\r
3487                         stream_.bufferSize * stream_.nDeviceChannels[0],\r
3488                         stream_.deviceFormat[0] );\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_.deviceBuffer[j++*bufferBytes], bufferBytes );\r
3494       }\r
3495 \r
3496     }\r
3497     else {\r
3498 \r
3499       if ( stream_.doByteSwap[0] )\r
3500         byteSwapBuffer( stream_.userBuffer[0],\r
3501                         stream_.bufferSize * stream_.nUserChannels[0],\r
3502                         stream_.userFormat );\r
3503 \r
3504       for ( i=0, j=0; i<nChannels; i++ ) {\r
3505         if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
3506           memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
3507                   &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );\r
3508       }\r
3509 \r
3510     }\r
3511   }\r
3512 \r
3513   // Don't bother draining input\r
3514   if ( handle->drainCounter ) {\r
3515     handle->drainCounter++;\r
3516     goto unlock;\r
3517   }\r
3518 \r
3519   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
3520 \r
3521     bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);\r
3522 \r
3523     if (stream_.doConvertBuffer[1]) {\r
3524 \r
3525       // Always interleave ASIO input data.\r
3526       for ( i=0, j=0; i<nChannels; i++ ) {\r
3527         if ( handle->bufferInfos[i].isInput == ASIOTrue )\r
3528           memcpy( &stream_.deviceBuffer[j++*bufferBytes],\r
3529                   handle->bufferInfos[i].buffers[bufferIndex],\r
3530                   bufferBytes );\r
3531       }\r
3532 \r
3533       if ( stream_.doByteSwap[1] )\r
3534         byteSwapBuffer( stream_.deviceBuffer,\r
3535                         stream_.bufferSize * stream_.nDeviceChannels[1],\r
3536                         stream_.deviceFormat[1] );\r
3537       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
3538 \r
3539     }\r
3540     else {\r
3541       for ( i=0, j=0; i<nChannels; i++ ) {\r
3542         if ( handle->bufferInfos[i].isInput == ASIOTrue ) {\r
3543           memcpy( &stream_.userBuffer[1][bufferBytes*j++],\r
3544                   handle->bufferInfos[i].buffers[bufferIndex],\r
3545                   bufferBytes );\r
3546         }\r
3547       }\r
3548 \r
3549       if ( stream_.doByteSwap[1] )\r
3550         byteSwapBuffer( stream_.userBuffer[1],\r
3551                         stream_.bufferSize * stream_.nUserChannels[1],\r
3552                         stream_.userFormat );\r
3553     }\r
3554   }\r
3555 \r
3556  unlock:\r
3557   // The following call was suggested by Malte Clasen.  While the API\r
3558   // documentation indicates it should not be required, some device\r
3559   // drivers apparently do not function correctly without it.\r
3560   ASIOOutputReady();\r
3561 \r
3562   RtApi::tickStreamTime();\r
3563   return SUCCESS;\r
3564 }\r
3565 \r
3566 static void sampleRateChanged( ASIOSampleRate sRate )\r
3567 {\r
3568   // The ASIO documentation says that this usually only happens during\r
3569   // external sync.  Audio processing is not stopped by the driver,\r
3570   // actual sample rate might not have even changed, maybe only the\r
3571   // sample rate status of an AES/EBU or S/PDIF digital input at the\r
3572   // audio device.\r
3573 \r
3574   RtApi *object = (RtApi *) asioCallbackInfo->object;\r
3575   try {\r
3576     object->stopStream();\r
3577   }\r
3578   catch ( RtAudioError &exception ) {\r
3579     std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;\r
3580     return;\r
3581   }\r
3582 \r
3583   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
3584 }\r
3585 \r
3586 static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
3587 {\r
3588   long ret = 0;\r
3589 \r
3590   switch( selector ) {\r
3591   case kAsioSelectorSupported:\r
3592     if ( value == kAsioResetRequest\r
3593          || value == kAsioEngineVersion\r
3594          || value == kAsioResyncRequest\r
3595          || value == kAsioLatenciesChanged\r
3596          // The following three were added for ASIO 2.0, you don't\r
3597          // necessarily have to support them.\r
3598          || value == kAsioSupportsTimeInfo\r
3599          || value == kAsioSupportsTimeCode\r
3600          || value == kAsioSupportsInputMonitor)\r
3601       ret = 1L;\r
3602     break;\r
3603   case kAsioResetRequest:\r
3604     // Defer the task and perform the reset of the driver during the\r
3605     // next "safe" situation.  You cannot reset the driver right now,\r
3606     // as this code is called from the driver.  Reset the driver is\r
3607     // done by completely destruct is. I.e. ASIOStop(),\r
3608     // ASIODisposeBuffers(), Destruction Afterwards you initialize the\r
3609     // driver again.\r
3610     std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;\r
3611     ret = 1L;\r
3612     break;\r
3613   case kAsioResyncRequest:\r
3614     // This informs the application that the driver encountered some\r
3615     // non-fatal data loss.  It is used for synchronization purposes\r
3616     // of different media.  Added mainly to work around the Win16Mutex\r
3617     // problems in Windows 95/98 with the Windows Multimedia system,\r
3618     // which could lose data because the Mutex was held too long by\r
3619     // another thread.  However a driver can issue it in other\r
3620     // situations, too.\r
3621     // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;\r
3622     asioXRun = true;\r
3623     ret = 1L;\r
3624     break;\r
3625   case kAsioLatenciesChanged:\r
3626     // This will inform the host application that the drivers were\r
3627     // latencies changed.  Beware, it this does not mean that the\r
3628     // buffer sizes have changed!  You might need to update internal\r
3629     // delay data.\r
3630     std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;\r
3631     ret = 1L;\r
3632     break;\r
3633   case kAsioEngineVersion:\r
3634     // Return the supported ASIO version of the host application.  If\r
3635     // a host application does not implement this selector, ASIO 1.0\r
3636     // is assumed by the driver.\r
3637     ret = 2L;\r
3638     break;\r
3639   case kAsioSupportsTimeInfo:\r
3640     // Informs the driver whether the\r
3641     // asioCallbacks.bufferSwitchTimeInfo() callback is supported.\r
3642     // For compatibility with ASIO 1.0 drivers the host application\r
3643     // should always support the "old" bufferSwitch method, too.\r
3644     ret = 0;\r
3645     break;\r
3646   case kAsioSupportsTimeCode:\r
3647     // Informs the driver whether application is interested in time\r
3648     // code info.  If an application does not need to know about time\r
3649     // code, the driver has less work to do.\r
3650     ret = 0;\r
3651     break;\r
3652   }\r
3653   return ret;\r
3654 }\r
3655 \r
3656 static const char* getAsioErrorString( ASIOError result )\r
3657 {\r
3658   struct Messages\r
3659   {\r
3660     ASIOError value;\r
3661     const char*message;\r
3662   };\r
3663 \r
3664   static const Messages m[] =\r
3665     {\r
3666       {   ASE_NotPresent,    "Hardware input or output is not present or available." },\r
3667       {   ASE_HWMalfunction,  "Hardware is malfunctioning." },\r
3668       {   ASE_InvalidParameter, "Invalid input parameter." },\r
3669       {   ASE_InvalidMode,      "Invalid mode." },\r
3670       {   ASE_SPNotAdvancing,     "Sample position not advancing." },\r
3671       {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },\r
3672       {   ASE_NoMemory,           "Not enough memory to complete the request." }\r
3673     };\r
3674 \r
3675   for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )\r
3676     if ( m[i].value == result ) return m[i].message;\r
3677 \r
3678   return "Unknown error.";\r
3679 }\r
3680 \r
3681 //******************** End of __WINDOWS_ASIO__ *********************//\r
3682 #endif\r
3683 \r
3684 \r
3685 #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
3686 \r
3687 // Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
3688 // - Introduces support for the Windows WASAPI API\r
3689 // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
3690 // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
3691 // - Includes automatic internal conversion of sample rate and buffer size between hardware and the user\r
3692 \r
3693 #ifndef INITGUID\r
3694   #define INITGUID\r
3695 #endif\r
3696 #include <audioclient.h>\r
3697 #include <avrt.h>\r
3698 #include <mmdeviceapi.h>\r
3699 #include <FunctionDiscoveryKeys_devpkey.h>\r
3700 \r
3701 //=============================================================================\r
3702 \r
3703 #define SAFE_RELEASE( objectPtr )\\r
3704 if ( objectPtr )\\r
3705 {\\r
3706   objectPtr->Release();\\r
3707   objectPtr = NULL;\\r
3708 }\r
3709 \r
3710 typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
3711 \r
3712 //-----------------------------------------------------------------------------\r
3713 \r
3714 // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
3715 // Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
3716 // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
3717 // provide intermediate storage for read / write synchronization.\r
3718 class WasapiBuffer\r
3719 {\r
3720 public:\r
3721   WasapiBuffer()\r
3722     : buffer_( NULL ),\r
3723       bufferSize_( 0 ),\r
3724       inIndex_( 0 ),\r
3725       outIndex_( 0 ) {}\r
3726 \r
3727   ~WasapiBuffer() {\r
3728     free( buffer_ );\r
3729   }\r
3730 \r
3731   // sets the length of the internal ring buffer\r
3732   void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
3733     free( buffer_ );\r
3734 \r
3735     buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
3736 \r
3737     bufferSize_ = bufferSize;\r
3738     inIndex_ = 0;\r
3739     outIndex_ = 0;\r
3740   }\r
3741 \r
3742   // attempt to push a buffer into the ring buffer at the current "in" index\r
3743   bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3744   {\r
3745     if ( !buffer ||                 // incoming buffer is NULL\r
3746          bufferSize == 0 ||         // incoming buffer has no data\r
3747          bufferSize > bufferSize_ ) // incoming buffer too large\r
3748     {\r
3749       return false;\r
3750     }\r
3751 \r
3752     unsigned int relOutIndex = outIndex_;\r
3753     unsigned int inIndexEnd = inIndex_ + bufferSize;\r
3754     if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
3755       relOutIndex += bufferSize_;\r
3756     }\r
3757 \r
3758     // "in" index can end on the "out" index but cannot begin at it\r
3759     if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
3760       return false; // not enough space between "in" index and "out" index\r
3761     }\r
3762 \r
3763     // copy buffer from external to internal\r
3764     int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
3765     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3766     int fromInSize = bufferSize - fromZeroSize;\r
3767 \r
3768     switch( format )\r
3769       {\r
3770       case RTAUDIO_SINT8:\r
3771         memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
3772         memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
3773         break;\r
3774       case RTAUDIO_SINT16:\r
3775         memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
3776         memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
3777         break;\r
3778       case RTAUDIO_SINT24:\r
3779         memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
3780         memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
3781         break;\r
3782       case RTAUDIO_SINT32:\r
3783         memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
3784         memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
3785         break;\r
3786       case RTAUDIO_FLOAT32:\r
3787         memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
3788         memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
3789         break;\r
3790       case RTAUDIO_FLOAT64:\r
3791         memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
3792         memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
3793         break;\r
3794     }\r
3795 \r
3796     // update "in" index\r
3797     inIndex_ += bufferSize;\r
3798     inIndex_ %= bufferSize_;\r
3799 \r
3800     return true;\r
3801   }\r
3802 \r
3803   // attempt to pull a buffer from the ring buffer from the current "out" index\r
3804   bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
3805   {\r
3806     if ( !buffer ||                 // incoming buffer is NULL\r
3807          bufferSize == 0 ||         // incoming buffer has no data\r
3808          bufferSize > bufferSize_ ) // incoming buffer too large\r
3809     {\r
3810       return false;\r
3811     }\r
3812 \r
3813     unsigned int relInIndex = inIndex_;\r
3814     unsigned int outIndexEnd = outIndex_ + bufferSize;\r
3815     if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
3816       relInIndex += bufferSize_;\r
3817     }\r
3818 \r
3819     // "out" index can begin at and end on the "in" index\r
3820     if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
3821       return false; // not enough space between "out" index and "in" index\r
3822     }\r
3823 \r
3824     // copy buffer from internal to external\r
3825     int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
3826     fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
3827     int fromOutSize = bufferSize - fromZeroSize;\r
3828 \r
3829     switch( format )\r
3830     {\r
3831       case RTAUDIO_SINT8:\r
3832         memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
3833         memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
3834         break;\r
3835       case RTAUDIO_SINT16:\r
3836         memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
3837         memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
3838         break;\r
3839       case RTAUDIO_SINT24:\r
3840         memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
3841         memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
3842         break;\r
3843       case RTAUDIO_SINT32:\r
3844         memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
3845         memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
3846         break;\r
3847       case RTAUDIO_FLOAT32:\r
3848         memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
3849         memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
3850         break;\r
3851       case RTAUDIO_FLOAT64:\r
3852         memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
3853         memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
3854         break;\r
3855     }\r
3856 \r
3857     // update "out" index\r
3858     outIndex_ += bufferSize;\r
3859     outIndex_ %= bufferSize_;\r
3860 \r
3861     return true;\r
3862   }\r
3863 \r
3864 private:\r
3865   char* buffer_;\r
3866   unsigned int bufferSize_;\r
3867   unsigned int inIndex_;\r
3868   unsigned int outIndex_;\r
3869 };\r
3870 \r
3871 //-----------------------------------------------------------------------------\r
3872 \r
3873 // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate\r
3874 // between HW and the user. The convertBufferWasapi function is used to perform this conversion\r
3875 // between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
3876 // This sample rate converter favors speed over quality, and works best with conversions between\r
3877 // one rate and its multiple.\r
3878 void convertBufferWasapi( char* outBuffer,\r
3879                           const char* inBuffer,\r
3880                           const unsigned int& channelCount,\r
3881                           const unsigned int& inSampleRate,\r
3882                           const unsigned int& outSampleRate,\r
3883                           const unsigned int& inSampleCount,\r
3884                           unsigned int& outSampleCount,\r
3885                           const RtAudioFormat& format )\r
3886 {\r
3887   // calculate the new outSampleCount and relative sampleStep\r
3888   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
3889   float sampleStep = 1.0f / sampleRatio;\r
3890   float inSampleFraction = 0.0f;\r
3891 \r
3892   outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio );\r
3893 \r
3894   // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
3895   for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
3896   {\r
3897     unsigned int inSample = ( unsigned int ) inSampleFraction;\r
3898 \r
3899     switch ( format )\r
3900     {\r
3901       case RTAUDIO_SINT8:\r
3902         memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
3903         break;\r
3904       case RTAUDIO_SINT16:\r
3905         memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
3906         break;\r
3907       case RTAUDIO_SINT24:\r
3908         memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
3909         break;\r
3910       case RTAUDIO_SINT32:\r
3911         memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
3912         break;\r
3913       case RTAUDIO_FLOAT32:\r
3914         memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
3915         break;\r
3916       case RTAUDIO_FLOAT64:\r
3917         memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
3918         break;\r
3919     }\r
3920 \r
3921     // jump to next in sample\r
3922     inSampleFraction += sampleStep;\r
3923   }\r
3924 }\r
3925 \r
3926 //-----------------------------------------------------------------------------\r
3927 \r
3928 // A structure to hold various information related to the WASAPI implementation.\r
3929 struct WasapiHandle\r
3930 {\r
3931   IAudioClient* captureAudioClient;\r
3932   IAudioClient* renderAudioClient;\r
3933   IAudioCaptureClient* captureClient;\r
3934   IAudioRenderClient* renderClient;\r
3935   HANDLE captureEvent;\r
3936   HANDLE renderEvent;\r
3937 \r
3938   WasapiHandle()\r
3939   : captureAudioClient( NULL ),\r
3940     renderAudioClient( NULL ),\r
3941     captureClient( NULL ),\r
3942     renderClient( NULL ),\r
3943     captureEvent( NULL ),\r
3944     renderEvent( NULL ) {}\r
3945 };\r
3946 \r
3947 //=============================================================================\r
3948 \r
3949 RtApiWasapi::RtApiWasapi()\r
3950   : coInitialized_( false ), deviceEnumerator_( NULL )\r
3951 {\r
3952   // WASAPI can run either apartment or multi-threaded\r
3953   HRESULT hr = CoInitialize( NULL );\r
3954   if ( !FAILED( hr ) )\r
3955     coInitialized_ = true;\r
3956 \r
3957   // Instantiate device enumerator\r
3958   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
3959                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
3960                          ( void** ) &deviceEnumerator_ );\r
3961 \r
3962   if ( FAILED( hr ) ) {\r
3963     errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
3964     error( RtAudioError::DRIVER_ERROR );\r
3965   }\r
3966 }\r
3967 \r
3968 //-----------------------------------------------------------------------------\r
3969 \r
3970 RtApiWasapi::~RtApiWasapi()\r
3971 {\r
3972   if ( stream_.state != STREAM_CLOSED )\r
3973     closeStream();\r
3974 \r
3975   SAFE_RELEASE( deviceEnumerator_ );\r
3976 \r
3977   // If this object previously called CoInitialize()\r
3978   if ( coInitialized_ )\r
3979     CoUninitialize();\r
3980 }\r
3981 \r
3982 //=============================================================================\r
3983 \r
3984 unsigned int RtApiWasapi::getDeviceCount( void )\r
3985 {\r
3986   unsigned int captureDeviceCount = 0;\r
3987   unsigned int renderDeviceCount = 0;\r
3988 \r
3989   IMMDeviceCollection* captureDevices = NULL;\r
3990   IMMDeviceCollection* renderDevices = NULL;\r
3991 \r
3992   // Count capture devices\r
3993   errorText_.clear();\r
3994   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
3995   if ( FAILED( hr ) ) {\r
3996     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
3997     goto Exit;\r
3998   }\r
3999 \r
4000   hr = captureDevices->GetCount( &captureDeviceCount );\r
4001   if ( FAILED( hr ) ) {\r
4002     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
4003     goto Exit;\r
4004   }\r
4005 \r
4006   // Count render devices\r
4007   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4008   if ( FAILED( hr ) ) {\r
4009     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
4010     goto Exit;\r
4011   }\r
4012 \r
4013   hr = renderDevices->GetCount( &renderDeviceCount );\r
4014   if ( FAILED( hr ) ) {\r
4015     errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
4016     goto Exit;\r
4017   }\r
4018 \r
4019 Exit:\r
4020   // release all references\r
4021   SAFE_RELEASE( captureDevices );\r
4022   SAFE_RELEASE( renderDevices );\r
4023 \r
4024   if ( errorText_.empty() )\r
4025     return captureDeviceCount + renderDeviceCount;\r
4026 \r
4027   error( RtAudioError::DRIVER_ERROR );\r
4028   return 0;\r
4029 }\r
4030 \r
4031 //-----------------------------------------------------------------------------\r
4032 \r
4033 RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
4034 {\r
4035   RtAudio::DeviceInfo info;\r
4036   unsigned int captureDeviceCount = 0;\r
4037   unsigned int renderDeviceCount = 0;\r
4038   std::string defaultDeviceName;\r
4039   bool isCaptureDevice = false;\r
4040 \r
4041   PROPVARIANT deviceNameProp;\r
4042   PROPVARIANT defaultDeviceNameProp;\r
4043 \r
4044   IMMDeviceCollection* captureDevices = NULL;\r
4045   IMMDeviceCollection* renderDevices = NULL;\r
4046   IMMDevice* devicePtr = NULL;\r
4047   IMMDevice* defaultDevicePtr = NULL;\r
4048   IAudioClient* audioClient = NULL;\r
4049   IPropertyStore* devicePropStore = NULL;\r
4050   IPropertyStore* defaultDevicePropStore = NULL;\r
4051 \r
4052   WAVEFORMATEX* deviceFormat = NULL;\r
4053   WAVEFORMATEX* closestMatchFormat = NULL;\r
4054 \r
4055   // probed\r
4056   info.probed = false;\r
4057 \r
4058   // Count capture devices\r
4059   errorText_.clear();\r
4060   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4061   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4062   if ( FAILED( hr ) ) {\r
4063     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
4064     goto Exit;\r
4065   }\r
4066 \r
4067   hr = captureDevices->GetCount( &captureDeviceCount );\r
4068   if ( FAILED( hr ) ) {\r
4069     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
4070     goto Exit;\r
4071   }\r
4072 \r
4073   // Count render devices\r
4074   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4075   if ( FAILED( hr ) ) {\r
4076     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
4077     goto Exit;\r
4078   }\r
4079 \r
4080   hr = renderDevices->GetCount( &renderDeviceCount );\r
4081   if ( FAILED( hr ) ) {\r
4082     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
4083     goto Exit;\r
4084   }\r
4085 \r
4086   // validate device index\r
4087   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4088     errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
4089     errorType = RtAudioError::INVALID_USE;\r
4090     goto Exit;\r
4091   }\r
4092 \r
4093   // determine whether index falls within capture or render devices\r
4094   if ( device >= renderDeviceCount ) {\r
4095     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4096     if ( FAILED( hr ) ) {\r
4097       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
4098       goto Exit;\r
4099     }\r
4100     isCaptureDevice = true;\r
4101   }\r
4102   else {\r
4103     hr = renderDevices->Item( device, &devicePtr );\r
4104     if ( FAILED( hr ) ) {\r
4105       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
4106       goto Exit;\r
4107     }\r
4108     isCaptureDevice = false;\r
4109   }\r
4110 \r
4111   // get default device name\r
4112   if ( isCaptureDevice ) {\r
4113     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
4114     if ( FAILED( hr ) ) {\r
4115       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
4116       goto Exit;\r
4117     }\r
4118   }\r
4119   else {\r
4120     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
4121     if ( FAILED( hr ) ) {\r
4122       errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
4123       goto Exit;\r
4124     }\r
4125   }\r
4126 \r
4127   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
4128   if ( FAILED( hr ) ) {\r
4129     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
4130     goto Exit;\r
4131   }\r
4132   PropVariantInit( &defaultDeviceNameProp );\r
4133 \r
4134   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
4135   if ( FAILED( hr ) ) {\r
4136     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
4137     goto Exit;\r
4138   }\r
4139 \r
4140   defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);\r
4141 \r
4142   // name\r
4143   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
4144   if ( FAILED( hr ) ) {\r
4145     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
4146     goto Exit;\r
4147   }\r
4148 \r
4149   PropVariantInit( &deviceNameProp );\r
4150 \r
4151   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
4152   if ( FAILED( hr ) ) {\r
4153     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
4154     goto Exit;\r
4155   }\r
4156 \r
4157   info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);\r
4158 \r
4159   // is default\r
4160   if ( isCaptureDevice ) {\r
4161     info.isDefaultInput = info.name == defaultDeviceName;\r
4162     info.isDefaultOutput = false;\r
4163   }\r
4164   else {\r
4165     info.isDefaultInput = false;\r
4166     info.isDefaultOutput = info.name == defaultDeviceName;\r
4167   }\r
4168 \r
4169   // channel count\r
4170   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
4171   if ( FAILED( hr ) ) {\r
4172     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
4173     goto Exit;\r
4174   }\r
4175 \r
4176   hr = audioClient->GetMixFormat( &deviceFormat );\r
4177   if ( FAILED( hr ) ) {\r
4178     errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
4179     goto Exit;\r
4180   }\r
4181 \r
4182   if ( isCaptureDevice ) {\r
4183     info.inputChannels = deviceFormat->nChannels;\r
4184     info.outputChannels = 0;\r
4185     info.duplexChannels = 0;\r
4186   }\r
4187   else {\r
4188     info.inputChannels = 0;\r
4189     info.outputChannels = deviceFormat->nChannels;\r
4190     info.duplexChannels = 0;\r
4191   }\r
4192 \r
4193   // sample rates\r
4194   info.sampleRates.clear();\r
4195 \r
4196   // allow support for all sample rates as we have a built-in sample rate converter\r
4197   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
4198     info.sampleRates.push_back( SAMPLE_RATES[i] );\r
4199   }\r
4200   info.preferredSampleRate = deviceFormat->nSamplesPerSec;\r
4201 \r
4202   // native format\r
4203   info.nativeFormats = 0;\r
4204 \r
4205   if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
4206        ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4207          ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
4208   {\r
4209     if ( deviceFormat->wBitsPerSample == 32 ) {\r
4210       info.nativeFormats |= RTAUDIO_FLOAT32;\r
4211     }\r
4212     else if ( deviceFormat->wBitsPerSample == 64 ) {\r
4213       info.nativeFormats |= RTAUDIO_FLOAT64;\r
4214     }\r
4215   }\r
4216   else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
4217            ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
4218              ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
4219   {\r
4220     if ( deviceFormat->wBitsPerSample == 8 ) {\r
4221       info.nativeFormats |= RTAUDIO_SINT8;\r
4222     }\r
4223     else if ( deviceFormat->wBitsPerSample == 16 ) {\r
4224       info.nativeFormats |= RTAUDIO_SINT16;\r
4225     }\r
4226     else if ( deviceFormat->wBitsPerSample == 24 ) {\r
4227       info.nativeFormats |= RTAUDIO_SINT24;\r
4228     }\r
4229     else if ( deviceFormat->wBitsPerSample == 32 ) {\r
4230       info.nativeFormats |= RTAUDIO_SINT32;\r
4231     }\r
4232   }\r
4233 \r
4234   // probed\r
4235   info.probed = true;\r
4236 \r
4237 Exit:\r
4238   // release all references\r
4239   PropVariantClear( &deviceNameProp );\r
4240   PropVariantClear( &defaultDeviceNameProp );\r
4241 \r
4242   SAFE_RELEASE( captureDevices );\r
4243   SAFE_RELEASE( renderDevices );\r
4244   SAFE_RELEASE( devicePtr );\r
4245   SAFE_RELEASE( defaultDevicePtr );\r
4246   SAFE_RELEASE( audioClient );\r
4247   SAFE_RELEASE( devicePropStore );\r
4248   SAFE_RELEASE( defaultDevicePropStore );\r
4249 \r
4250   CoTaskMemFree( deviceFormat );\r
4251   CoTaskMemFree( closestMatchFormat );\r
4252 \r
4253   if ( !errorText_.empty() )\r
4254     error( errorType );\r
4255   return info;\r
4256 }\r
4257 \r
4258 //-----------------------------------------------------------------------------\r
4259 \r
4260 unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
4261 {\r
4262   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4263     if ( getDeviceInfo( i ).isDefaultOutput ) {\r
4264       return i;\r
4265     }\r
4266   }\r
4267 \r
4268   return 0;\r
4269 }\r
4270 \r
4271 //-----------------------------------------------------------------------------\r
4272 \r
4273 unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
4274 {\r
4275   for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
4276     if ( getDeviceInfo( i ).isDefaultInput ) {\r
4277       return i;\r
4278     }\r
4279   }\r
4280 \r
4281   return 0;\r
4282 }\r
4283 \r
4284 //-----------------------------------------------------------------------------\r
4285 \r
4286 void RtApiWasapi::closeStream( void )\r
4287 {\r
4288   if ( stream_.state == STREAM_CLOSED ) {\r
4289     errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
4290     error( RtAudioError::WARNING );\r
4291     return;\r
4292   }\r
4293 \r
4294   if ( stream_.state != STREAM_STOPPED )\r
4295     stopStream();\r
4296 \r
4297   // clean up stream memory\r
4298   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
4299   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
4300 \r
4301   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
4302   SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
4303 \r
4304   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
4305     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
4306 \r
4307   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
4308     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
4309 \r
4310   delete ( WasapiHandle* ) stream_.apiHandle;\r
4311   stream_.apiHandle = NULL;\r
4312 \r
4313   for ( int i = 0; i < 2; i++ ) {\r
4314     if ( stream_.userBuffer[i] ) {\r
4315       free( stream_.userBuffer[i] );\r
4316       stream_.userBuffer[i] = 0;\r
4317     }\r
4318   }\r
4319 \r
4320   if ( stream_.deviceBuffer ) {\r
4321     free( stream_.deviceBuffer );\r
4322     stream_.deviceBuffer = 0;\r
4323   }\r
4324 \r
4325   // update stream state\r
4326   stream_.state = STREAM_CLOSED;\r
4327 }\r
4328 \r
4329 //-----------------------------------------------------------------------------\r
4330 \r
4331 void RtApiWasapi::startStream( void )\r
4332 {\r
4333   verifyStream();\r
4334   RtApi::startStream();\r
4335 \r
4336   if ( stream_.state == STREAM_RUNNING ) {\r
4337     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
4338     error( RtAudioError::WARNING );\r
4339     return;\r
4340   }\r
4341 \r
4342   // update stream state\r
4343   stream_.state = STREAM_RUNNING;\r
4344 \r
4345   // create WASAPI stream thread\r
4346   stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
4347 \r
4348   if ( !stream_.callbackInfo.thread ) {\r
4349     errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
4350     error( RtAudioError::THREAD_ERROR );\r
4351   }\r
4352   else {\r
4353     SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
4354     ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
4355   }\r
4356 }\r
4357 \r
4358 //-----------------------------------------------------------------------------\r
4359 \r
4360 void RtApiWasapi::stopStream( void )\r
4361 {\r
4362   verifyStream();\r
4363 \r
4364   if ( stream_.state == STREAM_STOPPED ) {\r
4365     errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
4366     error( RtAudioError::WARNING );\r
4367     return;\r
4368   }\r
4369 \r
4370   // inform stream thread by setting stream state to STREAM_STOPPING\r
4371   stream_.state = STREAM_STOPPING;\r
4372 \r
4373   // wait until stream thread is stopped\r
4374   while( stream_.state != STREAM_STOPPED ) {\r
4375     Sleep( 1 );\r
4376   }\r
4377 \r
4378   // Wait for the last buffer to play before stopping.\r
4379   Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
4380 \r
4381   // stop capture client if applicable\r
4382   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4383     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4384     if ( FAILED( hr ) ) {\r
4385       errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
4386       error( RtAudioError::DRIVER_ERROR );\r
4387       return;\r
4388     }\r
4389   }\r
4390 \r
4391   // stop render client if applicable\r
4392   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4393     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4394     if ( FAILED( hr ) ) {\r
4395       errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
4396       error( RtAudioError::DRIVER_ERROR );\r
4397       return;\r
4398     }\r
4399   }\r
4400 \r
4401   // close thread handle\r
4402   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4403     errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
4404     error( RtAudioError::THREAD_ERROR );\r
4405     return;\r
4406   }\r
4407 \r
4408   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4409 }\r
4410 \r
4411 //-----------------------------------------------------------------------------\r
4412 \r
4413 void RtApiWasapi::abortStream( void )\r
4414 {\r
4415   verifyStream();\r
4416 \r
4417   if ( stream_.state == STREAM_STOPPED ) {\r
4418     errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
4419     error( RtAudioError::WARNING );\r
4420     return;\r
4421   }\r
4422 \r
4423   // inform stream thread by setting stream state to STREAM_STOPPING\r
4424   stream_.state = STREAM_STOPPING;\r
4425 \r
4426   // wait until stream thread is stopped\r
4427   while ( stream_.state != STREAM_STOPPED ) {\r
4428     Sleep( 1 );\r
4429   }\r
4430 \r
4431   // stop capture client if applicable\r
4432   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
4433     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
4434     if ( FAILED( hr ) ) {\r
4435       errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
4436       error( RtAudioError::DRIVER_ERROR );\r
4437       return;\r
4438     }\r
4439   }\r
4440 \r
4441   // stop render client if applicable\r
4442   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
4443     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
4444     if ( FAILED( hr ) ) {\r
4445       errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
4446       error( RtAudioError::DRIVER_ERROR );\r
4447       return;\r
4448     }\r
4449   }\r
4450 \r
4451   // close thread handle\r
4452   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
4453     errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
4454     error( RtAudioError::THREAD_ERROR );\r
4455     return;\r
4456   }\r
4457 \r
4458   stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
4459 }\r
4460 \r
4461 //-----------------------------------------------------------------------------\r
4462 \r
4463 bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
4464                                    unsigned int firstChannel, unsigned int sampleRate,\r
4465                                    RtAudioFormat format, unsigned int* bufferSize,\r
4466                                    RtAudio::StreamOptions* options )\r
4467 {\r
4468   bool methodResult = FAILURE;\r
4469   unsigned int captureDeviceCount = 0;\r
4470   unsigned int renderDeviceCount = 0;\r
4471 \r
4472   IMMDeviceCollection* captureDevices = NULL;\r
4473   IMMDeviceCollection* renderDevices = NULL;\r
4474   IMMDevice* devicePtr = NULL;\r
4475   WAVEFORMATEX* deviceFormat = NULL;\r
4476   unsigned int bufferBytes;\r
4477   stream_.state = STREAM_STOPPED;\r
4478 \r
4479   // create API Handle if not already created\r
4480   if ( !stream_.apiHandle )\r
4481     stream_.apiHandle = ( void* ) new WasapiHandle();\r
4482 \r
4483   // Count capture devices\r
4484   errorText_.clear();\r
4485   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4486   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
4487   if ( FAILED( hr ) ) {\r
4488     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
4489     goto Exit;\r
4490   }\r
4491 \r
4492   hr = captureDevices->GetCount( &captureDeviceCount );\r
4493   if ( FAILED( hr ) ) {\r
4494     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
4495     goto Exit;\r
4496   }\r
4497 \r
4498   // Count render devices\r
4499   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
4500   if ( FAILED( hr ) ) {\r
4501     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
4502     goto Exit;\r
4503   }\r
4504 \r
4505   hr = renderDevices->GetCount( &renderDeviceCount );\r
4506   if ( FAILED( hr ) ) {\r
4507     errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
4508     goto Exit;\r
4509   }\r
4510 \r
4511   // validate device index\r
4512   if ( device >= captureDeviceCount + renderDeviceCount ) {\r
4513     errorType = RtAudioError::INVALID_USE;\r
4514     errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
4515     goto Exit;\r
4516   }\r
4517 \r
4518   // determine whether index falls within capture or render devices\r
4519   if ( device >= renderDeviceCount ) {\r
4520     if ( mode != INPUT ) {\r
4521       errorType = RtAudioError::INVALID_USE;\r
4522       errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
4523       goto Exit;\r
4524     }\r
4525 \r
4526     // retrieve captureAudioClient from devicePtr\r
4527     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4528 \r
4529     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
4530     if ( FAILED( hr ) ) {\r
4531       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
4532       goto Exit;\r
4533     }\r
4534 \r
4535     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4536                               NULL, ( void** ) &captureAudioClient );\r
4537     if ( FAILED( hr ) ) {\r
4538       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4539       goto Exit;\r
4540     }\r
4541 \r
4542     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
4543     if ( FAILED( hr ) ) {\r
4544       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4545       goto Exit;\r
4546     }\r
4547 \r
4548     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4549     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4550   }\r
4551   else {\r
4552     if ( mode != OUTPUT ) {\r
4553       errorType = RtAudioError::INVALID_USE;\r
4554       errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
4555       goto Exit;\r
4556     }\r
4557 \r
4558     // retrieve renderAudioClient from devicePtr\r
4559     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4560 \r
4561     hr = renderDevices->Item( device, &devicePtr );\r
4562     if ( FAILED( hr ) ) {\r
4563       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
4564       goto Exit;\r
4565     }\r
4566 \r
4567     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
4568                               NULL, ( void** ) &renderAudioClient );\r
4569     if ( FAILED( hr ) ) {\r
4570       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
4571       goto Exit;\r
4572     }\r
4573 \r
4574     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
4575     if ( FAILED( hr ) ) {\r
4576       errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
4577       goto Exit;\r
4578     }\r
4579 \r
4580     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
4581     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
4582   }\r
4583 \r
4584   // fill stream data\r
4585   if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
4586        ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
4587     stream_.mode = DUPLEX;\r
4588   }\r
4589   else {\r
4590     stream_.mode = mode;\r
4591   }\r
4592 \r
4593   stream_.device[mode] = device;\r
4594   stream_.doByteSwap[mode] = false;\r
4595   stream_.sampleRate = sampleRate;\r
4596   stream_.bufferSize = *bufferSize;\r
4597   stream_.nBuffers = 1;\r
4598   stream_.nUserChannels[mode] = channels;\r
4599   stream_.channelOffset[mode] = firstChannel;\r
4600   stream_.userFormat = format;\r
4601   stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
4602 \r
4603   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
4604     stream_.userInterleaved = false;\r
4605   else\r
4606     stream_.userInterleaved = true;\r
4607   stream_.deviceInterleaved[mode] = true;\r
4608 \r
4609   // Set flags for buffer conversion.\r
4610   stream_.doConvertBuffer[mode] = false;\r
4611   if ( stream_.userFormat != stream_.deviceFormat[mode] ||\r
4612        stream_.nUserChannels != stream_.nDeviceChannels )\r
4613     stream_.doConvertBuffer[mode] = true;\r
4614   else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
4615             stream_.nUserChannels[mode] > 1 )\r
4616     stream_.doConvertBuffer[mode] = true;\r
4617 \r
4618   if ( stream_.doConvertBuffer[mode] )\r
4619     setConvertInfo( mode, 0 );\r
4620 \r
4621   // Allocate necessary internal buffers\r
4622   bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
4623 \r
4624   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
4625   if ( !stream_.userBuffer[mode] ) {\r
4626     errorType = RtAudioError::MEMORY_ERROR;\r
4627     errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
4628     goto Exit;\r
4629   }\r
4630 \r
4631   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
4632     stream_.callbackInfo.priority = 15;\r
4633   else\r
4634     stream_.callbackInfo.priority = 0;\r
4635 \r
4636   ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
4637   ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
4638 \r
4639   methodResult = SUCCESS;\r
4640 \r
4641 Exit:\r
4642   //clean up\r
4643   SAFE_RELEASE( captureDevices );\r
4644   SAFE_RELEASE( renderDevices );\r
4645   SAFE_RELEASE( devicePtr );\r
4646   CoTaskMemFree( deviceFormat );\r
4647 \r
4648   // if method failed, close the stream\r
4649   if ( methodResult == FAILURE )\r
4650     closeStream();\r
4651 \r
4652   if ( !errorText_.empty() )\r
4653     error( errorType );\r
4654   return methodResult;\r
4655 }\r
4656 \r
4657 //=============================================================================\r
4658 \r
4659 DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
4660 {\r
4661   if ( wasapiPtr )\r
4662     ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
4663 \r
4664   return 0;\r
4665 }\r
4666 \r
4667 DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
4668 {\r
4669   if ( wasapiPtr )\r
4670     ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
4671 \r
4672   return 0;\r
4673 }\r
4674 \r
4675 DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
4676 {\r
4677   if ( wasapiPtr )\r
4678     ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
4679 \r
4680   return 0;\r
4681 }\r
4682 \r
4683 //-----------------------------------------------------------------------------\r
4684 \r
4685 void RtApiWasapi::wasapiThread()\r
4686 {\r
4687   // as this is a new thread, we must CoInitialize it\r
4688   CoInitialize( NULL );\r
4689 \r
4690   HRESULT hr;\r
4691 \r
4692   IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
4693   IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
4694   IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
4695   IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
4696   HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
4697   HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
4698 \r
4699   WAVEFORMATEX* captureFormat = NULL;\r
4700   WAVEFORMATEX* renderFormat = NULL;\r
4701   float captureSrRatio = 0.0f;\r
4702   float renderSrRatio = 0.0f;\r
4703   WasapiBuffer captureBuffer;\r
4704   WasapiBuffer renderBuffer;\r
4705 \r
4706   // declare local stream variables\r
4707   RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
4708   BYTE* streamBuffer = NULL;\r
4709   unsigned long captureFlags = 0;\r
4710   unsigned int bufferFrameCount = 0;\r
4711   unsigned int numFramesPadding = 0;\r
4712   unsigned int convBufferSize = 0;\r
4713   bool callbackPushed = false;\r
4714   bool callbackPulled = false;\r
4715   bool callbackStopped = false;\r
4716   int callbackResult = 0;\r
4717 \r
4718   // convBuffer is used to store converted buffers between WASAPI and the user\r
4719   char* convBuffer = NULL;\r
4720   unsigned int convBuffSize = 0;\r
4721   unsigned int deviceBuffSize = 0;\r
4722 \r
4723   errorText_.clear();\r
4724   RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
4725 \r
4726   // Attempt to assign "Pro Audio" characteristic to thread\r
4727   HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );\r
4728   if ( AvrtDll ) {\r
4729     DWORD taskIndex = 0;\r
4730     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
4731     AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
4732     FreeLibrary( AvrtDll );\r
4733   }\r
4734 \r
4735   // start capture stream if applicable\r
4736   if ( captureAudioClient ) {\r
4737     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
4738     if ( FAILED( hr ) ) {\r
4739       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4740       goto Exit;\r
4741     }\r
4742 \r
4743     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
4744 \r
4745     // initialize capture stream according to desire buffer size\r
4746     float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
4747     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
4748 \r
4749     if ( !captureClient ) {\r
4750       hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4751                                            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4752                                            desiredBufferPeriod,\r
4753                                            desiredBufferPeriod,\r
4754                                            captureFormat,\r
4755                                            NULL );\r
4756       if ( FAILED( hr ) ) {\r
4757         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
4758         goto Exit;\r
4759       }\r
4760 \r
4761       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
4762                                            ( void** ) &captureClient );\r
4763       if ( FAILED( hr ) ) {\r
4764         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
4765         goto Exit;\r
4766       }\r
4767 \r
4768       // configure captureEvent to trigger on every available capture buffer\r
4769       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4770       if ( !captureEvent ) {\r
4771         errorType = RtAudioError::SYSTEM_ERROR;\r
4772         errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
4773         goto Exit;\r
4774       }\r
4775 \r
4776       hr = captureAudioClient->SetEventHandle( captureEvent );\r
4777       if ( FAILED( hr ) ) {\r
4778         errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
4779         goto Exit;\r
4780       }\r
4781 \r
4782       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
4783       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
4784     }\r
4785 \r
4786     unsigned int inBufferSize = 0;\r
4787     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
4788     if ( FAILED( hr ) ) {\r
4789       errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
4790       goto Exit;\r
4791     }\r
4792 \r
4793     // scale outBufferSize according to stream->user sample rate ratio\r
4794     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
4795     inBufferSize *= stream_.nDeviceChannels[INPUT];\r
4796 \r
4797     // set captureBuffer size\r
4798     captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
4799 \r
4800     // reset the capture stream\r
4801     hr = captureAudioClient->Reset();\r
4802     if ( FAILED( hr ) ) {\r
4803       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
4804       goto Exit;\r
4805     }\r
4806 \r
4807     // start the capture stream\r
4808     hr = captureAudioClient->Start();\r
4809     if ( FAILED( hr ) ) {\r
4810       errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
4811       goto Exit;\r
4812     }\r
4813   }\r
4814 \r
4815   // start render stream if applicable\r
4816   if ( renderAudioClient ) {\r
4817     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
4818     if ( FAILED( hr ) ) {\r
4819       errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
4820       goto Exit;\r
4821     }\r
4822 \r
4823     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
4824 \r
4825     // initialize render stream according to desire buffer size\r
4826     float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
4827     REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
4828 \r
4829     if ( !renderClient ) {\r
4830       hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
4831                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
4832                                           desiredBufferPeriod,\r
4833                                           desiredBufferPeriod,\r
4834                                           renderFormat,\r
4835                                           NULL );\r
4836       if ( FAILED( hr ) ) {\r
4837         errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
4838         goto Exit;\r
4839       }\r
4840 \r
4841       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
4842                                           ( void** ) &renderClient );\r
4843       if ( FAILED( hr ) ) {\r
4844         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
4845         goto Exit;\r
4846       }\r
4847 \r
4848       // configure renderEvent to trigger on every available render buffer\r
4849       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
4850       if ( !renderEvent ) {\r
4851         errorType = RtAudioError::SYSTEM_ERROR;\r
4852         errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
4853         goto Exit;\r
4854       }\r
4855 \r
4856       hr = renderAudioClient->SetEventHandle( renderEvent );\r
4857       if ( FAILED( hr ) ) {\r
4858         errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
4859         goto Exit;\r
4860       }\r
4861 \r
4862       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
4863       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
4864     }\r
4865 \r
4866     unsigned int outBufferSize = 0;\r
4867     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
4868     if ( FAILED( hr ) ) {\r
4869       errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
4870       goto Exit;\r
4871     }\r
4872 \r
4873     // scale inBufferSize according to user->stream sample rate ratio\r
4874     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
4875     outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
4876 \r
4877     // set renderBuffer size\r
4878     renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4879 \r
4880     // reset the render stream\r
4881     hr = renderAudioClient->Reset();\r
4882     if ( FAILED( hr ) ) {\r
4883       errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
4884       goto Exit;\r
4885     }\r
4886 \r
4887     // start the render stream\r
4888     hr = renderAudioClient->Start();\r
4889     if ( FAILED( hr ) ) {\r
4890       errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
4891       goto Exit;\r
4892     }\r
4893   }\r
4894 \r
4895   if ( stream_.mode == INPUT ) {\r
4896     convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4897     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
4898   }\r
4899   else if ( stream_.mode == OUTPUT ) {\r
4900     convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4901     deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
4902   }\r
4903   else if ( stream_.mode == DUPLEX ) {\r
4904     convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4905                              ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4906     deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
4907                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
4908   }\r
4909 \r
4910   convBuffer = ( char* ) malloc( convBuffSize );\r
4911   stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
4912   if ( !convBuffer || !stream_.deviceBuffer ) {\r
4913     errorType = RtAudioError::MEMORY_ERROR;\r
4914     errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
4915     goto Exit;\r
4916   }\r
4917 \r
4918   // stream process loop\r
4919   while ( stream_.state != STREAM_STOPPING ) {\r
4920     if ( !callbackPulled ) {\r
4921       // Callback Input\r
4922       // ==============\r
4923       // 1. Pull callback buffer from inputBuffer\r
4924       // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
4925       //                          Convert callback buffer to user format\r
4926 \r
4927       if ( captureAudioClient ) {\r
4928         // Pull callback buffer from inputBuffer\r
4929         callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
4930                                                    ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
4931                                                    stream_.deviceFormat[INPUT] );\r
4932 \r
4933         if ( callbackPulled ) {\r
4934           // Convert callback buffer to user sample rate\r
4935           convertBufferWasapi( stream_.deviceBuffer,\r
4936                                convBuffer,\r
4937                                stream_.nDeviceChannels[INPUT],\r
4938                                captureFormat->nSamplesPerSec,\r
4939                                stream_.sampleRate,\r
4940                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
4941                                convBufferSize,\r
4942                                stream_.deviceFormat[INPUT] );\r
4943 \r
4944           if ( stream_.doConvertBuffer[INPUT] ) {\r
4945             // Convert callback buffer to user format\r
4946             convertBuffer( stream_.userBuffer[INPUT],\r
4947                            stream_.deviceBuffer,\r
4948                            stream_.convertInfo[INPUT] );\r
4949           }\r
4950           else {\r
4951             // no further conversion, simple copy deviceBuffer to userBuffer\r
4952             memcpy( stream_.userBuffer[INPUT],\r
4953                     stream_.deviceBuffer,\r
4954                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
4955           }\r
4956         }\r
4957       }\r
4958       else {\r
4959         // if there is no capture stream, set callbackPulled flag\r
4960         callbackPulled = true;\r
4961       }\r
4962 \r
4963       // Execute Callback\r
4964       // ================\r
4965       // 1. Execute user callback method\r
4966       // 2. Handle return value from callback\r
4967 \r
4968       // if callback has not requested the stream to stop\r
4969       if ( callbackPulled && !callbackStopped ) {\r
4970         // Execute user callback method\r
4971         callbackResult = callback( stream_.userBuffer[OUTPUT],\r
4972                                    stream_.userBuffer[INPUT],\r
4973                                    stream_.bufferSize,\r
4974                                    getStreamTime(),\r
4975                                    captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
4976                                    stream_.callbackInfo.userData );\r
4977 \r
4978         // Handle return value from callback\r
4979         if ( callbackResult == 1 ) {\r
4980           // instantiate a thread to stop this thread\r
4981           HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
4982           if ( !threadHandle ) {\r
4983             errorType = RtAudioError::THREAD_ERROR;\r
4984             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
4985             goto Exit;\r
4986           }\r
4987           else if ( !CloseHandle( threadHandle ) ) {\r
4988             errorType = RtAudioError::THREAD_ERROR;\r
4989             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
4990             goto Exit;\r
4991           }\r
4992 \r
4993           callbackStopped = true;\r
4994         }\r
4995         else if ( callbackResult == 2 ) {\r
4996           // instantiate a thread to stop this thread\r
4997           HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
4998           if ( !threadHandle ) {\r
4999             errorType = RtAudioError::THREAD_ERROR;\r
5000             errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
5001             goto Exit;\r
5002           }\r
5003           else if ( !CloseHandle( threadHandle ) ) {\r
5004             errorType = RtAudioError::THREAD_ERROR;\r
5005             errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
5006             goto Exit;\r
5007           }\r
5008 \r
5009           callbackStopped = true;\r
5010         }\r
5011       }\r
5012     }\r
5013 \r
5014     // Callback Output\r
5015     // ===============\r
5016     // 1. Convert callback buffer to stream format\r
5017     // 2. Convert callback buffer to stream sample rate and channel count\r
5018     // 3. Push callback buffer into outputBuffer\r
5019 \r
5020     if ( renderAudioClient && callbackPulled ) {\r
5021       if ( stream_.doConvertBuffer[OUTPUT] ) {\r
5022         // Convert callback buffer to stream format\r
5023         convertBuffer( stream_.deviceBuffer,\r
5024                        stream_.userBuffer[OUTPUT],\r
5025                        stream_.convertInfo[OUTPUT] );\r
5026 \r
5027       }\r
5028 \r
5029       // Convert callback buffer to stream sample rate\r
5030       convertBufferWasapi( convBuffer,\r
5031                            stream_.deviceBuffer,\r
5032                            stream_.nDeviceChannels[OUTPUT],\r
5033                            stream_.sampleRate,\r
5034                            renderFormat->nSamplesPerSec,\r
5035                            stream_.bufferSize,\r
5036                            convBufferSize,\r
5037                            stream_.deviceFormat[OUTPUT] );\r
5038 \r
5039       // Push callback buffer into outputBuffer\r
5040       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
5041                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
5042                                                 stream_.deviceFormat[OUTPUT] );\r
5043     }\r
5044     else {\r
5045       // if there is no render stream, set callbackPushed flag\r
5046       callbackPushed = true;\r
5047     }\r
5048 \r
5049     // Stream Capture\r
5050     // ==============\r
5051     // 1. Get capture buffer from stream\r
5052     // 2. Push capture buffer into inputBuffer\r
5053     // 3. If 2. was successful: Release capture buffer\r
5054 \r
5055     if ( captureAudioClient ) {\r
5056       // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
5057       if ( !callbackPulled ) {\r
5058         WaitForSingleObject( captureEvent, INFINITE );\r
5059       }\r
5060 \r
5061       // Get capture buffer from stream\r
5062       hr = captureClient->GetBuffer( &streamBuffer,\r
5063                                      &bufferFrameCount,\r
5064                                      &captureFlags, NULL, NULL );\r
5065       if ( FAILED( hr ) ) {\r
5066         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
5067         goto Exit;\r
5068       }\r
5069 \r
5070       if ( bufferFrameCount != 0 ) {\r
5071         // Push capture buffer into inputBuffer\r
5072         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
5073                                        bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
5074                                        stream_.deviceFormat[INPUT] ) )\r
5075         {\r
5076           // Release capture buffer\r
5077           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
5078           if ( FAILED( hr ) ) {\r
5079             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5080             goto Exit;\r
5081           }\r
5082         }\r
5083         else\r
5084         {\r
5085           // Inform WASAPI that capture was unsuccessful\r
5086           hr = captureClient->ReleaseBuffer( 0 );\r
5087           if ( FAILED( hr ) ) {\r
5088             errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5089             goto Exit;\r
5090           }\r
5091         }\r
5092       }\r
5093       else\r
5094       {\r
5095         // Inform WASAPI that capture was unsuccessful\r
5096         hr = captureClient->ReleaseBuffer( 0 );\r
5097         if ( FAILED( hr ) ) {\r
5098           errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
5099           goto Exit;\r
5100         }\r
5101       }\r
5102     }\r
5103 \r
5104     // Stream Render\r
5105     // =============\r
5106     // 1. Get render buffer from stream\r
5107     // 2. Pull next buffer from outputBuffer\r
5108     // 3. If 2. was successful: Fill render buffer with next buffer\r
5109     //                          Release render buffer\r
5110 \r
5111     if ( renderAudioClient ) {\r
5112       // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
5113       if ( callbackPulled && !callbackPushed ) {\r
5114         WaitForSingleObject( renderEvent, INFINITE );\r
5115       }\r
5116 \r
5117       // Get render buffer from stream\r
5118       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
5119       if ( FAILED( hr ) ) {\r
5120         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
5121         goto Exit;\r
5122       }\r
5123 \r
5124       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
5125       if ( FAILED( hr ) ) {\r
5126         errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
5127         goto Exit;\r
5128       }\r
5129 \r
5130       bufferFrameCount -= numFramesPadding;\r
5131 \r
5132       if ( bufferFrameCount != 0 ) {\r
5133         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
5134         if ( FAILED( hr ) ) {\r
5135           errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
5136           goto Exit;\r
5137         }\r
5138 \r
5139         // Pull next buffer from outputBuffer\r
5140         // Fill render buffer with next buffer\r
5141         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
5142                                       bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
5143                                       stream_.deviceFormat[OUTPUT] ) )\r
5144         {\r
5145           // Release render buffer\r
5146           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
5147           if ( FAILED( hr ) ) {\r
5148             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5149             goto Exit;\r
5150           }\r
5151         }\r
5152         else\r
5153         {\r
5154           // Inform WASAPI that render was unsuccessful\r
5155           hr = renderClient->ReleaseBuffer( 0, 0 );\r
5156           if ( FAILED( hr ) ) {\r
5157             errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5158             goto Exit;\r
5159           }\r
5160         }\r
5161       }\r
5162       else\r
5163       {\r
5164         // Inform WASAPI that render was unsuccessful\r
5165         hr = renderClient->ReleaseBuffer( 0, 0 );\r
5166         if ( FAILED( hr ) ) {\r
5167           errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
5168           goto Exit;\r
5169         }\r
5170       }\r
5171     }\r
5172 \r
5173     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
5174     if ( callbackPushed ) {\r
5175       callbackPulled = false;\r
5176       // tick stream time\r
5177       RtApi::tickStreamTime();\r
5178     }\r
5179 \r
5180   }\r
5181 \r
5182 Exit:\r
5183   // clean up\r
5184   CoTaskMemFree( captureFormat );\r
5185   CoTaskMemFree( renderFormat );\r
5186 \r
5187   free ( convBuffer );\r
5188 \r
5189   CoUninitialize();\r
5190 \r
5191   // update stream state\r
5192   stream_.state = STREAM_STOPPED;\r
5193 \r
5194   if ( errorText_.empty() )\r
5195     return;\r
5196   else\r
5197     error( errorType );\r
5198 }\r
5199 \r
5200 //******************** End of __WINDOWS_WASAPI__ *********************//\r
5201 #endif\r
5202 \r
5203 \r
5204 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
5205 \r
5206 // Modified by Robin Davies, October 2005\r
5207 // - Improvements to DirectX pointer chasing.\r
5208 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
5209 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
5210 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
5211 // Changed device query structure for RtAudio 4.0.7, January 2010\r
5212 \r
5213 #include <dsound.h>\r
5214 #include <assert.h>\r
5215 #include <algorithm>\r
5216 \r
5217 #if defined(__MINGW32__)\r
5218   // missing from latest mingw winapi\r
5219 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
5220 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
5221 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
5222 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
5223 #endif\r
5224 \r
5225 #define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
5226 \r
5227 #ifdef _MSC_VER // if Microsoft Visual C++\r
5228 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
5229 #endif\r
5230 \r
5231 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
5232 {\r
5233   if ( pointer > bufferSize ) pointer -= bufferSize;\r
5234   if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
5235   if ( pointer < earlierPointer ) pointer += bufferSize;\r
5236   return pointer >= earlierPointer && pointer < laterPointer;\r
5237 }\r
5238 \r
5239 // A structure to hold various information related to the DirectSound\r
5240 // API implementation.\r
5241 struct DsHandle {\r
5242   unsigned int drainCounter; // Tracks callback counts when draining\r
5243   bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
5244   void *id[2];\r
5245   void *buffer[2];\r
5246   bool xrun[2];\r
5247   UINT bufferPointer[2];\r
5248   DWORD dsBufferSize[2];\r
5249   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
5250   HANDLE condition;\r
5251 \r
5252   DsHandle()\r
5253     :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
5254 };\r
5255 \r
5256 // Declarations for utility functions, callbacks, and structures\r
5257 // specific to the DirectSound implementation.\r
5258 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
5259                                           LPCTSTR description,\r
5260                                           LPCTSTR module,\r
5261                                           LPVOID lpContext );\r
5262 \r
5263 static const char* getErrorString( int code );\r
5264 \r
5265 static unsigned __stdcall callbackHandler( void *ptr );\r
5266 \r
5267 struct DsDevice {\r
5268   LPGUID id[2];\r
5269   bool validId[2];\r
5270   bool found;\r
5271   std::string name;\r
5272 \r
5273   DsDevice()\r
5274   : found(false) { validId[0] = false; validId[1] = false; }\r
5275 };\r
5276 \r
5277 struct DsProbeData {\r
5278   bool isInput;\r
5279   std::vector<struct DsDevice>* dsDevices;\r
5280 };\r
5281 \r
5282 RtApiDs :: RtApiDs()\r
5283 {\r
5284   // Dsound will run both-threaded. If CoInitialize fails, then just\r
5285   // accept whatever the mainline chose for a threading model.\r
5286   coInitialized_ = false;\r
5287   HRESULT hr = CoInitialize( NULL );\r
5288   if ( !FAILED( hr ) ) coInitialized_ = true;\r
5289 }\r
5290 \r
5291 RtApiDs :: ~RtApiDs()\r
5292 {\r
5293   if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
5294   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
5295 }\r
5296 \r
5297 // The DirectSound default output is always the first device.\r
5298 unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
5299 {\r
5300   return 0;\r
5301 }\r
5302 \r
5303 // The DirectSound default input is always the first input device,\r
5304 // which is the first capture device enumerated.\r
5305 unsigned int RtApiDs :: getDefaultInputDevice( void )\r
5306 {\r
5307   return 0;\r
5308 }\r
5309 \r
5310 unsigned int RtApiDs :: getDeviceCount( void )\r
5311 {\r
5312   // Set query flag for previously found devices to false, so that we\r
5313   // can check for any devices that have disappeared.\r
5314   for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
5315     dsDevices[i].found = false;\r
5316 \r
5317   // Query DirectSound devices.\r
5318   struct DsProbeData probeInfo;\r
5319   probeInfo.isInput = false;\r
5320   probeInfo.dsDevices = &dsDevices;\r
5321   HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5322   if ( FAILED( result ) ) {\r
5323     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
5324     errorText_ = errorStream_.str();\r
5325     error( RtAudioError::WARNING );\r
5326   }\r
5327 \r
5328   // Query DirectSoundCapture devices.\r
5329   probeInfo.isInput = true;\r
5330   result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
5331   if ( FAILED( result ) ) {\r
5332     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
5333     errorText_ = errorStream_.str();\r
5334     error( RtAudioError::WARNING );\r
5335   }\r
5336 \r
5337   // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).\r
5338   for ( unsigned int i=0; i<dsDevices.size(); ) {\r
5339     if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );\r
5340     else i++;\r
5341   }\r
5342 \r
5343   return static_cast<unsigned int>(dsDevices.size());\r
5344 }\r
5345 \r
5346 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
5347 {\r
5348   RtAudio::DeviceInfo info;\r
5349   info.probed = false;\r
5350 \r
5351   if ( dsDevices.size() == 0 ) {\r
5352     // Force a query of all devices\r
5353     getDeviceCount();\r
5354     if ( dsDevices.size() == 0 ) {\r
5355       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
5356       error( RtAudioError::INVALID_USE );\r
5357       return info;\r
5358     }\r
5359   }\r
5360 \r
5361   if ( device >= dsDevices.size() ) {\r
5362     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
5363     error( RtAudioError::INVALID_USE );\r
5364     return info;\r
5365   }\r
5366 \r
5367   HRESULT result;\r
5368   if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
5369 \r
5370   LPDIRECTSOUND output;\r
5371   DSCAPS outCaps;\r
5372   result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5373   if ( FAILED( result ) ) {\r
5374     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5375     errorText_ = errorStream_.str();\r
5376     error( RtAudioError::WARNING );\r
5377     goto probeInput;\r
5378   }\r
5379 \r
5380   outCaps.dwSize = sizeof( outCaps );\r
5381   result = output->GetCaps( &outCaps );\r
5382   if ( FAILED( result ) ) {\r
5383     output->Release();\r
5384     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
5385     errorText_ = errorStream_.str();\r
5386     error( RtAudioError::WARNING );\r
5387     goto probeInput;\r
5388   }\r
5389 \r
5390   // Get output channel information.\r
5391   info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
5392 \r
5393   // Get sample rate information.\r
5394   info.sampleRates.clear();\r
5395   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
5396     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
5397          SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {\r
5398       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
5399 \r
5400       if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
5401         info.preferredSampleRate = SAMPLE_RATES[k];\r
5402     }\r
5403   }\r
5404 \r
5405   // Get format information.\r
5406   if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
5407   if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
5408 \r
5409   output->Release();\r
5410 \r
5411   if ( getDefaultOutputDevice() == device )\r
5412     info.isDefaultOutput = true;\r
5413 \r
5414   if ( dsDevices[ device ].validId[1] == false ) {\r
5415     info.name = dsDevices[ device ].name;\r
5416     info.probed = true;\r
5417     return info;\r
5418   }\r
5419 \r
5420  probeInput:\r
5421 \r
5422   LPDIRECTSOUNDCAPTURE input;\r
5423   result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5424   if ( FAILED( result ) ) {\r
5425     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5426     errorText_ = errorStream_.str();\r
5427     error( RtAudioError::WARNING );\r
5428     return info;\r
5429   }\r
5430 \r
5431   DSCCAPS inCaps;\r
5432   inCaps.dwSize = sizeof( inCaps );\r
5433   result = input->GetCaps( &inCaps );\r
5434   if ( FAILED( result ) ) {\r
5435     input->Release();\r
5436     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
5437     errorText_ = errorStream_.str();\r
5438     error( RtAudioError::WARNING );\r
5439     return info;\r
5440   }\r
5441 \r
5442   // Get input channel information.\r
5443   info.inputChannels = inCaps.dwChannels;\r
5444 \r
5445   // Get sample rate and format information.\r
5446   std::vector<unsigned int> rates;\r
5447   if ( inCaps.dwChannels >= 2 ) {\r
5448     if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5449     if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5450     if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5451     if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5452     if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5453     if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5454     if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5455     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5456 \r
5457     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5458       if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
5459       if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
5460       if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
5461       if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
5462     }\r
5463     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5464       if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
5465       if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
5466       if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
5467       if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
5468     }\r
5469   }\r
5470   else if ( inCaps.dwChannels == 1 ) {\r
5471     if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5472     if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5473     if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5474     if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
5475     if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5476     if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5477     if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5478     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
5479 \r
5480     if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
5481       if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
5482       if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
5483       if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
5484       if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
5485     }\r
5486     else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
5487       if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
5488       if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
5489       if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
5490       if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
5491     }\r
5492   }\r
5493   else info.inputChannels = 0; // technically, this would be an error\r
5494 \r
5495   input->Release();\r
5496 \r
5497   if ( info.inputChannels == 0 ) return info;\r
5498 \r
5499   // Copy the supported rates to the info structure but avoid duplication.\r
5500   bool found;\r
5501   for ( unsigned int i=0; i<rates.size(); i++ ) {\r
5502     found = false;\r
5503     for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
5504       if ( rates[i] == info.sampleRates[j] ) {\r
5505         found = true;\r
5506         break;\r
5507       }\r
5508     }\r
5509     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
5510   }\r
5511   std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
5512 \r
5513   // If device opens for both playback and capture, we determine the channels.\r
5514   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
5515     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
5516 \r
5517   if ( device == 0 ) info.isDefaultInput = true;\r
5518 \r
5519   // Copy name and return.\r
5520   info.name = dsDevices[ device ].name;\r
5521   info.probed = true;\r
5522   return info;\r
5523 }\r
5524 \r
5525 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
5526                                  unsigned int firstChannel, unsigned int sampleRate,\r
5527                                  RtAudioFormat format, unsigned int *bufferSize,\r
5528                                  RtAudio::StreamOptions *options )\r
5529 {\r
5530   if ( channels + firstChannel > 2 ) {\r
5531     errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
5532     return FAILURE;\r
5533   }\r
5534 \r
5535   size_t nDevices = dsDevices.size();\r
5536   if ( nDevices == 0 ) {\r
5537     // This should not happen because a check is made before this function is called.\r
5538     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
5539     return FAILURE;\r
5540   }\r
5541 \r
5542   if ( device >= nDevices ) {\r
5543     // This should not happen because a check is made before this function is called.\r
5544     errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
5545     return FAILURE;\r
5546   }\r
5547 \r
5548   if ( mode == OUTPUT ) {\r
5549     if ( dsDevices[ device ].validId[0] == false ) {\r
5550       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
5551       errorText_ = errorStream_.str();\r
5552       return FAILURE;\r
5553     }\r
5554   }\r
5555   else { // mode == INPUT\r
5556     if ( dsDevices[ device ].validId[1] == false ) {\r
5557       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
5558       errorText_ = errorStream_.str();\r
5559       return FAILURE;\r
5560     }\r
5561   }\r
5562 \r
5563   // According to a note in PortAudio, using GetDesktopWindow()\r
5564   // instead of GetForegroundWindow() is supposed to avoid problems\r
5565   // that occur when the application's window is not the foreground\r
5566   // window.  Also, if the application window closes before the\r
5567   // DirectSound buffer, DirectSound can crash.  In the past, I had\r
5568   // problems when using GetDesktopWindow() but it seems fine now\r
5569   // (January 2010).  I'll leave it commented here.\r
5570   // HWND hWnd = GetForegroundWindow();\r
5571   HWND hWnd = GetDesktopWindow();\r
5572 \r
5573   // Check the numberOfBuffers parameter and limit the lowest value to\r
5574   // two.  This is a judgement call and a value of two is probably too\r
5575   // low for capture, but it should work for playback.\r
5576   int nBuffers = 0;\r
5577   if ( options ) nBuffers = options->numberOfBuffers;\r
5578   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
5579   if ( nBuffers < 2 ) nBuffers = 3;\r
5580 \r
5581   // Check the lower range of the user-specified buffer size and set\r
5582   // (arbitrarily) to a lower bound of 32.\r
5583   if ( *bufferSize < 32 ) *bufferSize = 32;\r
5584 \r
5585   // Create the wave format structure.  The data format setting will\r
5586   // be determined later.\r
5587   WAVEFORMATEX waveFormat;\r
5588   ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
5589   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
5590   waveFormat.nChannels = channels + firstChannel;\r
5591   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
5592 \r
5593   // Determine the device buffer size. By default, we'll use the value\r
5594   // defined above (32K), but we will grow it to make allowances for\r
5595   // very large software buffer sizes.\r
5596   DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
5597   DWORD dsPointerLeadTime = 0;\r
5598 \r
5599   void *ohandle = 0, *bhandle = 0;\r
5600   HRESULT result;\r
5601   if ( mode == OUTPUT ) {\r
5602 \r
5603     LPDIRECTSOUND output;\r
5604     result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
5605     if ( FAILED( result ) ) {\r
5606       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
5607       errorText_ = errorStream_.str();\r
5608       return FAILURE;\r
5609     }\r
5610 \r
5611     DSCAPS outCaps;\r
5612     outCaps.dwSize = sizeof( outCaps );\r
5613     result = output->GetCaps( &outCaps );\r
5614     if ( FAILED( result ) ) {\r
5615       output->Release();\r
5616       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
5617       errorText_ = errorStream_.str();\r
5618       return FAILURE;\r
5619     }\r
5620 \r
5621     // Check channel information.\r
5622     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
5623       errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
5624       errorText_ = errorStream_.str();\r
5625       return FAILURE;\r
5626     }\r
5627 \r
5628     // Check format information.  Use 16-bit format unless not\r
5629     // supported or user requests 8-bit.\r
5630     if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
5631          !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
5632       waveFormat.wBitsPerSample = 16;\r
5633       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5634     }\r
5635     else {\r
5636       waveFormat.wBitsPerSample = 8;\r
5637       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5638     }\r
5639     stream_.userFormat = format;\r
5640 \r
5641     // Update wave format structure and buffer information.\r
5642     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5643     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5644     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5645 \r
5646     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5647     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5648       dsBufferSize *= 2;\r
5649 \r
5650     // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
5651     // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
5652     // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
5653     result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
5654     if ( FAILED( result ) ) {\r
5655       output->Release();\r
5656       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
5657       errorText_ = errorStream_.str();\r
5658       return FAILURE;\r
5659     }\r
5660 \r
5661     // Even though we will write to the secondary buffer, we need to\r
5662     // access the primary buffer to set the correct output format\r
5663     // (since the default is 8-bit, 22 kHz!).  Setup the DS primary\r
5664     // buffer description.\r
5665     DSBUFFERDESC bufferDescription;\r
5666     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5667     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5668     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
5669 \r
5670     // Obtain the primary buffer\r
5671     LPDIRECTSOUNDBUFFER buffer;\r
5672     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5673     if ( FAILED( result ) ) {\r
5674       output->Release();\r
5675       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
5676       errorText_ = errorStream_.str();\r
5677       return FAILURE;\r
5678     }\r
5679 \r
5680     // Set the primary DS buffer sound format.\r
5681     result = buffer->SetFormat( &waveFormat );\r
5682     if ( FAILED( result ) ) {\r
5683       output->Release();\r
5684       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
5685       errorText_ = errorStream_.str();\r
5686       return FAILURE;\r
5687     }\r
5688 \r
5689     // Setup the secondary DS buffer description.\r
5690     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
5691     bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
5692     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5693                                   DSBCAPS_GLOBALFOCUS |\r
5694                                   DSBCAPS_GETCURRENTPOSITION2 |\r
5695                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
5696     bufferDescription.dwBufferBytes = dsBufferSize;\r
5697     bufferDescription.lpwfxFormat = &waveFormat;\r
5698 \r
5699     // Try to create the secondary DS buffer.  If that doesn't work,\r
5700     // try to use software mixing.  Otherwise, there's a problem.\r
5701     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5702     if ( FAILED( result ) ) {\r
5703       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
5704                                     DSBCAPS_GLOBALFOCUS |\r
5705                                     DSBCAPS_GETCURRENTPOSITION2 |\r
5706                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
5707       result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
5708       if ( FAILED( result ) ) {\r
5709         output->Release();\r
5710         errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
5711         errorText_ = errorStream_.str();\r
5712         return FAILURE;\r
5713       }\r
5714     }\r
5715 \r
5716     // Get the buffer size ... might be different from what we specified.\r
5717     DSBCAPS dsbcaps;\r
5718     dsbcaps.dwSize = sizeof( DSBCAPS );\r
5719     result = buffer->GetCaps( &dsbcaps );\r
5720     if ( FAILED( result ) ) {\r
5721       output->Release();\r
5722       buffer->Release();\r
5723       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5724       errorText_ = errorStream_.str();\r
5725       return FAILURE;\r
5726     }\r
5727 \r
5728     dsBufferSize = dsbcaps.dwBufferBytes;\r
5729 \r
5730     // Lock the DS buffer\r
5731     LPVOID audioPtr;\r
5732     DWORD dataLen;\r
5733     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5734     if ( FAILED( result ) ) {\r
5735       output->Release();\r
5736       buffer->Release();\r
5737       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
5738       errorText_ = errorStream_.str();\r
5739       return FAILURE;\r
5740     }\r
5741 \r
5742     // Zero the DS buffer\r
5743     ZeroMemory( audioPtr, dataLen );\r
5744 \r
5745     // Unlock the DS buffer\r
5746     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5747     if ( FAILED( result ) ) {\r
5748       output->Release();\r
5749       buffer->Release();\r
5750       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
5751       errorText_ = errorStream_.str();\r
5752       return FAILURE;\r
5753     }\r
5754 \r
5755     ohandle = (void *) output;\r
5756     bhandle = (void *) buffer;\r
5757   }\r
5758 \r
5759   if ( mode == INPUT ) {\r
5760 \r
5761     LPDIRECTSOUNDCAPTURE input;\r
5762     result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
5763     if ( FAILED( result ) ) {\r
5764       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
5765       errorText_ = errorStream_.str();\r
5766       return FAILURE;\r
5767     }\r
5768 \r
5769     DSCCAPS inCaps;\r
5770     inCaps.dwSize = sizeof( inCaps );\r
5771     result = input->GetCaps( &inCaps );\r
5772     if ( FAILED( result ) ) {\r
5773       input->Release();\r
5774       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
5775       errorText_ = errorStream_.str();\r
5776       return FAILURE;\r
5777     }\r
5778 \r
5779     // Check channel information.\r
5780     if ( inCaps.dwChannels < channels + firstChannel ) {\r
5781       errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
5782       return FAILURE;\r
5783     }\r
5784 \r
5785     // Check format information.  Use 16-bit format unless user\r
5786     // requests 8-bit.\r
5787     DWORD deviceFormats;\r
5788     if ( channels + firstChannel == 2 ) {\r
5789       deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
5790       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5791         waveFormat.wBitsPerSample = 8;\r
5792         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5793       }\r
5794       else { // assume 16-bit is supported\r
5795         waveFormat.wBitsPerSample = 16;\r
5796         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5797       }\r
5798     }\r
5799     else { // channel == 1\r
5800       deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
5801       if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
5802         waveFormat.wBitsPerSample = 8;\r
5803         stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
5804       }\r
5805       else { // assume 16-bit is supported\r
5806         waveFormat.wBitsPerSample = 16;\r
5807         stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
5808       }\r
5809     }\r
5810     stream_.userFormat = format;\r
5811 \r
5812     // Update wave format structure and buffer information.\r
5813     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
5814     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
5815     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
5816 \r
5817     // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
5818     while ( dsPointerLeadTime * 2U > dsBufferSize )\r
5819       dsBufferSize *= 2;\r
5820 \r
5821     // Setup the secondary DS buffer description.\r
5822     DSCBUFFERDESC bufferDescription;\r
5823     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
5824     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
5825     bufferDescription.dwFlags = 0;\r
5826     bufferDescription.dwReserved = 0;\r
5827     bufferDescription.dwBufferBytes = dsBufferSize;\r
5828     bufferDescription.lpwfxFormat = &waveFormat;\r
5829 \r
5830     // Create the capture buffer.\r
5831     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
5832     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
5833     if ( FAILED( result ) ) {\r
5834       input->Release();\r
5835       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
5836       errorText_ = errorStream_.str();\r
5837       return FAILURE;\r
5838     }\r
5839 \r
5840     // Get the buffer size ... might be different from what we specified.\r
5841     DSCBCAPS dscbcaps;\r
5842     dscbcaps.dwSize = sizeof( DSCBCAPS );\r
5843     result = buffer->GetCaps( &dscbcaps );\r
5844     if ( FAILED( result ) ) {\r
5845       input->Release();\r
5846       buffer->Release();\r
5847       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
5848       errorText_ = errorStream_.str();\r
5849       return FAILURE;\r
5850     }\r
5851 \r
5852     dsBufferSize = dscbcaps.dwBufferBytes;\r
5853 \r
5854     // NOTE: We could have a problem here if this is a duplex stream\r
5855     // and the play and capture hardware buffer sizes are different\r
5856     // (I'm actually not sure if that is a problem or not).\r
5857     // Currently, we are not verifying that.\r
5858 \r
5859     // Lock the capture buffer\r
5860     LPVOID audioPtr;\r
5861     DWORD dataLen;\r
5862     result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
5863     if ( FAILED( result ) ) {\r
5864       input->Release();\r
5865       buffer->Release();\r
5866       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
5867       errorText_ = errorStream_.str();\r
5868       return FAILURE;\r
5869     }\r
5870 \r
5871     // Zero the buffer\r
5872     ZeroMemory( audioPtr, dataLen );\r
5873 \r
5874     // Unlock the buffer\r
5875     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
5876     if ( FAILED( result ) ) {\r
5877       input->Release();\r
5878       buffer->Release();\r
5879       errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
5880       errorText_ = errorStream_.str();\r
5881       return FAILURE;\r
5882     }\r
5883 \r
5884     ohandle = (void *) input;\r
5885     bhandle = (void *) buffer;\r
5886   }\r
5887 \r
5888   // Set various stream parameters\r
5889   DsHandle *handle = 0;\r
5890   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
5891   stream_.nUserChannels[mode] = channels;\r
5892   stream_.bufferSize = *bufferSize;\r
5893   stream_.channelOffset[mode] = firstChannel;\r
5894   stream_.deviceInterleaved[mode] = true;\r
5895   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
5896   else stream_.userInterleaved = true;\r
5897 \r
5898   // Set flag for buffer conversion\r
5899   stream_.doConvertBuffer[mode] = false;\r
5900   if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
5901     stream_.doConvertBuffer[mode] = true;\r
5902   if (stream_.userFormat != stream_.deviceFormat[mode])\r
5903     stream_.doConvertBuffer[mode] = true;\r
5904   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
5905        stream_.nUserChannels[mode] > 1 )\r
5906     stream_.doConvertBuffer[mode] = true;\r
5907 \r
5908   // Allocate necessary internal buffers\r
5909   long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
5910   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
5911   if ( stream_.userBuffer[mode] == NULL ) {\r
5912     errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
5913     goto error;\r
5914   }\r
5915 \r
5916   if ( stream_.doConvertBuffer[mode] ) {\r
5917 \r
5918     bool makeBuffer = true;\r
5919     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
5920     if ( mode == INPUT ) {\r
5921       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
5922         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
5923         if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
5924       }\r
5925     }\r
5926 \r
5927     if ( makeBuffer ) {\r
5928       bufferBytes *= *bufferSize;\r
5929       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
5930       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
5931       if ( stream_.deviceBuffer == NULL ) {\r
5932         errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
5933         goto error;\r
5934       }\r
5935     }\r
5936   }\r
5937 \r
5938   // Allocate our DsHandle structures for the stream.\r
5939   if ( stream_.apiHandle == 0 ) {\r
5940     try {\r
5941       handle = new DsHandle;\r
5942     }\r
5943     catch ( std::bad_alloc& ) {\r
5944       errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
5945       goto error;\r
5946     }\r
5947 \r
5948     // Create a manual-reset event.\r
5949     handle->condition = CreateEvent( NULL,   // no security\r
5950                                      TRUE,   // manual-reset\r
5951                                      FALSE,  // non-signaled initially\r
5952                                      NULL ); // unnamed\r
5953     stream_.apiHandle = (void *) handle;\r
5954   }\r
5955   else\r
5956     handle = (DsHandle *) stream_.apiHandle;\r
5957   handle->id[mode] = ohandle;\r
5958   handle->buffer[mode] = bhandle;\r
5959   handle->dsBufferSize[mode] = dsBufferSize;\r
5960   handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
5961 \r
5962   stream_.device[mode] = device;\r
5963   stream_.state = STREAM_STOPPED;\r
5964   if ( stream_.mode == OUTPUT && mode == INPUT )\r
5965     // We had already set up an output stream.\r
5966     stream_.mode = DUPLEX;\r
5967   else\r
5968     stream_.mode = mode;\r
5969   stream_.nBuffers = nBuffers;\r
5970   stream_.sampleRate = sampleRate;\r
5971 \r
5972   // Setup the buffer conversion information structure.\r
5973   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
5974 \r
5975   // Setup the callback thread.\r
5976   if ( stream_.callbackInfo.isRunning == false ) {\r
5977     unsigned threadId;\r
5978     stream_.callbackInfo.isRunning = true;\r
5979     stream_.callbackInfo.object = (void *) this;\r
5980     stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
5981                                                   &stream_.callbackInfo, 0, &threadId );\r
5982     if ( stream_.callbackInfo.thread == 0 ) {\r
5983       errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
5984       goto error;\r
5985     }\r
5986 \r
5987     // Boost DS thread priority\r
5988     SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
5989   }\r
5990   return SUCCESS;\r
5991 \r
5992  error:\r
5993   if ( handle ) {\r
5994     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
5995       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
5996       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
5997       if ( buffer ) buffer->Release();\r
5998       object->Release();\r
5999     }\r
6000     if ( handle->buffer[1] ) {\r
6001       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
6002       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6003       if ( buffer ) buffer->Release();\r
6004       object->Release();\r
6005     }\r
6006     CloseHandle( handle->condition );\r
6007     delete handle;\r
6008     stream_.apiHandle = 0;\r
6009   }\r
6010 \r
6011   for ( int i=0; i<2; i++ ) {\r
6012     if ( stream_.userBuffer[i] ) {\r
6013       free( stream_.userBuffer[i] );\r
6014       stream_.userBuffer[i] = 0;\r
6015     }\r
6016   }\r
6017 \r
6018   if ( stream_.deviceBuffer ) {\r
6019     free( stream_.deviceBuffer );\r
6020     stream_.deviceBuffer = 0;\r
6021   }\r
6022 \r
6023   stream_.state = STREAM_CLOSED;\r
6024   return FAILURE;\r
6025 }\r
6026 \r
6027 void RtApiDs :: closeStream()\r
6028 {\r
6029   if ( stream_.state == STREAM_CLOSED ) {\r
6030     errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
6031     error( RtAudioError::WARNING );\r
6032     return;\r
6033   }\r
6034 \r
6035   // Stop the callback thread.\r
6036   stream_.callbackInfo.isRunning = false;\r
6037   WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
6038   CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
6039 \r
6040   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6041   if ( handle ) {\r
6042     if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
6043       LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
6044       LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6045       if ( buffer ) {\r
6046         buffer->Stop();\r
6047         buffer->Release();\r
6048       }\r
6049       object->Release();\r
6050     }\r
6051     if ( handle->buffer[1] ) {\r
6052       LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
6053       LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6054       if ( buffer ) {\r
6055         buffer->Stop();\r
6056         buffer->Release();\r
6057       }\r
6058       object->Release();\r
6059     }\r
6060     CloseHandle( handle->condition );\r
6061     delete handle;\r
6062     stream_.apiHandle = 0;\r
6063   }\r
6064 \r
6065   for ( int i=0; i<2; i++ ) {\r
6066     if ( stream_.userBuffer[i] ) {\r
6067       free( stream_.userBuffer[i] );\r
6068       stream_.userBuffer[i] = 0;\r
6069     }\r
6070   }\r
6071 \r
6072   if ( stream_.deviceBuffer ) {\r
6073     free( stream_.deviceBuffer );\r
6074     stream_.deviceBuffer = 0;\r
6075   }\r
6076 \r
6077   stream_.mode = UNINITIALIZED;\r
6078   stream_.state = STREAM_CLOSED;\r
6079 }\r
6080 \r
6081 void RtApiDs :: startStream()\r
6082 {\r
6083   verifyStream();\r
6084   RtApi::startStream();\r
6085 \r
6086   if ( stream_.state == STREAM_RUNNING ) {\r
6087     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
6088     error( RtAudioError::WARNING );\r
6089     return;\r
6090   }\r
6091 \r
6092   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6093 \r
6094   // Increase scheduler frequency on lesser windows (a side-effect of\r
6095   // increasing timer accuracy).  On greater windows (Win2K or later),\r
6096   // this is already in effect.\r
6097   timeBeginPeriod( 1 );\r
6098 \r
6099   buffersRolling = false;\r
6100   duplexPrerollBytes = 0;\r
6101 \r
6102   if ( stream_.mode == DUPLEX ) {\r
6103     // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
6104     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
6105   }\r
6106 \r
6107   HRESULT result = 0;\r
6108   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6109 \r
6110     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6111     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
6112     if ( FAILED( result ) ) {\r
6113       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
6114       errorText_ = errorStream_.str();\r
6115       goto unlock;\r
6116     }\r
6117   }\r
6118 \r
6119   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6120 \r
6121     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6122     result = buffer->Start( DSCBSTART_LOOPING );\r
6123     if ( FAILED( result ) ) {\r
6124       errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
6125       errorText_ = errorStream_.str();\r
6126       goto unlock;\r
6127     }\r
6128   }\r
6129 \r
6130   handle->drainCounter = 0;\r
6131   handle->internalDrain = false;\r
6132   ResetEvent( handle->condition );\r
6133   stream_.state = STREAM_RUNNING;\r
6134 \r
6135  unlock:\r
6136   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6137 }\r
6138 \r
6139 void RtApiDs :: stopStream()\r
6140 {\r
6141   verifyStream();\r
6142   if ( stream_.state == STREAM_STOPPED ) {\r
6143     errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
6144     error( RtAudioError::WARNING );\r
6145     return;\r
6146   }\r
6147 \r
6148   HRESULT result = 0;\r
6149   LPVOID audioPtr;\r
6150   DWORD dataLen;\r
6151   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6152   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6153     if ( handle->drainCounter == 0 ) {\r
6154       handle->drainCounter = 2;\r
6155       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
6156     }\r
6157 \r
6158     stream_.state = STREAM_STOPPED;\r
6159 \r
6160     MUTEX_LOCK( &stream_.mutex );\r
6161 \r
6162     // Stop the buffer and clear memory\r
6163     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6164     result = buffer->Stop();\r
6165     if ( FAILED( result ) ) {\r
6166       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
6167       errorText_ = errorStream_.str();\r
6168       goto unlock;\r
6169     }\r
6170 \r
6171     // Lock the buffer and clear it so that if we start to play again,\r
6172     // we won't have old data playing.\r
6173     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6174     if ( FAILED( result ) ) {\r
6175       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
6176       errorText_ = errorStream_.str();\r
6177       goto unlock;\r
6178     }\r
6179 \r
6180     // Zero the DS buffer\r
6181     ZeroMemory( audioPtr, dataLen );\r
6182 \r
6183     // Unlock the DS buffer\r
6184     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6185     if ( FAILED( result ) ) {\r
6186       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
6187       errorText_ = errorStream_.str();\r
6188       goto unlock;\r
6189     }\r
6190 \r
6191     // If we start playing again, we must begin at beginning of buffer.\r
6192     handle->bufferPointer[0] = 0;\r
6193   }\r
6194 \r
6195   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6196     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6197     audioPtr = NULL;\r
6198     dataLen = 0;\r
6199 \r
6200     stream_.state = STREAM_STOPPED;\r
6201 \r
6202     if ( stream_.mode != DUPLEX )\r
6203       MUTEX_LOCK( &stream_.mutex );\r
6204 \r
6205     result = buffer->Stop();\r
6206     if ( FAILED( result ) ) {\r
6207       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
6208       errorText_ = errorStream_.str();\r
6209       goto unlock;\r
6210     }\r
6211 \r
6212     // Lock the buffer and clear it so that if we start to play again,\r
6213     // we won't have old data playing.\r
6214     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
6215     if ( FAILED( result ) ) {\r
6216       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
6217       errorText_ = errorStream_.str();\r
6218       goto unlock;\r
6219     }\r
6220 \r
6221     // Zero the DS buffer\r
6222     ZeroMemory( audioPtr, dataLen );\r
6223 \r
6224     // Unlock the DS buffer\r
6225     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
6226     if ( FAILED( result ) ) {\r
6227       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
6228       errorText_ = errorStream_.str();\r
6229       goto unlock;\r
6230     }\r
6231 \r
6232     // If we start recording again, we must begin at beginning of buffer.\r
6233     handle->bufferPointer[1] = 0;\r
6234   }\r
6235 \r
6236  unlock:\r
6237   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
6238   MUTEX_UNLOCK( &stream_.mutex );\r
6239 \r
6240   if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
6241 }\r
6242 \r
6243 void RtApiDs :: abortStream()\r
6244 {\r
6245   verifyStream();\r
6246   if ( stream_.state == STREAM_STOPPED ) {\r
6247     errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
6248     error( RtAudioError::WARNING );\r
6249     return;\r
6250   }\r
6251 \r
6252   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6253   handle->drainCounter = 2;\r
6254 \r
6255   stopStream();\r
6256 }\r
6257 \r
6258 void RtApiDs :: callbackEvent()\r
6259 {\r
6260   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
6261     Sleep( 50 ); // sleep 50 milliseconds\r
6262     return;\r
6263   }\r
6264 \r
6265   if ( stream_.state == STREAM_CLOSED ) {\r
6266     errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
6267     error( RtAudioError::WARNING );\r
6268     return;\r
6269   }\r
6270 \r
6271   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
6272   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
6273 \r
6274   // Check if we were draining the stream and signal is finished.\r
6275   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
6276 \r
6277     stream_.state = STREAM_STOPPING;\r
6278     if ( handle->internalDrain == false )\r
6279       SetEvent( handle->condition );\r
6280     else\r
6281       stopStream();\r
6282     return;\r
6283   }\r
6284 \r
6285   // Invoke user callback to get fresh output data UNLESS we are\r
6286   // draining stream.\r
6287   if ( handle->drainCounter == 0 ) {\r
6288     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
6289     double streamTime = getStreamTime();\r
6290     RtAudioStreamStatus status = 0;\r
6291     if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
6292       status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
6293       handle->xrun[0] = false;\r
6294     }\r
6295     if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
6296       status |= RTAUDIO_INPUT_OVERFLOW;\r
6297       handle->xrun[1] = false;\r
6298     }\r
6299     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
6300                                   stream_.bufferSize, streamTime, status, info->userData );\r
6301     if ( cbReturnValue == 2 ) {\r
6302       stream_.state = STREAM_STOPPING;\r
6303       handle->drainCounter = 2;\r
6304       abortStream();\r
6305       return;\r
6306     }\r
6307     else if ( cbReturnValue == 1 ) {\r
6308       handle->drainCounter = 1;\r
6309       handle->internalDrain = true;\r
6310     }\r
6311   }\r
6312 \r
6313   HRESULT result;\r
6314   DWORD currentWritePointer, safeWritePointer;\r
6315   DWORD currentReadPointer, safeReadPointer;\r
6316   UINT nextWritePointer;\r
6317 \r
6318   LPVOID buffer1 = NULL;\r
6319   LPVOID buffer2 = NULL;\r
6320   DWORD bufferSize1 = 0;\r
6321   DWORD bufferSize2 = 0;\r
6322 \r
6323   char *buffer;\r
6324   long bufferBytes;\r
6325 \r
6326   MUTEX_LOCK( &stream_.mutex );\r
6327   if ( stream_.state == STREAM_STOPPED ) {\r
6328     MUTEX_UNLOCK( &stream_.mutex );\r
6329     return;\r
6330   }\r
6331 \r
6332   if ( buffersRolling == false ) {\r
6333     if ( stream_.mode == DUPLEX ) {\r
6334       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6335 \r
6336       // It takes a while for the devices to get rolling. As a result,\r
6337       // there's no guarantee that the capture and write device pointers\r
6338       // will move in lockstep.  Wait here for both devices to start\r
6339       // rolling, and then set our buffer pointers accordingly.\r
6340       // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
6341       // bytes later than the write buffer.\r
6342 \r
6343       // Stub: a serious risk of having a pre-emptive scheduling round\r
6344       // take place between the two GetCurrentPosition calls... but I'm\r
6345       // really not sure how to solve the problem.  Temporarily boost to\r
6346       // Realtime priority, maybe; but I'm not sure what priority the\r
6347       // DirectSound service threads run at. We *should* be roughly\r
6348       // within a ms or so of correct.\r
6349 \r
6350       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6351       LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6352 \r
6353       DWORD startSafeWritePointer, startSafeReadPointer;\r
6354 \r
6355       result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
6356       if ( FAILED( result ) ) {\r
6357         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6358         errorText_ = errorStream_.str();\r
6359         MUTEX_UNLOCK( &stream_.mutex );\r
6360         error( RtAudioError::SYSTEM_ERROR );\r
6361         return;\r
6362       }\r
6363       result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
6364       if ( FAILED( result ) ) {\r
6365         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6366         errorText_ = errorStream_.str();\r
6367         MUTEX_UNLOCK( &stream_.mutex );\r
6368         error( RtAudioError::SYSTEM_ERROR );\r
6369         return;\r
6370       }\r
6371       while ( true ) {\r
6372         result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
6373         if ( FAILED( result ) ) {\r
6374           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6375           errorText_ = errorStream_.str();\r
6376           MUTEX_UNLOCK( &stream_.mutex );\r
6377           error( RtAudioError::SYSTEM_ERROR );\r
6378           return;\r
6379         }\r
6380         result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
6381         if ( FAILED( result ) ) {\r
6382           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6383           errorText_ = errorStream_.str();\r
6384           MUTEX_UNLOCK( &stream_.mutex );\r
6385           error( RtAudioError::SYSTEM_ERROR );\r
6386           return;\r
6387         }\r
6388         if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
6389         Sleep( 1 );\r
6390       }\r
6391 \r
6392       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
6393 \r
6394       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6395       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6396       handle->bufferPointer[1] = safeReadPointer;\r
6397     }\r
6398     else if ( stream_.mode == OUTPUT ) {\r
6399 \r
6400       // Set the proper nextWritePosition after initial startup.\r
6401       LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6402       result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6403       if ( FAILED( result ) ) {\r
6404         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6405         errorText_ = errorStream_.str();\r
6406         MUTEX_UNLOCK( &stream_.mutex );\r
6407         error( RtAudioError::SYSTEM_ERROR );\r
6408         return;\r
6409       }\r
6410       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
6411       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
6412     }\r
6413 \r
6414     buffersRolling = true;\r
6415   }\r
6416 \r
6417   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
6418 \r
6419     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
6420 \r
6421     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
6422       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6423       bufferBytes *= formatBytes( stream_.userFormat );\r
6424       memset( stream_.userBuffer[0], 0, bufferBytes );\r
6425     }\r
6426 \r
6427     // Setup parameters and do buffer conversion if necessary.\r
6428     if ( stream_.doConvertBuffer[0] ) {\r
6429       buffer = stream_.deviceBuffer;\r
6430       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
6431       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
6432       bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
6433     }\r
6434     else {\r
6435       buffer = stream_.userBuffer[0];\r
6436       bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
6437       bufferBytes *= formatBytes( stream_.userFormat );\r
6438     }\r
6439 \r
6440     // No byte swapping necessary in DirectSound implementation.\r
6441 \r
6442     // Ahhh ... windoze.  16-bit data is signed but 8-bit data is\r
6443     // unsigned.  So, we need to convert our signed 8-bit data here to\r
6444     // unsigned.\r
6445     if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
6446       for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
6447 \r
6448     DWORD dsBufferSize = handle->dsBufferSize[0];\r
6449     nextWritePointer = handle->bufferPointer[0];\r
6450 \r
6451     DWORD endWrite, leadPointer;\r
6452     while ( true ) {\r
6453       // Find out where the read and "safe write" pointers are.\r
6454       result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );\r
6455       if ( FAILED( result ) ) {\r
6456         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
6457         errorText_ = errorStream_.str();\r
6458         MUTEX_UNLOCK( &stream_.mutex );\r
6459         error( RtAudioError::SYSTEM_ERROR );\r
6460         return;\r
6461       }\r
6462 \r
6463       // We will copy our output buffer into the region between\r
6464       // safeWritePointer and leadPointer.  If leadPointer is not\r
6465       // beyond the next endWrite position, wait until it is.\r
6466       leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
6467       //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
6468       if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
6469       if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
6470       endWrite = nextWritePointer + bufferBytes;\r
6471 \r
6472       // Check whether the entire write region is behind the play pointer.\r
6473       if ( leadPointer >= endWrite ) break;\r
6474 \r
6475       // If we are here, then we must wait until the leadPointer advances\r
6476       // beyond the end of our next write region. We use the\r
6477       // Sleep() function to suspend operation until that happens.\r
6478       double millis = ( endWrite - leadPointer ) * 1000.0;\r
6479       millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
6480       if ( millis < 1.0 ) millis = 1.0;\r
6481       Sleep( (DWORD) millis );\r
6482     }\r
6483 \r
6484     if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
6485          || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) {\r
6486       // We've strayed into the forbidden zone ... resync the read pointer.\r
6487       handle->xrun[0] = true;\r
6488       nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
6489       if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
6490       handle->bufferPointer[0] = nextWritePointer;\r
6491       endWrite = nextWritePointer + bufferBytes;\r
6492     }\r
6493 \r
6494     // Lock free space in the buffer\r
6495     result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
6496                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6497     if ( FAILED( result ) ) {\r
6498       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
6499       errorText_ = errorStream_.str();\r
6500       MUTEX_UNLOCK( &stream_.mutex );\r
6501       error( RtAudioError::SYSTEM_ERROR );\r
6502       return;\r
6503     }\r
6504 \r
6505     // Copy our buffer into the DS buffer\r
6506     CopyMemory( buffer1, buffer, bufferSize1 );\r
6507     if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
6508 \r
6509     // Update our buffer offset and unlock sound buffer\r
6510     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6511     if ( FAILED( result ) ) {\r
6512       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
6513       errorText_ = errorStream_.str();\r
6514       MUTEX_UNLOCK( &stream_.mutex );\r
6515       error( RtAudioError::SYSTEM_ERROR );\r
6516       return;\r
6517     }\r
6518     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6519     handle->bufferPointer[0] = nextWritePointer;\r
6520   }\r
6521 \r
6522   // Don't bother draining input\r
6523   if ( handle->drainCounter ) {\r
6524     handle->drainCounter++;\r
6525     goto unlock;\r
6526   }\r
6527 \r
6528   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
6529 \r
6530     // Setup parameters.\r
6531     if ( stream_.doConvertBuffer[1] ) {\r
6532       buffer = stream_.deviceBuffer;\r
6533       bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
6534       bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
6535     }\r
6536     else {\r
6537       buffer = stream_.userBuffer[1];\r
6538       bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
6539       bufferBytes *= formatBytes( stream_.userFormat );\r
6540     }\r
6541 \r
6542     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
6543     long nextReadPointer = handle->bufferPointer[1];\r
6544     DWORD dsBufferSize = handle->dsBufferSize[1];\r
6545 \r
6546     // Find out where the write and "safe read" pointers are.\r
6547     result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6548     if ( FAILED( result ) ) {\r
6549       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
6550       errorText_ = errorStream_.str();\r
6551       MUTEX_UNLOCK( &stream_.mutex );\r
6552       error( RtAudioError::SYSTEM_ERROR );\r
6553       return;\r
6554     }\r
6555 \r
6556     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6557     DWORD endRead = nextReadPointer + bufferBytes;\r
6558 \r
6559     // Handling depends on whether we are INPUT or DUPLEX.\r
6560     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
6561     // then a wait here will drag the write pointers into the forbidden zone.\r
6562     //\r
6563     // In DUPLEX mode, rather than wait, we will back off the read pointer until\r
6564     // it's in a safe position. This causes dropouts, but it seems to be the only\r
6565     // practical way to sync up the read and write pointers reliably, given the\r
6566     // the very complex relationship between phase and increment of the read and write\r
6567     // pointers.\r
6568     //\r
6569     // In order to minimize audible dropouts in DUPLEX mode, we will\r
6570     // provide a pre-roll period of 0.5 seconds in which we return\r
6571     // zeros from the read buffer while the pointers sync up.\r
6572 \r
6573     if ( stream_.mode == DUPLEX ) {\r
6574       if ( safeReadPointer < endRead ) {\r
6575         if ( duplexPrerollBytes <= 0 ) {\r
6576           // Pre-roll time over. Be more agressive.\r
6577           int adjustment = endRead-safeReadPointer;\r
6578 \r
6579           handle->xrun[1] = true;\r
6580           // Two cases:\r
6581           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
6582           //     and perform fine adjustments later.\r
6583           //   - small adjustments: back off by twice as much.\r
6584           if ( adjustment >= 2*bufferBytes )\r
6585             nextReadPointer = safeReadPointer-2*bufferBytes;\r
6586           else\r
6587             nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
6588 \r
6589           if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6590 \r
6591         }\r
6592         else {\r
6593           // In pre=roll time. Just do it.\r
6594           nextReadPointer = safeReadPointer - bufferBytes;\r
6595           while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
6596         }\r
6597         endRead = nextReadPointer + bufferBytes;\r
6598       }\r
6599     }\r
6600     else { // mode == INPUT\r
6601       while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
6602         // See comments for playback.\r
6603         double millis = (endRead - safeReadPointer) * 1000.0;\r
6604         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
6605         if ( millis < 1.0 ) millis = 1.0;\r
6606         Sleep( (DWORD) millis );\r
6607 \r
6608         // Wake up and find out where we are now.\r
6609         result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );\r
6610         if ( FAILED( result ) ) {\r
6611           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\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 ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
6619       }\r
6620     }\r
6621 \r
6622     // Lock free space in the buffer\r
6623     result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
6624                              &bufferSize1, &buffer2, &bufferSize2, 0 );\r
6625     if ( FAILED( result ) ) {\r
6626       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
6627       errorText_ = errorStream_.str();\r
6628       MUTEX_UNLOCK( &stream_.mutex );\r
6629       error( RtAudioError::SYSTEM_ERROR );\r
6630       return;\r
6631     }\r
6632 \r
6633     if ( duplexPrerollBytes <= 0 ) {\r
6634       // Copy our buffer into the DS buffer\r
6635       CopyMemory( buffer, buffer1, bufferSize1 );\r
6636       if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
6637     }\r
6638     else {\r
6639       memset( buffer, 0, bufferSize1 );\r
6640       if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
6641       duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
6642     }\r
6643 \r
6644     // Update our buffer offset and unlock sound buffer\r
6645     nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
6646     dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
6647     if ( FAILED( result ) ) {\r
6648       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
6649       errorText_ = errorStream_.str();\r
6650       MUTEX_UNLOCK( &stream_.mutex );\r
6651       error( RtAudioError::SYSTEM_ERROR );\r
6652       return;\r
6653     }\r
6654     handle->bufferPointer[1] = nextReadPointer;\r
6655 \r
6656     // No byte swapping necessary in DirectSound implementation.\r
6657 \r
6658     // If necessary, convert 8-bit data from unsigned to signed.\r
6659     if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
6660       for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
6661 \r
6662     // Do buffer conversion if necessary.\r
6663     if ( stream_.doConvertBuffer[1] )\r
6664       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
6665   }\r
6666 \r
6667  unlock:\r
6668   MUTEX_UNLOCK( &stream_.mutex );\r
6669   RtApi::tickStreamTime();\r
6670 }\r
6671 \r
6672 // Definitions for utility functions and callbacks\r
6673 // specific to the DirectSound implementation.\r
6674 \r
6675 static unsigned __stdcall callbackHandler( void *ptr )\r
6676 {\r
6677   CallbackInfo *info = (CallbackInfo *) ptr;\r
6678   RtApiDs *object = (RtApiDs *) info->object;\r
6679   bool* isRunning = &info->isRunning;\r
6680 \r
6681   while ( *isRunning == true ) {\r
6682     object->callbackEvent();\r
6683   }\r
6684 \r
6685   _endthreadex( 0 );\r
6686   return 0;\r
6687 }\r
6688 \r
6689 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
6690                                           LPCTSTR description,\r
6691                                           LPCTSTR /*module*/,\r
6692                                           LPVOID lpContext )\r
6693 {\r
6694   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
6695   std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
6696 \r
6697   HRESULT hr;\r
6698   bool validDevice = false;\r
6699   if ( probeInfo.isInput == true ) {\r
6700     DSCCAPS caps;\r
6701     LPDIRECTSOUNDCAPTURE object;\r
6702 \r
6703     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
6704     if ( hr != DS_OK ) return TRUE;\r
6705 \r
6706     caps.dwSize = sizeof(caps);\r
6707     hr = object->GetCaps( &caps );\r
6708     if ( hr == DS_OK ) {\r
6709       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
6710         validDevice = true;\r
6711     }\r
6712     object->Release();\r
6713   }\r
6714   else {\r
6715     DSCAPS caps;\r
6716     LPDIRECTSOUND object;\r
6717     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
6718     if ( hr != DS_OK ) return TRUE;\r
6719 \r
6720     caps.dwSize = sizeof(caps);\r
6721     hr = object->GetCaps( &caps );\r
6722     if ( hr == DS_OK ) {\r
6723       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
6724         validDevice = true;\r
6725     }\r
6726     object->Release();\r
6727   }\r
6728 \r
6729   // If good device, then save its name and guid.\r
6730   std::string name = convertCharPointerToStdString( description );\r
6731   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
6732   if ( lpguid == NULL )\r
6733     name = "Default Device";\r
6734   if ( validDevice ) {\r
6735     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
6736       if ( dsDevices[i].name == name ) {\r
6737         dsDevices[i].found = true;\r
6738         if ( probeInfo.isInput ) {\r
6739           dsDevices[i].id[1] = lpguid;\r
6740           dsDevices[i].validId[1] = true;\r
6741         }\r
6742         else {\r
6743           dsDevices[i].id[0] = lpguid;\r
6744           dsDevices[i].validId[0] = true;\r
6745         }\r
6746         return TRUE;\r
6747       }\r
6748     }\r
6749 \r
6750     DsDevice device;\r
6751     device.name = name;\r
6752     device.found = true;\r
6753     if ( probeInfo.isInput ) {\r
6754       device.id[1] = lpguid;\r
6755       device.validId[1] = true;\r
6756     }\r
6757     else {\r
6758       device.id[0] = lpguid;\r
6759       device.validId[0] = true;\r
6760     }\r
6761     dsDevices.push_back( device );\r
6762   }\r
6763 \r
6764   return TRUE;\r
6765 }\r
6766 \r
6767 static const char* getErrorString( int code )\r
6768 {\r
6769   switch ( code ) {\r
6770 \r
6771   case DSERR_ALLOCATED:\r
6772     return "Already allocated";\r
6773 \r
6774   case DSERR_CONTROLUNAVAIL:\r
6775     return "Control unavailable";\r
6776 \r
6777   case DSERR_INVALIDPARAM:\r
6778     return "Invalid parameter";\r
6779 \r
6780   case DSERR_INVALIDCALL:\r
6781     return "Invalid call";\r
6782 \r
6783   case DSERR_GENERIC:\r
6784     return "Generic error";\r
6785 \r
6786   case DSERR_PRIOLEVELNEEDED:\r
6787     return "Priority level needed";\r
6788 \r
6789   case DSERR_OUTOFMEMORY:\r
6790     return "Out of memory";\r
6791 \r
6792   case DSERR_BADFORMAT:\r
6793     return "The sample rate or the channel format is not supported";\r
6794 \r
6795   case DSERR_UNSUPPORTED:\r
6796     return "Not supported";\r
6797 \r
6798   case DSERR_NODRIVER:\r
6799     return "No driver";\r
6800 \r
6801   case DSERR_ALREADYINITIALIZED:\r
6802     return "Already initialized";\r
6803 \r
6804   case DSERR_NOAGGREGATION:\r
6805     return "No aggregation";\r
6806 \r
6807   case DSERR_BUFFERLOST:\r
6808     return "Buffer lost";\r
6809 \r
6810   case DSERR_OTHERAPPHASPRIO:\r
6811     return "Another application already has priority";\r
6812 \r
6813   case DSERR_UNINITIALIZED:\r
6814     return "Uninitialized";\r
6815 \r
6816   default:\r
6817     return "DirectSound unknown error";\r
6818   }\r
6819 }\r
6820 //******************** End of __WINDOWS_DS__ *********************//\r
6821 #endif\r
6822 \r
6823 \r
6824 #if defined(__LINUX_ALSA__)\r
6825 \r
6826 #include <alsa/asoundlib.h>\r
6827 #include <unistd.h>\r
6828 \r
6829   // A structure to hold various information related to the ALSA API\r
6830   // implementation.\r
6831 struct AlsaHandle {\r
6832   snd_pcm_t *handles[2];\r
6833   bool synchronized;\r
6834   bool xrun[2];\r
6835   pthread_cond_t runnable_cv;\r
6836   bool runnable;\r
6837 \r
6838   AlsaHandle()\r
6839     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
6840 };\r
6841 \r
6842 static void *alsaCallbackHandler( void * ptr );\r
6843 \r
6844 RtApiAlsa :: RtApiAlsa()\r
6845 {\r
6846   // Nothing to do here.\r
6847 }\r
6848 \r
6849 RtApiAlsa :: ~RtApiAlsa()\r
6850 {\r
6851   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
6852 }\r
6853 \r
6854 unsigned int RtApiAlsa :: getDeviceCount( void )\r
6855 {\r
6856   unsigned nDevices = 0;\r
6857   int result, subdevice, card;\r
6858   char name[64];\r
6859   snd_ctl_t *handle;\r
6860 \r
6861   // Count cards and devices\r
6862   card = -1;\r
6863   snd_card_next( &card );\r
6864   while ( card >= 0 ) {\r
6865     sprintf( name, "hw:%d", card );\r
6866     result = snd_ctl_open( &handle, name, 0 );\r
6867     if ( result < 0 ) {\r
6868       errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6869       errorText_ = errorStream_.str();\r
6870       error( RtAudioError::WARNING );\r
6871       goto nextcard;\r
6872     }\r
6873     subdevice = -1;\r
6874     while( 1 ) {\r
6875       result = snd_ctl_pcm_next_device( handle, &subdevice );\r
6876       if ( result < 0 ) {\r
6877         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6878         errorText_ = errorStream_.str();\r
6879         error( RtAudioError::WARNING );\r
6880         break;\r
6881       }\r
6882       if ( subdevice < 0 )\r
6883         break;\r
6884       nDevices++;\r
6885     }\r
6886   nextcard:\r
6887     snd_ctl_close( handle );\r
6888     snd_card_next( &card );\r
6889   }\r
6890 \r
6891   result = snd_ctl_open( &handle, "default", 0 );\r
6892   if (result == 0) {\r
6893     nDevices++;\r
6894     snd_ctl_close( handle );\r
6895   }\r
6896 \r
6897   return nDevices;\r
6898 }\r
6899 \r
6900 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
6901 {\r
6902   RtAudio::DeviceInfo info;\r
6903   info.probed = false;\r
6904 \r
6905   unsigned nDevices = 0;\r
6906   int result, subdevice, card;\r
6907   char name[64];\r
6908   snd_ctl_t *chandle;\r
6909 \r
6910   // Count cards and devices\r
6911   card = -1;\r
6912   subdevice = -1;\r
6913   snd_card_next( &card );\r
6914   while ( card >= 0 ) {\r
6915     sprintf( name, "hw:%d", card );\r
6916     result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
6917     if ( result < 0 ) {\r
6918       errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
6919       errorText_ = errorStream_.str();\r
6920       error( RtAudioError::WARNING );\r
6921       goto nextcard;\r
6922     }\r
6923     subdevice = -1;\r
6924     while( 1 ) {\r
6925       result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
6926       if ( result < 0 ) {\r
6927         errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
6928         errorText_ = errorStream_.str();\r
6929         error( RtAudioError::WARNING );\r
6930         break;\r
6931       }\r
6932       if ( subdevice < 0 ) break;\r
6933       if ( nDevices == device ) {\r
6934         sprintf( name, "hw:%d,%d", card, subdevice );\r
6935         goto foundDevice;\r
6936       }\r
6937       nDevices++;\r
6938     }\r
6939   nextcard:\r
6940     snd_ctl_close( chandle );\r
6941     snd_card_next( &card );\r
6942   }\r
6943 \r
6944   result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
6945   if ( result == 0 ) {\r
6946     if ( nDevices == device ) {\r
6947       strcpy( name, "default" );\r
6948       goto foundDevice;\r
6949     }\r
6950     nDevices++;\r
6951   }\r
6952 \r
6953   if ( nDevices == 0 ) {\r
6954     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
6955     error( RtAudioError::INVALID_USE );\r
6956     return info;\r
6957   }\r
6958 \r
6959   if ( device >= nDevices ) {\r
6960     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
6961     error( RtAudioError::INVALID_USE );\r
6962     return info;\r
6963   }\r
6964 \r
6965  foundDevice:\r
6966 \r
6967   // If a stream is already open, we cannot probe the stream devices.\r
6968   // Thus, use the saved results.\r
6969   if ( stream_.state != STREAM_CLOSED &&\r
6970        ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
6971     snd_ctl_close( chandle );\r
6972     if ( device >= devices_.size() ) {\r
6973       errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
6974       error( RtAudioError::WARNING );\r
6975       return info;\r
6976     }\r
6977     return devices_[ device ];\r
6978   }\r
6979 \r
6980   int openMode = SND_PCM_ASYNC;\r
6981   snd_pcm_stream_t stream;\r
6982   snd_pcm_info_t *pcminfo;\r
6983   snd_pcm_info_alloca( &pcminfo );\r
6984   snd_pcm_t *phandle;\r
6985   snd_pcm_hw_params_t *params;\r
6986   snd_pcm_hw_params_alloca( &params );\r
6987 \r
6988   // First try for playback unless default device (which has subdev -1)\r
6989   stream = SND_PCM_STREAM_PLAYBACK;\r
6990   snd_pcm_info_set_stream( pcminfo, stream );\r
6991   if ( subdevice != -1 ) {\r
6992     snd_pcm_info_set_device( pcminfo, subdevice );\r
6993     snd_pcm_info_set_subdevice( pcminfo, 0 );\r
6994 \r
6995     result = snd_ctl_pcm_info( chandle, pcminfo );\r
6996     if ( result < 0 ) {\r
6997       // Device probably doesn't support playback.\r
6998       goto captureProbe;\r
6999     }\r
7000   }\r
7001 \r
7002   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
7003   if ( result < 0 ) {\r
7004     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7005     errorText_ = errorStream_.str();\r
7006     error( RtAudioError::WARNING );\r
7007     goto captureProbe;\r
7008   }\r
7009 \r
7010   // The device is open ... fill the parameter structure.\r
7011   result = snd_pcm_hw_params_any( phandle, params );\r
7012   if ( result < 0 ) {\r
7013     snd_pcm_close( phandle );\r
7014     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7015     errorText_ = errorStream_.str();\r
7016     error( RtAudioError::WARNING );\r
7017     goto captureProbe;\r
7018   }\r
7019 \r
7020   // Get output channel information.\r
7021   unsigned int value;\r
7022   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
7023   if ( result < 0 ) {\r
7024     snd_pcm_close( phandle );\r
7025     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
7026     errorText_ = errorStream_.str();\r
7027     error( RtAudioError::WARNING );\r
7028     goto captureProbe;\r
7029   }\r
7030   info.outputChannels = value;\r
7031   snd_pcm_close( phandle );\r
7032 \r
7033  captureProbe:\r
7034   stream = SND_PCM_STREAM_CAPTURE;\r
7035   snd_pcm_info_set_stream( pcminfo, stream );\r
7036 \r
7037   // Now try for capture unless default device (with subdev = -1)\r
7038   if ( subdevice != -1 ) {\r
7039     result = snd_ctl_pcm_info( chandle, pcminfo );\r
7040     snd_ctl_close( chandle );\r
7041     if ( result < 0 ) {\r
7042       // Device probably doesn't support capture.\r
7043       if ( info.outputChannels == 0 ) return info;\r
7044       goto probeParameters;\r
7045     }\r
7046   }\r
7047   else\r
7048     snd_ctl_close( chandle );\r
7049 \r
7050   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7051   if ( result < 0 ) {\r
7052     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7053     errorText_ = errorStream_.str();\r
7054     error( RtAudioError::WARNING );\r
7055     if ( info.outputChannels == 0 ) return info;\r
7056     goto probeParameters;\r
7057   }\r
7058 \r
7059   // The device is open ... fill the parameter structure.\r
7060   result = snd_pcm_hw_params_any( phandle, params );\r
7061   if ( result < 0 ) {\r
7062     snd_pcm_close( phandle );\r
7063     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7064     errorText_ = errorStream_.str();\r
7065     error( RtAudioError::WARNING );\r
7066     if ( info.outputChannels == 0 ) return info;\r
7067     goto probeParameters;\r
7068   }\r
7069 \r
7070   result = snd_pcm_hw_params_get_channels_max( params, &value );\r
7071   if ( result < 0 ) {\r
7072     snd_pcm_close( phandle );\r
7073     errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
7074     errorText_ = errorStream_.str();\r
7075     error( RtAudioError::WARNING );\r
7076     if ( info.outputChannels == 0 ) return info;\r
7077     goto probeParameters;\r
7078   }\r
7079   info.inputChannels = value;\r
7080   snd_pcm_close( phandle );\r
7081 \r
7082   // If device opens for both playback and capture, we determine the channels.\r
7083   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
7084     info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
7085 \r
7086   // ALSA doesn't provide default devices so we'll use the first available one.\r
7087   if ( device == 0 && info.outputChannels > 0 )\r
7088     info.isDefaultOutput = true;\r
7089   if ( device == 0 && info.inputChannels > 0 )\r
7090     info.isDefaultInput = true;\r
7091 \r
7092  probeParameters:\r
7093   // At this point, we just need to figure out the supported data\r
7094   // formats and sample rates.  We'll proceed by opening the device in\r
7095   // the direction with the maximum number of channels, or playback if\r
7096   // they are equal.  This might limit our sample rate options, but so\r
7097   // be it.\r
7098 \r
7099   if ( info.outputChannels >= info.inputChannels )\r
7100     stream = SND_PCM_STREAM_PLAYBACK;\r
7101   else\r
7102     stream = SND_PCM_STREAM_CAPTURE;\r
7103   snd_pcm_info_set_stream( pcminfo, stream );\r
7104 \r
7105   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
7106   if ( result < 0 ) {\r
7107     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7108     errorText_ = errorStream_.str();\r
7109     error( RtAudioError::WARNING );\r
7110     return info;\r
7111   }\r
7112 \r
7113   // The device is open ... fill the parameter structure.\r
7114   result = snd_pcm_hw_params_any( phandle, params );\r
7115   if ( result < 0 ) {\r
7116     snd_pcm_close( phandle );\r
7117     errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
7118     errorText_ = errorStream_.str();\r
7119     error( RtAudioError::WARNING );\r
7120     return info;\r
7121   }\r
7122 \r
7123   // Test our discrete set of sample rate values.\r
7124   info.sampleRates.clear();\r
7125   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
7126     if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {\r
7127       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
7128 \r
7129       if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
7130         info.preferredSampleRate = SAMPLE_RATES[i];\r
7131     }\r
7132   }\r
7133   if ( info.sampleRates.size() == 0 ) {\r
7134     snd_pcm_close( phandle );\r
7135     errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
7136     errorText_ = errorStream_.str();\r
7137     error( RtAudioError::WARNING );\r
7138     return info;\r
7139   }\r
7140 \r
7141   // Probe the supported data formats ... we don't care about endian-ness just yet\r
7142   snd_pcm_format_t format;\r
7143   info.nativeFormats = 0;\r
7144   format = SND_PCM_FORMAT_S8;\r
7145   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7146     info.nativeFormats |= RTAUDIO_SINT8;\r
7147   format = SND_PCM_FORMAT_S16;\r
7148   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7149     info.nativeFormats |= RTAUDIO_SINT16;\r
7150   format = SND_PCM_FORMAT_S24;\r
7151   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7152     info.nativeFormats |= RTAUDIO_SINT24;\r
7153   format = SND_PCM_FORMAT_S32;\r
7154   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7155     info.nativeFormats |= RTAUDIO_SINT32;\r
7156   format = SND_PCM_FORMAT_FLOAT;\r
7157   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7158     info.nativeFormats |= RTAUDIO_FLOAT32;\r
7159   format = SND_PCM_FORMAT_FLOAT64;\r
7160   if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
7161     info.nativeFormats |= RTAUDIO_FLOAT64;\r
7162 \r
7163   // Check that we have at least one supported format\r
7164   if ( info.nativeFormats == 0 ) {\r
7165     snd_pcm_close( phandle );\r
7166     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
7167     errorText_ = errorStream_.str();\r
7168     error( RtAudioError::WARNING );\r
7169     return info;\r
7170   }\r
7171 \r
7172   // Get the device name\r
7173   char *cardname;\r
7174   result = snd_card_get_name( card, &cardname );\r
7175   if ( result >= 0 ) {\r
7176     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
7177     free( cardname );\r
7178   }\r
7179   info.name = name;\r
7180 \r
7181   // That's all ... close the device and return\r
7182   snd_pcm_close( phandle );\r
7183   info.probed = true;\r
7184   return info;\r
7185 }\r
7186 \r
7187 void RtApiAlsa :: saveDeviceInfo( void )\r
7188 {\r
7189   devices_.clear();\r
7190 \r
7191   unsigned int nDevices = getDeviceCount();\r
7192   devices_.resize( nDevices );\r
7193   for ( unsigned int i=0; i<nDevices; i++ )\r
7194     devices_[i] = getDeviceInfo( i );\r
7195 }\r
7196 \r
7197 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
7198                                    unsigned int firstChannel, unsigned int sampleRate,\r
7199                                    RtAudioFormat format, unsigned int *bufferSize,\r
7200                                    RtAudio::StreamOptions *options )\r
7201 \r
7202 {\r
7203 #if defined(__RTAUDIO_DEBUG__)\r
7204   snd_output_t *out;\r
7205   snd_output_stdio_attach(&out, stderr, 0);\r
7206 #endif\r
7207 \r
7208   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
7209 \r
7210   unsigned nDevices = 0;\r
7211   int result, subdevice, card;\r
7212   char name[64];\r
7213   snd_ctl_t *chandle;\r
7214 \r
7215   if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
7216     snprintf(name, sizeof(name), "%s", "default");\r
7217   else {\r
7218     // Count cards and devices\r
7219     card = -1;\r
7220     snd_card_next( &card );\r
7221     while ( card >= 0 ) {\r
7222       sprintf( name, "hw:%d", card );\r
7223       result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
7224       if ( result < 0 ) {\r
7225         errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
7226         errorText_ = errorStream_.str();\r
7227         return FAILURE;\r
7228       }\r
7229       subdevice = -1;\r
7230       while( 1 ) {\r
7231         result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
7232         if ( result < 0 ) break;\r
7233         if ( subdevice < 0 ) break;\r
7234         if ( nDevices == device ) {\r
7235           sprintf( name, "hw:%d,%d", card, subdevice );\r
7236           snd_ctl_close( chandle );\r
7237           goto foundDevice;\r
7238         }\r
7239         nDevices++;\r
7240       }\r
7241       snd_ctl_close( chandle );\r
7242       snd_card_next( &card );\r
7243     }\r
7244 \r
7245     result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
7246     if ( result == 0 ) {\r
7247       if ( nDevices == device ) {\r
7248         strcpy( name, "default" );\r
7249         goto foundDevice;\r
7250       }\r
7251       nDevices++;\r
7252     }\r
7253 \r
7254     if ( nDevices == 0 ) {\r
7255       // This should not happen because a check is made before this function is called.\r
7256       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
7257       return FAILURE;\r
7258     }\r
7259 \r
7260     if ( device >= nDevices ) {\r
7261       // This should not happen because a check is made before this function is called.\r
7262       errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
7263       return FAILURE;\r
7264     }\r
7265   }\r
7266 \r
7267  foundDevice:\r
7268 \r
7269   // The getDeviceInfo() function will not work for a device that is\r
7270   // already open.  Thus, we'll probe the system before opening a\r
7271   // stream and save the results for use by getDeviceInfo().\r
7272   if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
7273     this->saveDeviceInfo();\r
7274 \r
7275   snd_pcm_stream_t stream;\r
7276   if ( mode == OUTPUT )\r
7277     stream = SND_PCM_STREAM_PLAYBACK;\r
7278   else\r
7279     stream = SND_PCM_STREAM_CAPTURE;\r
7280 \r
7281   snd_pcm_t *phandle;\r
7282   int openMode = SND_PCM_ASYNC;\r
7283   result = snd_pcm_open( &phandle, name, stream, openMode );\r
7284   if ( result < 0 ) {\r
7285     if ( mode == OUTPUT )\r
7286       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
7287     else\r
7288       errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
7289     errorText_ = errorStream_.str();\r
7290     return FAILURE;\r
7291   }\r
7292 \r
7293   // Fill the parameter structure.\r
7294   snd_pcm_hw_params_t *hw_params;\r
7295   snd_pcm_hw_params_alloca( &hw_params );\r
7296   result = snd_pcm_hw_params_any( phandle, hw_params );\r
7297   if ( result < 0 ) {\r
7298     snd_pcm_close( phandle );\r
7299     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
7300     errorText_ = errorStream_.str();\r
7301     return FAILURE;\r
7302   }\r
7303 \r
7304 #if defined(__RTAUDIO_DEBUG__)\r
7305   fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
7306   snd_pcm_hw_params_dump( hw_params, out );\r
7307 #endif\r
7308 \r
7309   // Set access ... check user preference.\r
7310   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
7311     stream_.userInterleaved = false;\r
7312     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7313     if ( result < 0 ) {\r
7314       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7315       stream_.deviceInterleaved[mode] =  true;\r
7316     }\r
7317     else\r
7318       stream_.deviceInterleaved[mode] = false;\r
7319   }\r
7320   else {\r
7321     stream_.userInterleaved = true;\r
7322     result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
7323     if ( result < 0 ) {\r
7324       result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
7325       stream_.deviceInterleaved[mode] =  false;\r
7326     }\r
7327     else\r
7328       stream_.deviceInterleaved[mode] =  true;\r
7329   }\r
7330 \r
7331   if ( result < 0 ) {\r
7332     snd_pcm_close( phandle );\r
7333     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
7334     errorText_ = errorStream_.str();\r
7335     return FAILURE;\r
7336   }\r
7337 \r
7338   // Determine how to set the device format.\r
7339   stream_.userFormat = format;\r
7340   snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
7341 \r
7342   if ( format == RTAUDIO_SINT8 )\r
7343     deviceFormat = SND_PCM_FORMAT_S8;\r
7344   else if ( format == RTAUDIO_SINT16 )\r
7345     deviceFormat = SND_PCM_FORMAT_S16;\r
7346   else if ( format == RTAUDIO_SINT24 )\r
7347     deviceFormat = SND_PCM_FORMAT_S24;\r
7348   else if ( format == RTAUDIO_SINT32 )\r
7349     deviceFormat = SND_PCM_FORMAT_S32;\r
7350   else if ( format == RTAUDIO_FLOAT32 )\r
7351     deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7352   else if ( format == RTAUDIO_FLOAT64 )\r
7353     deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7354 \r
7355   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
7356     stream_.deviceFormat[mode] = format;\r
7357     goto setFormat;\r
7358   }\r
7359 \r
7360   // The user requested format is not natively supported by the device.\r
7361   deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
7362   if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
7363     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
7364     goto setFormat;\r
7365   }\r
7366 \r
7367   deviceFormat = SND_PCM_FORMAT_FLOAT;\r
7368   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7369     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
7370     goto setFormat;\r
7371   }\r
7372 \r
7373   deviceFormat = SND_PCM_FORMAT_S32;\r
7374   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7375     stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
7376     goto setFormat;\r
7377   }\r
7378 \r
7379   deviceFormat = SND_PCM_FORMAT_S24;\r
7380   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7381     stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
7382     goto setFormat;\r
7383   }\r
7384 \r
7385   deviceFormat = SND_PCM_FORMAT_S16;\r
7386   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7387     stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
7388     goto setFormat;\r
7389   }\r
7390 \r
7391   deviceFormat = SND_PCM_FORMAT_S8;\r
7392   if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
7393     stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
7394     goto setFormat;\r
7395   }\r
7396 \r
7397   // If we get here, no supported format was found.\r
7398   snd_pcm_close( phandle );\r
7399   errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
7400   errorText_ = errorStream_.str();\r
7401   return FAILURE;\r
7402 \r
7403  setFormat:\r
7404   result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
7405   if ( result < 0 ) {\r
7406     snd_pcm_close( phandle );\r
7407     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
7408     errorText_ = errorStream_.str();\r
7409     return FAILURE;\r
7410   }\r
7411 \r
7412   // Determine whether byte-swaping is necessary.\r
7413   stream_.doByteSwap[mode] = false;\r
7414   if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
7415     result = snd_pcm_format_cpu_endian( deviceFormat );\r
7416     if ( result == 0 )\r
7417       stream_.doByteSwap[mode] = true;\r
7418     else if (result < 0) {\r
7419       snd_pcm_close( phandle );\r
7420       errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
7421       errorText_ = errorStream_.str();\r
7422       return FAILURE;\r
7423     }\r
7424   }\r
7425 \r
7426   // Set the sample rate.\r
7427   result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
7428   if ( result < 0 ) {\r
7429     snd_pcm_close( phandle );\r
7430     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
7431     errorText_ = errorStream_.str();\r
7432     return FAILURE;\r
7433   }\r
7434 \r
7435   // Determine the number of channels for this device.  We support a possible\r
7436   // minimum device channel number > than the value requested by the user.\r
7437   stream_.nUserChannels[mode] = channels;\r
7438   unsigned int value;\r
7439   result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
7440   unsigned int deviceChannels = value;\r
7441   if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
7442     snd_pcm_close( phandle );\r
7443     errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
7444     errorText_ = errorStream_.str();\r
7445     return FAILURE;\r
7446   }\r
7447 \r
7448   result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
7449   if ( result < 0 ) {\r
7450     snd_pcm_close( phandle );\r
7451     errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7452     errorText_ = errorStream_.str();\r
7453     return FAILURE;\r
7454   }\r
7455   deviceChannels = value;\r
7456   if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
7457   stream_.nDeviceChannels[mode] = deviceChannels;\r
7458 \r
7459   // Set the device channels.\r
7460   result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
7461   if ( result < 0 ) {\r
7462     snd_pcm_close( phandle );\r
7463     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
7464     errorText_ = errorStream_.str();\r
7465     return FAILURE;\r
7466   }\r
7467 \r
7468   // Set the buffer (or period) size.\r
7469   int dir = 0;\r
7470   snd_pcm_uframes_t periodSize = *bufferSize;\r
7471   result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
7472   if ( result < 0 ) {\r
7473     snd_pcm_close( phandle );\r
7474     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
7475     errorText_ = errorStream_.str();\r
7476     return FAILURE;\r
7477   }\r
7478   *bufferSize = periodSize;\r
7479 \r
7480   // Set the buffer number, which in ALSA is referred to as the "period".\r
7481   unsigned int periods = 0;\r
7482   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
7483   if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
7484   if ( periods < 2 ) periods = 4; // a fairly safe default value\r
7485   result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
7486   if ( result < 0 ) {\r
7487     snd_pcm_close( phandle );\r
7488     errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
7489     errorText_ = errorStream_.str();\r
7490     return FAILURE;\r
7491   }\r
7492 \r
7493   // If attempting to setup a duplex stream, the bufferSize parameter\r
7494   // MUST be the same in both directions!\r
7495   if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
7496     snd_pcm_close( phandle );\r
7497     errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
7498     errorText_ = errorStream_.str();\r
7499     return FAILURE;\r
7500   }\r
7501 \r
7502   stream_.bufferSize = *bufferSize;\r
7503 \r
7504   // Install the hardware configuration\r
7505   result = snd_pcm_hw_params( phandle, hw_params );\r
7506   if ( result < 0 ) {\r
7507     snd_pcm_close( phandle );\r
7508     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7509     errorText_ = errorStream_.str();\r
7510     return FAILURE;\r
7511   }\r
7512 \r
7513 #if defined(__RTAUDIO_DEBUG__)\r
7514   fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
7515   snd_pcm_hw_params_dump( hw_params, out );\r
7516 #endif\r
7517 \r
7518   // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
7519   snd_pcm_sw_params_t *sw_params = NULL;\r
7520   snd_pcm_sw_params_alloca( &sw_params );\r
7521   snd_pcm_sw_params_current( phandle, sw_params );\r
7522   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
7523   snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
7524   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
7525 \r
7526   // The following two settings were suggested by Theo Veenker\r
7527   //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
7528   //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
7529 \r
7530   // here are two options for a fix\r
7531   //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
7532   snd_pcm_uframes_t val;\r
7533   snd_pcm_sw_params_get_boundary( sw_params, &val );\r
7534   snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
7535 \r
7536   result = snd_pcm_sw_params( phandle, sw_params );\r
7537   if ( result < 0 ) {\r
7538     snd_pcm_close( phandle );\r
7539     errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
7540     errorText_ = errorStream_.str();\r
7541     return FAILURE;\r
7542   }\r
7543 \r
7544 #if defined(__RTAUDIO_DEBUG__)\r
7545   fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
7546   snd_pcm_sw_params_dump( sw_params, out );\r
7547 #endif\r
7548 \r
7549   // Set flags for buffer conversion\r
7550   stream_.doConvertBuffer[mode] = false;\r
7551   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
7552     stream_.doConvertBuffer[mode] = true;\r
7553   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
7554     stream_.doConvertBuffer[mode] = true;\r
7555   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
7556        stream_.nUserChannels[mode] > 1 )\r
7557     stream_.doConvertBuffer[mode] = true;\r
7558 \r
7559   // Allocate the ApiHandle if necessary and then save.\r
7560   AlsaHandle *apiInfo = 0;\r
7561   if ( stream_.apiHandle == 0 ) {\r
7562     try {\r
7563       apiInfo = (AlsaHandle *) new AlsaHandle;\r
7564     }\r
7565     catch ( std::bad_alloc& ) {\r
7566       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
7567       goto error;\r
7568     }\r
7569 \r
7570     if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
7571       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
7572       goto error;\r
7573     }\r
7574 \r
7575     stream_.apiHandle = (void *) apiInfo;\r
7576     apiInfo->handles[0] = 0;\r
7577     apiInfo->handles[1] = 0;\r
7578   }\r
7579   else {\r
7580     apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7581   }\r
7582   apiInfo->handles[mode] = phandle;\r
7583   phandle = 0;\r
7584 \r
7585   // Allocate necessary internal buffers.\r
7586   unsigned long bufferBytes;\r
7587   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
7588   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
7589   if ( stream_.userBuffer[mode] == NULL ) {\r
7590     errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
7591     goto error;\r
7592   }\r
7593 \r
7594   if ( stream_.doConvertBuffer[mode] ) {\r
7595 \r
7596     bool makeBuffer = true;\r
7597     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
7598     if ( mode == INPUT ) {\r
7599       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
7600         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
7601         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
7602       }\r
7603     }\r
7604 \r
7605     if ( makeBuffer ) {\r
7606       bufferBytes *= *bufferSize;\r
7607       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
7608       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
7609       if ( stream_.deviceBuffer == NULL ) {\r
7610         errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
7611         goto error;\r
7612       }\r
7613     }\r
7614   }\r
7615 \r
7616   stream_.sampleRate = sampleRate;\r
7617   stream_.nBuffers = periods;\r
7618   stream_.device[mode] = device;\r
7619   stream_.state = STREAM_STOPPED;\r
7620 \r
7621   // Setup the buffer conversion information structure.\r
7622   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
7623 \r
7624   // Setup thread if necessary.\r
7625   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
7626     // We had already set up an output stream.\r
7627     stream_.mode = DUPLEX;\r
7628     // Link the streams if possible.\r
7629     apiInfo->synchronized = false;\r
7630     if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
7631       apiInfo->synchronized = true;\r
7632     else {\r
7633       errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
7634       error( RtAudioError::WARNING );\r
7635     }\r
7636   }\r
7637   else {\r
7638     stream_.mode = mode;\r
7639 \r
7640     // Setup callback thread.\r
7641     stream_.callbackInfo.object = (void *) this;\r
7642 \r
7643     // Set the thread attributes for joinable and realtime scheduling\r
7644     // priority (optional).  The higher priority will only take affect\r
7645     // if the program is run as root or suid. Note, under Linux\r
7646     // processes with CAP_SYS_NICE privilege, a user can change\r
7647     // scheduling policy and priority (thus need not be root). See\r
7648     // POSIX "capabilities".\r
7649     pthread_attr_t attr;\r
7650     pthread_attr_init( &attr );\r
7651     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
7652 \r
7653 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
7654     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
7655       // We previously attempted to increase the audio callback priority\r
7656       // to SCHED_RR here via the attributes.  However, while no errors\r
7657       // were reported in doing so, it did not work.  So, now this is\r
7658       // done in the alsaCallbackHandler function.\r
7659       stream_.callbackInfo.doRealtime = true;\r
7660       int priority = options->priority;\r
7661       int min = sched_get_priority_min( SCHED_RR );\r
7662       int max = sched_get_priority_max( SCHED_RR );\r
7663       if ( priority < min ) priority = min;\r
7664       else if ( priority > max ) priority = max;\r
7665       stream_.callbackInfo.priority = priority;\r
7666     }\r
7667 #endif\r
7668 \r
7669     stream_.callbackInfo.isRunning = true;\r
7670     result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
7671     pthread_attr_destroy( &attr );\r
7672     if ( result ) {\r
7673       stream_.callbackInfo.isRunning = false;\r
7674       errorText_ = "RtApiAlsa::error creating callback thread!";\r
7675       goto error;\r
7676     }\r
7677   }\r
7678 \r
7679   return SUCCESS;\r
7680 \r
7681  error:\r
7682   if ( apiInfo ) {\r
7683     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7684     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7685     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7686     delete apiInfo;\r
7687     stream_.apiHandle = 0;\r
7688   }\r
7689 \r
7690   if ( phandle) snd_pcm_close( phandle );\r
7691 \r
7692   for ( int i=0; i<2; i++ ) {\r
7693     if ( stream_.userBuffer[i] ) {\r
7694       free( stream_.userBuffer[i] );\r
7695       stream_.userBuffer[i] = 0;\r
7696     }\r
7697   }\r
7698 \r
7699   if ( stream_.deviceBuffer ) {\r
7700     free( stream_.deviceBuffer );\r
7701     stream_.deviceBuffer = 0;\r
7702   }\r
7703 \r
7704   stream_.state = STREAM_CLOSED;\r
7705   return FAILURE;\r
7706 }\r
7707 \r
7708 void RtApiAlsa :: closeStream()\r
7709 {\r
7710   if ( stream_.state == STREAM_CLOSED ) {\r
7711     errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
7712     error( RtAudioError::WARNING );\r
7713     return;\r
7714   }\r
7715 \r
7716   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7717   stream_.callbackInfo.isRunning = false;\r
7718   MUTEX_LOCK( &stream_.mutex );\r
7719   if ( stream_.state == STREAM_STOPPED ) {\r
7720     apiInfo->runnable = true;\r
7721     pthread_cond_signal( &apiInfo->runnable_cv );\r
7722   }\r
7723   MUTEX_UNLOCK( &stream_.mutex );\r
7724   pthread_join( stream_.callbackInfo.thread, NULL );\r
7725 \r
7726   if ( stream_.state == STREAM_RUNNING ) {\r
7727     stream_.state = STREAM_STOPPED;\r
7728     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
7729       snd_pcm_drop( apiInfo->handles[0] );\r
7730     if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
7731       snd_pcm_drop( apiInfo->handles[1] );\r
7732   }\r
7733 \r
7734   if ( apiInfo ) {\r
7735     pthread_cond_destroy( &apiInfo->runnable_cv );\r
7736     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
7737     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
7738     delete apiInfo;\r
7739     stream_.apiHandle = 0;\r
7740   }\r
7741 \r
7742   for ( int i=0; i<2; i++ ) {\r
7743     if ( stream_.userBuffer[i] ) {\r
7744       free( stream_.userBuffer[i] );\r
7745       stream_.userBuffer[i] = 0;\r
7746     }\r
7747   }\r
7748 \r
7749   if ( stream_.deviceBuffer ) {\r
7750     free( stream_.deviceBuffer );\r
7751     stream_.deviceBuffer = 0;\r
7752   }\r
7753 \r
7754   stream_.mode = UNINITIALIZED;\r
7755   stream_.state = STREAM_CLOSED;\r
7756 }\r
7757 \r
7758 void RtApiAlsa :: startStream()\r
7759 {\r
7760   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
7761 \r
7762   verifyStream();\r
7763   RtApi::startStream();\r
7764   if ( stream_.state == STREAM_RUNNING ) {\r
7765     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
7766     error( RtAudioError::WARNING );\r
7767     return;\r
7768   }\r
7769 \r
7770   MUTEX_LOCK( &stream_.mutex );\r
7771 \r
7772   int result = 0;\r
7773   snd_pcm_state_t state;\r
7774   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7775   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7776   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7777     state = snd_pcm_state( handle[0] );\r
7778     if ( state != SND_PCM_STATE_PREPARED ) {\r
7779       result = snd_pcm_prepare( handle[0] );\r
7780       if ( result < 0 ) {\r
7781         errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
7782         errorText_ = errorStream_.str();\r
7783         goto unlock;\r
7784       }\r
7785     }\r
7786   }\r
7787 \r
7788   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7789     result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
7790     state = snd_pcm_state( handle[1] );\r
7791     if ( state != SND_PCM_STATE_PREPARED ) {\r
7792       result = snd_pcm_prepare( handle[1] );\r
7793       if ( result < 0 ) {\r
7794         errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
7795         errorText_ = errorStream_.str();\r
7796         goto unlock;\r
7797       }\r
7798     }\r
7799   }\r
7800 \r
7801   stream_.state = STREAM_RUNNING;\r
7802 \r
7803  unlock:\r
7804   apiInfo->runnable = true;\r
7805   pthread_cond_signal( &apiInfo->runnable_cv );\r
7806   MUTEX_UNLOCK( &stream_.mutex );\r
7807 \r
7808   if ( result >= 0 ) return;\r
7809   error( RtAudioError::SYSTEM_ERROR );\r
7810 }\r
7811 \r
7812 void RtApiAlsa :: stopStream()\r
7813 {\r
7814   verifyStream();\r
7815   if ( stream_.state == STREAM_STOPPED ) {\r
7816     errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
7817     error( RtAudioError::WARNING );\r
7818     return;\r
7819   }\r
7820 \r
7821   stream_.state = STREAM_STOPPED;\r
7822   MUTEX_LOCK( &stream_.mutex );\r
7823 \r
7824   int result = 0;\r
7825   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7826   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7827   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7828     if ( apiInfo->synchronized )\r
7829       result = snd_pcm_drop( handle[0] );\r
7830     else\r
7831       result = snd_pcm_drain( handle[0] );\r
7832     if ( result < 0 ) {\r
7833       errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
7834       errorText_ = errorStream_.str();\r
7835       goto unlock;\r
7836     }\r
7837   }\r
7838 \r
7839   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7840     result = snd_pcm_drop( handle[1] );\r
7841     if ( result < 0 ) {\r
7842       errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
7843       errorText_ = errorStream_.str();\r
7844       goto unlock;\r
7845     }\r
7846   }\r
7847 \r
7848  unlock:\r
7849   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7850   MUTEX_UNLOCK( &stream_.mutex );\r
7851 \r
7852   if ( result >= 0 ) return;\r
7853   error( RtAudioError::SYSTEM_ERROR );\r
7854 }\r
7855 \r
7856 void RtApiAlsa :: abortStream()\r
7857 {\r
7858   verifyStream();\r
7859   if ( stream_.state == STREAM_STOPPED ) {\r
7860     errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
7861     error( RtAudioError::WARNING );\r
7862     return;\r
7863   }\r
7864 \r
7865   stream_.state = STREAM_STOPPED;\r
7866   MUTEX_LOCK( &stream_.mutex );\r
7867 \r
7868   int result = 0;\r
7869   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7870   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
7871   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
7872     result = snd_pcm_drop( handle[0] );\r
7873     if ( result < 0 ) {\r
7874       errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
7875       errorText_ = errorStream_.str();\r
7876       goto unlock;\r
7877     }\r
7878   }\r
7879 \r
7880   if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
7881     result = snd_pcm_drop( handle[1] );\r
7882     if ( result < 0 ) {\r
7883       errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
7884       errorText_ = errorStream_.str();\r
7885       goto unlock;\r
7886     }\r
7887   }\r
7888 \r
7889  unlock:\r
7890   apiInfo->runnable = false; // fixes high CPU usage when stopped\r
7891   MUTEX_UNLOCK( &stream_.mutex );\r
7892 \r
7893   if ( result >= 0 ) return;\r
7894   error( RtAudioError::SYSTEM_ERROR );\r
7895 }\r
7896 \r
7897 void RtApiAlsa :: callbackEvent()\r
7898 {\r
7899   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
7900   if ( stream_.state == STREAM_STOPPED ) {\r
7901     MUTEX_LOCK( &stream_.mutex );\r
7902     while ( !apiInfo->runnable )\r
7903       pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
7904 \r
7905     if ( stream_.state != STREAM_RUNNING ) {\r
7906       MUTEX_UNLOCK( &stream_.mutex );\r
7907       return;\r
7908     }\r
7909     MUTEX_UNLOCK( &stream_.mutex );\r
7910   }\r
7911 \r
7912   if ( stream_.state == STREAM_CLOSED ) {\r
7913     errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
7914     error( RtAudioError::WARNING );\r
7915     return;\r
7916   }\r
7917 \r
7918   int doStopStream = 0;\r
7919   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
7920   double streamTime = getStreamTime();\r
7921   RtAudioStreamStatus status = 0;\r
7922   if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
7923     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
7924     apiInfo->xrun[0] = false;\r
7925   }\r
7926   if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
7927     status |= RTAUDIO_INPUT_OVERFLOW;\r
7928     apiInfo->xrun[1] = false;\r
7929   }\r
7930   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
7931                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
7932 \r
7933   if ( doStopStream == 2 ) {\r
7934     abortStream();\r
7935     return;\r
7936   }\r
7937 \r
7938   MUTEX_LOCK( &stream_.mutex );\r
7939 \r
7940   // The state might change while waiting on a mutex.\r
7941   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
7942 \r
7943   int result;\r
7944   char *buffer;\r
7945   int channels;\r
7946   snd_pcm_t **handle;\r
7947   snd_pcm_sframes_t frames;\r
7948   RtAudioFormat format;\r
7949   handle = (snd_pcm_t **) apiInfo->handles;\r
7950 \r
7951   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
7952 \r
7953     // Setup parameters.\r
7954     if ( stream_.doConvertBuffer[1] ) {\r
7955       buffer = stream_.deviceBuffer;\r
7956       channels = stream_.nDeviceChannels[1];\r
7957       format = stream_.deviceFormat[1];\r
7958     }\r
7959     else {\r
7960       buffer = stream_.userBuffer[1];\r
7961       channels = stream_.nUserChannels[1];\r
7962       format = stream_.userFormat;\r
7963     }\r
7964 \r
7965     // Read samples from device in interleaved/non-interleaved format.\r
7966     if ( stream_.deviceInterleaved[1] )\r
7967       result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
7968     else {\r
7969       void *bufs[channels];\r
7970       size_t offset = stream_.bufferSize * formatBytes( format );\r
7971       for ( int i=0; i<channels; i++ )\r
7972         bufs[i] = (void *) (buffer + (i * offset));\r
7973       result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
7974     }\r
7975 \r
7976     if ( result < (int) stream_.bufferSize ) {\r
7977       // Either an error or overrun occured.\r
7978       if ( result == -EPIPE ) {\r
7979         snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
7980         if ( state == SND_PCM_STATE_XRUN ) {\r
7981           apiInfo->xrun[1] = true;\r
7982           result = snd_pcm_prepare( handle[1] );\r
7983           if ( result < 0 ) {\r
7984             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
7985             errorText_ = errorStream_.str();\r
7986           }\r
7987         }\r
7988         else {\r
7989           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
7990           errorText_ = errorStream_.str();\r
7991         }\r
7992       }\r
7993       else {\r
7994         errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
7995         errorText_ = errorStream_.str();\r
7996       }\r
7997       error( RtAudioError::WARNING );\r
7998       goto tryOutput;\r
7999     }\r
8000 \r
8001     // Do byte swapping if necessary.\r
8002     if ( stream_.doByteSwap[1] )\r
8003       byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
8004 \r
8005     // Do buffer conversion if necessary.\r
8006     if ( stream_.doConvertBuffer[1] )\r
8007       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
8008 \r
8009     // Check stream latency\r
8010     result = snd_pcm_delay( handle[1], &frames );\r
8011     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
8012   }\r
8013 \r
8014  tryOutput:\r
8015 \r
8016   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8017 \r
8018     // Setup parameters and do buffer conversion if necessary.\r
8019     if ( stream_.doConvertBuffer[0] ) {\r
8020       buffer = stream_.deviceBuffer;\r
8021       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
8022       channels = stream_.nDeviceChannels[0];\r
8023       format = stream_.deviceFormat[0];\r
8024     }\r
8025     else {\r
8026       buffer = stream_.userBuffer[0];\r
8027       channels = stream_.nUserChannels[0];\r
8028       format = stream_.userFormat;\r
8029     }\r
8030 \r
8031     // Do byte swapping if necessary.\r
8032     if ( stream_.doByteSwap[0] )\r
8033       byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
8034 \r
8035     // Write samples to device in interleaved/non-interleaved format.\r
8036     if ( stream_.deviceInterleaved[0] )\r
8037       result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
8038     else {\r
8039       void *bufs[channels];\r
8040       size_t offset = stream_.bufferSize * formatBytes( format );\r
8041       for ( int i=0; i<channels; i++ )\r
8042         bufs[i] = (void *) (buffer + (i * offset));\r
8043       result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
8044     }\r
8045 \r
8046     if ( result < (int) stream_.bufferSize ) {\r
8047       // Either an error or underrun occured.\r
8048       if ( result == -EPIPE ) {\r
8049         snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
8050         if ( state == SND_PCM_STATE_XRUN ) {\r
8051           apiInfo->xrun[0] = true;\r
8052           result = snd_pcm_prepare( handle[0] );\r
8053           if ( result < 0 ) {\r
8054             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
8055             errorText_ = errorStream_.str();\r
8056           }\r
8057           else\r
8058             errorText_ =  "RtApiAlsa::callbackEvent: audio write error, underrun.";\r
8059         }\r
8060         else {\r
8061           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
8062           errorText_ = errorStream_.str();\r
8063         }\r
8064       }\r
8065       else {\r
8066         errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
8067         errorText_ = errorStream_.str();\r
8068       }\r
8069       error( RtAudioError::WARNING );\r
8070       goto unlock;\r
8071     }\r
8072 \r
8073     // Check stream latency\r
8074     result = snd_pcm_delay( handle[0], &frames );\r
8075     if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
8076   }\r
8077 \r
8078  unlock:\r
8079   MUTEX_UNLOCK( &stream_.mutex );\r
8080 \r
8081   RtApi::tickStreamTime();\r
8082   if ( doStopStream == 1 ) this->stopStream();\r
8083 }\r
8084 \r
8085 static void *alsaCallbackHandler( void *ptr )\r
8086 {\r
8087   CallbackInfo *info = (CallbackInfo *) ptr;\r
8088   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
8089   bool *isRunning = &info->isRunning;\r
8090 \r
8091 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
8092   if ( info->doRealtime ) {\r
8093     pthread_t tID = pthread_self();      // ID of this thread\r
8094     sched_param prio = { info->priority }; // scheduling priority of thread\r
8095     pthread_setschedparam( tID, SCHED_RR, &prio );\r
8096   }\r
8097 #endif\r
8098 \r
8099   while ( *isRunning == true ) {\r
8100     pthread_testcancel();\r
8101     object->callbackEvent();\r
8102   }\r
8103 \r
8104   pthread_exit( NULL );\r
8105 }\r
8106 \r
8107 //******************** End of __LINUX_ALSA__ *********************//\r
8108 #endif\r
8109 \r
8110 #if defined(__LINUX_PULSE__)\r
8111 \r
8112 // Code written by Peter Meerwald, pmeerw@pmeerw.net\r
8113 // and Tristan Matthews.\r
8114 \r
8115 #include <pulse/error.h>\r
8116 #include <pulse/simple.h>\r
8117 #include <pulse/pulseaudio.h>\r
8118 #include <cstdio>\r
8119 \r
8120 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
8121                                                       44100, 48000, 96000, 0};\r
8122 \r
8123 struct rtaudio_pa_format_mapping_t {\r
8124   RtAudioFormat rtaudio_format;\r
8125   pa_sample_format_t pa_format;\r
8126 };\r
8127 \r
8128 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
8129   {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
8130   {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
8131   {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
8132   {0, PA_SAMPLE_INVALID}};\r
8133 \r
8134 struct PulseAudioHandle {\r
8135   pa_simple *s_play;\r
8136   pa_simple *s_rec;\r
8137   pthread_t thread;\r
8138   pthread_cond_t runnable_cv;\r
8139   bool runnable;\r
8140   PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
8141 };\r
8142 \r
8143 RtApiPulse::~RtApiPulse()\r
8144 {\r
8145   if ( stream_.state != STREAM_CLOSED )\r
8146     closeStream();\r
8147 }\r
8148 \r
8149 unsigned int RtApiPulse::getDeviceCount( void )\r
8150 {\r
8151   return 1;\r
8152 }\r
8153 \r
8154 void RtApiPulse::sinkInfoCallback(pa_context*, const pa_sink_info* info, int, void* arg)\r
8155 {\r
8156   RtApiPulse* api = (RtApiPulse *) arg;\r
8157   if (info) {\r
8158     api->channels_ = info->sample_spec.channels;\r
8159   }\r
8160   pa_threaded_mainloop_signal(api->mainloop_, 0);\r
8161 }\r
8162 \r
8163 void RtApiPulse::contextStateCallback(pa_context* c, void* arg)\r
8164 {\r
8165   pa_threaded_mainloop* mainloop = (pa_threaded_mainloop*) arg;\r
8166 \r
8167   switch (pa_context_get_state(c)) {\r
8168   case PA_CONTEXT_READY:\r
8169   case PA_CONTEXT_TERMINATED:\r
8170   case PA_CONTEXT_FAILED:\r
8171     pa_threaded_mainloop_signal(mainloop, 0);\r
8172     break;\r
8173   default:\r
8174     break;\r
8175   }\r
8176 }\r
8177 \r
8178 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
8179 {\r
8180   /* Set up some defaults in case we crash and burn */\r
8181   RtAudio::DeviceInfo info;\r
8182   info.probed = true;\r
8183   info.name = "PulseAudio";\r
8184   info.outputChannels = 2;\r
8185   info.inputChannels = 2;\r
8186   info.duplexChannels = 2;\r
8187   info.isDefaultOutput = true;\r
8188   info.isDefaultInput = true;\r
8189 \r
8190   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
8191     info.sampleRates.push_back( *sr );\r
8192 \r
8193   info.preferredSampleRate = 48000;\r
8194   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
8195 \r
8196   /* Get the number of output channels from pulseaudio.  A simple task, you say?\r
8197      "What is your mainloop?" */\r
8198   mainloop_ = pa_threaded_mainloop_new();\r
8199   if (!mainloop_) {\r
8200     return info;\r
8201   }\r
8202 \r
8203   pa_threaded_mainloop_start(mainloop_);\r
8204   pa_threaded_mainloop_lock(mainloop_);\r
8205 \r
8206   /* "And what is your context?" */\r
8207   pa_context* context = pa_context_new(pa_threaded_mainloop_get_api(mainloop_), "RtAudio");\r
8208   if (!context) {\r
8209     pa_threaded_mainloop_unlock(mainloop_);\r
8210     pa_threaded_mainloop_stop(mainloop_);\r
8211     pa_threaded_mainloop_free(mainloop_);\r
8212     mainloop_ = 0;\r
8213     return info;\r
8214   }\r
8215 \r
8216   pa_context_set_state_callback(context, contextStateCallback, mainloop_);\r
8217 \r
8218   pa_context_connect(context, 0, (pa_context_flags_t) 0, 0);\r
8219 \r
8220   /* "And what is your favourite colour?" */\r
8221   int connected = 0;\r
8222   pa_context_state_t state = pa_context_get_state(context);\r
8223   for (; !connected; state = pa_context_get_state(context)) {\r
8224     switch (state) {\r
8225     case PA_CONTEXT_READY:\r
8226       connected = 1;\r
8227       continue;\r
8228     case PA_CONTEXT_FAILED:\r
8229     case PA_CONTEXT_TERMINATED:\r
8230       /* Blue! No, I mean red! */\r
8231       pa_threaded_mainloop_unlock(mainloop_);\r
8232       pa_context_disconnect(context);\r
8233       pa_context_unref(context);\r
8234       pa_threaded_mainloop_stop(mainloop_);\r
8235       pa_threaded_mainloop_free(mainloop_);\r
8236       mainloop_ = 0;\r
8237       return info;\r
8238     default:\r
8239       pa_threaded_mainloop_wait(mainloop_);\r
8240       break;\r
8241     }\r
8242   }\r
8243 \r
8244   pa_operation* op = pa_context_get_sink_info_by_index(context, 0, sinkInfoCallback, this);\r
8245 \r
8246   if (op) {\r
8247     pa_operation_unref(op);\r
8248   }\r
8249 \r
8250   pa_threaded_mainloop_wait(mainloop_);\r
8251   pa_threaded_mainloop_unlock(mainloop_);\r
8252 \r
8253   pa_context_disconnect(context);\r
8254   pa_context_unref(context);\r
8255 \r
8256   pa_threaded_mainloop_stop(mainloop_);\r
8257   pa_threaded_mainloop_free(mainloop_);\r
8258   mainloop_ = 0;\r
8259 \r
8260   info.outputChannels = channels_;\r
8261 \r
8262   return info;\r
8263 }\r
8264 \r
8265 static void *pulseaudio_callback( void * user )\r
8266 {\r
8267   CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
8268   RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
8269   volatile bool *isRunning = &cbi->isRunning;\r
8270 \r
8271   while ( *isRunning ) {\r
8272     pthread_testcancel();\r
8273     context->callbackEvent();\r
8274   }\r
8275 \r
8276   pthread_exit( NULL );\r
8277 }\r
8278 \r
8279 void RtApiPulse::closeStream( void )\r
8280 {\r
8281   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8282 \r
8283   stream_.callbackInfo.isRunning = false;\r
8284   if ( pah ) {\r
8285     MUTEX_LOCK( &stream_.mutex );\r
8286     if ( stream_.state == STREAM_STOPPED ) {\r
8287       pah->runnable = true;\r
8288       pthread_cond_signal( &pah->runnable_cv );\r
8289     }\r
8290     MUTEX_UNLOCK( &stream_.mutex );\r
8291 \r
8292     pthread_join( pah->thread, 0 );\r
8293     if ( pah->s_play ) {\r
8294       pa_simple_flush( pah->s_play, NULL );\r
8295       pa_simple_free( pah->s_play );\r
8296     }\r
8297     if ( pah->s_rec )\r
8298       pa_simple_free( pah->s_rec );\r
8299 \r
8300     pthread_cond_destroy( &pah->runnable_cv );\r
8301     delete pah;\r
8302     stream_.apiHandle = 0;\r
8303   }\r
8304 \r
8305   if ( stream_.userBuffer[0] ) {\r
8306     free( stream_.userBuffer[0] );\r
8307     stream_.userBuffer[0] = 0;\r
8308   }\r
8309   if ( stream_.userBuffer[1] ) {\r
8310     free( stream_.userBuffer[1] );\r
8311     stream_.userBuffer[1] = 0;\r
8312   }\r
8313 \r
8314   stream_.state = STREAM_CLOSED;\r
8315   stream_.mode = UNINITIALIZED;\r
8316 }\r
8317 \r
8318 void RtApiPulse::callbackEvent( void )\r
8319 {\r
8320   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8321 \r
8322   if ( stream_.state == STREAM_STOPPED ) {\r
8323     MUTEX_LOCK( &stream_.mutex );\r
8324     while ( !pah->runnable )\r
8325       pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
8326 \r
8327     if ( stream_.state != STREAM_RUNNING ) {\r
8328       MUTEX_UNLOCK( &stream_.mutex );\r
8329       return;\r
8330     }\r
8331     MUTEX_UNLOCK( &stream_.mutex );\r
8332   }\r
8333 \r
8334   if ( stream_.state == STREAM_CLOSED ) {\r
8335     errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
8336       "this shouldn't happen!";\r
8337     error( RtAudioError::WARNING );\r
8338     return;\r
8339   }\r
8340 \r
8341   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
8342   double streamTime = getStreamTime();\r
8343   RtAudioStreamStatus status = 0;\r
8344   int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
8345                                stream_.bufferSize, streamTime, status,\r
8346                                stream_.callbackInfo.userData );\r
8347 \r
8348   if ( doStopStream == 2 ) {\r
8349     abortStream();\r
8350     return;\r
8351   }\r
8352 \r
8353   MUTEX_LOCK( &stream_.mutex );\r
8354   void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
8355   void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
8356 \r
8357   if ( stream_.state != STREAM_RUNNING )\r
8358     goto unlock;\r
8359 \r
8360   int pa_error;\r
8361   size_t bytes;\r
8362   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
8363     if ( stream_.doConvertBuffer[OUTPUT] ) {\r
8364         convertBuffer( stream_.deviceBuffer,\r
8365                        stream_.userBuffer[OUTPUT],\r
8366                        stream_.convertInfo[OUTPUT] );\r
8367         bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
8368                 formatBytes( stream_.deviceFormat[OUTPUT] );\r
8369     } else\r
8370         bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
8371                 formatBytes( stream_.userFormat );\r
8372 \r
8373     if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
8374       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
8375         pa_strerror( pa_error ) << ".";\r
8376       errorText_ = errorStream_.str();\r
8377       error( RtAudioError::WARNING );\r
8378     }\r
8379   }\r
8380 \r
8381   if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
8382     if ( stream_.doConvertBuffer[INPUT] )\r
8383       bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
8384         formatBytes( stream_.deviceFormat[INPUT] );\r
8385     else\r
8386       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
8387         formatBytes( stream_.userFormat );\r
8388 \r
8389     if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
8390       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
8391         pa_strerror( pa_error ) << ".";\r
8392       errorText_ = errorStream_.str();\r
8393       error( RtAudioError::WARNING );\r
8394     }\r
8395     if ( stream_.doConvertBuffer[INPUT] ) {\r
8396       convertBuffer( stream_.userBuffer[INPUT],\r
8397                      stream_.deviceBuffer,\r
8398                      stream_.convertInfo[INPUT] );\r
8399     }\r
8400   }\r
8401 \r
8402  unlock:\r
8403   MUTEX_UNLOCK( &stream_.mutex );\r
8404   RtApi::tickStreamTime();\r
8405 \r
8406   if (pah->s_play) {\r
8407     int e = 0;\r
8408     pa_usec_t const lat = pa_simple_get_latency(pah->s_play, &e);\r
8409     if (e == 0) {\r
8410       stream_.latency[0] = lat * stream_.sampleRate / 1000000;\r
8411     }\r
8412   }\r
8413 \r
8414   if ( doStopStream == 1 )\r
8415     stopStream();\r
8416 }\r
8417 \r
8418 void RtApiPulse::startStream( void )\r
8419 {\r
8420   RtApi::startStream();\r
8421 \r
8422   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8423 \r
8424   if ( stream_.state == STREAM_CLOSED ) {\r
8425     errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
8426     error( RtAudioError::INVALID_USE );\r
8427     return;\r
8428   }\r
8429   if ( stream_.state == STREAM_RUNNING ) {\r
8430     errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
8431     error( RtAudioError::WARNING );\r
8432     return;\r
8433   }\r
8434 \r
8435   MUTEX_LOCK( &stream_.mutex );\r
8436 \r
8437   stream_.state = STREAM_RUNNING;\r
8438 \r
8439   pah->runnable = true;\r
8440   pthread_cond_signal( &pah->runnable_cv );\r
8441   MUTEX_UNLOCK( &stream_.mutex );\r
8442 }\r
8443 \r
8444 void RtApiPulse::stopStream( void )\r
8445 {\r
8446   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8447 \r
8448   if ( stream_.state == STREAM_CLOSED ) {\r
8449     errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
8450     error( RtAudioError::INVALID_USE );\r
8451     return;\r
8452   }\r
8453   if ( stream_.state == STREAM_STOPPED ) {\r
8454     errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
8455     error( RtAudioError::WARNING );\r
8456     return;\r
8457   }\r
8458 \r
8459   stream_.state = STREAM_STOPPED;\r
8460   pah->runnable = false;\r
8461   MUTEX_LOCK( &stream_.mutex );\r
8462 \r
8463   if ( pah && pah->s_play ) {\r
8464     int pa_error;\r
8465     if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
8466       errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
8467         pa_strerror( pa_error ) << ".";\r
8468       errorText_ = errorStream_.str();\r
8469       MUTEX_UNLOCK( &stream_.mutex );\r
8470       error( RtAudioError::SYSTEM_ERROR );\r
8471       return;\r
8472     }\r
8473   }\r
8474 \r
8475   stream_.state = STREAM_STOPPED;\r
8476   MUTEX_UNLOCK( &stream_.mutex );\r
8477 }\r
8478 \r
8479 void RtApiPulse::abortStream( void )\r
8480 {\r
8481   PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
8482 \r
8483   if ( stream_.state == STREAM_CLOSED ) {\r
8484     errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
8485     error( RtAudioError::INVALID_USE );\r
8486     return;\r
8487   }\r
8488   if ( stream_.state == STREAM_STOPPED ) {\r
8489     errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
8490     error( RtAudioError::WARNING );\r
8491     return;\r
8492   }\r
8493 \r
8494   stream_.state = STREAM_STOPPED;\r
8495   pah->runnable = false;\r
8496   MUTEX_LOCK( &stream_.mutex );\r
8497 \r
8498   if ( pah && pah->s_play ) {\r
8499     int pa_error;\r
8500     if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
8501       errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
8502         pa_strerror( pa_error ) << ".";\r
8503       errorText_ = errorStream_.str();\r
8504       MUTEX_UNLOCK( &stream_.mutex );\r
8505       error( RtAudioError::SYSTEM_ERROR );\r
8506       return;\r
8507     }\r
8508   }\r
8509 \r
8510   stream_.state = STREAM_STOPPED;\r
8511   MUTEX_UNLOCK( &stream_.mutex );\r
8512 }\r
8513 \r
8514 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
8515                                   unsigned int channels, unsigned int firstChannel,\r
8516                                   unsigned int sampleRate, RtAudioFormat format,\r
8517                                   unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
8518 {\r
8519   PulseAudioHandle *pah = 0;\r
8520   unsigned long bufferBytes = 0;\r
8521   pa_sample_spec ss;\r
8522 \r
8523   if ( device != 0 ) return false;\r
8524   if ( mode != INPUT && mode != OUTPUT ) return false;\r
8525   ss.channels = channels;\r
8526 \r
8527   if ( firstChannel != 0 ) return false;\r
8528 \r
8529   bool sr_found = false;\r
8530   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
8531     if ( sampleRate == *sr ) {\r
8532       sr_found = true;\r
8533       stream_.sampleRate = sampleRate;\r
8534       ss.rate = sampleRate;\r
8535       break;\r
8536     }\r
8537   }\r
8538   if ( !sr_found ) {\r
8539     errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
8540     return false;\r
8541   }\r
8542 \r
8543   bool sf_found = 0;\r
8544   for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
8545         sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
8546     if ( format == sf->rtaudio_format ) {\r
8547       sf_found = true;\r
8548       stream_.userFormat = sf->rtaudio_format;\r
8549       stream_.deviceFormat[mode] = stream_.userFormat;\r
8550       ss.format = sf->pa_format;\r
8551       break;\r
8552     }\r
8553   }\r
8554   if ( !sf_found ) { // Use internal data format conversion.\r
8555     stream_.userFormat = format;\r
8556     stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
8557     ss.format = PA_SAMPLE_FLOAT32LE;\r
8558   }\r
8559 \r
8560   // Set other stream parameters.\r
8561   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
8562   else stream_.userInterleaved = true;\r
8563   stream_.deviceInterleaved[mode] = true;\r
8564   stream_.nBuffers = 1;\r
8565   stream_.doByteSwap[mode] = false;\r
8566   stream_.nUserChannels[mode] = channels;\r
8567   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
8568   stream_.channelOffset[mode] = 0;\r
8569   std::string streamName = "RtAudio";\r
8570 \r
8571   // Set flags for buffer conversion.\r
8572   stream_.doConvertBuffer[mode] = false;\r
8573   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
8574     stream_.doConvertBuffer[mode] = true;\r
8575   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
8576     stream_.doConvertBuffer[mode] = true;\r
8577 \r
8578   // Allocate necessary internal buffers.\r
8579   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
8580   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
8581   if ( stream_.userBuffer[mode] == NULL ) {\r
8582     errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
8583     goto error;\r
8584   }\r
8585   stream_.bufferSize = *bufferSize;\r
8586 \r
8587   if ( stream_.doConvertBuffer[mode] ) {\r
8588 \r
8589     bool makeBuffer = true;\r
8590     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
8591     if ( mode == INPUT ) {\r
8592       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
8593         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
8594         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
8595       }\r
8596     }\r
8597 \r
8598     if ( makeBuffer ) {\r
8599       bufferBytes *= *bufferSize;\r
8600       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
8601       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
8602       if ( stream_.deviceBuffer == NULL ) {\r
8603         errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
8604         goto error;\r
8605       }\r
8606     }\r
8607   }\r
8608 \r
8609   stream_.device[mode] = device;\r
8610 \r
8611   // Setup the buffer conversion information structure.\r
8612   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
8613 \r
8614   if ( !stream_.apiHandle ) {\r
8615     PulseAudioHandle *pah = new PulseAudioHandle;\r
8616     if ( !pah ) {\r
8617       errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
8618       goto error;\r
8619     }\r
8620 \r
8621     stream_.apiHandle = pah;\r
8622     if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
8623       errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
8624       goto error;\r
8625     }\r
8626   }\r
8627   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
8628 \r
8629   int error;\r
8630   if ( options && !options->streamName.empty() ) streamName = options->streamName;\r
8631   switch ( mode ) {\r
8632   case INPUT:\r
8633     pa_buffer_attr buffer_attr;\r
8634     buffer_attr.fragsize = bufferBytes;\r
8635     buffer_attr.maxlength = -1;\r
8636     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
8637     if ( !pah->s_rec ) {\r
8638       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
8639       goto error;\r
8640     }\r
8641     break;\r
8642   case OUTPUT:\r
8643     /* XXX: hard-coded for DCP-o-matic */\r
8644     pa_channel_map map;\r
8645     pa_channel_map_init(&map);\r
8646     /* XXX: need to check 7.1 */\r
8647     map.channels = channels;\r
8648 \r
8649     if (channels > 0) {\r
8650       map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;\r
8651     }\r
8652     if (channels > 1) {\r
8653       map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;\r
8654     }\r
8655     if (channels > 2) {\r
8656       map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;\r
8657     }\r
8658     if (channels > 3) {\r
8659       map.map[3] = PA_CHANNEL_POSITION_LFE;\r
8660     }\r
8661     if (channels > 4) {\r
8662       map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;\r
8663     }\r
8664     if (channels > 5) {\r
8665       map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;\r
8666     }\r
8667     if (channels > 6) {\r
8668       map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;\r
8669     }\r
8670     if (channels > 7) {\r
8671       map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;\r
8672     }\r
8673 \r
8674     pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, &map, NULL, &error );\r
8675     if ( !pah->s_play ) {\r
8676       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
8677       goto error;\r
8678     }\r
8679     break;\r
8680   default:\r
8681     goto error;\r
8682   }\r
8683 \r
8684   if ( stream_.mode == UNINITIALIZED )\r
8685     stream_.mode = mode;\r
8686   else if ( stream_.mode == mode )\r
8687     goto error;\r
8688   else\r
8689     stream_.mode = DUPLEX;\r
8690 \r
8691   if ( !stream_.callbackInfo.isRunning ) {\r
8692     stream_.callbackInfo.object = this;\r
8693     stream_.callbackInfo.isRunning = true;\r
8694     if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
8695       errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
8696       goto error;\r
8697     }\r
8698   }\r
8699 \r
8700   stream_.state = STREAM_STOPPED;\r
8701   return true;\r
8702 \r
8703  error:\r
8704   if ( pah && stream_.callbackInfo.isRunning ) {\r
8705     pthread_cond_destroy( &pah->runnable_cv );\r
8706     delete pah;\r
8707     stream_.apiHandle = 0;\r
8708   }\r
8709 \r
8710   for ( int i=0; i<2; i++ ) {\r
8711     if ( stream_.userBuffer[i] ) {\r
8712       free( stream_.userBuffer[i] );\r
8713       stream_.userBuffer[i] = 0;\r
8714     }\r
8715   }\r
8716 \r
8717   if ( stream_.deviceBuffer ) {\r
8718     free( stream_.deviceBuffer );\r
8719     stream_.deviceBuffer = 0;\r
8720   }\r
8721 \r
8722   return FAILURE;\r
8723 }\r
8724 \r
8725 //******************** End of __LINUX_PULSE__ *********************//\r
8726 #endif\r
8727 \r
8728 #if defined(__LINUX_OSS__)\r
8729 \r
8730 #include <unistd.h>\r
8731 #include <sys/ioctl.h>\r
8732 #include <unistd.h>\r
8733 #include <fcntl.h>\r
8734 #include <sys/soundcard.h>\r
8735 #include <errno.h>\r
8736 #include <math.h>\r
8737 \r
8738 static void *ossCallbackHandler(void * ptr);\r
8739 \r
8740 // A structure to hold various information related to the OSS API\r
8741 // implementation.\r
8742 struct OssHandle {\r
8743   int id[2];    // device ids\r
8744   bool xrun[2];\r
8745   bool triggered;\r
8746   pthread_cond_t runnable;\r
8747 \r
8748   OssHandle()\r
8749     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
8750 };\r
8751 \r
8752 RtApiOss :: RtApiOss()\r
8753 {\r
8754   // Nothing to do here.\r
8755 }\r
8756 \r
8757 RtApiOss :: ~RtApiOss()\r
8758 {\r
8759   if ( stream_.state != STREAM_CLOSED ) closeStream();\r
8760 }\r
8761 \r
8762 unsigned int RtApiOss :: getDeviceCount( void )\r
8763 {\r
8764   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8765   if ( mixerfd == -1 ) {\r
8766     errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
8767     error( RtAudioError::WARNING );\r
8768     return 0;\r
8769   }\r
8770 \r
8771   oss_sysinfo sysinfo;\r
8772   if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
8773     close( mixerfd );\r
8774     errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
8775     error( RtAudioError::WARNING );\r
8776     return 0;\r
8777   }\r
8778 \r
8779   close( mixerfd );\r
8780   return sysinfo.numaudios;\r
8781 }\r
8782 \r
8783 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
8784 {\r
8785   RtAudio::DeviceInfo info;\r
8786   info.probed = false;\r
8787 \r
8788   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8789   if ( mixerfd == -1 ) {\r
8790     errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
8791     error( RtAudioError::WARNING );\r
8792     return info;\r
8793   }\r
8794 \r
8795   oss_sysinfo sysinfo;\r
8796   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8797   if ( result == -1 ) {\r
8798     close( mixerfd );\r
8799     errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
8800     error( RtAudioError::WARNING );\r
8801     return info;\r
8802   }\r
8803 \r
8804   unsigned nDevices = sysinfo.numaudios;\r
8805   if ( nDevices == 0 ) {\r
8806     close( mixerfd );\r
8807     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
8808     error( RtAudioError::INVALID_USE );\r
8809     return info;\r
8810   }\r
8811 \r
8812   if ( device >= nDevices ) {\r
8813     close( mixerfd );\r
8814     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
8815     error( RtAudioError::INVALID_USE );\r
8816     return info;\r
8817   }\r
8818 \r
8819   oss_audioinfo ainfo;\r
8820   ainfo.dev = device;\r
8821   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8822   close( mixerfd );\r
8823   if ( result == -1 ) {\r
8824     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8825     errorText_ = errorStream_.str();\r
8826     error( RtAudioError::WARNING );\r
8827     return info;\r
8828   }\r
8829 \r
8830   // Probe channels\r
8831   if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
8832   if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
8833   if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
8834     if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
8835       info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
8836   }\r
8837 \r
8838   // Probe data formats ... do for input\r
8839   unsigned long mask = ainfo.iformats;\r
8840   if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
8841     info.nativeFormats |= RTAUDIO_SINT16;\r
8842   if ( mask & AFMT_S8 )\r
8843     info.nativeFormats |= RTAUDIO_SINT8;\r
8844   if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
8845     info.nativeFormats |= RTAUDIO_SINT32;\r
8846   if ( mask & AFMT_FLOAT )\r
8847     info.nativeFormats |= RTAUDIO_FLOAT32;\r
8848   if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
8849     info.nativeFormats |= RTAUDIO_SINT24;\r
8850 \r
8851   // Check that we have at least one supported format\r
8852   if ( info.nativeFormats == 0 ) {\r
8853     errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
8854     errorText_ = errorStream_.str();\r
8855     error( RtAudioError::WARNING );\r
8856     return info;\r
8857   }\r
8858 \r
8859   // Probe the supported sample rates.\r
8860   info.sampleRates.clear();\r
8861   if ( ainfo.nrates ) {\r
8862     for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
8863       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8864         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
8865           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8866 \r
8867           if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
8868             info.preferredSampleRate = SAMPLE_RATES[k];\r
8869 \r
8870           break;\r
8871         }\r
8872       }\r
8873     }\r
8874   }\r
8875   else {\r
8876     // Check min and max rate values;\r
8877     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
8878       if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {\r
8879         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
8880 \r
8881         if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
8882           info.preferredSampleRate = SAMPLE_RATES[k];\r
8883       }\r
8884     }\r
8885   }\r
8886 \r
8887   if ( info.sampleRates.size() == 0 ) {\r
8888     errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
8889     errorText_ = errorStream_.str();\r
8890     error( RtAudioError::WARNING );\r
8891   }\r
8892   else {\r
8893     info.probed = true;\r
8894     info.name = ainfo.name;\r
8895   }\r
8896 \r
8897   return info;\r
8898 }\r
8899 \r
8900 \r
8901 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
8902                                   unsigned int firstChannel, unsigned int sampleRate,\r
8903                                   RtAudioFormat format, unsigned int *bufferSize,\r
8904                                   RtAudio::StreamOptions *options )\r
8905 {\r
8906   int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
8907   if ( mixerfd == -1 ) {\r
8908     errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
8909     return FAILURE;\r
8910   }\r
8911 \r
8912   oss_sysinfo sysinfo;\r
8913   int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
8914   if ( result == -1 ) {\r
8915     close( mixerfd );\r
8916     errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
8917     return FAILURE;\r
8918   }\r
8919 \r
8920   unsigned nDevices = sysinfo.numaudios;\r
8921   if ( nDevices == 0 ) {\r
8922     // This should not happen because a check is made before this function is called.\r
8923     close( mixerfd );\r
8924     errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
8925     return FAILURE;\r
8926   }\r
8927 \r
8928   if ( device >= nDevices ) {\r
8929     // This should not happen because a check is made before this function is called.\r
8930     close( mixerfd );\r
8931     errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
8932     return FAILURE;\r
8933   }\r
8934 \r
8935   oss_audioinfo ainfo;\r
8936   ainfo.dev = device;\r
8937   result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
8938   close( mixerfd );\r
8939   if ( result == -1 ) {\r
8940     errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
8941     errorText_ = errorStream_.str();\r
8942     return FAILURE;\r
8943   }\r
8944 \r
8945   // Check if device supports input or output\r
8946   if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
8947        ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
8948     if ( mode == OUTPUT )\r
8949       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
8950     else\r
8951       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
8952     errorText_ = errorStream_.str();\r
8953     return FAILURE;\r
8954   }\r
8955 \r
8956   int flags = 0;\r
8957   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
8958   if ( mode == OUTPUT )\r
8959     flags |= O_WRONLY;\r
8960   else { // mode == INPUT\r
8961     if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
8962       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
8963       close( handle->id[0] );\r
8964       handle->id[0] = 0;\r
8965       if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
8966         errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
8967         errorText_ = errorStream_.str();\r
8968         return FAILURE;\r
8969       }\r
8970       // Check that the number previously set channels is the same.\r
8971       if ( stream_.nUserChannels[0] != channels ) {\r
8972         errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
8973         errorText_ = errorStream_.str();\r
8974         return FAILURE;\r
8975       }\r
8976       flags |= O_RDWR;\r
8977     }\r
8978     else\r
8979       flags |= O_RDONLY;\r
8980   }\r
8981 \r
8982   // Set exclusive access if specified.\r
8983   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
8984 \r
8985   // Try to open the device.\r
8986   int fd;\r
8987   fd = open( ainfo.devnode, flags, 0 );\r
8988   if ( fd == -1 ) {\r
8989     if ( errno == EBUSY )\r
8990       errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
8991     else\r
8992       errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
8993     errorText_ = errorStream_.str();\r
8994     return FAILURE;\r
8995   }\r
8996 \r
8997   // For duplex operation, specifically set this mode (this doesn't seem to work).\r
8998   /*\r
8999     if ( flags | O_RDWR ) {\r
9000     result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
9001     if ( result == -1) {\r
9002     errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
9003     errorText_ = errorStream_.str();\r
9004     return FAILURE;\r
9005     }\r
9006     }\r
9007   */\r
9008 \r
9009   // Check the device channel support.\r
9010   stream_.nUserChannels[mode] = channels;\r
9011   if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
9012     close( fd );\r
9013     errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
9014     errorText_ = errorStream_.str();\r
9015     return FAILURE;\r
9016   }\r
9017 \r
9018   // Set the number of channels.\r
9019   int deviceChannels = channels + firstChannel;\r
9020   result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
9021   if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
9022     close( fd );\r
9023     errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
9024     errorText_ = errorStream_.str();\r
9025     return FAILURE;\r
9026   }\r
9027   stream_.nDeviceChannels[mode] = deviceChannels;\r
9028 \r
9029   // Get the data format mask\r
9030   int mask;\r
9031   result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
9032   if ( result == -1 ) {\r
9033     close( fd );\r
9034     errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
9035     errorText_ = errorStream_.str();\r
9036     return FAILURE;\r
9037   }\r
9038 \r
9039   // Determine how to set the device format.\r
9040   stream_.userFormat = format;\r
9041   int deviceFormat = -1;\r
9042   stream_.doByteSwap[mode] = false;\r
9043   if ( format == RTAUDIO_SINT8 ) {\r
9044     if ( mask & AFMT_S8 ) {\r
9045       deviceFormat = AFMT_S8;\r
9046       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
9047     }\r
9048   }\r
9049   else if ( format == RTAUDIO_SINT16 ) {\r
9050     if ( mask & AFMT_S16_NE ) {\r
9051       deviceFormat = AFMT_S16_NE;\r
9052       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9053     }\r
9054     else if ( mask & AFMT_S16_OE ) {\r
9055       deviceFormat = AFMT_S16_OE;\r
9056       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9057       stream_.doByteSwap[mode] = true;\r
9058     }\r
9059   }\r
9060   else if ( format == RTAUDIO_SINT24 ) {\r
9061     if ( mask & AFMT_S24_NE ) {\r
9062       deviceFormat = AFMT_S24_NE;\r
9063       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9064     }\r
9065     else if ( mask & AFMT_S24_OE ) {\r
9066       deviceFormat = AFMT_S24_OE;\r
9067       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9068       stream_.doByteSwap[mode] = true;\r
9069     }\r
9070   }\r
9071   else if ( format == RTAUDIO_SINT32 ) {\r
9072     if ( mask & AFMT_S32_NE ) {\r
9073       deviceFormat = AFMT_S32_NE;\r
9074       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9075     }\r
9076     else if ( mask & AFMT_S32_OE ) {\r
9077       deviceFormat = AFMT_S32_OE;\r
9078       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9079       stream_.doByteSwap[mode] = true;\r
9080     }\r
9081   }\r
9082 \r
9083   if ( deviceFormat == -1 ) {\r
9084     // The user requested format is not natively supported by the device.\r
9085     if ( mask & AFMT_S16_NE ) {\r
9086       deviceFormat = AFMT_S16_NE;\r
9087       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9088     }\r
9089     else if ( mask & AFMT_S32_NE ) {\r
9090       deviceFormat = AFMT_S32_NE;\r
9091       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9092     }\r
9093     else if ( mask & AFMT_S24_NE ) {\r
9094       deviceFormat = AFMT_S24_NE;\r
9095       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9096     }\r
9097     else if ( mask & AFMT_S16_OE ) {\r
9098       deviceFormat = AFMT_S16_OE;\r
9099       stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
9100       stream_.doByteSwap[mode] = true;\r
9101     }\r
9102     else if ( mask & AFMT_S32_OE ) {\r
9103       deviceFormat = AFMT_S32_OE;\r
9104       stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
9105       stream_.doByteSwap[mode] = true;\r
9106     }\r
9107     else if ( mask & AFMT_S24_OE ) {\r
9108       deviceFormat = AFMT_S24_OE;\r
9109       stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
9110       stream_.doByteSwap[mode] = true;\r
9111     }\r
9112     else if ( mask & AFMT_S8) {\r
9113       deviceFormat = AFMT_S8;\r
9114       stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
9115     }\r
9116   }\r
9117 \r
9118   if ( stream_.deviceFormat[mode] == 0 ) {\r
9119     // This really shouldn't happen ...\r
9120     close( fd );\r
9121     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
9122     errorText_ = errorStream_.str();\r
9123     return FAILURE;\r
9124   }\r
9125 \r
9126   // Set the data format.\r
9127   int temp = deviceFormat;\r
9128   result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
9129   if ( result == -1 || deviceFormat != temp ) {\r
9130     close( fd );\r
9131     errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
9132     errorText_ = errorStream_.str();\r
9133     return FAILURE;\r
9134   }\r
9135 \r
9136   // Attempt to set the buffer size.  According to OSS, the minimum\r
9137   // number of buffers is two.  The supposed minimum buffer size is 16\r
9138   // bytes, so that will be our lower bound.  The argument to this\r
9139   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
9140   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
9141   // We'll check the actual value used near the end of the setup\r
9142   // procedure.\r
9143   int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
9144   if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
9145   int buffers = 0;\r
9146   if ( options ) buffers = options->numberOfBuffers;\r
9147   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
9148   if ( buffers < 2 ) buffers = 3;\r
9149   temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
9150   result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
9151   if ( result == -1 ) {\r
9152     close( fd );\r
9153     errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
9154     errorText_ = errorStream_.str();\r
9155     return FAILURE;\r
9156   }\r
9157   stream_.nBuffers = buffers;\r
9158 \r
9159   // Save buffer size (in sample frames).\r
9160   *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
9161   stream_.bufferSize = *bufferSize;\r
9162 \r
9163   // Set the sample rate.\r
9164   int srate = sampleRate;\r
9165   result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
9166   if ( result == -1 ) {\r
9167     close( fd );\r
9168     errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
9169     errorText_ = errorStream_.str();\r
9170     return FAILURE;\r
9171   }\r
9172 \r
9173   // Verify the sample rate setup worked.\r
9174   if ( abs( srate - sampleRate ) > 100 ) {\r
9175     close( fd );\r
9176     errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
9177     errorText_ = errorStream_.str();\r
9178     return FAILURE;\r
9179   }\r
9180   stream_.sampleRate = sampleRate;\r
9181 \r
9182   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
9183     // We're doing duplex setup here.\r
9184     stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
9185     stream_.nDeviceChannels[0] = deviceChannels;\r
9186   }\r
9187 \r
9188   // Set interleaving parameters.\r
9189   stream_.userInterleaved = true;\r
9190   stream_.deviceInterleaved[mode] =  true;\r
9191   if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
9192     stream_.userInterleaved = false;\r
9193 \r
9194   // Set flags for buffer conversion\r
9195   stream_.doConvertBuffer[mode] = false;\r
9196   if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
9197     stream_.doConvertBuffer[mode] = true;\r
9198   if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
9199     stream_.doConvertBuffer[mode] = true;\r
9200   if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
9201        stream_.nUserChannels[mode] > 1 )\r
9202     stream_.doConvertBuffer[mode] = true;\r
9203 \r
9204   // Allocate the stream handles if necessary and then save.\r
9205   if ( stream_.apiHandle == 0 ) {\r
9206     try {\r
9207       handle = new OssHandle;\r
9208     }\r
9209     catch ( std::bad_alloc& ) {\r
9210       errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
9211       goto error;\r
9212     }\r
9213 \r
9214     if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
9215       errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
9216       goto error;\r
9217     }\r
9218 \r
9219     stream_.apiHandle = (void *) handle;\r
9220   }\r
9221   else {\r
9222     handle = (OssHandle *) stream_.apiHandle;\r
9223   }\r
9224   handle->id[mode] = fd;\r
9225 \r
9226   // Allocate necessary internal buffers.\r
9227   unsigned long bufferBytes;\r
9228   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
9229   stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
9230   if ( stream_.userBuffer[mode] == NULL ) {\r
9231     errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
9232     goto error;\r
9233   }\r
9234 \r
9235   if ( stream_.doConvertBuffer[mode] ) {\r
9236 \r
9237     bool makeBuffer = true;\r
9238     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
9239     if ( mode == INPUT ) {\r
9240       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
9241         unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
9242         if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
9243       }\r
9244     }\r
9245 \r
9246     if ( makeBuffer ) {\r
9247       bufferBytes *= *bufferSize;\r
9248       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
9249       stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
9250       if ( stream_.deviceBuffer == NULL ) {\r
9251         errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
9252         goto error;\r
9253       }\r
9254     }\r
9255   }\r
9256 \r
9257   stream_.device[mode] = device;\r
9258   stream_.state = STREAM_STOPPED;\r
9259 \r
9260   // Setup the buffer conversion information structure.\r
9261   if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
9262 \r
9263   // Setup thread if necessary.\r
9264   if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
9265     // We had already set up an output stream.\r
9266     stream_.mode = DUPLEX;\r
9267     if ( stream_.device[0] == device ) handle->id[0] = fd;\r
9268   }\r
9269   else {\r
9270     stream_.mode = mode;\r
9271 \r
9272     // Setup callback thread.\r
9273     stream_.callbackInfo.object = (void *) this;\r
9274 \r
9275     // Set the thread attributes for joinable and realtime scheduling\r
9276     // priority.  The higher priority will only take affect if the\r
9277     // program is run as root or suid.\r
9278     pthread_attr_t attr;\r
9279     pthread_attr_init( &attr );\r
9280     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
9281 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
9282     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
9283       struct sched_param param;\r
9284       int priority = options->priority;\r
9285       int min = sched_get_priority_min( SCHED_RR );\r
9286       int max = sched_get_priority_max( SCHED_RR );\r
9287       if ( priority < min ) priority = min;\r
9288       else if ( priority > max ) priority = max;\r
9289       param.sched_priority = priority;\r
9290       pthread_attr_setschedparam( &attr, &param );\r
9291       pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
9292     }\r
9293     else\r
9294       pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9295 #else\r
9296     pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
9297 #endif\r
9298 \r
9299     stream_.callbackInfo.isRunning = true;\r
9300     result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
9301     pthread_attr_destroy( &attr );\r
9302     if ( result ) {\r
9303       stream_.callbackInfo.isRunning = false;\r
9304       errorText_ = "RtApiOss::error creating callback thread!";\r
9305       goto error;\r
9306     }\r
9307   }\r
9308 \r
9309   return SUCCESS;\r
9310 \r
9311  error:\r
9312   if ( handle ) {\r
9313     pthread_cond_destroy( &handle->runnable );\r
9314     if ( handle->id[0] ) close( handle->id[0] );\r
9315     if ( handle->id[1] ) close( handle->id[1] );\r
9316     delete handle;\r
9317     stream_.apiHandle = 0;\r
9318   }\r
9319 \r
9320   for ( int i=0; i<2; i++ ) {\r
9321     if ( stream_.userBuffer[i] ) {\r
9322       free( stream_.userBuffer[i] );\r
9323       stream_.userBuffer[i] = 0;\r
9324     }\r
9325   }\r
9326 \r
9327   if ( stream_.deviceBuffer ) {\r
9328     free( stream_.deviceBuffer );\r
9329     stream_.deviceBuffer = 0;\r
9330   }\r
9331 \r
9332   return FAILURE;\r
9333 }\r
9334 \r
9335 void RtApiOss :: closeStream()\r
9336 {\r
9337   if ( stream_.state == STREAM_CLOSED ) {\r
9338     errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
9339     error( RtAudioError::WARNING );\r
9340     return;\r
9341   }\r
9342 \r
9343   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9344   stream_.callbackInfo.isRunning = false;\r
9345   MUTEX_LOCK( &stream_.mutex );\r
9346   if ( stream_.state == STREAM_STOPPED )\r
9347     pthread_cond_signal( &handle->runnable );\r
9348   MUTEX_UNLOCK( &stream_.mutex );\r
9349   pthread_join( stream_.callbackInfo.thread, NULL );\r
9350 \r
9351   if ( stream_.state == STREAM_RUNNING ) {\r
9352     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
9353       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9354     else\r
9355       ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9356     stream_.state = STREAM_STOPPED;\r
9357   }\r
9358 \r
9359   if ( handle ) {\r
9360     pthread_cond_destroy( &handle->runnable );\r
9361     if ( handle->id[0] ) close( handle->id[0] );\r
9362     if ( handle->id[1] ) close( handle->id[1] );\r
9363     delete handle;\r
9364     stream_.apiHandle = 0;\r
9365   }\r
9366 \r
9367   for ( int i=0; i<2; i++ ) {\r
9368     if ( stream_.userBuffer[i] ) {\r
9369       free( stream_.userBuffer[i] );\r
9370       stream_.userBuffer[i] = 0;\r
9371     }\r
9372   }\r
9373 \r
9374   if ( stream_.deviceBuffer ) {\r
9375     free( stream_.deviceBuffer );\r
9376     stream_.deviceBuffer = 0;\r
9377   }\r
9378 \r
9379   stream_.mode = UNINITIALIZED;\r
9380   stream_.state = STREAM_CLOSED;\r
9381 }\r
9382 \r
9383 void RtApiOss :: startStream()\r
9384 {\r
9385   verifyStream();\r
9386   RtApi::startStream();\r
9387   if ( stream_.state == STREAM_RUNNING ) {\r
9388     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
9389     error( RtAudioError::WARNING );\r
9390     return;\r
9391   }\r
9392 \r
9393   MUTEX_LOCK( &stream_.mutex );\r
9394 \r
9395   stream_.state = STREAM_RUNNING;\r
9396 \r
9397   // No need to do anything else here ... OSS automatically starts\r
9398   // when fed samples.\r
9399 \r
9400   MUTEX_UNLOCK( &stream_.mutex );\r
9401 \r
9402   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9403   pthread_cond_signal( &handle->runnable );\r
9404 }\r
9405 \r
9406 void RtApiOss :: stopStream()\r
9407 {\r
9408   verifyStream();\r
9409   if ( stream_.state == STREAM_STOPPED ) {\r
9410     errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
9411     error( RtAudioError::WARNING );\r
9412     return;\r
9413   }\r
9414 \r
9415   MUTEX_LOCK( &stream_.mutex );\r
9416 \r
9417   // The state might change while waiting on a mutex.\r
9418   if ( stream_.state == STREAM_STOPPED ) {\r
9419     MUTEX_UNLOCK( &stream_.mutex );\r
9420     return;\r
9421   }\r
9422 \r
9423   int result = 0;\r
9424   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9425   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9426 \r
9427     // Flush the output with zeros a few times.\r
9428     char *buffer;\r
9429     int samples;\r
9430     RtAudioFormat format;\r
9431 \r
9432     if ( stream_.doConvertBuffer[0] ) {\r
9433       buffer = stream_.deviceBuffer;\r
9434       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9435       format = stream_.deviceFormat[0];\r
9436     }\r
9437     else {\r
9438       buffer = stream_.userBuffer[0];\r
9439       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9440       format = stream_.userFormat;\r
9441     }\r
9442 \r
9443     memset( buffer, 0, samples * formatBytes(format) );\r
9444     for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
9445       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9446       if ( result == -1 ) {\r
9447         errorText_ = "RtApiOss::stopStream: audio write error.";\r
9448         error( RtAudioError::WARNING );\r
9449       }\r
9450     }\r
9451 \r
9452     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9453     if ( result == -1 ) {\r
9454       errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9455       errorText_ = errorStream_.str();\r
9456       goto unlock;\r
9457     }\r
9458     handle->triggered = false;\r
9459   }\r
9460 \r
9461   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9462     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9463     if ( result == -1 ) {\r
9464       errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9465       errorText_ = errorStream_.str();\r
9466       goto unlock;\r
9467     }\r
9468   }\r
9469 \r
9470  unlock:\r
9471   stream_.state = STREAM_STOPPED;\r
9472   MUTEX_UNLOCK( &stream_.mutex );\r
9473 \r
9474   if ( result != -1 ) return;\r
9475   error( RtAudioError::SYSTEM_ERROR );\r
9476 }\r
9477 \r
9478 void RtApiOss :: abortStream()\r
9479 {\r
9480   verifyStream();\r
9481   if ( stream_.state == STREAM_STOPPED ) {\r
9482     errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
9483     error( RtAudioError::WARNING );\r
9484     return;\r
9485   }\r
9486 \r
9487   MUTEX_LOCK( &stream_.mutex );\r
9488 \r
9489   // The state might change while waiting on a mutex.\r
9490   if ( stream_.state == STREAM_STOPPED ) {\r
9491     MUTEX_UNLOCK( &stream_.mutex );\r
9492     return;\r
9493   }\r
9494 \r
9495   int result = 0;\r
9496   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9497   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9498     result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
9499     if ( result == -1 ) {\r
9500       errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
9501       errorText_ = errorStream_.str();\r
9502       goto unlock;\r
9503     }\r
9504     handle->triggered = false;\r
9505   }\r
9506 \r
9507   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
9508     result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
9509     if ( result == -1 ) {\r
9510       errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
9511       errorText_ = errorStream_.str();\r
9512       goto unlock;\r
9513     }\r
9514   }\r
9515 \r
9516  unlock:\r
9517   stream_.state = STREAM_STOPPED;\r
9518   MUTEX_UNLOCK( &stream_.mutex );\r
9519 \r
9520   if ( result != -1 ) return;\r
9521   error( RtAudioError::SYSTEM_ERROR );\r
9522 }\r
9523 \r
9524 void RtApiOss :: callbackEvent()\r
9525 {\r
9526   OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
9527   if ( stream_.state == STREAM_STOPPED ) {\r
9528     MUTEX_LOCK( &stream_.mutex );\r
9529     pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
9530     if ( stream_.state != STREAM_RUNNING ) {\r
9531       MUTEX_UNLOCK( &stream_.mutex );\r
9532       return;\r
9533     }\r
9534     MUTEX_UNLOCK( &stream_.mutex );\r
9535   }\r
9536 \r
9537   if ( stream_.state == STREAM_CLOSED ) {\r
9538     errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
9539     error( RtAudioError::WARNING );\r
9540     return;\r
9541   }\r
9542 \r
9543   // Invoke user callback to get fresh output data.\r
9544   int doStopStream = 0;\r
9545   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
9546   double streamTime = getStreamTime();\r
9547   RtAudioStreamStatus status = 0;\r
9548   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
9549     status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
9550     handle->xrun[0] = false;\r
9551   }\r
9552   if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
9553     status |= RTAUDIO_INPUT_OVERFLOW;\r
9554     handle->xrun[1] = false;\r
9555   }\r
9556   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
9557                            stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
9558   if ( doStopStream == 2 ) {\r
9559     this->abortStream();\r
9560     return;\r
9561   }\r
9562 \r
9563   MUTEX_LOCK( &stream_.mutex );\r
9564 \r
9565   // The state might change while waiting on a mutex.\r
9566   if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
9567 \r
9568   int result;\r
9569   char *buffer;\r
9570   int samples;\r
9571   RtAudioFormat format;\r
9572 \r
9573   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
9574 \r
9575     // Setup parameters and do buffer conversion if necessary.\r
9576     if ( stream_.doConvertBuffer[0] ) {\r
9577       buffer = stream_.deviceBuffer;\r
9578       convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
9579       samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
9580       format = stream_.deviceFormat[0];\r
9581     }\r
9582     else {\r
9583       buffer = stream_.userBuffer[0];\r
9584       samples = stream_.bufferSize * stream_.nUserChannels[0];\r
9585       format = stream_.userFormat;\r
9586     }\r
9587 \r
9588     // Do byte swapping if necessary.\r
9589     if ( stream_.doByteSwap[0] )\r
9590       byteSwapBuffer( buffer, samples, format );\r
9591 \r
9592     if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
9593       int trig = 0;\r
9594       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9595       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9596       trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
9597       ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
9598       handle->triggered = true;\r
9599     }\r
9600     else\r
9601       // Write samples to device.\r
9602       result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
9603 \r
9604     if ( result == -1 ) {\r
9605       // We'll assume this is an underrun, though there isn't a\r
9606       // specific means for determining that.\r
9607       handle->xrun[0] = true;\r
9608       errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
9609       error( RtAudioError::WARNING );\r
9610       // Continue on to input section.\r
9611     }\r
9612   }\r
9613 \r
9614   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
9615 \r
9616     // Setup parameters.\r
9617     if ( stream_.doConvertBuffer[1] ) {\r
9618       buffer = stream_.deviceBuffer;\r
9619       samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
9620       format = stream_.deviceFormat[1];\r
9621     }\r
9622     else {\r
9623       buffer = stream_.userBuffer[1];\r
9624       samples = stream_.bufferSize * stream_.nUserChannels[1];\r
9625       format = stream_.userFormat;\r
9626     }\r
9627 \r
9628     // Read samples from device.\r
9629     result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
9630 \r
9631     if ( result == -1 ) {\r
9632       // We'll assume this is an overrun, though there isn't a\r
9633       // specific means for determining that.\r
9634       handle->xrun[1] = true;\r
9635       errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
9636       error( RtAudioError::WARNING );\r
9637       goto unlock;\r
9638     }\r
9639 \r
9640     // Do byte swapping if necessary.\r
9641     if ( stream_.doByteSwap[1] )\r
9642       byteSwapBuffer( buffer, samples, format );\r
9643 \r
9644     // Do buffer conversion if necessary.\r
9645     if ( stream_.doConvertBuffer[1] )\r
9646       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
9647   }\r
9648 \r
9649  unlock:\r
9650   MUTEX_UNLOCK( &stream_.mutex );\r
9651 \r
9652   RtApi::tickStreamTime();\r
9653   if ( doStopStream == 1 ) this->stopStream();\r
9654 }\r
9655 \r
9656 static void *ossCallbackHandler( void *ptr )\r
9657 {\r
9658   CallbackInfo *info = (CallbackInfo *) ptr;\r
9659   RtApiOss *object = (RtApiOss *) info->object;\r
9660   bool *isRunning = &info->isRunning;\r
9661 \r
9662   while ( *isRunning == true ) {\r
9663     pthread_testcancel();\r
9664     object->callbackEvent();\r
9665   }\r
9666 \r
9667   pthread_exit( NULL );\r
9668 }\r
9669 \r
9670 //******************** End of __LINUX_OSS__ *********************//\r
9671 #endif\r
9672 \r
9673 \r
9674 // *************************************************** //\r
9675 //\r
9676 // Protected common (OS-independent) RtAudio methods.\r
9677 //\r
9678 // *************************************************** //\r
9679 \r
9680 // This method can be modified to control the behavior of error\r
9681 // message printing.\r
9682 void RtApi :: error( RtAudioError::Type type )\r
9683 {\r
9684   errorStream_.str(""); // clear the ostringstream\r
9685 \r
9686   RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
9687   if ( errorCallback ) {\r
9688     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
9689 \r
9690     if ( firstErrorOccurred_ )\r
9691       return;\r
9692 \r
9693     firstErrorOccurred_ = true;\r
9694     const std::string errorMessage = errorText_;\r
9695 \r
9696     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
9697       stream_.callbackInfo.isRunning = false; // exit from the thread\r
9698       abortStream();\r
9699     }\r
9700 \r
9701     errorCallback( type, errorMessage );\r
9702     firstErrorOccurred_ = false;\r
9703     return;\r
9704   }\r
9705 \r
9706   if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
9707     std::cerr << '\n' << errorText_ << "\n\n";\r
9708   else if ( type != RtAudioError::WARNING )\r
9709     throw( RtAudioError( errorText_, type ) );\r
9710 }\r
9711 \r
9712 void RtApi :: verifyStream()\r
9713 {\r
9714   if ( stream_.state == STREAM_CLOSED ) {\r
9715     errorText_ = "RtApi:: a stream is not open!";\r
9716     error( RtAudioError::INVALID_USE );\r
9717   }\r
9718 }\r
9719 \r
9720 void RtApi :: clearStreamInfo()\r
9721 {\r
9722   stream_.mode = UNINITIALIZED;\r
9723   stream_.state = STREAM_CLOSED;\r
9724   stream_.sampleRate = 0;\r
9725   stream_.bufferSize = 0;\r
9726   stream_.nBuffers = 0;\r
9727   stream_.userFormat = 0;\r
9728   stream_.userInterleaved = true;\r
9729   stream_.streamTime = 0.0;\r
9730   stream_.apiHandle = 0;\r
9731   stream_.deviceBuffer = 0;\r
9732   stream_.callbackInfo.callback = 0;\r
9733   stream_.callbackInfo.userData = 0;\r
9734   stream_.callbackInfo.isRunning = false;\r
9735   stream_.callbackInfo.errorCallback = 0;\r
9736   for ( int i=0; i<2; i++ ) {\r
9737     stream_.device[i] = 11111;\r
9738     stream_.doConvertBuffer[i] = false;\r
9739     stream_.deviceInterleaved[i] = true;\r
9740     stream_.doByteSwap[i] = false;\r
9741     stream_.nUserChannels[i] = 0;\r
9742     stream_.nDeviceChannels[i] = 0;\r
9743     stream_.channelOffset[i] = 0;\r
9744     stream_.deviceFormat[i] = 0;\r
9745     stream_.latency[i] = 0;\r
9746     stream_.userBuffer[i] = 0;\r
9747     stream_.convertInfo[i].channels = 0;\r
9748     stream_.convertInfo[i].inJump = 0;\r
9749     stream_.convertInfo[i].outJump = 0;\r
9750     stream_.convertInfo[i].inFormat = 0;\r
9751     stream_.convertInfo[i].outFormat = 0;\r
9752     stream_.convertInfo[i].inOffset.clear();\r
9753     stream_.convertInfo[i].outOffset.clear();\r
9754   }\r
9755 }\r
9756 \r
9757 unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
9758 {\r
9759   if ( format == RTAUDIO_SINT16 )\r
9760     return 2;\r
9761   else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
9762     return 4;\r
9763   else if ( format == RTAUDIO_FLOAT64 )\r
9764     return 8;\r
9765   else if ( format == RTAUDIO_SINT24 )\r
9766     return 3;\r
9767   else if ( format == RTAUDIO_SINT8 )\r
9768     return 1;\r
9769 \r
9770   errorText_ = "RtApi::formatBytes: undefined format.";\r
9771   error( RtAudioError::WARNING );\r
9772 \r
9773   return 0;\r
9774 }\r
9775 \r
9776 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
9777 {\r
9778   if ( mode == INPUT ) { // convert device to user buffer\r
9779     stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
9780     stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
9781     stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
9782     stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
9783   }\r
9784   else { // convert user to device buffer\r
9785     stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
9786     stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
9787     stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
9788     stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
9789   }\r
9790 \r
9791   if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
9792     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
9793   else\r
9794     stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
9795 \r
9796   // Set up the interleave/deinterleave offsets.\r
9797   if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
9798     if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
9799          ( mode == INPUT && stream_.userInterleaved ) ) {\r
9800       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9801         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9802         stream_.convertInfo[mode].outOffset.push_back( k );\r
9803         stream_.convertInfo[mode].inJump = 1;\r
9804       }\r
9805     }\r
9806     else {\r
9807       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9808         stream_.convertInfo[mode].inOffset.push_back( k );\r
9809         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9810         stream_.convertInfo[mode].outJump = 1;\r
9811       }\r
9812     }\r
9813   }\r
9814   else { // no (de)interleaving\r
9815     if ( stream_.userInterleaved ) {\r
9816       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9817         stream_.convertInfo[mode].inOffset.push_back( k );\r
9818         stream_.convertInfo[mode].outOffset.push_back( k );\r
9819       }\r
9820     }\r
9821     else {\r
9822       for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
9823         stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
9824         stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
9825         stream_.convertInfo[mode].inJump = 1;\r
9826         stream_.convertInfo[mode].outJump = 1;\r
9827       }\r
9828     }\r
9829   }\r
9830 \r
9831   // Add channel offset.\r
9832   if ( firstChannel > 0 ) {\r
9833     if ( stream_.deviceInterleaved[mode] ) {\r
9834       if ( mode == OUTPUT ) {\r
9835         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9836           stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
9837       }\r
9838       else {\r
9839         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9840           stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
9841       }\r
9842     }\r
9843     else {\r
9844       if ( mode == OUTPUT ) {\r
9845         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9846           stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
9847       }\r
9848       else {\r
9849         for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
9850           stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );\r
9851       }\r
9852     }\r
9853   }\r
9854 }\r
9855 \r
9856 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
9857 {\r
9858   // This function does format conversion, input/output channel compensation, and\r
9859   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
9860   // the lower three bytes of a 32-bit integer.\r
9861 \r
9862   // Clear our device buffer when in/out duplex device channels are different\r
9863   if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
9864        ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
9865     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
9866 \r
9867   int j;\r
9868   if (info.outFormat == RTAUDIO_FLOAT64) {\r
9869     Float64 scale;\r
9870     Float64 *out = (Float64 *)outBuffer;\r
9871 \r
9872     if (info.inFormat == RTAUDIO_SINT8) {\r
9873       signed char *in = (signed char *)inBuffer;\r
9874       scale = 1.0 / 127.5;\r
9875       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9876         for (j=0; j<info.channels; j++) {\r
9877           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9878           out[info.outOffset[j]] += 0.5;\r
9879           out[info.outOffset[j]] *= scale;\r
9880         }\r
9881         in += info.inJump;\r
9882         out += info.outJump;\r
9883       }\r
9884     }\r
9885     else if (info.inFormat == RTAUDIO_SINT16) {\r
9886       Int16 *in = (Int16 *)inBuffer;\r
9887       scale = 1.0 / 32767.5;\r
9888       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9889         for (j=0; j<info.channels; j++) {\r
9890           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9891           out[info.outOffset[j]] += 0.5;\r
9892           out[info.outOffset[j]] *= scale;\r
9893         }\r
9894         in += info.inJump;\r
9895         out += info.outJump;\r
9896       }\r
9897     }\r
9898     else if (info.inFormat == RTAUDIO_SINT24) {\r
9899       Int24 *in = (Int24 *)inBuffer;\r
9900       scale = 1.0 / 8388607.5;\r
9901       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9902         for (j=0; j<info.channels; j++) {\r
9903           out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
9904           out[info.outOffset[j]] += 0.5;\r
9905           out[info.outOffset[j]] *= scale;\r
9906         }\r
9907         in += info.inJump;\r
9908         out += info.outJump;\r
9909       }\r
9910     }\r
9911     else if (info.inFormat == RTAUDIO_SINT32) {\r
9912       Int32 *in = (Int32 *)inBuffer;\r
9913       scale = 1.0 / 2147483647.5;\r
9914       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9915         for (j=0; j<info.channels; j++) {\r
9916           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9917           out[info.outOffset[j]] += 0.5;\r
9918           out[info.outOffset[j]] *= scale;\r
9919         }\r
9920         in += info.inJump;\r
9921         out += info.outJump;\r
9922       }\r
9923     }\r
9924     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
9925       Float32 *in = (Float32 *)inBuffer;\r
9926       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9927         for (j=0; j<info.channels; j++) {\r
9928           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
9929         }\r
9930         in += info.inJump;\r
9931         out += info.outJump;\r
9932       }\r
9933     }\r
9934     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
9935       // Channel compensation and/or (de)interleaving only.\r
9936       Float64 *in = (Float64 *)inBuffer;\r
9937       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9938         for (j=0; j<info.channels; j++) {\r
9939           out[info.outOffset[j]] = in[info.inOffset[j]];\r
9940         }\r
9941         in += info.inJump;\r
9942         out += info.outJump;\r
9943       }\r
9944     }\r
9945   }\r
9946   else if (info.outFormat == RTAUDIO_FLOAT32) {\r
9947     Float32 scale;\r
9948     Float32 *out = (Float32 *)outBuffer;\r
9949 \r
9950     if (info.inFormat == RTAUDIO_SINT8) {\r
9951       signed char *in = (signed char *)inBuffer;\r
9952       scale = (Float32) ( 1.0 / 127.5 );\r
9953       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9954         for (j=0; j<info.channels; j++) {\r
9955           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9956           out[info.outOffset[j]] += 0.5;\r
9957           out[info.outOffset[j]] *= scale;\r
9958         }\r
9959         in += info.inJump;\r
9960         out += info.outJump;\r
9961       }\r
9962     }\r
9963     else if (info.inFormat == RTAUDIO_SINT16) {\r
9964       Int16 *in = (Int16 *)inBuffer;\r
9965       scale = (Float32) ( 1.0 / 32767.5 );\r
9966       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9967         for (j=0; j<info.channels; j++) {\r
9968           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
9969           out[info.outOffset[j]] += 0.5;\r
9970           out[info.outOffset[j]] *= scale;\r
9971         }\r
9972         in += info.inJump;\r
9973         out += info.outJump;\r
9974       }\r
9975     }\r
9976     else if (info.inFormat == RTAUDIO_SINT24) {\r
9977       Int24 *in = (Int24 *)inBuffer;\r
9978       scale = (Float32) ( 1.0 / 8388607.5 );\r
9979       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
9980         for (j=0; j<info.channels; j++) {\r
9981           out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
9982           out[info.outOffset[j]] += 0.5;\r
9983           out[info.outOffset[j]] *= scale;\r
9984         }\r
9985         in += info.inJump;\r
9986         out += info.outJump;\r
9987       }\r
9988     }\r
9989     else if (info.inFormat == RTAUDIO_SINT32) {\r
9990       Int32 *in = (Int32 *)inBuffer;\r
9991       scale = (Float32) ( 1.0 / 2147483647.5 );\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]] = (Float32) in[info.inOffset[j]];\r
9995           out[info.outOffset[j]] += 0.5;\r
9996           out[info.outOffset[j]] *= scale;\r
9997         }\r
9998         in += info.inJump;\r
9999         out += info.outJump;\r
10000       }\r
10001     }\r
10002     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10003       // Channel compensation and/or (de)interleaving only.\r
10004       Float32 *in = (Float32 *)inBuffer;\r
10005       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10006         for (j=0; j<info.channels; j++) {\r
10007           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10008         }\r
10009         in += info.inJump;\r
10010         out += info.outJump;\r
10011       }\r
10012     }\r
10013     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10014       Float64 *in = (Float64 *)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]] = (Float32) in[info.inOffset[j]];\r
10018         }\r
10019         in += info.inJump;\r
10020         out += info.outJump;\r
10021       }\r
10022     }\r
10023   }\r
10024   else if (info.outFormat == RTAUDIO_SINT32) {\r
10025     Int32 *out = (Int32 *)outBuffer;\r
10026     if (info.inFormat == RTAUDIO_SINT8) {\r
10027       signed char *in = (signed char *)inBuffer;\r
10028       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10029         for (j=0; j<info.channels; j++) {\r
10030           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
10031           out[info.outOffset[j]] <<= 24;\r
10032         }\r
10033         in += info.inJump;\r
10034         out += info.outJump;\r
10035       }\r
10036     }\r
10037     else if (info.inFormat == RTAUDIO_SINT16) {\r
10038       Int16 *in = (Int16 *)inBuffer;\r
10039       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10040         for (j=0; j<info.channels; j++) {\r
10041           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
10042           out[info.outOffset[j]] <<= 16;\r
10043         }\r
10044         in += info.inJump;\r
10045         out += info.outJump;\r
10046       }\r
10047     }\r
10048     else if (info.inFormat == RTAUDIO_SINT24) {\r
10049       Int24 *in = (Int24 *)inBuffer;\r
10050       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10051         for (j=0; j<info.channels; j++) {\r
10052           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
10053           out[info.outOffset[j]] <<= 8;\r
10054         }\r
10055         in += info.inJump;\r
10056         out += info.outJump;\r
10057       }\r
10058     }\r
10059     else if (info.inFormat == RTAUDIO_SINT32) {\r
10060       // Channel compensation and/or (de)interleaving only.\r
10061       Int32 *in = (Int32 *)inBuffer;\r
10062       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10063         for (j=0; j<info.channels; j++) {\r
10064           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10065         }\r
10066         in += info.inJump;\r
10067         out += info.outJump;\r
10068       }\r
10069     }\r
10070     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10071       Float32 *in = (Float32 *)inBuffer;\r
10072       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10073         for (j=0; j<info.channels; j++) {\r
10074           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
10075         }\r
10076         in += info.inJump;\r
10077         out += info.outJump;\r
10078       }\r
10079     }\r
10080     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10081       Float64 *in = (Float64 *)inBuffer;\r
10082       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10083         for (j=0; j<info.channels; j++) {\r
10084           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
10085         }\r
10086         in += info.inJump;\r
10087         out += info.outJump;\r
10088       }\r
10089     }\r
10090   }\r
10091   else if (info.outFormat == RTAUDIO_SINT24) {\r
10092     Int24 *out = (Int24 *)outBuffer;\r
10093     if (info.inFormat == RTAUDIO_SINT8) {\r
10094       signed char *in = (signed char *)inBuffer;\r
10095       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10096         for (j=0; j<info.channels; j++) {\r
10097           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
10098           //out[info.outOffset[j]] <<= 16;\r
10099         }\r
10100         in += info.inJump;\r
10101         out += info.outJump;\r
10102       }\r
10103     }\r
10104     else if (info.inFormat == RTAUDIO_SINT16) {\r
10105       Int16 *in = (Int16 *)inBuffer;\r
10106       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10107         for (j=0; j<info.channels; j++) {\r
10108           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
10109           //out[info.outOffset[j]] <<= 8;\r
10110         }\r
10111         in += info.inJump;\r
10112         out += info.outJump;\r
10113       }\r
10114     }\r
10115     else if (info.inFormat == RTAUDIO_SINT24) {\r
10116       // Channel compensation and/or (de)interleaving only.\r
10117       Int24 *in = (Int24 *)inBuffer;\r
10118       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10119         for (j=0; j<info.channels; j++) {\r
10120           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10121         }\r
10122         in += info.inJump;\r
10123         out += info.outJump;\r
10124       }\r
10125     }\r
10126     else if (info.inFormat == RTAUDIO_SINT32) {\r
10127       Int32 *in = (Int32 *)inBuffer;\r
10128       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10129         for (j=0; j<info.channels; j++) {\r
10130           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
10131           //out[info.outOffset[j]] >>= 8;\r
10132         }\r
10133         in += info.inJump;\r
10134         out += info.outJump;\r
10135       }\r
10136     }\r
10137     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10138       Float32 *in = (Float32 *)inBuffer;\r
10139       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10140         for (j=0; j<info.channels; j++) {\r
10141           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
10142         }\r
10143         in += info.inJump;\r
10144         out += info.outJump;\r
10145       }\r
10146     }\r
10147     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10148       Float64 *in = (Float64 *)inBuffer;\r
10149       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10150         for (j=0; j<info.channels; j++) {\r
10151           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
10152         }\r
10153         in += info.inJump;\r
10154         out += info.outJump;\r
10155       }\r
10156     }\r
10157   }\r
10158   else if (info.outFormat == RTAUDIO_SINT16) {\r
10159     Int16 *out = (Int16 *)outBuffer;\r
10160     if (info.inFormat == RTAUDIO_SINT8) {\r
10161       signed char *in = (signed char *)inBuffer;\r
10162       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10163         for (j=0; j<info.channels; j++) {\r
10164           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
10165           out[info.outOffset[j]] <<= 8;\r
10166         }\r
10167         in += info.inJump;\r
10168         out += info.outJump;\r
10169       }\r
10170     }\r
10171     else if (info.inFormat == RTAUDIO_SINT16) {\r
10172       // Channel compensation and/or (de)interleaving only.\r
10173       Int16 *in = (Int16 *)inBuffer;\r
10174       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10175         for (j=0; j<info.channels; j++) {\r
10176           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10177         }\r
10178         in += info.inJump;\r
10179         out += info.outJump;\r
10180       }\r
10181     }\r
10182     else if (info.inFormat == RTAUDIO_SINT24) {\r
10183       Int24 *in = (Int24 *)inBuffer;\r
10184       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10185         for (j=0; j<info.channels; j++) {\r
10186           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
10187         }\r
10188         in += info.inJump;\r
10189         out += info.outJump;\r
10190       }\r
10191     }\r
10192     else if (info.inFormat == RTAUDIO_SINT32) {\r
10193       Int32 *in = (Int32 *)inBuffer;\r
10194       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10195         for (j=0; j<info.channels; j++) {\r
10196           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
10197         }\r
10198         in += info.inJump;\r
10199         out += info.outJump;\r
10200       }\r
10201     }\r
10202     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10203       Float32 *in = (Float32 *)inBuffer;\r
10204       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10205         for (j=0; j<info.channels; j++) {\r
10206           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
10207         }\r
10208         in += info.inJump;\r
10209         out += info.outJump;\r
10210       }\r
10211     }\r
10212     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10213       Float64 *in = (Float64 *)inBuffer;\r
10214       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10215         for (j=0; j<info.channels; j++) {\r
10216           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
10217         }\r
10218         in += info.inJump;\r
10219         out += info.outJump;\r
10220       }\r
10221     }\r
10222   }\r
10223   else if (info.outFormat == RTAUDIO_SINT8) {\r
10224     signed char *out = (signed char *)outBuffer;\r
10225     if (info.inFormat == RTAUDIO_SINT8) {\r
10226       // Channel compensation and/or (de)interleaving only.\r
10227       signed char *in = (signed char *)inBuffer;\r
10228       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10229         for (j=0; j<info.channels; j++) {\r
10230           out[info.outOffset[j]] = in[info.inOffset[j]];\r
10231         }\r
10232         in += info.inJump;\r
10233         out += info.outJump;\r
10234       }\r
10235     }\r
10236     if (info.inFormat == RTAUDIO_SINT16) {\r
10237       Int16 *in = (Int16 *)inBuffer;\r
10238       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10239         for (j=0; j<info.channels; j++) {\r
10240           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
10241         }\r
10242         in += info.inJump;\r
10243         out += info.outJump;\r
10244       }\r
10245     }\r
10246     else if (info.inFormat == RTAUDIO_SINT24) {\r
10247       Int24 *in = (Int24 *)inBuffer;\r
10248       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10249         for (j=0; j<info.channels; j++) {\r
10250           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
10251         }\r
10252         in += info.inJump;\r
10253         out += info.outJump;\r
10254       }\r
10255     }\r
10256     else if (info.inFormat == RTAUDIO_SINT32) {\r
10257       Int32 *in = (Int32 *)inBuffer;\r
10258       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10259         for (j=0; j<info.channels; j++) {\r
10260           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
10261         }\r
10262         in += info.inJump;\r
10263         out += info.outJump;\r
10264       }\r
10265     }\r
10266     else if (info.inFormat == RTAUDIO_FLOAT32) {\r
10267       Float32 *in = (Float32 *)inBuffer;\r
10268       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10269         for (j=0; j<info.channels; j++) {\r
10270           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10271         }\r
10272         in += info.inJump;\r
10273         out += info.outJump;\r
10274       }\r
10275     }\r
10276     else if (info.inFormat == RTAUDIO_FLOAT64) {\r
10277       Float64 *in = (Float64 *)inBuffer;\r
10278       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
10279         for (j=0; j<info.channels; j++) {\r
10280           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
10281         }\r
10282         in += info.inJump;\r
10283         out += info.outJump;\r
10284       }\r
10285     }\r
10286   }\r
10287 }\r
10288 \r
10289 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
10290 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
10291 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
10292 \r
10293 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
10294 {\r
10295   char val;\r
10296   char *ptr;\r
10297 \r
10298   ptr = buffer;\r
10299   if ( format == RTAUDIO_SINT16 ) {\r
10300     for ( unsigned int i=0; i<samples; i++ ) {\r
10301       // Swap 1st and 2nd bytes.\r
10302       val = *(ptr);\r
10303       *(ptr) = *(ptr+1);\r
10304       *(ptr+1) = val;\r
10305 \r
10306       // Increment 2 bytes.\r
10307       ptr += 2;\r
10308     }\r
10309   }\r
10310   else if ( format == RTAUDIO_SINT32 ||\r
10311             format == RTAUDIO_FLOAT32 ) {\r
10312     for ( unsigned int i=0; i<samples; i++ ) {\r
10313       // Swap 1st and 4th bytes.\r
10314       val = *(ptr);\r
10315       *(ptr) = *(ptr+3);\r
10316       *(ptr+3) = val;\r
10317 \r
10318       // Swap 2nd and 3rd bytes.\r
10319       ptr += 1;\r
10320       val = *(ptr);\r
10321       *(ptr) = *(ptr+1);\r
10322       *(ptr+1) = val;\r
10323 \r
10324       // Increment 3 more bytes.\r
10325       ptr += 3;\r
10326     }\r
10327   }\r
10328   else if ( format == RTAUDIO_SINT24 ) {\r
10329     for ( unsigned int i=0; i<samples; i++ ) {\r
10330       // Swap 1st and 3rd bytes.\r
10331       val = *(ptr);\r
10332       *(ptr) = *(ptr+2);\r
10333       *(ptr+2) = val;\r
10334 \r
10335       // Increment 2 more bytes.\r
10336       ptr += 2;\r
10337     }\r
10338   }\r
10339   else if ( format == RTAUDIO_FLOAT64 ) {\r
10340     for ( unsigned int i=0; i<samples; i++ ) {\r
10341       // Swap 1st and 8th bytes\r
10342       val = *(ptr);\r
10343       *(ptr) = *(ptr+7);\r
10344       *(ptr+7) = val;\r
10345 \r
10346       // Swap 2nd and 7th bytes\r
10347       ptr += 1;\r
10348       val = *(ptr);\r
10349       *(ptr) = *(ptr+5);\r
10350       *(ptr+5) = val;\r
10351 \r
10352       // Swap 3rd and 6th bytes\r
10353       ptr += 1;\r
10354       val = *(ptr);\r
10355       *(ptr) = *(ptr+3);\r
10356       *(ptr+3) = val;\r
10357 \r
10358       // Swap 4th and 5th bytes\r
10359       ptr += 1;\r
10360       val = *(ptr);\r
10361       *(ptr) = *(ptr+1);\r
10362       *(ptr+1) = val;\r
10363 \r
10364       // Increment 5 more bytes.\r
10365       ptr += 5;\r
10366     }\r
10367   }\r
10368 }\r
10369 \r
10370   // Indentation settings for Vim and Emacs\r
10371   //\r
10372   // Local Variables:\r
10373   // c-basic-offset: 2\r
10374   // indent-tabs-mode: nil\r
10375   // End:\r
10376   //\r
10377   // vim: et sts=2 sw=2\r