Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / rtaudiomidi / RtAudio.cpp
blob061afb5fc3c5fb131d4b810781a1ab30aca44235
1 /************************************************************************/
2 /*! \class RtAudio
3 \brief Realtime audio i/o C++ classes.
5 RtAudio provides a common API (Application Programming Interface)
6 for realtime audio input/output across Linux (native ALSA, Jack,
7 and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
8 (DirectSound and ASIO) operating systems.
10 RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
12 RtAudio: realtime audio i/o C++ classes
13 Copyright (c) 2001-2012 Gary P. Scavone
15 Permission is hereby granted, free of charge, to any person
16 obtaining a copy of this software and associated documentation files
17 (the "Software"), to deal in the Software without restriction,
18 including without limitation the rights to use, copy, modify, merge,
19 publish, distribute, sublicense, and/or sell copies of the Software,
20 and to permit persons to whom the Software is furnished to do so,
21 subject to the following conditions:
23 The above copyright notice and this permission notice shall be
24 included in all copies or substantial portions of the Software.
26 Any person wishing to distribute modifications to the Software is
27 asked to send the modifications to the original developer so that
28 they can be incorporated into the canonical version. This is,
29 however, not a binding provision of this license.
31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
34 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
35 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
36 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 /************************************************************************/
41 // RtAudio: Version 4.0.11
43 #include "RtAudio.h"
44 #include <iostream>
45 #include <cstdlib>
46 #include <cstring>
47 #include <climits>
49 // Static variable definitions.
50 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
51 const unsigned int RtApi::SAMPLE_RATES[] = {
52 4000, 5512, 8000, 9600, 11025, 16000, 22050,
53 32000, 44100, 48000, 88200, 96000, 176400, 192000
56 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
57 #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
58 #define MUTEX_DESTROY(A) DeleteCriticalSection(A)
59 #define MUTEX_LOCK(A) EnterCriticalSection(A)
60 #define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
61 #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
62 // pthread API
63 #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
64 #define MUTEX_DESTROY(A) pthread_mutex_destroy(A)
65 #define MUTEX_LOCK(A) pthread_mutex_lock(A)
66 #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A)
67 #else
68 #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions
69 #define MUTEX_DESTROY(A) abs(*A) // dummy definitions
70 #endif
72 // *************************************************** //
74 // RtAudio definitions.
76 // *************************************************** //
78 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
80 apis.clear();
82 // The order here will control the order of RtAudio's API search in
83 // the constructor.
84 #if defined(__UNIX_JACK__)
85 apis.push_back( UNIX_JACK );
86 #endif
87 #if defined(__LINUX_ALSA__)
88 apis.push_back( LINUX_ALSA );
89 #endif
90 #if defined(__LINUX_PULSE__)
91 apis.push_back( LINUX_PULSE );
92 #endif
93 #if defined(__LINUX_OSS__)
94 apis.push_back( LINUX_OSS );
95 #endif
96 #if defined(__WINDOWS_ASIO__)
97 apis.push_back( WINDOWS_ASIO );
98 #endif
99 #if defined(__WINDOWS_DS__)
100 apis.push_back( WINDOWS_DS );
101 #endif
102 #if defined(__MACOSX_CORE__)
103 apis.push_back( MACOSX_CORE );
104 #endif
105 #if defined(__RTAUDIO_DUMMY__)
106 apis.push_back( RTAUDIO_DUMMY );
107 #endif
110 void RtAudio :: openRtApi( RtAudio::Api api )
112 if ( rtapi_ )
113 delete rtapi_;
114 rtapi_ = 0;
116 #if defined(__UNIX_JACK__)
117 if ( api == UNIX_JACK )
118 rtapi_ = new RtApiJack();
119 #endif
120 #if defined(__LINUX_ALSA__)
121 if ( api == LINUX_ALSA )
122 rtapi_ = new RtApiAlsa();
123 #endif
124 #if defined(__LINUX_PULSE__)
125 if ( api == LINUX_PULSE )
126 rtapi_ = new RtApiPulse();
127 #endif
128 #if defined(__LINUX_OSS__)
129 if ( api == LINUX_OSS )
130 rtapi_ = new RtApiOss();
131 #endif
132 #if defined(__WINDOWS_ASIO__)
133 if ( api == WINDOWS_ASIO )
134 rtapi_ = new RtApiAsio();
135 #endif
136 #if defined(__WINDOWS_DS__)
137 if ( api == WINDOWS_DS )
138 rtapi_ = new RtApiDs();
139 #endif
140 #if defined(__MACOSX_CORE__)
141 if ( api == MACOSX_CORE )
142 rtapi_ = new RtApiCore();
143 #endif
144 #if defined(__RTAUDIO_DUMMY__)
145 if ( api == RTAUDIO_DUMMY )
146 rtapi_ = new RtApiDummy();
147 #endif
150 RtAudio :: RtAudio( RtAudio::Api api ) throw()
152 rtapi_ = 0;
154 if ( api != UNSPECIFIED ) {
155 // Attempt to open the specified API.
156 openRtApi( api );
157 if ( rtapi_ ) return;
159 // No compiled support for specified API value. Issue a debug
160 // warning and continue as if no API was specified.
161 std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
164 // Iterate through the compiled APIs and return as soon as we find
165 // one with at least one device or we reach the end of the list.
166 std::vector< RtAudio::Api > apis;
167 getCompiledApi( apis );
168 for ( unsigned int i=0; i<apis.size(); i++ ) {
169 openRtApi( apis[i] );
170 if ( rtapi_->getDeviceCount() ) break;
173 if ( rtapi_ ) return;
175 // It should not be possible to get here because the preprocessor
176 // definition __RTAUDIO_DUMMY__ is automatically defined if no
177 // API-specific definitions are passed to the compiler. But just in
178 // case something weird happens, we'll print out an error message.
179 std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n";
182 RtAudio :: ~RtAudio() throw()
184 delete rtapi_;
187 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
188 RtAudio::StreamParameters *inputParameters,
189 RtAudioFormat format, unsigned int sampleRate,
190 unsigned int *bufferFrames,
191 RtAudioCallback callback, void *userData,
192 RtAudio::StreamOptions *options )
194 return rtapi_->openStream( outputParameters, inputParameters, format,
195 sampleRate, bufferFrames, callback,
196 userData, options );
199 // *************************************************** //
201 // Public RtApi definitions (see end of file for
202 // private or protected utility functions).
204 // *************************************************** //
206 RtApi :: RtApi()
208 stream_.state = STREAM_CLOSED;
209 stream_.mode = UNINITIALIZED;
210 stream_.apiHandle = 0;
211 stream_.userBuffer[0] = 0;
212 stream_.userBuffer[1] = 0;
213 MUTEX_INITIALIZE( &stream_.mutex );
214 showWarnings_ = true;
217 RtApi :: ~RtApi()
219 MUTEX_DESTROY( &stream_.mutex );
222 void RtApi :: openStream( RtAudio::StreamParameters *oParams,
223 RtAudio::StreamParameters *iParams,
224 RtAudioFormat format, unsigned int sampleRate,
225 unsigned int *bufferFrames,
226 RtAudioCallback callback, void *userData,
227 RtAudio::StreamOptions *options )
229 if ( stream_.state != STREAM_CLOSED ) {
230 errorText_ = "RtApi::openStream: a stream is already open!";
231 error( RtError::INVALID_USE );
234 if ( oParams && oParams->nChannels < 1 ) {
235 errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
236 error( RtError::INVALID_USE );
239 if ( iParams && iParams->nChannels < 1 ) {
240 errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
241 error( RtError::INVALID_USE );
244 if ( oParams == NULL && iParams == NULL ) {
245 errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
246 error( RtError::INVALID_USE );
249 if ( formatBytes(format) == 0 ) {
250 errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
251 error( RtError::INVALID_USE );
254 unsigned int nDevices = getDeviceCount();
255 unsigned int oChannels = 0;
256 if ( oParams ) {
257 oChannels = oParams->nChannels;
258 if ( oParams->deviceId >= nDevices ) {
259 errorText_ = "RtApi::openStream: output device parameter value is invalid.";
260 error( RtError::INVALID_USE );
264 unsigned int iChannels = 0;
265 if ( iParams ) {
266 iChannels = iParams->nChannels;
267 if ( iParams->deviceId >= nDevices ) {
268 errorText_ = "RtApi::openStream: input device parameter value is invalid.";
269 error( RtError::INVALID_USE );
273 clearStreamInfo();
274 bool result;
276 if ( oChannels > 0 ) {
278 result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
279 sampleRate, format, bufferFrames, options );
280 if ( result == false ) error( RtError::SYSTEM_ERROR );
283 if ( iChannels > 0 ) {
285 result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
286 sampleRate, format, bufferFrames, options );
287 if ( result == false ) {
288 if ( oChannels > 0 ) closeStream();
289 error( RtError::SYSTEM_ERROR );
293 stream_.callbackInfo.callback = (void *) callback;
294 stream_.callbackInfo.userData = userData;
296 if ( options ) options->numberOfBuffers = stream_.nBuffers;
297 stream_.state = STREAM_STOPPED;
300 unsigned int RtApi :: getDefaultInputDevice( void )
302 // Should be implemented in subclasses if possible.
303 return 0;
306 unsigned int RtApi :: getDefaultOutputDevice( void )
308 // Should be implemented in subclasses if possible.
309 return 0;
312 void RtApi :: closeStream( void )
314 // MUST be implemented in subclasses!
315 return;
318 bool RtApi :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
319 unsigned int firstChannel, unsigned int sampleRate,
320 RtAudioFormat format, unsigned int *bufferSize,
321 RtAudio::StreamOptions *options )
323 // MUST be implemented in subclasses!
324 return FAILURE;
327 void RtApi :: tickStreamTime( void )
329 // Subclasses that do not provide their own implementation of
330 // getStreamTime should call this function once per buffer I/O to
331 // provide basic stream time support.
333 stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
335 #if defined( HAVE_GETTIMEOFDAY )
336 gettimeofday( &stream_.lastTickTimestamp, NULL );
337 #endif
340 long RtApi :: getStreamLatency( void )
342 verifyStream();
344 long totalLatency = 0;
345 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
346 totalLatency = stream_.latency[0];
347 if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
348 totalLatency += stream_.latency[1];
350 return totalLatency;
353 double RtApi :: getStreamTime( void )
355 verifyStream();
357 #if defined( HAVE_GETTIMEOFDAY )
358 // Return a very accurate estimate of the stream time by
359 // adding in the elapsed time since the last tick.
360 struct timeval then;
361 struct timeval now;
363 if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )
364 return stream_.streamTime;
366 gettimeofday( &now, NULL );
367 then = stream_.lastTickTimestamp;
368 return stream_.streamTime +
369 ((now.tv_sec + 0.000001 * now.tv_usec) -
370 (then.tv_sec + 0.000001 * then.tv_usec));
371 #else
372 return stream_.streamTime;
373 #endif
376 unsigned int RtApi :: getStreamSampleRate( void )
378 verifyStream();
380 return stream_.sampleRate;
384 // *************************************************** //
386 // OS/API-specific methods.
388 // *************************************************** //
390 #if defined(__MACOSX_CORE__)
392 // The OS X CoreAudio API is designed to use a separate callback
393 // procedure for each of its audio devices. A single RtAudio duplex
394 // stream using two different devices is supported here, though it
395 // cannot be guaranteed to always behave correctly because we cannot
396 // synchronize these two callbacks.
398 // A property listener is installed for over/underrun information.
399 // However, no functionality is currently provided to allow property
400 // listeners to trigger user handlers because it is unclear what could
401 // be done if a critical stream parameter (buffer size, sample rate,
402 // device disconnect) notification arrived. The listeners entail
403 // quite a bit of extra code and most likely, a user program wouldn't
404 // be prepared for the result anyway. However, we do provide a flag
405 // to the client callback function to inform of an over/underrun.
407 // A structure to hold various information related to the CoreAudio API
408 // implementation.
409 struct CoreHandle {
410 AudioDeviceID id[2]; // device ids
411 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
412 AudioDeviceIOProcID procId[2];
413 #endif
414 UInt32 iStream[2]; // device stream index (or first if using multiple)
415 UInt32 nStreams[2]; // number of streams to use
416 bool xrun[2];
417 char *deviceBuffer;
418 pthread_cond_t condition;
419 int drainCounter; // Tracks callback counts when draining
420 bool internalDrain; // Indicates if stop is initiated from callback or not.
422 CoreHandle()
423 :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
426 ThreadHandle threadId;
428 RtApiCore:: RtApiCore()
430 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )
431 // This is a largely undocumented but absolutely necessary
432 // requirement starting with OS-X 10.6. If not called, queries and
433 // updates to various audio device properties are not handled
434 // correctly.
435 CFRunLoopRef theRunLoop = NULL;
436 AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
437 kAudioObjectPropertyScopeGlobal,
438 kAudioObjectPropertyElementMaster };
439 OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
440 if ( result != noErr ) {
441 errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";
442 error( RtError::WARNING );
444 #endif
447 RtApiCore :: ~RtApiCore()
449 // The subclass destructor gets called before the base class
450 // destructor, so close an existing stream before deallocating
451 // apiDeviceId memory.
452 if ( stream_.state != STREAM_CLOSED ) closeStream();
455 unsigned int RtApiCore :: getDeviceCount( void )
457 // Find out how many audio devices there are, if any.
458 UInt32 dataSize;
459 AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
460 OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );
461 if ( result != noErr ) {
462 errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
463 error( RtError::WARNING );
464 return 0;
467 return dataSize / sizeof( AudioDeviceID );
470 unsigned int RtApiCore :: getDefaultInputDevice( void )
472 unsigned int nDevices = getDeviceCount();
473 if ( nDevices <= 1 ) return 0;
475 AudioDeviceID id;
476 UInt32 dataSize = sizeof( AudioDeviceID );
477 AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
478 OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
479 if ( result != noErr ) {
480 errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
481 error( RtError::WARNING );
482 return 0;
485 dataSize *= nDevices;
486 AudioDeviceID deviceList[ nDevices ];
487 property.mSelector = kAudioHardwarePropertyDevices;
488 result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
489 if ( result != noErr ) {
490 errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
491 error( RtError::WARNING );
492 return 0;
495 for ( unsigned int i=0; i<nDevices; i++ )
496 if ( id == deviceList[i] ) return i;
498 errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
499 error( RtError::WARNING );
500 return 0;
503 unsigned int RtApiCore :: getDefaultOutputDevice( void )
505 unsigned int nDevices = getDeviceCount();
506 if ( nDevices <= 1 ) return 0;
508 AudioDeviceID id;
509 UInt32 dataSize = sizeof( AudioDeviceID );
510 AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
511 OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
512 if ( result != noErr ) {
513 errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
514 error( RtError::WARNING );
515 return 0;
518 dataSize = sizeof( AudioDeviceID ) * nDevices;
519 AudioDeviceID deviceList[ nDevices ];
520 property.mSelector = kAudioHardwarePropertyDevices;
521 result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
522 if ( result != noErr ) {
523 errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
524 error( RtError::WARNING );
525 return 0;
528 for ( unsigned int i=0; i<nDevices; i++ )
529 if ( id == deviceList[i] ) return i;
531 errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
532 error( RtError::WARNING );
533 return 0;
536 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
538 RtAudio::DeviceInfo info;
539 info.probed = false;
541 // Get device ID
542 unsigned int nDevices = getDeviceCount();
543 if ( nDevices == 0 ) {
544 errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
545 error( RtError::INVALID_USE );
548 if ( device >= nDevices ) {
549 errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
550 error( RtError::INVALID_USE );
553 AudioDeviceID deviceList[ nDevices ];
554 UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
555 AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
556 kAudioObjectPropertyScopeGlobal,
557 kAudioObjectPropertyElementMaster };
558 OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
559 0, NULL, &dataSize, (void *) &deviceList );
560 if ( result != noErr ) {
561 errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
562 error( RtError::WARNING );
563 return info;
566 AudioDeviceID id = deviceList[ device ];
568 // Get the device name.
569 info.name.erase();
570 CFStringRef cfname;
571 dataSize = sizeof( CFStringRef );
572 property.mSelector = kAudioObjectPropertyManufacturer;
573 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
574 if ( result != noErr ) {
575 errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
576 errorText_ = errorStream_.str();
577 error( RtError::WARNING );
578 return info;
581 //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
582 int length = CFStringGetLength(cfname);
583 char *mname = (char *)malloc(length * 3 + 1);
584 CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());
585 info.name.append( (const char *)mname, strlen(mname) );
586 info.name.append( ": " );
587 CFRelease( cfname );
588 free(mname);
590 property.mSelector = kAudioObjectPropertyName;
591 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
592 if ( result != noErr ) {
593 errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
594 errorText_ = errorStream_.str();
595 error( RtError::WARNING );
596 return info;
599 //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
600 length = CFStringGetLength(cfname);
601 char *name = (char *)malloc(length * 3 + 1);
602 CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());
603 info.name.append( (const char *)name, strlen(name) );
604 CFRelease( cfname );
605 free(name);
607 // Get the output stream "configuration".
608 AudioBufferList *bufferList = nil;
609 property.mSelector = kAudioDevicePropertyStreamConfiguration;
610 property.mScope = kAudioDevicePropertyScopeOutput;
611 // property.mElement = kAudioObjectPropertyElementWildcard;
612 dataSize = 0;
613 result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
614 if ( result != noErr || dataSize == 0 ) {
615 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
616 errorText_ = errorStream_.str();
617 error( RtError::WARNING );
618 return info;
621 // Allocate the AudioBufferList.
622 bufferList = (AudioBufferList *) malloc( dataSize );
623 if ( bufferList == NULL ) {
624 errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
625 error( RtError::WARNING );
626 return info;
629 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
630 if ( result != noErr || dataSize == 0 ) {
631 free( bufferList );
632 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
633 errorText_ = errorStream_.str();
634 error( RtError::WARNING );
635 return info;
638 // Get output channel information.
639 unsigned int i, nStreams = bufferList->mNumberBuffers;
640 for ( i=0; i<nStreams; i++ )
641 info.outputChannels += bufferList->mBuffers[i].mNumberChannels;
642 free( bufferList );
644 // Get the input stream "configuration".
645 property.mScope = kAudioDevicePropertyScopeInput;
646 result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
647 if ( result != noErr || dataSize == 0 ) {
648 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
649 errorText_ = errorStream_.str();
650 error( RtError::WARNING );
651 return info;
654 // Allocate the AudioBufferList.
655 bufferList = (AudioBufferList *) malloc( dataSize );
656 if ( bufferList == NULL ) {
657 errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
658 error( RtError::WARNING );
659 return info;
662 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
663 if (result != noErr || dataSize == 0) {
664 free( bufferList );
665 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
666 errorText_ = errorStream_.str();
667 error( RtError::WARNING );
668 return info;
671 // Get input channel information.
672 nStreams = bufferList->mNumberBuffers;
673 for ( i=0; i<nStreams; i++ )
674 info.inputChannels += bufferList->mBuffers[i].mNumberChannels;
675 free( bufferList );
677 // If device opens for both playback and capture, we determine the channels.
678 if ( info.outputChannels > 0 && info.inputChannels > 0 )
679 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
681 // Probe the device sample rates.
682 bool isInput = false;
683 if ( info.outputChannels == 0 ) isInput = true;
685 // Determine the supported sample rates.
686 property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
687 if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;
688 result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
689 if ( result != kAudioHardwareNoError || dataSize == 0 ) {
690 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
691 errorText_ = errorStream_.str();
692 error( RtError::WARNING );
693 return info;
696 UInt32 nRanges = dataSize / sizeof( AudioValueRange );
697 AudioValueRange rangeList[ nRanges ];
698 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );
699 if ( result != kAudioHardwareNoError ) {
700 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
701 errorText_ = errorStream_.str();
702 error( RtError::WARNING );
703 return info;
706 Float64 minimumRate = 100000000.0, maximumRate = 0.0;
707 for ( UInt32 i=0; i<nRanges; i++ ) {
708 if ( rangeList[i].mMinimum < minimumRate ) minimumRate = rangeList[i].mMinimum;
709 if ( rangeList[i].mMaximum > maximumRate ) maximumRate = rangeList[i].mMaximum;
712 info.sampleRates.clear();
713 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
714 if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )
715 info.sampleRates.push_back( SAMPLE_RATES[k] );
718 if ( info.sampleRates.size() == 0 ) {
719 errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
720 errorText_ = errorStream_.str();
721 error( RtError::WARNING );
722 return info;
725 // CoreAudio always uses 32-bit floating point data for PCM streams.
726 // Thus, any other "physical" formats supported by the device are of
727 // no interest to the client.
728 info.nativeFormats = RTAUDIO_FLOAT32;
730 if ( info.outputChannels > 0 )
731 if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
732 if ( info.inputChannels > 0 )
733 if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
735 info.probed = true;
736 return info;
739 OSStatus callbackHandler( AudioDeviceID inDevice,
740 const AudioTimeStamp* inNow,
741 const AudioBufferList* inInputData,
742 const AudioTimeStamp* inInputTime,
743 AudioBufferList* outOutputData,
744 const AudioTimeStamp* inOutputTime,
745 void* infoPointer )
747 CallbackInfo *info = (CallbackInfo *) infoPointer;
749 RtApiCore *object = (RtApiCore *) info->object;
750 if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )
751 return kAudioHardwareUnspecifiedError;
752 else
753 return kAudioHardwareNoError;
756 OSStatus xrunListener( AudioObjectID inDevice,
757 UInt32 nAddresses,
758 const AudioObjectPropertyAddress properties[],
759 void* handlePointer )
761 CoreHandle *handle = (CoreHandle *) handlePointer;
762 for ( UInt32 i=0; i<nAddresses; i++ ) {
763 if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {
764 if ( properties[i].mScope == kAudioDevicePropertyScopeInput )
765 handle->xrun[1] = true;
766 else
767 handle->xrun[0] = true;
771 return kAudioHardwareNoError;
774 OSStatus rateListener( AudioObjectID inDevice,
775 UInt32 nAddresses,
776 const AudioObjectPropertyAddress properties[],
777 void* ratePointer )
780 Float64 *rate = (Float64 *) ratePointer;
781 UInt32 dataSize = sizeof( Float64 );
782 AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,
783 kAudioObjectPropertyScopeGlobal,
784 kAudioObjectPropertyElementMaster };
785 AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );
786 return kAudioHardwareNoError;
789 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
790 unsigned int firstChannel, unsigned int sampleRate,
791 RtAudioFormat format, unsigned int *bufferSize,
792 RtAudio::StreamOptions *options )
794 // Get device ID
795 unsigned int nDevices = getDeviceCount();
796 if ( nDevices == 0 ) {
797 // This should not happen because a check is made before this function is called.
798 errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";
799 return FAILURE;
802 if ( device >= nDevices ) {
803 // This should not happen because a check is made before this function is called.
804 errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";
805 return FAILURE;
808 AudioDeviceID deviceList[ nDevices ];
809 UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
810 AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
811 kAudioObjectPropertyScopeGlobal,
812 kAudioObjectPropertyElementMaster };
813 OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
814 0, NULL, &dataSize, (void *) &deviceList );
815 if ( result != noErr ) {
816 errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
817 return FAILURE;
820 AudioDeviceID id = deviceList[ device ];
822 // Setup for stream mode.
823 bool isInput = false;
824 if ( mode == INPUT ) {
825 isInput = true;
826 property.mScope = kAudioDevicePropertyScopeInput;
828 else
829 property.mScope = kAudioDevicePropertyScopeOutput;
831 // Get the stream "configuration".
832 AudioBufferList *bufferList = nil;
833 dataSize = 0;
834 property.mSelector = kAudioDevicePropertyStreamConfiguration;
835 result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
836 if ( result != noErr || dataSize == 0 ) {
837 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
838 errorText_ = errorStream_.str();
839 return FAILURE;
842 // Allocate the AudioBufferList.
843 bufferList = (AudioBufferList *) malloc( dataSize );
844 if ( bufferList == NULL ) {
845 errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
846 return FAILURE;
849 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
850 if (result != noErr || dataSize == 0) {
851 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
852 errorText_ = errorStream_.str();
853 return FAILURE;
856 // Search for one or more streams that contain the desired number of
857 // channels. CoreAudio devices can have an arbitrary number of
858 // streams and each stream can have an arbitrary number of channels.
859 // For each stream, a single buffer of interleaved samples is
860 // provided. RtAudio prefers the use of one stream of interleaved
861 // data or multiple consecutive single-channel streams. However, we
862 // now support multiple consecutive multi-channel streams of
863 // interleaved data as well.
864 UInt32 iStream, offsetCounter = firstChannel;
865 UInt32 nStreams = bufferList->mNumberBuffers;
866 bool monoMode = false;
867 bool foundStream = false;
869 // First check that the device supports the requested number of
870 // channels.
871 UInt32 deviceChannels = 0;
872 for ( iStream=0; iStream<nStreams; iStream++ )
873 deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;
875 if ( deviceChannels < ( channels + firstChannel ) ) {
876 free( bufferList );
877 errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";
878 errorText_ = errorStream_.str();
879 return FAILURE;
882 // Look for a single stream meeting our needs.
883 UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
884 for ( iStream=0; iStream<nStreams; iStream++ ) {
885 streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
886 if ( streamChannels >= channels + offsetCounter ) {
887 firstStream = iStream;
888 channelOffset = offsetCounter;
889 foundStream = true;
890 break;
892 if ( streamChannels > offsetCounter ) break;
893 offsetCounter -= streamChannels;
896 // If we didn't find a single stream above, then we should be able
897 // to meet the channel specification with multiple streams.
898 if ( foundStream == false ) {
899 monoMode = true;
900 offsetCounter = firstChannel;
901 for ( iStream=0; iStream<nStreams; iStream++ ) {
902 streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
903 if ( streamChannels > offsetCounter ) break;
904 offsetCounter -= streamChannels;
907 firstStream = iStream;
908 channelOffset = offsetCounter;
909 Int32 channelCounter = channels + offsetCounter - streamChannels;
911 if ( streamChannels > 1 ) monoMode = false;
912 while ( channelCounter > 0 ) {
913 streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;
914 if ( streamChannels > 1 ) monoMode = false;
915 channelCounter -= streamChannels;
916 streamCount++;
920 free( bufferList );
922 // Determine the buffer size.
923 AudioValueRange bufferRange;
924 dataSize = sizeof( AudioValueRange );
925 property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
926 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );
928 if ( result != noErr ) {
929 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
930 errorText_ = errorStream_.str();
931 return FAILURE;
934 if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
935 else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
936 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
938 // Set the buffer size. For multiple streams, I'm assuming we only
939 // need to make this setting for the master channel.
940 UInt32 theSize = (UInt32) *bufferSize;
941 dataSize = sizeof( UInt32 );
942 property.mSelector = kAudioDevicePropertyBufferFrameSize;
943 result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );
945 if ( result != noErr ) {
946 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
947 errorText_ = errorStream_.str();
948 return FAILURE;
951 // If attempting to setup a duplex stream, the bufferSize parameter
952 // MUST be the same in both directions!
953 *bufferSize = theSize;
954 if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
955 errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";
956 errorText_ = errorStream_.str();
957 return FAILURE;
960 stream_.bufferSize = *bufferSize;
961 stream_.nBuffers = 1;
963 // Try to set "hog" mode ... it's not clear to me this is working.
964 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {
965 pid_t hog_pid;
966 dataSize = sizeof( hog_pid );
967 property.mSelector = kAudioDevicePropertyHogMode;
968 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );
969 if ( result != noErr ) {
970 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";
971 errorText_ = errorStream_.str();
972 return FAILURE;
975 if ( hog_pid != getpid() ) {
976 hog_pid = getpid();
977 result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );
978 if ( result != noErr ) {
979 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
980 errorText_ = errorStream_.str();
981 return FAILURE;
986 // Check and if necessary, change the sample rate for the device.
987 Float64 nominalRate;
988 dataSize = sizeof( Float64 );
989 property.mSelector = kAudioDevicePropertyNominalSampleRate;
990 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );
992 if ( result != noErr ) {
993 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";
994 errorText_ = errorStream_.str();
995 return FAILURE;
998 // Only change the sample rate if off by more than 1 Hz.
999 if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {
1001 // Set a property listener for the sample rate change
1002 Float64 reportedRate = 0.0;
1003 AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1004 result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
1005 if ( result != noErr ) {
1006 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";
1007 errorText_ = errorStream_.str();
1008 return FAILURE;
1011 nominalRate = (Float64) sampleRate;
1012 result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );
1014 if ( result != noErr ) {
1015 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";
1016 errorText_ = errorStream_.str();
1017 return FAILURE;
1020 //OL - commented out these microCounter related lines which were causing unnessecary waits
1022 // Now wait until the reported nominal rate is what we just set.
1023 // UInt32 microCounter = 0;
1024 // while ( reportedRate != nominalRate ) {
1025 // microCounter += 5000;
1026 // if ( microCounter > 5000000 ) break;
1027 // usleep( 5000 );
1028 // }
1030 // Remove the property listener.
1031 AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
1033 // if ( microCounter > 5000000 ) {
1034 // errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";
1035 // errorText_ = errorStream_.str();
1036 // return FAILURE;
1037 // }
1040 // Now set the stream format for all streams. Also, check the
1041 // physical format of the device and change that if necessary.
1042 AudioStreamBasicDescription description;
1043 dataSize = sizeof( AudioStreamBasicDescription );
1044 property.mSelector = kAudioStreamPropertyVirtualFormat;
1045 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );
1046 if ( result != noErr ) {
1047 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
1048 errorText_ = errorStream_.str();
1049 return FAILURE;
1052 // Set the sample rate and data format id. However, only make the
1053 // change if the sample rate is not within 1.0 of the desired
1054 // rate and the format is not linear pcm.
1055 bool updateFormat = false;
1056 if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {
1057 description.mSampleRate = (Float64) sampleRate;
1058 updateFormat = true;
1061 if ( description.mFormatID != kAudioFormatLinearPCM ) {
1062 description.mFormatID = kAudioFormatLinearPCM;
1063 updateFormat = true;
1066 if ( updateFormat ) {
1067 result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );
1068 if ( result != noErr ) {
1069 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
1070 errorText_ = errorStream_.str();
1071 return FAILURE;
1075 // Now check the physical format.
1076 property.mSelector = kAudioStreamPropertyPhysicalFormat;
1077 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );
1078 if ( result != noErr ) {
1079 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
1080 errorText_ = errorStream_.str();
1081 return FAILURE;
1084 //std::cout << "Current physical stream format:" << std::endl;
1085 //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl;
1086 //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
1087 //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl;
1088 //std::cout << " sample rate = " << description.mSampleRate << std::endl;
1090 if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {
1091 description.mFormatID = kAudioFormatLinearPCM;
1092 //description.mSampleRate = (Float64) sampleRate;
1093 AudioStreamBasicDescription testDescription = description;
1094 UInt32 formatFlags;
1096 // We'll try higher bit rates first and then work our way down.
1097 std::vector< std::pair<UInt32, UInt32> > physicalFormats;
1098 formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;
1099 physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );
1100 formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;
1101 physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );
1102 physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) ); // 24-bit packed
1103 formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );
1104 physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low
1105 formatFlags |= kAudioFormatFlagIsAlignedHigh;
1106 physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high
1107 formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;
1108 physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );
1109 physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );
1111 bool setPhysicalFormat = false;
1112 for( unsigned int i=0; i<physicalFormats.size(); i++ ) {
1113 testDescription = description;
1114 testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;
1115 testDescription.mFormatFlags = physicalFormats[i].second;
1116 if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )
1117 testDescription.mBytesPerFrame = 4 * testDescription.mChannelsPerFrame;
1118 else
1119 testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
1120 testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
1121 result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );
1122 if ( result == noErr ) {
1123 setPhysicalFormat = true;
1124 //std::cout << "Updated physical stream format:" << std::endl;
1125 //std::cout << " mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;
1126 //std::cout << " aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
1127 //std::cout << " bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;
1128 //std::cout << " sample rate = " << testDescription.mSampleRate << std::endl;
1129 break;
1133 if ( !setPhysicalFormat ) {
1134 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
1135 errorText_ = errorStream_.str();
1136 return FAILURE;
1138 } // done setting virtual/physical formats.
1140 // Get the stream / device latency.
1141 UInt32 latency;
1142 dataSize = sizeof( UInt32 );
1143 property.mSelector = kAudioDevicePropertyLatency;
1144 if ( AudioObjectHasProperty( id, &property ) == true ) {
1145 result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );
1146 if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
1147 else {
1148 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
1149 errorText_ = errorStream_.str();
1150 error( RtError::WARNING );
1154 // Byte-swapping: According to AudioHardware.h, the stream data will
1155 // always be presented in native-endian format, so we should never
1156 // need to byte swap.
1157 stream_.doByteSwap[mode] = false;
1159 // From the CoreAudio documentation, PCM data must be supplied as
1160 // 32-bit floats.
1161 stream_.userFormat = format;
1162 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
1164 if ( streamCount == 1 )
1165 stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
1166 else // multiple streams
1167 stream_.nDeviceChannels[mode] = channels;
1168 stream_.nUserChannels[mode] = channels;
1169 stream_.channelOffset[mode] = channelOffset; // offset within a CoreAudio stream
1170 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
1171 else stream_.userInterleaved = true;
1172 stream_.deviceInterleaved[mode] = true;
1173 if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;
1175 // Set flags for buffer conversion.
1176 stream_.doConvertBuffer[mode] = false;
1177 if ( stream_.userFormat != stream_.deviceFormat[mode] )
1178 stream_.doConvertBuffer[mode] = true;
1179 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
1180 stream_.doConvertBuffer[mode] = true;
1181 if ( streamCount == 1 ) {
1182 if ( stream_.nUserChannels[mode] > 1 &&
1183 stream_.userInterleaved != stream_.deviceInterleaved[mode] )
1184 stream_.doConvertBuffer[mode] = true;
1186 else if ( monoMode && stream_.userInterleaved )
1187 stream_.doConvertBuffer[mode] = true;
1189 // Allocate our CoreHandle structure for the stream.
1190 CoreHandle *handle = 0;
1191 if ( stream_.apiHandle == 0 ) {
1192 try {
1193 handle = new CoreHandle;
1195 catch ( std::bad_alloc& ) {
1196 errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
1197 goto error;
1200 if ( pthread_cond_init( &handle->condition, NULL ) ) {
1201 errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
1202 goto error;
1204 stream_.apiHandle = (void *) handle;
1206 else
1207 handle = (CoreHandle *) stream_.apiHandle;
1208 handle->iStream[mode] = firstStream;
1209 handle->nStreams[mode] = streamCount;
1210 handle->id[mode] = id;
1212 // Allocate necessary internal buffers.
1213 unsigned long bufferBytes;
1214 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
1215 // stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
1216 stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );
1217 memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );
1218 if ( stream_.userBuffer[mode] == NULL ) {
1219 errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
1220 goto error;
1223 // If possible, we will make use of the CoreAudio stream buffers as
1224 // "device buffers". However, we can't do this if using multiple
1225 // streams.
1226 if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {
1228 bool makeBuffer = true;
1229 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
1230 if ( mode == INPUT ) {
1231 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
1232 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
1233 if ( bufferBytes <= bytesOut ) makeBuffer = false;
1237 if ( makeBuffer ) {
1238 bufferBytes *= *bufferSize;
1239 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
1240 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
1241 if ( stream_.deviceBuffer == NULL ) {
1242 errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
1243 goto error;
1248 stream_.sampleRate = sampleRate;
1249 stream_.device[mode] = device;
1250 stream_.state = STREAM_STOPPED;
1251 stream_.callbackInfo.object = (void *) this;
1253 // Setup the buffer conversion information structure.
1254 if ( stream_.doConvertBuffer[mode] ) {
1255 if ( streamCount > 1 ) setConvertInfo( mode, 0 );
1256 else setConvertInfo( mode, channelOffset );
1259 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
1260 // Only one callback procedure per device.
1261 stream_.mode = DUPLEX;
1262 else {
1263 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
1264 result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );
1265 #else
1266 // deprecated in favor of AudioDeviceCreateIOProcID()
1267 result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
1268 #endif
1269 if ( result != noErr ) {
1270 errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
1271 errorText_ = errorStream_.str();
1272 goto error;
1274 if ( stream_.mode == OUTPUT && mode == INPUT )
1275 stream_.mode = DUPLEX;
1276 else
1277 stream_.mode = mode;
1280 // Setup the device property listener for over/underload.
1281 property.mSelector = kAudioDeviceProcessorOverload;
1282 result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
1284 return SUCCESS;
1286 error:
1287 if ( handle ) {
1288 pthread_cond_destroy( &handle->condition );
1289 delete handle;
1290 stream_.apiHandle = 0;
1293 for ( int i=0; i<2; i++ ) {
1294 if ( stream_.userBuffer[i] ) {
1295 free( stream_.userBuffer[i] );
1296 stream_.userBuffer[i] = 0;
1300 if ( stream_.deviceBuffer ) {
1301 free( stream_.deviceBuffer );
1302 stream_.deviceBuffer = 0;
1305 return FAILURE;
1308 void RtApiCore :: closeStream( void )
1310 if ( stream_.state == STREAM_CLOSED ) {
1311 errorText_ = "RtApiCore::closeStream(): no open stream to close!";
1312 error( RtError::WARNING );
1313 return;
1316 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1317 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1318 if ( stream_.state == STREAM_RUNNING )
1319 AudioDeviceStop( handle->id[0], callbackHandler );
1320 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
1321 AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
1322 #else
1323 // deprecated in favor of AudioDeviceDestroyIOProcID()
1324 AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
1325 #endif
1328 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1329 if ( stream_.state == STREAM_RUNNING )
1330 AudioDeviceStop( handle->id[1], callbackHandler );
1331 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
1332 AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
1333 #else
1334 // deprecated in favor of AudioDeviceDestroyIOProcID()
1335 AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
1336 #endif
1339 for ( int i=0; i<2; i++ ) {
1340 if ( stream_.userBuffer[i] ) {
1341 free( stream_.userBuffer[i] );
1342 stream_.userBuffer[i] = 0;
1346 if ( stream_.deviceBuffer ) {
1347 free( stream_.deviceBuffer );
1348 stream_.deviceBuffer = 0;
1351 // Destroy pthread condition variable.
1352 pthread_cond_destroy( &handle->condition );
1353 delete handle;
1354 stream_.apiHandle = 0;
1356 stream_.mode = UNINITIALIZED;
1357 stream_.state = STREAM_CLOSED;
1360 void RtApiCore :: startStream( void )
1362 verifyStream();
1363 if ( stream_.state == STREAM_RUNNING ) {
1364 errorText_ = "RtApiCore::startStream(): the stream is already running!";
1365 error( RtError::WARNING );
1366 return;
1369 OSStatus result = noErr;
1370 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1371 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1373 result = AudioDeviceStart( handle->id[0], callbackHandler );
1374 if ( result != noErr ) {
1375 errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
1376 errorText_ = errorStream_.str();
1377 goto unlock;
1381 if ( stream_.mode == INPUT ||
1382 ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1384 result = AudioDeviceStart( handle->id[1], callbackHandler );
1385 if ( result != noErr ) {
1386 errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
1387 errorText_ = errorStream_.str();
1388 goto unlock;
1392 handle->drainCounter = 0;
1393 handle->internalDrain = false;
1394 stream_.state = STREAM_RUNNING;
1396 unlock:
1397 if ( result == noErr ) return;
1398 error( RtError::SYSTEM_ERROR );
1401 void RtApiCore :: stopStream( void )
1403 verifyStream();
1404 if ( stream_.state == STREAM_STOPPED ) {
1405 errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
1406 error( RtError::WARNING );
1407 return;
1410 OSStatus result = noErr;
1411 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1412 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1414 if ( handle->drainCounter == 0 ) {
1415 handle->drainCounter = 2;
1416 pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
1419 result = AudioDeviceStop( handle->id[0], callbackHandler );
1420 if ( result != noErr ) {
1421 errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
1422 errorText_ = errorStream_.str();
1423 goto unlock;
1427 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1429 result = AudioDeviceStop( handle->id[1], callbackHandler );
1430 if ( result != noErr ) {
1431 errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
1432 errorText_ = errorStream_.str();
1433 goto unlock;
1437 stream_.state = STREAM_STOPPED;
1439 unlock:
1440 if ( result == noErr ) return;
1441 error( RtError::SYSTEM_ERROR );
1444 void RtApiCore :: abortStream( void )
1446 verifyStream();
1447 if ( stream_.state == STREAM_STOPPED ) {
1448 errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
1449 error( RtError::WARNING );
1450 return;
1453 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1454 handle->drainCounter = 2;
1456 stopStream();
1459 // This function will be called by a spawned thread when the user
1460 // callback function signals that the stream should be stopped or
1461 // aborted. It is better to handle it this way because the
1462 // callbackEvent() function probably should return before the AudioDeviceStop()
1463 // function is called.
1464 extern "C" void *coreStopStream( void *ptr )
1466 CallbackInfo *info = (CallbackInfo *) ptr;
1467 RtApiCore *object = (RtApiCore *) info->object;
1469 object->stopStream();
1470 pthread_exit( NULL );
1473 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
1474 const AudioBufferList *inBufferList,
1475 const AudioBufferList *outBufferList )
1477 if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
1478 if ( stream_.state == STREAM_CLOSED ) {
1479 errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
1480 error( RtError::WARNING );
1481 return FAILURE;
1484 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
1485 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1487 // Check if we were draining the stream and signal is finished.
1488 if ( handle->drainCounter > 3 ) {
1490 stream_.state = STREAM_STOPPING;
1491 if ( handle->internalDrain == true )
1492 pthread_create( &threadId, NULL, coreStopStream, info );
1493 else // external call to stopStream()
1494 pthread_cond_signal( &handle->condition );
1495 return SUCCESS;
1498 AudioDeviceID outputDevice = handle->id[0];
1500 // Invoke user callback to get fresh output data UNLESS we are
1501 // draining stream or duplex mode AND the input/output devices are
1502 // different AND this function is called for the input device.
1503 if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {
1504 RtAudioCallback callback = (RtAudioCallback) info->callback;
1505 double streamTime = getStreamTime();
1506 RtAudioStreamStatus status = 0;
1507 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
1508 status |= RTAUDIO_OUTPUT_UNDERFLOW;
1509 handle->xrun[0] = false;
1511 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
1512 status |= RTAUDIO_INPUT_OVERFLOW;
1513 handle->xrun[1] = false;
1516 int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
1517 stream_.bufferSize, streamTime, status, info->userData );
1518 if ( cbReturnValue == 2 ) {
1519 stream_.state = STREAM_STOPPING;
1520 handle->drainCounter = 2;
1521 abortStream();
1522 return SUCCESS;
1524 else if ( cbReturnValue == 1 ) {
1525 handle->drainCounter = 1;
1526 handle->internalDrain = true;
1530 if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
1532 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
1534 if ( handle->nStreams[0] == 1 ) {
1535 memset( outBufferList->mBuffers[handle->iStream[0]].mData,
1537 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
1539 else { // fill multiple streams with zeros
1540 for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
1541 memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1543 outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
1547 else if ( handle->nStreams[0] == 1 ) {
1548 if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer
1549 convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,
1550 stream_.userBuffer[0], stream_.convertInfo[0] );
1552 else { // copy from user buffer
1553 memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
1554 stream_.userBuffer[0],
1555 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
1558 else { // fill multiple streams
1559 Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];
1560 if ( stream_.doConvertBuffer[0] ) {
1561 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
1562 inBuffer = (Float32 *) stream_.deviceBuffer;
1565 if ( stream_.deviceInterleaved[0] == false ) { // mono mode
1566 UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
1567 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
1568 memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1569 (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );
1572 else { // fill multiple multi-channel streams with interleaved data
1573 UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;
1574 Float32 *out, *in;
1576 bool inInterleaved = ( stream_.userInterleaved ) ? true : false;
1577 UInt32 inChannels = stream_.nUserChannels[0];
1578 if ( stream_.doConvertBuffer[0] ) {
1579 inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
1580 inChannels = stream_.nDeviceChannels[0];
1583 if ( inInterleaved ) inOffset = 1;
1584 else inOffset = stream_.bufferSize;
1586 channelsLeft = inChannels;
1587 for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
1588 in = inBuffer;
1589 out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;
1590 streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;
1592 outJump = 0;
1593 // Account for possible channel offset in first stream
1594 if ( i == 0 && stream_.channelOffset[0] > 0 ) {
1595 streamChannels -= stream_.channelOffset[0];
1596 outJump = stream_.channelOffset[0];
1597 out += outJump;
1600 // Account for possible unfilled channels at end of the last stream
1601 if ( streamChannels > channelsLeft ) {
1602 outJump = streamChannels - channelsLeft;
1603 streamChannels = channelsLeft;
1606 // Determine input buffer offsets and skips
1607 if ( inInterleaved ) {
1608 inJump = inChannels;
1609 in += inChannels - channelsLeft;
1611 else {
1612 inJump = 1;
1613 in += (inChannels - channelsLeft) * inOffset;
1616 for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
1617 for ( unsigned int j=0; j<streamChannels; j++ ) {
1618 *out++ = in[j*inOffset];
1620 out += outJump;
1621 in += inJump;
1623 channelsLeft -= streamChannels;
1628 if ( handle->drainCounter ) {
1629 handle->drainCounter++;
1630 goto unlock;
1634 AudioDeviceID inputDevice;
1635 inputDevice = handle->id[1];
1636 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
1638 if ( handle->nStreams[1] == 1 ) {
1639 if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer
1640 convertBuffer( stream_.userBuffer[1],
1641 (char *) inBufferList->mBuffers[handle->iStream[1]].mData,
1642 stream_.convertInfo[1] );
1644 else { // copy to user buffer
1645 memcpy( stream_.userBuffer[1],
1646 inBufferList->mBuffers[handle->iStream[1]].mData,
1647 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
1650 else { // read from multiple streams
1651 Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];
1652 if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;
1654 if ( stream_.deviceInterleaved[1] == false ) { // mono mode
1655 UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
1656 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
1657 memcpy( (void *)&outBuffer[i*stream_.bufferSize],
1658 inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
1661 else { // read from multiple multi-channel streams
1662 UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;
1663 Float32 *out, *in;
1665 bool outInterleaved = ( stream_.userInterleaved ) ? true : false;
1666 UInt32 outChannels = stream_.nUserChannels[1];
1667 if ( stream_.doConvertBuffer[1] ) {
1668 outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
1669 outChannels = stream_.nDeviceChannels[1];
1672 if ( outInterleaved ) outOffset = 1;
1673 else outOffset = stream_.bufferSize;
1675 channelsLeft = outChannels;
1676 for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {
1677 out = outBuffer;
1678 in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;
1679 streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;
1681 inJump = 0;
1682 // Account for possible channel offset in first stream
1683 if ( i == 0 && stream_.channelOffset[1] > 0 ) {
1684 streamChannels -= stream_.channelOffset[1];
1685 inJump = stream_.channelOffset[1];
1686 in += inJump;
1689 // Account for possible unread channels at end of the last stream
1690 if ( streamChannels > channelsLeft ) {
1691 inJump = streamChannels - channelsLeft;
1692 streamChannels = channelsLeft;
1695 // Determine output buffer offsets and skips
1696 if ( outInterleaved ) {
1697 outJump = outChannels;
1698 out += outChannels - channelsLeft;
1700 else {
1701 outJump = 1;
1702 out += (outChannels - channelsLeft) * outOffset;
1705 for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
1706 for ( unsigned int j=0; j<streamChannels; j++ ) {
1707 out[j*outOffset] = *in++;
1709 out += outJump;
1710 in += inJump;
1712 channelsLeft -= streamChannels;
1716 if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer
1717 convertBuffer( stream_.userBuffer[1],
1718 stream_.deviceBuffer,
1719 stream_.convertInfo[1] );
1724 unlock:
1725 //MUTEX_UNLOCK( &stream_.mutex );
1727 RtApi::tickStreamTime();
1728 return SUCCESS;
1731 const char* RtApiCore :: getErrorCode( OSStatus code )
1733 switch( code ) {
1735 case kAudioHardwareNotRunningError:
1736 return "kAudioHardwareNotRunningError";
1738 case kAudioHardwareUnspecifiedError:
1739 return "kAudioHardwareUnspecifiedError";
1741 case kAudioHardwareUnknownPropertyError:
1742 return "kAudioHardwareUnknownPropertyError";
1744 case kAudioHardwareBadPropertySizeError:
1745 return "kAudioHardwareBadPropertySizeError";
1747 case kAudioHardwareIllegalOperationError:
1748 return "kAudioHardwareIllegalOperationError";
1750 case kAudioHardwareBadObjectError:
1751 return "kAudioHardwareBadObjectError";
1753 case kAudioHardwareBadDeviceError:
1754 return "kAudioHardwareBadDeviceError";
1756 case kAudioHardwareBadStreamError:
1757 return "kAudioHardwareBadStreamError";
1759 case kAudioHardwareUnsupportedOperationError:
1760 return "kAudioHardwareUnsupportedOperationError";
1762 case kAudioDeviceUnsupportedFormatError:
1763 return "kAudioDeviceUnsupportedFormatError";
1765 case kAudioDevicePermissionsError:
1766 return "kAudioDevicePermissionsError";
1768 default:
1769 return "CoreAudio unknown error";
1773 //******************** End of __MACOSX_CORE__ *********************//
1774 #endif
1776 #if defined(__UNIX_JACK__)
1778 // JACK is a low-latency audio server, originally written for the
1779 // GNU/Linux operating system and now also ported to OS-X. It can
1780 // connect a number of different applications to an audio device, as
1781 // well as allowing them to share audio between themselves.
1783 // When using JACK with RtAudio, "devices" refer to JACK clients that
1784 // have ports connected to the server. The JACK server is typically
1785 // started in a terminal as follows:
1787 // .jackd -d alsa -d hw:0
1789 // or through an interface program such as qjackctl. Many of the
1790 // parameters normally set for a stream are fixed by the JACK server
1791 // and can be specified when the JACK server is started. In
1792 // particular,
1794 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
1796 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample
1797 // frames, and number of buffers = 4. Once the server is running, it
1798 // is not possible to override these values. If the values are not
1799 // specified in the command-line, the JACK server uses default values.
1801 // The JACK server does not have to be running when an instance of
1802 // RtApiJack is created, though the function getDeviceCount() will
1803 // report 0 devices found until JACK has been started. When no
1804 // devices are available (i.e., the JACK server is not running), a
1805 // stream cannot be opened.
1807 #include <jack/jack.h>
1808 #include <unistd.h>
1809 #include <cstdio>
1811 // A structure to hold various information related to the Jack API
1812 // implementation.
1813 struct JackHandle {
1814 jack_client_t *client;
1815 jack_port_t **ports[2];
1816 std::string deviceName[2];
1817 bool xrun[2];
1818 pthread_cond_t condition;
1819 int drainCounter; // Tracks callback counts when draining
1820 bool internalDrain; // Indicates if stop is initiated from callback or not.
1822 JackHandle()
1823 :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
1826 ThreadHandle threadId;
1827 void jackSilentError( const char * ) {};
1829 RtApiJack :: RtApiJack()
1831 // Nothing to do here.
1832 #if !defined(__RTAUDIO_DEBUG__)
1833 // Turn off Jack's internal error reporting.
1834 jack_set_error_function( &jackSilentError );
1835 #endif
1838 RtApiJack :: ~RtApiJack()
1840 if ( stream_.state != STREAM_CLOSED ) closeStream();
1843 unsigned int RtApiJack :: getDeviceCount( void )
1845 // See if we can become a jack client.
1846 jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;
1847 jack_status_t *status = NULL;
1848 jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );
1849 if ( client == 0 ) return 0;
1851 const char **ports;
1852 std::string port, previousPort;
1853 unsigned int nChannels = 0, nDevices = 0;
1854 ports = jack_get_ports( client, NULL, NULL, 0 );
1855 if ( ports ) {
1856 // Parse the port names up to the first colon (:).
1857 size_t iColon = 0;
1858 do {
1859 port = (char *) ports[ nChannels ];
1860 iColon = port.find(":");
1861 if ( iColon != std::string::npos ) {
1862 port = port.substr( 0, iColon + 1 );
1863 if ( port != previousPort ) {
1864 nDevices++;
1865 previousPort = port;
1868 } while ( ports[++nChannels] );
1869 free( ports );
1872 jack_client_close( client );
1873 return nDevices;
1876 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
1878 RtAudio::DeviceInfo info;
1879 info.probed = false;
1881 jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption
1882 jack_status_t *status = NULL;
1883 jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
1884 if ( client == 0 ) {
1885 errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
1886 error( RtError::WARNING );
1887 return info;
1890 const char **ports;
1891 std::string port, previousPort;
1892 unsigned int nPorts = 0, nDevices = 0;
1893 ports = jack_get_ports( client, NULL, NULL, 0 );
1894 if ( ports ) {
1895 // Parse the port names up to the first colon (:).
1896 size_t iColon = 0;
1897 do {
1898 port = (char *) ports[ nPorts ];
1899 iColon = port.find(":");
1900 if ( iColon != std::string::npos ) {
1901 port = port.substr( 0, iColon );
1902 if ( port != previousPort ) {
1903 if ( nDevices == device ) info.name = port;
1904 nDevices++;
1905 previousPort = port;
1908 } while ( ports[++nPorts] );
1909 free( ports );
1912 if ( device >= nDevices ) {
1913 jack_client_close( client );
1914 errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
1915 error( RtError::INVALID_USE );
1918 // Get the current jack server sample rate.
1919 info.sampleRates.clear();
1920 info.sampleRates.push_back( jack_get_sample_rate( client ) );
1922 // Count the available ports containing the client name as device
1923 // channels. Jack "input ports" equal RtAudio output channels.
1924 unsigned int nChannels = 0;
1925 ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );
1926 if ( ports ) {
1927 while ( ports[ nChannels ] ) nChannels++;
1928 free( ports );
1929 info.outputChannels = nChannels;
1932 // Jack "output ports" equal RtAudio input channels.
1933 nChannels = 0;
1934 ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );
1935 if ( ports ) {
1936 while ( ports[ nChannels ] ) nChannels++;
1937 free( ports );
1938 info.inputChannels = nChannels;
1941 if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
1942 jack_client_close(client);
1943 errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
1944 error( RtError::WARNING );
1945 return info;
1948 // If device opens for both playback and capture, we determine the channels.
1949 if ( info.outputChannels > 0 && info.inputChannels > 0 )
1950 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
1952 // Jack always uses 32-bit floats.
1953 info.nativeFormats = RTAUDIO_FLOAT32;
1955 // Jack doesn't provide default devices so we'll use the first available one.
1956 if ( device == 0 && info.outputChannels > 0 )
1957 info.isDefaultOutput = true;
1958 if ( device == 0 && info.inputChannels > 0 )
1959 info.isDefaultInput = true;
1961 jack_client_close(client);
1962 info.probed = true;
1963 return info;
1966 int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
1968 CallbackInfo *info = (CallbackInfo *) infoPointer;
1970 RtApiJack *object = (RtApiJack *) info->object;
1971 if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
1973 return 0;
1976 // This function will be called by a spawned thread when the Jack
1977 // server signals that it is shutting down. It is necessary to handle
1978 // it this way because the jackShutdown() function must return before
1979 // the jack_deactivate() function (in closeStream()) will return.
1980 extern "C" void *jackCloseStream( void *ptr )
1982 CallbackInfo *info = (CallbackInfo *) ptr;
1983 RtApiJack *object = (RtApiJack *) info->object;
1985 object->closeStream();
1987 pthread_exit( NULL );
1989 void jackShutdown( void *infoPointer )
1991 CallbackInfo *info = (CallbackInfo *) infoPointer;
1992 RtApiJack *object = (RtApiJack *) info->object;
1994 // Check current stream state. If stopped, then we'll assume this
1995 // was called as a result of a call to RtApiJack::stopStream (the
1996 // deactivation of a client handle causes this function to be called).
1997 // If not, we'll assume the Jack server is shutting down or some
1998 // other problem occurred and we should close the stream.
1999 if ( object->isStreamRunning() == false ) return;
2001 pthread_create( &threadId, NULL, jackCloseStream, info );
2002 std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
2005 int jackXrun( void *infoPointer )
2007 JackHandle *handle = (JackHandle *) infoPointer;
2009 if ( handle->ports[0] ) handle->xrun[0] = true;
2010 if ( handle->ports[1] ) handle->xrun[1] = true;
2012 return 0;
2015 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
2016 unsigned int firstChannel, unsigned int sampleRate,
2017 RtAudioFormat format, unsigned int *bufferSize,
2018 RtAudio::StreamOptions *options )
2020 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2022 // Look for jack server and try to become a client (only do once per stream).
2023 jack_client_t *client = 0;
2024 if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
2025 jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;
2026 jack_status_t *status = NULL;
2027 if ( options && !options->streamName.empty() )
2028 client = jack_client_open( options->streamName.c_str(), jackoptions, status );
2029 else
2030 client = jack_client_open( "RtApiJack", jackoptions, status );
2031 if ( client == 0 ) {
2032 errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
2033 error( RtError::WARNING );
2034 return FAILURE;
2037 else {
2038 // The handle must have been created on an earlier pass.
2039 client = handle->client;
2042 const char **ports;
2043 std::string port, previousPort, deviceName;
2044 unsigned int nPorts = 0, nDevices = 0;
2045 ports = jack_get_ports( client, NULL, NULL, 0 );
2046 if ( ports ) {
2047 // Parse the port names up to the first colon (:).
2048 size_t iColon = 0;
2049 do {
2050 port = (char *) ports[ nPorts ];
2051 iColon = port.find(":");
2052 if ( iColon != std::string::npos ) {
2053 port = port.substr( 0, iColon );
2054 if ( port != previousPort ) {
2055 if ( nDevices == device ) deviceName = port;
2056 nDevices++;
2057 previousPort = port;
2060 } while ( ports[++nPorts] );
2061 free( ports );
2064 if ( device >= nDevices ) {
2065 errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
2066 return FAILURE;
2069 // Count the available ports containing the client name as device
2070 // channels. Jack "input ports" equal RtAudio output channels.
2071 unsigned int nChannels = 0;
2072 unsigned long flag = JackPortIsInput;
2073 if ( mode == INPUT ) flag = JackPortIsOutput;
2074 ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
2075 if ( ports ) {
2076 while ( ports[ nChannels ] ) nChannels++;
2077 free( ports );
2080 // Compare the jack ports for specified client to the requested number of channels.
2081 if ( nChannels < (channels + firstChannel) ) {
2082 errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
2083 errorText_ = errorStream_.str();
2084 return FAILURE;
2087 // Check the jack server sample rate.
2088 unsigned int jackRate = jack_get_sample_rate( client );
2089 if ( sampleRate != jackRate ) {
2090 jack_client_close( client );
2091 errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
2092 errorText_ = errorStream_.str();
2093 return FAILURE;
2095 stream_.sampleRate = jackRate;
2097 // Get the latency of the JACK port.
2098 ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
2099 if ( ports[ firstChannel ] )
2100 stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
2101 free( ports );
2103 // The jack server always uses 32-bit floating-point data.
2104 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
2105 stream_.userFormat = format;
2107 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
2108 else stream_.userInterleaved = true;
2110 // Jack always uses non-interleaved buffers.
2111 stream_.deviceInterleaved[mode] = false;
2113 // Jack always provides host byte-ordered data.
2114 stream_.doByteSwap[mode] = false;
2116 // Get the buffer size. The buffer size and number of buffers
2117 // (periods) is set when the jack server is started.
2118 stream_.bufferSize = (int) jack_get_buffer_size( client );
2119 *bufferSize = stream_.bufferSize;
2121 stream_.nDeviceChannels[mode] = channels;
2122 stream_.nUserChannels[mode] = channels;
2124 // Set flags for buffer conversion.
2125 stream_.doConvertBuffer[mode] = false;
2126 if ( stream_.userFormat != stream_.deviceFormat[mode] )
2127 stream_.doConvertBuffer[mode] = true;
2128 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
2129 stream_.nUserChannels[mode] > 1 )
2130 stream_.doConvertBuffer[mode] = true;
2132 // Allocate our JackHandle structure for the stream.
2133 if ( handle == 0 ) {
2134 try {
2135 handle = new JackHandle;
2137 catch ( std::bad_alloc& ) {
2138 errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";
2139 goto error;
2142 if ( pthread_cond_init(&handle->condition, NULL) ) {
2143 errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
2144 goto error;
2146 stream_.apiHandle = (void *) handle;
2147 handle->client = client;
2149 handle->deviceName[mode] = deviceName;
2151 // Allocate necessary internal buffers.
2152 unsigned long bufferBytes;
2153 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
2154 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
2155 if ( stream_.userBuffer[mode] == NULL ) {
2156 errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";
2157 goto error;
2160 if ( stream_.doConvertBuffer[mode] ) {
2162 bool makeBuffer = true;
2163 if ( mode == OUTPUT )
2164 bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
2165 else { // mode == INPUT
2166 bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );
2167 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
2168 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
2169 if ( bufferBytes < bytesOut ) makeBuffer = false;
2173 if ( makeBuffer ) {
2174 bufferBytes *= *bufferSize;
2175 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
2176 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
2177 if ( stream_.deviceBuffer == NULL ) {
2178 errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";
2179 goto error;
2184 // Allocate memory for the Jack ports (channels) identifiers.
2185 handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
2186 if ( handle->ports[mode] == NULL ) {
2187 errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
2188 goto error;
2191 stream_.device[mode] = device;
2192 stream_.channelOffset[mode] = firstChannel;
2193 stream_.state = STREAM_STOPPED;
2194 stream_.callbackInfo.object = (void *) this;
2196 if ( stream_.mode == OUTPUT && mode == INPUT )
2197 // We had already set up the stream for output.
2198 stream_.mode = DUPLEX;
2199 else {
2200 stream_.mode = mode;
2201 jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
2202 jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );
2203 jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
2206 // Register our ports.
2207 char label[64];
2208 if ( mode == OUTPUT ) {
2209 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2210 snprintf( label, 64, "outport %d", i );
2211 handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
2212 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
2215 else {
2216 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2217 snprintf( label, 64, "inport %d", i );
2218 handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
2219 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
2223 // Setup the buffer conversion information structure. We don't use
2224 // buffers to do channel offsets, so we override that parameter
2225 // here.
2226 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
2228 return SUCCESS;
2230 error:
2231 if ( handle ) {
2232 pthread_cond_destroy( &handle->condition );
2233 jack_client_close( handle->client );
2235 if ( handle->ports[0] ) free( handle->ports[0] );
2236 if ( handle->ports[1] ) free( handle->ports[1] );
2238 delete handle;
2239 stream_.apiHandle = 0;
2242 for ( int i=0; i<2; i++ ) {
2243 if ( stream_.userBuffer[i] ) {
2244 free( stream_.userBuffer[i] );
2245 stream_.userBuffer[i] = 0;
2249 if ( stream_.deviceBuffer ) {
2250 free( stream_.deviceBuffer );
2251 stream_.deviceBuffer = 0;
2254 return FAILURE;
2257 void RtApiJack :: closeStream( void )
2259 if ( stream_.state == STREAM_CLOSED ) {
2260 errorText_ = "RtApiJack::closeStream(): no open stream to close!";
2261 error( RtError::WARNING );
2262 return;
2265 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2266 if ( handle ) {
2268 if ( stream_.state == STREAM_RUNNING )
2269 jack_deactivate( handle->client );
2271 jack_client_close( handle->client );
2274 if ( handle ) {
2275 if ( handle->ports[0] ) free( handle->ports[0] );
2276 if ( handle->ports[1] ) free( handle->ports[1] );
2277 pthread_cond_destroy( &handle->condition );
2278 delete handle;
2279 stream_.apiHandle = 0;
2282 for ( int i=0; i<2; i++ ) {
2283 if ( stream_.userBuffer[i] ) {
2284 free( stream_.userBuffer[i] );
2285 stream_.userBuffer[i] = 0;
2289 if ( stream_.deviceBuffer ) {
2290 free( stream_.deviceBuffer );
2291 stream_.deviceBuffer = 0;
2294 stream_.mode = UNINITIALIZED;
2295 stream_.state = STREAM_CLOSED;
2298 void RtApiJack :: startStream( void )
2300 verifyStream();
2301 if ( stream_.state == STREAM_RUNNING ) {
2302 errorText_ = "RtApiJack::startStream(): the stream is already running!";
2303 error( RtError::WARNING );
2304 return;
2307 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2308 int result = jack_activate( handle->client );
2309 if ( result ) {
2310 errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
2311 goto unlock;
2314 const char **ports;
2316 // Get the list of available ports.
2317 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2318 result = 1;
2319 ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);
2320 if ( ports == NULL) {
2321 errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
2322 goto unlock;
2325 // Now make the port connections. Since RtAudio wasn't designed to
2326 // allow the user to select particular channels of a device, we'll
2327 // just open the first "nChannels" ports with offset.
2328 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2329 result = 1;
2330 if ( ports[ stream_.channelOffset[0] + i ] )
2331 result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
2332 if ( result ) {
2333 free( ports );
2334 errorText_ = "RtApiJack::startStream(): error connecting output ports!";
2335 goto unlock;
2338 free(ports);
2341 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
2342 result = 1;
2343 ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );
2344 if ( ports == NULL) {
2345 errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
2346 goto unlock;
2349 // Now make the port connections. See note above.
2350 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2351 result = 1;
2352 if ( ports[ stream_.channelOffset[1] + i ] )
2353 result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
2354 if ( result ) {
2355 free( ports );
2356 errorText_ = "RtApiJack::startStream(): error connecting input ports!";
2357 goto unlock;
2360 free(ports);
2363 handle->drainCounter = 0;
2364 handle->internalDrain = false;
2365 stream_.state = STREAM_RUNNING;
2367 unlock:
2368 if ( result == 0 ) return;
2369 error( RtError::SYSTEM_ERROR );
2372 void RtApiJack :: stopStream( void )
2374 verifyStream();
2375 if ( stream_.state == STREAM_STOPPED ) {
2376 errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
2377 error( RtError::WARNING );
2378 return;
2381 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2382 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2384 if ( handle->drainCounter == 0 ) {
2385 handle->drainCounter = 2;
2386 pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
2390 jack_deactivate( handle->client );
2391 stream_.state = STREAM_STOPPED;
2394 void RtApiJack :: abortStream( void )
2396 verifyStream();
2397 if ( stream_.state == STREAM_STOPPED ) {
2398 errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
2399 error( RtError::WARNING );
2400 return;
2403 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2404 handle->drainCounter = 2;
2406 stopStream();
2409 // This function will be called by a spawned thread when the user
2410 // callback function signals that the stream should be stopped or
2411 // aborted. It is necessary to handle it this way because the
2412 // callbackEvent() function must return before the jack_deactivate()
2413 // function will return.
2414 extern "C" void *jackStopStream( void *ptr )
2416 CallbackInfo *info = (CallbackInfo *) ptr;
2417 RtApiJack *object = (RtApiJack *) info->object;
2419 object->stopStream();
2420 pthread_exit( NULL );
2423 bool RtApiJack :: callbackEvent( unsigned long nframes )
2425 if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
2426 if ( stream_.state == STREAM_CLOSED ) {
2427 errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
2428 error( RtError::WARNING );
2429 return FAILURE;
2431 if ( stream_.bufferSize != nframes ) {
2432 errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
2433 error( RtError::WARNING );
2434 return FAILURE;
2437 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
2438 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2440 // Check if we were draining the stream and signal is finished.
2441 if ( handle->drainCounter > 3 ) {
2443 stream_.state = STREAM_STOPPING;
2444 if ( handle->internalDrain == true )
2445 pthread_create( &threadId, NULL, jackStopStream, info );
2446 else
2447 pthread_cond_signal( &handle->condition );
2448 return SUCCESS;
2451 // Invoke user callback first, to get fresh output data.
2452 if ( handle->drainCounter == 0 ) {
2453 RtAudioCallback callback = (RtAudioCallback) info->callback;
2454 double streamTime = getStreamTime();
2455 RtAudioStreamStatus status = 0;
2456 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
2457 status |= RTAUDIO_OUTPUT_UNDERFLOW;
2458 handle->xrun[0] = false;
2460 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
2461 status |= RTAUDIO_INPUT_OVERFLOW;
2462 handle->xrun[1] = false;
2464 int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
2465 stream_.bufferSize, streamTime, status, info->userData );
2466 if ( cbReturnValue == 2 ) {
2467 stream_.state = STREAM_STOPPING;
2468 handle->drainCounter = 2;
2469 ThreadHandle id;
2470 pthread_create( &id, NULL, jackStopStream, info );
2471 return SUCCESS;
2473 else if ( cbReturnValue == 1 ) {
2474 handle->drainCounter = 1;
2475 handle->internalDrain = true;
2479 jack_default_audio_sample_t *jackbuffer;
2480 unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
2481 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2483 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
2485 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2486 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2487 memset( jackbuffer, 0, bufferBytes );
2491 else if ( stream_.doConvertBuffer[0] ) {
2493 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
2495 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2496 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2497 memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
2500 else { // no buffer conversion
2501 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2502 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2503 memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
2507 if ( handle->drainCounter ) {
2508 handle->drainCounter++;
2509 goto unlock;
2513 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
2515 if ( stream_.doConvertBuffer[1] ) {
2516 for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
2517 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2518 memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
2520 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
2522 else { // no buffer conversion
2523 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2524 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2525 memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
2530 unlock:
2531 RtApi::tickStreamTime();
2532 return SUCCESS;
2534 //******************** End of __UNIX_JACK__ *********************//
2535 #endif
2537 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows
2539 // The ASIO API is designed around a callback scheme, so this
2540 // implementation is similar to that used for OS-X CoreAudio and Linux
2541 // Jack. The primary constraint with ASIO is that it only allows
2542 // access to a single driver at a time. Thus, it is not possible to
2543 // have more than one simultaneous RtAudio stream.
2545 // This implementation also requires a number of external ASIO files
2546 // and a few global variables. The ASIO callback scheme does not
2547 // allow for the passing of user data, so we must create a global
2548 // pointer to our callbackInfo structure.
2550 // On unix systems, we make use of a pthread condition variable.
2551 // Since there is no equivalent in Windows, I hacked something based
2552 // on information found in
2553 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
2555 #include "asiosys.h"
2556 #include "asio.h"
2557 #include "iasiothiscallresolver.h"
2558 #include "asiodrivers.h"
2559 #include <cmath>
2561 AsioDrivers drivers;
2562 ASIOCallbacks asioCallbacks;
2563 ASIODriverInfo driverInfo;
2564 CallbackInfo *asioCallbackInfo;
2565 bool asioXRun;
2567 struct AsioHandle {
2568 int drainCounter; // Tracks callback counts when draining
2569 bool internalDrain; // Indicates if stop is initiated from callback or not.
2570 ASIOBufferInfo *bufferInfos;
2571 HANDLE condition;
2573 AsioHandle()
2574 :drainCounter(0), internalDrain(false), bufferInfos(0) {}
2577 // Function declarations (definitions at end of section)
2578 static const char* getAsioErrorString( ASIOError result );
2579 void sampleRateChanged( ASIOSampleRate sRate );
2580 long asioMessages( long selector, long value, void* message, double* opt );
2582 RtApiAsio :: RtApiAsio()
2584 // ASIO cannot run on a multi-threaded appartment. You can call
2585 // CoInitialize beforehand, but it must be for appartment threading
2586 // (in which case, CoInitilialize will return S_FALSE here).
2587 coInitialized_ = false;
2588 HRESULT hr = CoInitialize( NULL );
2589 if ( FAILED(hr) ) {
2590 errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";
2591 error( RtError::WARNING );
2593 coInitialized_ = true;
2595 drivers.removeCurrentDriver();
2596 driverInfo.asioVersion = 2;
2598 // See note in DirectSound implementation about GetDesktopWindow().
2599 driverInfo.sysRef = GetForegroundWindow();
2602 RtApiAsio :: ~RtApiAsio()
2604 if ( stream_.state != STREAM_CLOSED ) closeStream();
2605 if ( coInitialized_ ) CoUninitialize();
2608 unsigned int RtApiAsio :: getDeviceCount( void )
2610 return (unsigned int) drivers.asioGetNumDev();
2613 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
2615 RtAudio::DeviceInfo info;
2616 info.probed = false;
2618 // Get device ID
2619 unsigned int nDevices = getDeviceCount();
2620 if ( nDevices == 0 ) {
2621 errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
2622 error( RtError::INVALID_USE );
2625 if ( device >= nDevices ) {
2626 errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
2627 error( RtError::INVALID_USE );
2630 // If a stream is already open, we cannot probe other devices. Thus, use the saved results.
2631 if ( stream_.state != STREAM_CLOSED ) {
2632 if ( device >= devices_.size() ) {
2633 errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";
2634 error( RtError::WARNING );
2635 return info;
2637 return devices_[ device ];
2640 char driverName[32];
2641 ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
2642 if ( result != ASE_OK ) {
2643 errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";
2644 errorText_ = errorStream_.str();
2645 error( RtError::WARNING );
2646 return info;
2649 info.name = driverName;
2651 if ( !drivers.loadDriver( driverName ) ) {
2652 errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
2653 errorText_ = errorStream_.str();
2654 error( RtError::WARNING );
2655 return info;
2658 result = ASIOInit( &driverInfo );
2659 if ( result != ASE_OK ) {
2660 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
2661 errorText_ = errorStream_.str();
2662 error( RtError::WARNING );
2663 return info;
2666 // Determine the device channel information.
2667 long inputChannels, outputChannels;
2668 result = ASIOGetChannels( &inputChannels, &outputChannels );
2669 if ( result != ASE_OK ) {
2670 drivers.removeCurrentDriver();
2671 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
2672 errorText_ = errorStream_.str();
2673 error( RtError::WARNING );
2674 return info;
2677 info.outputChannels = outputChannels;
2678 info.inputChannels = inputChannels;
2679 if ( info.outputChannels > 0 && info.inputChannels > 0 )
2680 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
2682 // Determine the supported sample rates.
2683 info.sampleRates.clear();
2684 for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
2685 result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
2686 if ( result == ASE_OK )
2687 info.sampleRates.push_back( SAMPLE_RATES[i] );
2690 // Determine supported data types ... just check first channel and assume rest are the same.
2691 ASIOChannelInfo channelInfo;
2692 channelInfo.channel = 0;
2693 channelInfo.isInput = true;
2694 if ( info.inputChannels <= 0 ) channelInfo.isInput = false;
2695 result = ASIOGetChannelInfo( &channelInfo );
2696 if ( result != ASE_OK ) {
2697 drivers.removeCurrentDriver();
2698 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";
2699 errorText_ = errorStream_.str();
2700 error( RtError::WARNING );
2701 return info;
2704 info.nativeFormats = 0;
2705 if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
2706 info.nativeFormats |= RTAUDIO_SINT16;
2707 else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
2708 info.nativeFormats |= RTAUDIO_SINT32;
2709 else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
2710 info.nativeFormats |= RTAUDIO_FLOAT32;
2711 else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
2712 info.nativeFormats |= RTAUDIO_FLOAT64;
2714 if ( info.outputChannels > 0 )
2715 if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
2716 if ( info.inputChannels > 0 )
2717 if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
2719 info.probed = true;
2720 drivers.removeCurrentDriver();
2721 return info;
2724 void bufferSwitch( long index, ASIOBool processNow )
2726 RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
2727 object->callbackEvent( index );
2730 void RtApiAsio :: saveDeviceInfo( void )
2732 devices_.clear();
2734 unsigned int nDevices = getDeviceCount();
2735 devices_.resize( nDevices );
2736 for ( unsigned int i=0; i<nDevices; i++ )
2737 devices_[i] = getDeviceInfo( i );
2740 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
2741 unsigned int firstChannel, unsigned int sampleRate,
2742 RtAudioFormat format, unsigned int *bufferSize,
2743 RtAudio::StreamOptions *options )
2745 // For ASIO, a duplex stream MUST use the same driver.
2746 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {
2747 errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
2748 return FAILURE;
2751 char driverName[32];
2752 ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
2753 if ( result != ASE_OK ) {
2754 errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";
2755 errorText_ = errorStream_.str();
2756 return FAILURE;
2759 // Only load the driver once for duplex stream.
2760 if ( mode != INPUT || stream_.mode != OUTPUT ) {
2761 // The getDeviceInfo() function will not work when a stream is open
2762 // because ASIO does not allow multiple devices to run at the same
2763 // time. Thus, we'll probe the system before opening a stream and
2764 // save the results for use by getDeviceInfo().
2765 this->saveDeviceInfo();
2767 if ( !drivers.loadDriver( driverName ) ) {
2768 errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
2769 errorText_ = errorStream_.str();
2770 return FAILURE;
2773 result = ASIOInit( &driverInfo );
2774 if ( result != ASE_OK ) {
2775 errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
2776 errorText_ = errorStream_.str();
2777 return FAILURE;
2781 // Check the device channel count.
2782 long inputChannels, outputChannels;
2783 result = ASIOGetChannels( &inputChannels, &outputChannels );
2784 if ( result != ASE_OK ) {
2785 drivers.removeCurrentDriver();
2786 errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
2787 errorText_ = errorStream_.str();
2788 return FAILURE;
2791 if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
2792 ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
2793 drivers.removeCurrentDriver();
2794 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
2795 errorText_ = errorStream_.str();
2796 return FAILURE;
2798 stream_.nDeviceChannels[mode] = channels;
2799 stream_.nUserChannels[mode] = channels;
2800 stream_.channelOffset[mode] = firstChannel;
2802 // Verify the sample rate is supported.
2803 result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
2804 if ( result != ASE_OK ) {
2805 drivers.removeCurrentDriver();
2806 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
2807 errorText_ = errorStream_.str();
2808 return FAILURE;
2811 // Get the current sample rate
2812 ASIOSampleRate currentRate;
2813 result = ASIOGetSampleRate( &currentRate );
2814 if ( result != ASE_OK ) {
2815 drivers.removeCurrentDriver();
2816 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
2817 errorText_ = errorStream_.str();
2818 return FAILURE;
2821 // Set the sample rate only if necessary
2822 if ( currentRate != sampleRate ) {
2823 result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
2824 if ( result != ASE_OK ) {
2825 drivers.removeCurrentDriver();
2826 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
2827 errorText_ = errorStream_.str();
2828 return FAILURE;
2832 // Determine the driver data type.
2833 ASIOChannelInfo channelInfo;
2834 channelInfo.channel = 0;
2835 if ( mode == OUTPUT ) channelInfo.isInput = false;
2836 else channelInfo.isInput = true;
2837 result = ASIOGetChannelInfo( &channelInfo );
2838 if ( result != ASE_OK ) {
2839 drivers.removeCurrentDriver();
2840 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
2841 errorText_ = errorStream_.str();
2842 return FAILURE;
2845 // Assuming WINDOWS host is always little-endian.
2846 stream_.doByteSwap[mode] = false;
2847 stream_.userFormat = format;
2848 stream_.deviceFormat[mode] = 0;
2849 if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
2850 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
2851 if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
2853 else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
2854 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
2855 if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
2857 else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
2858 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
2859 if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
2861 else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
2862 stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
2863 if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
2866 if ( stream_.deviceFormat[mode] == 0 ) {
2867 drivers.removeCurrentDriver();
2868 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
2869 errorText_ = errorStream_.str();
2870 return FAILURE;
2873 // Set the buffer size. For a duplex stream, this will end up
2874 // setting the buffer size based on the input constraints, which
2875 // should be ok.
2876 long minSize, maxSize, preferSize, granularity;
2877 result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
2878 if ( result != ASE_OK ) {
2879 drivers.removeCurrentDriver();
2880 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
2881 errorText_ = errorStream_.str();
2882 return FAILURE;
2885 if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
2886 else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
2887 else if ( granularity == -1 ) {
2888 // Make sure bufferSize is a power of two.
2889 int log2_of_min_size = 0;
2890 int log2_of_max_size = 0;
2892 for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
2893 if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
2894 if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
2897 long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
2898 int min_delta_num = log2_of_min_size;
2900 for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
2901 long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
2902 if (current_delta < min_delta) {
2903 min_delta = current_delta;
2904 min_delta_num = i;
2908 *bufferSize = ( (unsigned int)1 << min_delta_num );
2909 if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
2910 else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
2912 else if ( granularity != 0 ) {
2913 // Set to an even multiple of granularity, rounding up.
2914 *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
2917 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {
2918 drivers.removeCurrentDriver();
2919 errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
2920 return FAILURE;
2923 stream_.bufferSize = *bufferSize;
2924 stream_.nBuffers = 2;
2926 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
2927 else stream_.userInterleaved = true;
2929 // ASIO always uses non-interleaved buffers.
2930 stream_.deviceInterleaved[mode] = false;
2932 // Allocate, if necessary, our AsioHandle structure for the stream.
2933 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2934 if ( handle == 0 ) {
2935 try {
2936 handle = new AsioHandle;
2938 catch ( std::bad_alloc& ) {
2939 //if ( handle == NULL ) {
2940 drivers.removeCurrentDriver();
2941 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
2942 return FAILURE;
2944 handle->bufferInfos = 0;
2946 // Create a manual-reset event.
2947 handle->condition = CreateEvent( NULL, // no security
2948 TRUE, // manual-reset
2949 FALSE, // non-signaled initially
2950 NULL ); // unnamed
2951 stream_.apiHandle = (void *) handle;
2954 // Create the ASIO internal buffers. Since RtAudio sets up input
2955 // and output separately, we'll have to dispose of previously
2956 // created output buffers for a duplex stream.
2957 long inputLatency, outputLatency;
2958 if ( mode == INPUT && stream_.mode == OUTPUT ) {
2959 ASIODisposeBuffers();
2960 if ( handle->bufferInfos ) free( handle->bufferInfos );
2963 // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
2964 bool buffersAllocated = false;
2965 unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
2966 handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
2967 if ( handle->bufferInfos == NULL ) {
2968 errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
2969 errorText_ = errorStream_.str();
2970 goto error;
2973 ASIOBufferInfo *infos;
2974 infos = handle->bufferInfos;
2975 for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
2976 infos->isInput = ASIOFalse;
2977 infos->channelNum = i + stream_.channelOffset[0];
2978 infos->buffers[0] = infos->buffers[1] = 0;
2980 for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
2981 infos->isInput = ASIOTrue;
2982 infos->channelNum = i + stream_.channelOffset[1];
2983 infos->buffers[0] = infos->buffers[1] = 0;
2986 // Set up the ASIO callback structure and create the ASIO data buffers.
2987 asioCallbacks.bufferSwitch = &bufferSwitch;
2988 asioCallbacks.sampleRateDidChange = &sampleRateChanged;
2989 asioCallbacks.asioMessage = &asioMessages;
2990 asioCallbacks.bufferSwitchTimeInfo = NULL;
2991 result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
2992 if ( result != ASE_OK ) {
2993 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
2994 errorText_ = errorStream_.str();
2995 goto error;
2997 buffersAllocated = true;
2999 // Set flags for buffer conversion.
3000 stream_.doConvertBuffer[mode] = false;
3001 if ( stream_.userFormat != stream_.deviceFormat[mode] )
3002 stream_.doConvertBuffer[mode] = true;
3003 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
3004 stream_.nUserChannels[mode] > 1 )
3005 stream_.doConvertBuffer[mode] = true;
3007 // Allocate necessary internal buffers
3008 unsigned long bufferBytes;
3009 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
3010 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
3011 if ( stream_.userBuffer[mode] == NULL ) {
3012 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
3013 goto error;
3016 if ( stream_.doConvertBuffer[mode] ) {
3018 bool makeBuffer = true;
3019 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
3020 if ( mode == INPUT ) {
3021 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
3022 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
3023 if ( bufferBytes <= bytesOut ) makeBuffer = false;
3027 if ( makeBuffer ) {
3028 bufferBytes *= *bufferSize;
3029 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
3030 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
3031 if ( stream_.deviceBuffer == NULL ) {
3032 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
3033 goto error;
3038 stream_.sampleRate = sampleRate;
3039 stream_.device[mode] = device;
3040 stream_.state = STREAM_STOPPED;
3041 asioCallbackInfo = &stream_.callbackInfo;
3042 stream_.callbackInfo.object = (void *) this;
3043 if ( stream_.mode == OUTPUT && mode == INPUT )
3044 // We had already set up an output stream.
3045 stream_.mode = DUPLEX;
3046 else
3047 stream_.mode = mode;
3049 // Determine device latencies
3050 result = ASIOGetLatencies( &inputLatency, &outputLatency );
3051 if ( result != ASE_OK ) {
3052 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
3053 errorText_ = errorStream_.str();
3054 error( RtError::WARNING); // warn but don't fail
3056 else {
3057 stream_.latency[0] = outputLatency;
3058 stream_.latency[1] = inputLatency;
3061 // Setup the buffer conversion information structure. We don't use
3062 // buffers to do channel offsets, so we override that parameter
3063 // here.
3064 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
3066 return SUCCESS;
3068 error:
3069 if ( buffersAllocated )
3070 ASIODisposeBuffers();
3071 drivers.removeCurrentDriver();
3073 if ( handle ) {
3074 CloseHandle( handle->condition );
3075 if ( handle->bufferInfos )
3076 free( handle->bufferInfos );
3077 delete handle;
3078 stream_.apiHandle = 0;
3081 for ( int i=0; i<2; i++ ) {
3082 if ( stream_.userBuffer[i] ) {
3083 free( stream_.userBuffer[i] );
3084 stream_.userBuffer[i] = 0;
3088 if ( stream_.deviceBuffer ) {
3089 free( stream_.deviceBuffer );
3090 stream_.deviceBuffer = 0;
3093 return FAILURE;
3096 void RtApiAsio :: closeStream()
3098 if ( stream_.state == STREAM_CLOSED ) {
3099 errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
3100 error( RtError::WARNING );
3101 return;
3104 if ( stream_.state == STREAM_RUNNING ) {
3105 stream_.state = STREAM_STOPPED;
3106 ASIOStop();
3108 ASIODisposeBuffers();
3109 drivers.removeCurrentDriver();
3111 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
3112 if ( handle ) {
3113 CloseHandle( handle->condition );
3114 if ( handle->bufferInfos )
3115 free( handle->bufferInfos );
3116 delete handle;
3117 stream_.apiHandle = 0;
3120 for ( int i=0; i<2; i++ ) {
3121 if ( stream_.userBuffer[i] ) {
3122 free( stream_.userBuffer[i] );
3123 stream_.userBuffer[i] = 0;
3127 if ( stream_.deviceBuffer ) {
3128 free( stream_.deviceBuffer );
3129 stream_.deviceBuffer = 0;
3132 stream_.mode = UNINITIALIZED;
3133 stream_.state = STREAM_CLOSED;
3136 bool stopThreadCalled = false;
3138 void RtApiAsio :: startStream()
3140 verifyStream();
3141 if ( stream_.state == STREAM_RUNNING ) {
3142 errorText_ = "RtApiAsio::startStream(): the stream is already running!";
3143 error( RtError::WARNING );
3144 return;
3147 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
3148 ASIOError result = ASIOStart();
3149 if ( result != ASE_OK ) {
3150 errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";
3151 errorText_ = errorStream_.str();
3152 goto unlock;
3155 handle->drainCounter = 0;
3156 handle->internalDrain = false;
3157 ResetEvent( handle->condition );
3158 stream_.state = STREAM_RUNNING;
3159 asioXRun = false;
3161 unlock:
3162 stopThreadCalled = false;
3164 if ( result == ASE_OK ) return;
3165 error( RtError::SYSTEM_ERROR );
3168 void RtApiAsio :: stopStream()
3170 verifyStream();
3171 if ( stream_.state == STREAM_STOPPED ) {
3172 errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
3173 error( RtError::WARNING );
3174 return;
3177 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
3178 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
3179 if ( handle->drainCounter == 0 ) {
3180 handle->drainCounter = 2;
3181 WaitForSingleObject( handle->condition, INFINITE ); // block until signaled
3185 stream_.state = STREAM_STOPPED;
3187 ASIOError result = ASIOStop();
3188 if ( result != ASE_OK ) {
3189 errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
3190 errorText_ = errorStream_.str();
3193 if ( result == ASE_OK ) return;
3194 error( RtError::SYSTEM_ERROR );
3197 void RtApiAsio :: abortStream()
3199 verifyStream();
3200 if ( stream_.state == STREAM_STOPPED ) {
3201 errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
3202 error( RtError::WARNING );
3203 return;
3206 // The following lines were commented-out because some behavior was
3207 // noted where the device buffers need to be zeroed to avoid
3208 // continuing sound, even when the device buffers are completely
3209 // disposed. So now, calling abort is the same as calling stop.
3210 // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
3211 // handle->drainCounter = 2;
3212 stopStream();
3215 // This function will be called by a spawned thread when the user
3216 // callback function signals that the stream should be stopped or
3217 // aborted. It is necessary to handle it this way because the
3218 // callbackEvent() function must return before the ASIOStop()
3219 // function will return.
3220 extern "C" unsigned __stdcall asioStopStream( void *ptr )
3222 CallbackInfo *info = (CallbackInfo *) ptr;
3223 RtApiAsio *object = (RtApiAsio *) info->object;
3225 object->stopStream();
3226 _endthreadex( 0 );
3227 return 0;
3230 bool RtApiAsio :: callbackEvent( long bufferIndex )
3232 if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
3233 if ( stream_.state == STREAM_CLOSED ) {
3234 errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
3235 error( RtError::WARNING );
3236 return FAILURE;
3239 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
3240 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
3242 // Check if we were draining the stream and signal if finished.
3243 if ( handle->drainCounter > 3 ) {
3245 stream_.state = STREAM_STOPPING;
3246 if ( handle->internalDrain == false )
3247 SetEvent( handle->condition );
3248 else { // spawn a thread to stop the stream
3249 unsigned threadId;
3250 stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
3251 &stream_.callbackInfo, 0, &threadId );
3253 return SUCCESS;
3256 // Invoke user callback to get fresh output data UNLESS we are
3257 // draining stream.
3258 if ( handle->drainCounter == 0 ) {
3259 RtAudioCallback callback = (RtAudioCallback) info->callback;
3260 double streamTime = getStreamTime();
3261 RtAudioStreamStatus status = 0;
3262 if ( stream_.mode != INPUT && asioXRun == true ) {
3263 status |= RTAUDIO_OUTPUT_UNDERFLOW;
3264 asioXRun = false;
3266 if ( stream_.mode != OUTPUT && asioXRun == true ) {
3267 status |= RTAUDIO_INPUT_OVERFLOW;
3268 asioXRun = false;
3270 int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
3271 stream_.bufferSize, streamTime, status, info->userData );
3272 if ( cbReturnValue == 2 ) {
3273 stream_.state = STREAM_STOPPING;
3274 handle->drainCounter = 2;
3275 unsigned threadId;
3276 stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
3277 &stream_.callbackInfo, 0, &threadId );
3278 return SUCCESS;
3280 else if ( cbReturnValue == 1 ) {
3281 handle->drainCounter = 1;
3282 handle->internalDrain = true;
3286 unsigned int nChannels, bufferBytes, i, j;
3287 nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
3288 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
3290 bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
3292 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
3294 for ( i=0, j=0; i<nChannels; i++ ) {
3295 if ( handle->bufferInfos[i].isInput != ASIOTrue )
3296 memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
3300 else if ( stream_.doConvertBuffer[0] ) {
3302 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
3303 if ( stream_.doByteSwap[0] )
3304 byteSwapBuffer( stream_.deviceBuffer,
3305 stream_.bufferSize * stream_.nDeviceChannels[0],
3306 stream_.deviceFormat[0] );
3308 for ( i=0, j=0; i<nChannels; i++ ) {
3309 if ( handle->bufferInfos[i].isInput != ASIOTrue )
3310 memcpy( handle->bufferInfos[i].buffers[bufferIndex],
3311 &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
3315 else {
3317 if ( stream_.doByteSwap[0] )
3318 byteSwapBuffer( stream_.userBuffer[0],
3319 stream_.bufferSize * stream_.nUserChannels[0],
3320 stream_.userFormat );
3322 for ( i=0, j=0; i<nChannels; i++ ) {
3323 if ( handle->bufferInfos[i].isInput != ASIOTrue )
3324 memcpy( handle->bufferInfos[i].buffers[bufferIndex],
3325 &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );
3330 if ( handle->drainCounter ) {
3331 handle->drainCounter++;
3332 goto unlock;
3336 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
3338 bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
3340 if (stream_.doConvertBuffer[1]) {
3342 // Always interleave ASIO input data.
3343 for ( i=0, j=0; i<nChannels; i++ ) {
3344 if ( handle->bufferInfos[i].isInput == ASIOTrue )
3345 memcpy( &stream_.deviceBuffer[j++*bufferBytes],
3346 handle->bufferInfos[i].buffers[bufferIndex],
3347 bufferBytes );
3350 if ( stream_.doByteSwap[1] )
3351 byteSwapBuffer( stream_.deviceBuffer,
3352 stream_.bufferSize * stream_.nDeviceChannels[1],
3353 stream_.deviceFormat[1] );
3354 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
3357 else {
3358 for ( i=0, j=0; i<nChannels; i++ ) {
3359 if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
3360 memcpy( &stream_.userBuffer[1][bufferBytes*j++],
3361 handle->bufferInfos[i].buffers[bufferIndex],
3362 bufferBytes );
3366 if ( stream_.doByteSwap[1] )
3367 byteSwapBuffer( stream_.userBuffer[1],
3368 stream_.bufferSize * stream_.nUserChannels[1],
3369 stream_.userFormat );
3373 unlock:
3374 // The following call was suggested by Malte Clasen. While the API
3375 // documentation indicates it should not be required, some device
3376 // drivers apparently do not function correctly without it.
3377 ASIOOutputReady();
3379 RtApi::tickStreamTime();
3380 return SUCCESS;
3383 void sampleRateChanged( ASIOSampleRate sRate )
3385 // The ASIO documentation says that this usually only happens during
3386 // external sync. Audio processing is not stopped by the driver,
3387 // actual sample rate might not have even changed, maybe only the
3388 // sample rate status of an AES/EBU or S/PDIF digital input at the
3389 // audio device.
3391 RtApi *object = (RtApi *) asioCallbackInfo->object;
3392 try {
3393 object->stopStream();
3395 catch ( RtError &exception ) {
3396 std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
3397 return;
3400 std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
3403 long asioMessages( long selector, long value, void* message, double* opt )
3405 long ret = 0;
3407 switch( selector ) {
3408 case kAsioSelectorSupported:
3409 if ( value == kAsioResetRequest
3410 || value == kAsioEngineVersion
3411 || value == kAsioResyncRequest
3412 || value == kAsioLatenciesChanged
3413 // The following three were added for ASIO 2.0, you don't
3414 // necessarily have to support them.
3415 || value == kAsioSupportsTimeInfo
3416 || value == kAsioSupportsTimeCode
3417 || value == kAsioSupportsInputMonitor)
3418 ret = 1L;
3419 break;
3420 case kAsioResetRequest:
3421 // Defer the task and perform the reset of the driver during the
3422 // next "safe" situation. You cannot reset the driver right now,
3423 // as this code is called from the driver. Reset the driver is
3424 // done by completely destruct is. I.e. ASIOStop(),
3425 // ASIODisposeBuffers(), Destruction Afterwards you initialize the
3426 // driver again.
3427 std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
3428 ret = 1L;
3429 break;
3430 case kAsioResyncRequest:
3431 // This informs the application that the driver encountered some
3432 // non-fatal data loss. It is used for synchronization purposes
3433 // of different media. Added mainly to work around the Win16Mutex
3434 // problems in Windows 95/98 with the Windows Multimedia system,
3435 // which could lose data because the Mutex was held too long by
3436 // another thread. However a driver can issue it in other
3437 // situations, too.
3438 // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
3439 asioXRun = true;
3440 ret = 1L;
3441 break;
3442 case kAsioLatenciesChanged:
3443 // This will inform the host application that the drivers were
3444 // latencies changed. Beware, it this does not mean that the
3445 // buffer sizes have changed! You might need to update internal
3446 // delay data.
3447 std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
3448 ret = 1L;
3449 break;
3450 case kAsioEngineVersion:
3451 // Return the supported ASIO version of the host application. If
3452 // a host application does not implement this selector, ASIO 1.0
3453 // is assumed by the driver.
3454 ret = 2L;
3455 break;
3456 case kAsioSupportsTimeInfo:
3457 // Informs the driver whether the
3458 // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
3459 // For compatibility with ASIO 1.0 drivers the host application
3460 // should always support the "old" bufferSwitch method, too.
3461 ret = 0;
3462 break;
3463 case kAsioSupportsTimeCode:
3464 // Informs the driver whether application is interested in time
3465 // code info. If an application does not need to know about time
3466 // code, the driver has less work to do.
3467 ret = 0;
3468 break;
3470 return ret;
3473 static const char* getAsioErrorString( ASIOError result )
3475 struct Messages
3477 ASIOError value;
3478 const char*message;
3481 static Messages m[] =
3483 { ASE_NotPresent, "Hardware input or output is not present or available." },
3484 { ASE_HWMalfunction, "Hardware is malfunctioning." },
3485 { ASE_InvalidParameter, "Invalid input parameter." },
3486 { ASE_InvalidMode, "Invalid mode." },
3487 { ASE_SPNotAdvancing, "Sample position not advancing." },
3488 { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },
3489 { ASE_NoMemory, "Not enough memory to complete the request." }
3492 for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
3493 if ( m[i].value == result ) return m[i].message;
3495 return "Unknown error.";
3497 //******************** End of __WINDOWS_ASIO__ *********************//
3498 #endif
3501 #if defined(__WINDOWS_DS__) // Windows DirectSound API
3503 // Modified by Robin Davies, October 2005
3504 // - Improvements to DirectX pointer chasing.
3505 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
3506 // - Auto-call CoInitialize for DSOUND and ASIO platforms.
3507 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
3508 // Changed device query structure for RtAudio 4.0.7, January 2010
3510 #include <dsound.h>
3511 #include <assert.h>
3512 #include <algorithm>
3514 #if defined(__MINGW32__)
3515 // missing from latest mingw winapi
3516 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
3517 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
3518 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
3519 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
3520 #endif
3522 #define MINIMUM_DEVICE_BUFFER_SIZE 32768
3524 #ifdef _MSC_VER // if Microsoft Visual C++
3525 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.
3526 #endif
3528 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
3530 if ( pointer > bufferSize ) pointer -= bufferSize;
3531 if ( laterPointer < earlierPointer ) laterPointer += bufferSize;
3532 if ( pointer < earlierPointer ) pointer += bufferSize;
3533 return pointer >= earlierPointer && pointer < laterPointer;
3536 // A structure to hold various information related to the DirectSound
3537 // API implementation.
3538 struct DsHandle {
3539 unsigned int drainCounter; // Tracks callback counts when draining
3540 bool internalDrain; // Indicates if stop is initiated from callback or not.
3541 void *id[2];
3542 void *buffer[2];
3543 bool xrun[2];
3544 UINT bufferPointer[2];
3545 DWORD dsBufferSize[2];
3546 DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
3547 HANDLE condition;
3549 DsHandle()
3550 :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; }
3553 // Declarations for utility functions, callbacks, and structures
3554 // specific to the DirectSound implementation.
3555 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
3556 LPCTSTR description,
3557 LPCTSTR module,
3558 LPVOID lpContext );
3560 static const char* getErrorString( int code );
3562 extern "C" unsigned __stdcall callbackHandler( void *ptr );
3564 struct DsDevice {
3565 LPGUID id[2];
3566 bool validId[2];
3567 bool found;
3568 std::string name;
3570 DsDevice()
3571 : found(false) { validId[0] = false; validId[1] = false; }
3574 std::vector< DsDevice > dsDevices;
3576 RtApiDs :: RtApiDs()
3578 // Dsound will run both-threaded. If CoInitialize fails, then just
3579 // accept whatever the mainline chose for a threading model.
3580 coInitialized_ = false;
3581 HRESULT hr = CoInitialize( NULL );
3582 if ( !FAILED( hr ) ) coInitialized_ = true;
3585 RtApiDs :: ~RtApiDs()
3587 if ( coInitialized_ ) CoUninitialize(); // balanced call.
3588 if ( stream_.state != STREAM_CLOSED ) closeStream();
3591 // The DirectSound default output is always the first device.
3592 unsigned int RtApiDs :: getDefaultOutputDevice( void )
3594 return 0;
3597 // The DirectSound default input is always the first input device,
3598 // which is the first capture device enumerated.
3599 unsigned int RtApiDs :: getDefaultInputDevice( void )
3601 return 0;
3604 unsigned int RtApiDs :: getDeviceCount( void )
3606 // Set query flag for previously found devices to false, so that we
3607 // can check for any devices that have disappeared.
3608 for ( unsigned int i=0; i<dsDevices.size(); i++ )
3609 dsDevices[i].found = false;
3611 // Query DirectSound devices.
3612 bool isInput = false;
3613 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput );
3614 if ( FAILED( result ) ) {
3615 errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
3616 errorText_ = errorStream_.str();
3617 error( RtError::WARNING );
3620 // Query DirectSoundCapture devices.
3621 isInput = true;
3622 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput );
3623 if ( FAILED( result ) ) {
3624 errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
3625 errorText_ = errorStream_.str();
3626 error( RtError::WARNING );
3629 // Clean out any devices that may have disappeared.
3630 std::vector< int > indices;
3631 for ( unsigned int i=0; i<dsDevices.size(); i++ )
3632 if ( dsDevices[i].found == false ) indices.push_back( i );
3633 unsigned int nErased = 0;
3634 for ( unsigned int i=0; i<indices.size(); i++ )
3635 dsDevices.erase( dsDevices.begin()-nErased++ );
3637 return dsDevices.size();
3640 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
3642 RtAudio::DeviceInfo info;
3643 info.probed = false;
3645 if ( dsDevices.size() == 0 ) {
3646 // Force a query of all devices
3647 getDeviceCount();
3648 if ( dsDevices.size() == 0 ) {
3649 errorText_ = "RtApiDs::getDeviceInfo: no devices found!";
3650 error( RtError::INVALID_USE );
3654 if ( device >= dsDevices.size() ) {
3655 errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";
3656 error( RtError::INVALID_USE );
3659 HRESULT result;
3660 if ( dsDevices[ device ].validId[0] == false ) goto probeInput;
3662 LPDIRECTSOUND output;
3663 DSCAPS outCaps;
3664 result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
3665 if ( FAILED( result ) ) {
3666 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
3667 errorText_ = errorStream_.str();
3668 error( RtError::WARNING );
3669 goto probeInput;
3672 outCaps.dwSize = sizeof( outCaps );
3673 result = output->GetCaps( &outCaps );
3674 if ( FAILED( result ) ) {
3675 output->Release();
3676 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
3677 errorText_ = errorStream_.str();
3678 error( RtError::WARNING );
3679 goto probeInput;
3682 // Get output channel information.
3683 info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
3685 // Get sample rate information.
3686 info.sampleRates.clear();
3687 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
3688 if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
3689 SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )
3690 info.sampleRates.push_back( SAMPLE_RATES[k] );
3693 // Get format information.
3694 if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
3695 if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
3697 output->Release();
3699 if ( getDefaultOutputDevice() == device )
3700 info.isDefaultOutput = true;
3702 if ( dsDevices[ device ].validId[1] == false ) {
3703 info.name = dsDevices[ device ].name;
3704 info.probed = true;
3705 return info;
3708 probeInput:
3710 LPDIRECTSOUNDCAPTURE input;
3711 result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
3712 if ( FAILED( result ) ) {
3713 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
3714 errorText_ = errorStream_.str();
3715 error( RtError::WARNING );
3716 return info;
3719 DSCCAPS inCaps;
3720 inCaps.dwSize = sizeof( inCaps );
3721 result = input->GetCaps( &inCaps );
3722 if ( FAILED( result ) ) {
3723 input->Release();
3724 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";
3725 errorText_ = errorStream_.str();
3726 error( RtError::WARNING );
3727 return info;
3730 // Get input channel information.
3731 info.inputChannels = inCaps.dwChannels;
3733 // Get sample rate and format information.
3734 std::vector<unsigned int> rates;
3735 if ( inCaps.dwChannels >= 2 ) {
3736 if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3737 if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3738 if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3739 if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3740 if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3741 if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3742 if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3743 if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3745 if ( info.nativeFormats & RTAUDIO_SINT16 ) {
3746 if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );
3747 if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );
3748 if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );
3749 if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );
3751 else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
3752 if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );
3753 if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );
3754 if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );
3755 if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );
3758 else if ( inCaps.dwChannels == 1 ) {
3759 if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3760 if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3761 if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3762 if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3763 if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3764 if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3765 if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3766 if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3768 if ( info.nativeFormats & RTAUDIO_SINT16 ) {
3769 if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );
3770 if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );
3771 if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );
3772 if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );
3774 else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
3775 if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );
3776 if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );
3777 if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );
3778 if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );
3781 else info.inputChannels = 0; // technically, this would be an error
3783 input->Release();
3785 if ( info.inputChannels == 0 ) return info;
3787 // Copy the supported rates to the info structure but avoid duplication.
3788 bool found;
3789 for ( unsigned int i=0; i<rates.size(); i++ ) {
3790 found = false;
3791 for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {
3792 if ( rates[i] == info.sampleRates[j] ) {
3793 found = true;
3794 break;
3797 if ( found == false ) info.sampleRates.push_back( rates[i] );
3799 std::sort( info.sampleRates.begin(), info.sampleRates.end() );
3801 // If device opens for both playback and capture, we determine the channels.
3802 if ( info.outputChannels > 0 && info.inputChannels > 0 )
3803 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
3805 if ( device == 0 ) info.isDefaultInput = true;
3807 // Copy name and return.
3808 info.name = dsDevices[ device ].name;
3809 info.probed = true;
3810 return info;
3813 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
3814 unsigned int firstChannel, unsigned int sampleRate,
3815 RtAudioFormat format, unsigned int *bufferSize,
3816 RtAudio::StreamOptions *options )
3818 if ( channels + firstChannel > 2 ) {
3819 errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
3820 return FAILURE;
3823 unsigned int nDevices = dsDevices.size();
3824 if ( nDevices == 0 ) {
3825 // This should not happen because a check is made before this function is called.
3826 errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";
3827 return FAILURE;
3830 if ( device >= nDevices ) {
3831 // This should not happen because a check is made before this function is called.
3832 errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";
3833 return FAILURE;
3836 if ( mode == OUTPUT ) {
3837 if ( dsDevices[ device ].validId[0] == false ) {
3838 errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
3839 errorText_ = errorStream_.str();
3840 return FAILURE;
3843 else { // mode == INPUT
3844 if ( dsDevices[ device ].validId[1] == false ) {
3845 errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
3846 errorText_ = errorStream_.str();
3847 return FAILURE;
3851 // According to a note in PortAudio, using GetDesktopWindow()
3852 // instead of GetForegroundWindow() is supposed to avoid problems
3853 // that occur when the application's window is not the foreground
3854 // window. Also, if the application window closes before the
3855 // DirectSound buffer, DirectSound can crash. In the past, I had
3856 // problems when using GetDesktopWindow() but it seems fine now
3857 // (January 2010). I'll leave it commented here.
3858 // HWND hWnd = GetForegroundWindow();
3859 HWND hWnd = GetDesktopWindow();
3861 // Check the numberOfBuffers parameter and limit the lowest value to
3862 // two. This is a judgement call and a value of two is probably too
3863 // low for capture, but it should work for playback.
3864 int nBuffers = 0;
3865 if ( options ) nBuffers = options->numberOfBuffers;
3866 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
3867 if ( nBuffers < 2 ) nBuffers = 3;
3869 // Check the lower range of the user-specified buffer size and set
3870 // (arbitrarily) to a lower bound of 32.
3871 if ( *bufferSize < 32 ) *bufferSize = 32;
3873 // Create the wave format structure. The data format setting will
3874 // be determined later.
3875 WAVEFORMATEX waveFormat;
3876 ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
3877 waveFormat.wFormatTag = WAVE_FORMAT_PCM;
3878 waveFormat.nChannels = channels + firstChannel;
3879 waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
3881 // Determine the device buffer size. By default, we'll use the value
3882 // defined above (32K), but we will grow it to make allowances for
3883 // very large software buffer sizes.
3884 DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;;
3885 DWORD dsPointerLeadTime = 0;
3887 void *ohandle = 0, *bhandle = 0;
3888 HRESULT result;
3889 if ( mode == OUTPUT ) {
3891 LPDIRECTSOUND output;
3892 result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
3893 if ( FAILED( result ) ) {
3894 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
3895 errorText_ = errorStream_.str();
3896 return FAILURE;
3899 DSCAPS outCaps;
3900 outCaps.dwSize = sizeof( outCaps );
3901 result = output->GetCaps( &outCaps );
3902 if ( FAILED( result ) ) {
3903 output->Release();
3904 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";
3905 errorText_ = errorStream_.str();
3906 return FAILURE;
3909 // Check channel information.
3910 if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
3911 errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";
3912 errorText_ = errorStream_.str();
3913 return FAILURE;
3916 // Check format information. Use 16-bit format unless not
3917 // supported or user requests 8-bit.
3918 if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
3919 !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
3920 waveFormat.wBitsPerSample = 16;
3921 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3923 else {
3924 waveFormat.wBitsPerSample = 8;
3925 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3927 stream_.userFormat = format;
3929 // Update wave format structure and buffer information.
3930 waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
3931 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
3932 dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
3934 // If the user wants an even bigger buffer, increase the device buffer size accordingly.
3935 while ( dsPointerLeadTime * 2U > dsBufferSize )
3936 dsBufferSize *= 2;
3938 // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.
3939 // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
3940 // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.
3941 result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );
3942 if ( FAILED( result ) ) {
3943 output->Release();
3944 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";
3945 errorText_ = errorStream_.str();
3946 return FAILURE;
3949 // Even though we will write to the secondary buffer, we need to
3950 // access the primary buffer to set the correct output format
3951 // (since the default is 8-bit, 22 kHz!). Setup the DS primary
3952 // buffer description.
3953 DSBUFFERDESC bufferDescription;
3954 ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
3955 bufferDescription.dwSize = sizeof( DSBUFFERDESC );
3956 bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
3958 // Obtain the primary buffer
3959 LPDIRECTSOUNDBUFFER buffer;
3960 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3961 if ( FAILED( result ) ) {
3962 output->Release();
3963 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";
3964 errorText_ = errorStream_.str();
3965 return FAILURE;
3968 // Set the primary DS buffer sound format.
3969 result = buffer->SetFormat( &waveFormat );
3970 if ( FAILED( result ) ) {
3971 output->Release();
3972 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";
3973 errorText_ = errorStream_.str();
3974 return FAILURE;
3977 // Setup the secondary DS buffer description.
3978 ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
3979 bufferDescription.dwSize = sizeof( DSBUFFERDESC );
3980 bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
3981 DSBCAPS_GLOBALFOCUS |
3982 DSBCAPS_GETCURRENTPOSITION2 |
3983 DSBCAPS_LOCHARDWARE ); // Force hardware mixing
3984 bufferDescription.dwBufferBytes = dsBufferSize;
3985 bufferDescription.lpwfxFormat = &waveFormat;
3987 // Try to create the secondary DS buffer. If that doesn't work,
3988 // try to use software mixing. Otherwise, there's a problem.
3989 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3990 if ( FAILED( result ) ) {
3991 bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
3992 DSBCAPS_GLOBALFOCUS |
3993 DSBCAPS_GETCURRENTPOSITION2 |
3994 DSBCAPS_LOCSOFTWARE ); // Force software mixing
3995 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3996 if ( FAILED( result ) ) {
3997 output->Release();
3998 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";
3999 errorText_ = errorStream_.str();
4000 return FAILURE;
4004 // Get the buffer size ... might be different from what we specified.
4005 DSBCAPS dsbcaps;
4006 dsbcaps.dwSize = sizeof( DSBCAPS );
4007 result = buffer->GetCaps( &dsbcaps );
4008 if ( FAILED( result ) ) {
4009 output->Release();
4010 buffer->Release();
4011 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
4012 errorText_ = errorStream_.str();
4013 return FAILURE;
4016 dsBufferSize = dsbcaps.dwBufferBytes;
4018 // Lock the DS buffer
4019 LPVOID audioPtr;
4020 DWORD dataLen;
4021 result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
4022 if ( FAILED( result ) ) {
4023 output->Release();
4024 buffer->Release();
4025 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";
4026 errorText_ = errorStream_.str();
4027 return FAILURE;
4030 // Zero the DS buffer
4031 ZeroMemory( audioPtr, dataLen );
4033 // Unlock the DS buffer
4034 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4035 if ( FAILED( result ) ) {
4036 output->Release();
4037 buffer->Release();
4038 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";
4039 errorText_ = errorStream_.str();
4040 return FAILURE;
4043 ohandle = (void *) output;
4044 bhandle = (void *) buffer;
4047 if ( mode == INPUT ) {
4049 LPDIRECTSOUNDCAPTURE input;
4050 result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
4051 if ( FAILED( result ) ) {
4052 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
4053 errorText_ = errorStream_.str();
4054 return FAILURE;
4057 DSCCAPS inCaps;
4058 inCaps.dwSize = sizeof( inCaps );
4059 result = input->GetCaps( &inCaps );
4060 if ( FAILED( result ) ) {
4061 input->Release();
4062 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";
4063 errorText_ = errorStream_.str();
4064 return FAILURE;
4067 // Check channel information.
4068 if ( inCaps.dwChannels < channels + firstChannel ) {
4069 errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
4070 return FAILURE;
4073 // Check format information. Use 16-bit format unless user
4074 // requests 8-bit.
4075 DWORD deviceFormats;
4076 if ( channels + firstChannel == 2 ) {
4077 deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
4078 if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
4079 waveFormat.wBitsPerSample = 8;
4080 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
4082 else { // assume 16-bit is supported
4083 waveFormat.wBitsPerSample = 16;
4084 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
4087 else { // channel == 1
4088 deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
4089 if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
4090 waveFormat.wBitsPerSample = 8;
4091 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
4093 else { // assume 16-bit is supported
4094 waveFormat.wBitsPerSample = 16;
4095 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
4098 stream_.userFormat = format;
4100 // Update wave format structure and buffer information.
4101 waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
4102 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
4103 dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
4105 // If the user wants an even bigger buffer, increase the device buffer size accordingly.
4106 while ( dsPointerLeadTime * 2U > dsBufferSize )
4107 dsBufferSize *= 2;
4109 // Setup the secondary DS buffer description.
4110 DSCBUFFERDESC bufferDescription;
4111 ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
4112 bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
4113 bufferDescription.dwFlags = 0;
4114 bufferDescription.dwReserved = 0;
4115 bufferDescription.dwBufferBytes = dsBufferSize;
4116 bufferDescription.lpwfxFormat = &waveFormat;
4118 // Create the capture buffer.
4119 LPDIRECTSOUNDCAPTUREBUFFER buffer;
4120 result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
4121 if ( FAILED( result ) ) {
4122 input->Release();
4123 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";
4124 errorText_ = errorStream_.str();
4125 return FAILURE;
4128 // Get the buffer size ... might be different from what we specified.
4129 DSCBCAPS dscbcaps;
4130 dscbcaps.dwSize = sizeof( DSCBCAPS );
4131 result = buffer->GetCaps( &dscbcaps );
4132 if ( FAILED( result ) ) {
4133 input->Release();
4134 buffer->Release();
4135 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
4136 errorText_ = errorStream_.str();
4137 return FAILURE;
4140 dsBufferSize = dscbcaps.dwBufferBytes;
4142 // NOTE: We could have a problem here if this is a duplex stream
4143 // and the play and capture hardware buffer sizes are different
4144 // (I'm actually not sure if that is a problem or not).
4145 // Currently, we are not verifying that.
4147 // Lock the capture buffer
4148 LPVOID audioPtr;
4149 DWORD dataLen;
4150 result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
4151 if ( FAILED( result ) ) {
4152 input->Release();
4153 buffer->Release();
4154 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";
4155 errorText_ = errorStream_.str();
4156 return FAILURE;
4159 // Zero the buffer
4160 ZeroMemory( audioPtr, dataLen );
4162 // Unlock the buffer
4163 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4164 if ( FAILED( result ) ) {
4165 input->Release();
4166 buffer->Release();
4167 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";
4168 errorText_ = errorStream_.str();
4169 return FAILURE;
4172 ohandle = (void *) input;
4173 bhandle = (void *) buffer;
4176 // Set various stream parameters
4177 DsHandle *handle = 0;
4178 stream_.nDeviceChannels[mode] = channels + firstChannel;
4179 stream_.nUserChannels[mode] = channels;
4180 stream_.bufferSize = *bufferSize;
4181 stream_.channelOffset[mode] = firstChannel;
4182 stream_.deviceInterleaved[mode] = true;
4183 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
4184 else stream_.userInterleaved = true;
4186 // Set flag for buffer conversion
4187 stream_.doConvertBuffer[mode] = false;
4188 if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])
4189 stream_.doConvertBuffer[mode] = true;
4190 if (stream_.userFormat != stream_.deviceFormat[mode])
4191 stream_.doConvertBuffer[mode] = true;
4192 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
4193 stream_.nUserChannels[mode] > 1 )
4194 stream_.doConvertBuffer[mode] = true;
4196 // Allocate necessary internal buffers
4197 long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
4198 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
4199 if ( stream_.userBuffer[mode] == NULL ) {
4200 errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
4201 goto error;
4204 if ( stream_.doConvertBuffer[mode] ) {
4206 bool makeBuffer = true;
4207 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
4208 if ( mode == INPUT ) {
4209 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
4210 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
4211 if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
4215 if ( makeBuffer ) {
4216 bufferBytes *= *bufferSize;
4217 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
4218 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
4219 if ( stream_.deviceBuffer == NULL ) {
4220 errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
4221 goto error;
4226 // Allocate our DsHandle structures for the stream.
4227 if ( stream_.apiHandle == 0 ) {
4228 try {
4229 handle = new DsHandle;
4231 catch ( std::bad_alloc& ) {
4232 errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
4233 goto error;
4236 // Create a manual-reset event.
4237 handle->condition = CreateEvent( NULL, // no security
4238 TRUE, // manual-reset
4239 FALSE, // non-signaled initially
4240 NULL ); // unnamed
4241 stream_.apiHandle = (void *) handle;
4243 else
4244 handle = (DsHandle *) stream_.apiHandle;
4245 handle->id[mode] = ohandle;
4246 handle->buffer[mode] = bhandle;
4247 handle->dsBufferSize[mode] = dsBufferSize;
4248 handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
4250 stream_.device[mode] = device;
4251 stream_.state = STREAM_STOPPED;
4252 if ( stream_.mode == OUTPUT && mode == INPUT )
4253 // We had already set up an output stream.
4254 stream_.mode = DUPLEX;
4255 else
4256 stream_.mode = mode;
4257 stream_.nBuffers = nBuffers;
4258 stream_.sampleRate = sampleRate;
4260 // Setup the buffer conversion information structure.
4261 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
4263 // Setup the callback thread.
4264 if ( stream_.callbackInfo.isRunning == false ) {
4265 unsigned threadId;
4266 stream_.callbackInfo.isRunning = true;
4267 stream_.callbackInfo.object = (void *) this;
4268 stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,
4269 &stream_.callbackInfo, 0, &threadId );
4270 if ( stream_.callbackInfo.thread == 0 ) {
4271 errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";
4272 goto error;
4275 // Boost DS thread priority
4276 SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
4278 return SUCCESS;
4280 error:
4281 if ( handle ) {
4282 if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
4283 LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
4284 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4285 if ( buffer ) buffer->Release();
4286 object->Release();
4288 if ( handle->buffer[1] ) {
4289 LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
4290 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4291 if ( buffer ) buffer->Release();
4292 object->Release();
4294 CloseHandle( handle->condition );
4295 delete handle;
4296 stream_.apiHandle = 0;
4299 for ( int i=0; i<2; i++ ) {
4300 if ( stream_.userBuffer[i] ) {
4301 free( stream_.userBuffer[i] );
4302 stream_.userBuffer[i] = 0;
4306 if ( stream_.deviceBuffer ) {
4307 free( stream_.deviceBuffer );
4308 stream_.deviceBuffer = 0;
4311 return FAILURE;
4314 void RtApiDs :: closeStream()
4316 if ( stream_.state == STREAM_CLOSED ) {
4317 errorText_ = "RtApiDs::closeStream(): no open stream to close!";
4318 error( RtError::WARNING );
4319 return;
4322 // Stop the callback thread.
4323 stream_.callbackInfo.isRunning = false;
4324 WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
4325 CloseHandle( (HANDLE) stream_.callbackInfo.thread );
4327 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4328 if ( handle ) {
4329 if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
4330 LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
4331 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4332 if ( buffer ) {
4333 buffer->Stop();
4334 buffer->Release();
4336 object->Release();
4338 if ( handle->buffer[1] ) {
4339 LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
4340 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4341 if ( buffer ) {
4342 buffer->Stop();
4343 buffer->Release();
4345 object->Release();
4347 CloseHandle( handle->condition );
4348 delete handle;
4349 stream_.apiHandle = 0;
4352 for ( int i=0; i<2; i++ ) {
4353 if ( stream_.userBuffer[i] ) {
4354 free( stream_.userBuffer[i] );
4355 stream_.userBuffer[i] = 0;
4359 if ( stream_.deviceBuffer ) {
4360 free( stream_.deviceBuffer );
4361 stream_.deviceBuffer = 0;
4364 stream_.mode = UNINITIALIZED;
4365 stream_.state = STREAM_CLOSED;
4368 void RtApiDs :: startStream()
4370 verifyStream();
4371 if ( stream_.state == STREAM_RUNNING ) {
4372 errorText_ = "RtApiDs::startStream(): the stream is already running!";
4373 error( RtError::WARNING );
4374 return;
4377 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4379 // Increase scheduler frequency on lesser windows (a side-effect of
4380 // increasing timer accuracy). On greater windows (Win2K or later),
4381 // this is already in effect.
4382 timeBeginPeriod( 1 );
4384 buffersRolling = false;
4385 duplexPrerollBytes = 0;
4387 if ( stream_.mode == DUPLEX ) {
4388 // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
4389 duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
4392 HRESULT result = 0;
4393 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4395 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4396 result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
4397 if ( FAILED( result ) ) {
4398 errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
4399 errorText_ = errorStream_.str();
4400 goto unlock;
4404 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4406 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4407 result = buffer->Start( DSCBSTART_LOOPING );
4408 if ( FAILED( result ) ) {
4409 errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";
4410 errorText_ = errorStream_.str();
4411 goto unlock;
4415 handle->drainCounter = 0;
4416 handle->internalDrain = false;
4417 ResetEvent( handle->condition );
4418 stream_.state = STREAM_RUNNING;
4420 unlock:
4421 if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
4424 void RtApiDs :: stopStream()
4426 verifyStream();
4427 if ( stream_.state == STREAM_STOPPED ) {
4428 errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
4429 error( RtError::WARNING );
4430 return;
4433 HRESULT result = 0;
4434 LPVOID audioPtr;
4435 DWORD dataLen;
4436 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4437 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4438 if ( handle->drainCounter == 0 ) {
4439 handle->drainCounter = 2;
4440 WaitForSingleObject( handle->condition, INFINITE ); // block until signaled
4443 stream_.state = STREAM_STOPPED;
4445 // Stop the buffer and clear memory
4446 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4447 result = buffer->Stop();
4448 if ( FAILED( result ) ) {
4449 errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";
4450 errorText_ = errorStream_.str();
4451 goto unlock;
4454 // Lock the buffer and clear it so that if we start to play again,
4455 // we won't have old data playing.
4456 result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
4457 if ( FAILED( result ) ) {
4458 errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";
4459 errorText_ = errorStream_.str();
4460 goto unlock;
4463 // Zero the DS buffer
4464 ZeroMemory( audioPtr, dataLen );
4466 // Unlock the DS buffer
4467 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4468 if ( FAILED( result ) ) {
4469 errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
4470 errorText_ = errorStream_.str();
4471 goto unlock;
4474 // If we start playing again, we must begin at beginning of buffer.
4475 handle->bufferPointer[0] = 0;
4478 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4479 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4480 audioPtr = NULL;
4481 dataLen = 0;
4483 stream_.state = STREAM_STOPPED;
4485 result = buffer->Stop();
4486 if ( FAILED( result ) ) {
4487 errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";
4488 errorText_ = errorStream_.str();
4489 goto unlock;
4492 // Lock the buffer and clear it so that if we start to play again,
4493 // we won't have old data playing.
4494 result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
4495 if ( FAILED( result ) ) {
4496 errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";
4497 errorText_ = errorStream_.str();
4498 goto unlock;
4501 // Zero the DS buffer
4502 ZeroMemory( audioPtr, dataLen );
4504 // Unlock the DS buffer
4505 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4506 if ( FAILED( result ) ) {
4507 errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
4508 errorText_ = errorStream_.str();
4509 goto unlock;
4512 // If we start recording again, we must begin at beginning of buffer.
4513 handle->bufferPointer[1] = 0;
4516 unlock:
4517 timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
4518 if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
4521 void RtApiDs :: abortStream()
4523 verifyStream();
4524 if ( stream_.state == STREAM_STOPPED ) {
4525 errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
4526 error( RtError::WARNING );
4527 return;
4530 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4531 handle->drainCounter = 2;
4533 stopStream();
4536 void RtApiDs :: callbackEvent()
4538 if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {
4539 Sleep( 50 ); // sleep 50 milliseconds
4540 return;
4543 if ( stream_.state == STREAM_CLOSED ) {
4544 errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
4545 error( RtError::WARNING );
4546 return;
4549 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
4550 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4552 // Check if we were draining the stream and signal is finished.
4553 if ( handle->drainCounter > stream_.nBuffers + 2 ) {
4555 stream_.state = STREAM_STOPPING;
4556 if ( handle->internalDrain == false )
4557 SetEvent( handle->condition );
4558 else
4559 stopStream();
4560 return;
4563 // Invoke user callback to get fresh output data UNLESS we are
4564 // draining stream.
4565 if ( handle->drainCounter == 0 ) {
4566 RtAudioCallback callback = (RtAudioCallback) info->callback;
4567 double streamTime = getStreamTime();
4568 RtAudioStreamStatus status = 0;
4569 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
4570 status |= RTAUDIO_OUTPUT_UNDERFLOW;
4571 handle->xrun[0] = false;
4573 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
4574 status |= RTAUDIO_INPUT_OVERFLOW;
4575 handle->xrun[1] = false;
4577 int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
4578 stream_.bufferSize, streamTime, status, info->userData );
4579 if ( cbReturnValue == 2 ) {
4580 stream_.state = STREAM_STOPPING;
4581 handle->drainCounter = 2;
4582 abortStream();
4583 return;
4585 else if ( cbReturnValue == 1 ) {
4586 handle->drainCounter = 1;
4587 handle->internalDrain = true;
4591 HRESULT result;
4592 DWORD currentWritePointer, safeWritePointer;
4593 DWORD currentReadPointer, safeReadPointer;
4594 UINT nextWritePointer;
4596 LPVOID buffer1 = NULL;
4597 LPVOID buffer2 = NULL;
4598 DWORD bufferSize1 = 0;
4599 DWORD bufferSize2 = 0;
4601 char *buffer;
4602 long bufferBytes;
4604 if ( buffersRolling == false ) {
4605 if ( stream_.mode == DUPLEX ) {
4606 //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
4608 // It takes a while for the devices to get rolling. As a result,
4609 // there's no guarantee that the capture and write device pointers
4610 // will move in lockstep. Wait here for both devices to start
4611 // rolling, and then set our buffer pointers accordingly.
4612 // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600
4613 // bytes later than the write buffer.
4615 // Stub: a serious risk of having a pre-emptive scheduling round
4616 // take place between the two GetCurrentPosition calls... but I'm
4617 // really not sure how to solve the problem. Temporarily boost to
4618 // Realtime priority, maybe; but I'm not sure what priority the
4619 // DirectSound service threads run at. We *should* be roughly
4620 // within a ms or so of correct.
4622 LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4623 LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4625 DWORD startSafeWritePointer, startSafeReadPointer;
4627 result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );
4628 if ( FAILED( result ) ) {
4629 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4630 errorText_ = errorStream_.str();
4631 error( RtError::SYSTEM_ERROR );
4633 result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );
4634 if ( FAILED( result ) ) {
4635 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4636 errorText_ = errorStream_.str();
4637 error( RtError::SYSTEM_ERROR );
4639 while ( true ) {
4640 result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );
4641 if ( FAILED( result ) ) {
4642 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4643 errorText_ = errorStream_.str();
4644 error( RtError::SYSTEM_ERROR );
4646 result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );
4647 if ( FAILED( result ) ) {
4648 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4649 errorText_ = errorStream_.str();
4650 error( RtError::SYSTEM_ERROR );
4652 if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;
4653 Sleep( 1 );
4656 //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
4658 handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
4659 if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
4660 handle->bufferPointer[1] = safeReadPointer;
4662 else if ( stream_.mode == OUTPUT ) {
4664 // Set the proper nextWritePosition after initial startup.
4665 LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4666 result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );
4667 if ( FAILED( result ) ) {
4668 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4669 errorText_ = errorStream_.str();
4670 error( RtError::SYSTEM_ERROR );
4672 handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
4673 if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
4676 buffersRolling = true;
4679 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4681 LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4683 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
4684 bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
4685 bufferBytes *= formatBytes( stream_.userFormat );
4686 memset( stream_.userBuffer[0], 0, bufferBytes );
4689 // Setup parameters and do buffer conversion if necessary.
4690 if ( stream_.doConvertBuffer[0] ) {
4691 buffer = stream_.deviceBuffer;
4692 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
4693 bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
4694 bufferBytes *= formatBytes( stream_.deviceFormat[0] );
4696 else {
4697 buffer = stream_.userBuffer[0];
4698 bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
4699 bufferBytes *= formatBytes( stream_.userFormat );
4702 // No byte swapping necessary in DirectSound implementation.
4704 // Ahhh ... windoze. 16-bit data is signed but 8-bit data is
4705 // unsigned. So, we need to convert our signed 8-bit data here to
4706 // unsigned.
4707 if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
4708 for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
4710 DWORD dsBufferSize = handle->dsBufferSize[0];
4711 nextWritePointer = handle->bufferPointer[0];
4713 DWORD endWrite, leadPointer;
4714 while ( true ) {
4715 // Find out where the read and "safe write" pointers are.
4716 result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );
4717 if ( FAILED( result ) ) {
4718 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4719 errorText_ = errorStream_.str();
4720 error( RtError::SYSTEM_ERROR );
4723 // We will copy our output buffer into the region between
4724 // safeWritePointer and leadPointer. If leadPointer is not
4725 // beyond the next endWrite position, wait until it is.
4726 leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];
4727 //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;
4728 if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;
4729 if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset
4730 endWrite = nextWritePointer + bufferBytes;
4732 // Check whether the entire write region is behind the play pointer.
4733 if ( leadPointer >= endWrite ) break;
4735 // If we are here, then we must wait until the leadPointer advances
4736 // beyond the end of our next write region. We use the
4737 // Sleep() function to suspend operation until that happens.
4738 double millis = ( endWrite - leadPointer ) * 1000.0;
4739 millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
4740 if ( millis < 1.0 ) millis = 1.0;
4741 Sleep( (DWORD) millis );
4744 if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )
4745 || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) {
4746 // We've strayed into the forbidden zone ... resync the read pointer.
4747 handle->xrun[0] = true;
4748 nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;
4749 if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;
4750 handle->bufferPointer[0] = nextWritePointer;
4751 endWrite = nextWritePointer + bufferBytes;
4754 // Lock free space in the buffer
4755 result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,
4756 &bufferSize1, &buffer2, &bufferSize2, 0 );
4757 if ( FAILED( result ) ) {
4758 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
4759 errorText_ = errorStream_.str();
4760 error( RtError::SYSTEM_ERROR );
4763 // Copy our buffer into the DS buffer
4764 CopyMemory( buffer1, buffer, bufferSize1 );
4765 if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );
4767 // Update our buffer offset and unlock sound buffer
4768 dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
4769 if ( FAILED( result ) ) {
4770 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
4771 errorText_ = errorStream_.str();
4772 error( RtError::SYSTEM_ERROR );
4774 nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
4775 handle->bufferPointer[0] = nextWritePointer;
4777 if ( handle->drainCounter ) {
4778 handle->drainCounter++;
4779 goto unlock;
4783 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4785 // Setup parameters.
4786 if ( stream_.doConvertBuffer[1] ) {
4787 buffer = stream_.deviceBuffer;
4788 bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];
4789 bufferBytes *= formatBytes( stream_.deviceFormat[1] );
4791 else {
4792 buffer = stream_.userBuffer[1];
4793 bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];
4794 bufferBytes *= formatBytes( stream_.userFormat );
4797 LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4798 long nextReadPointer = handle->bufferPointer[1];
4799 DWORD dsBufferSize = handle->dsBufferSize[1];
4801 // Find out where the write and "safe read" pointers are.
4802 result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );
4803 if ( FAILED( result ) ) {
4804 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4805 errorText_ = errorStream_.str();
4806 error( RtError::SYSTEM_ERROR );
4809 if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
4810 DWORD endRead = nextReadPointer + bufferBytes;
4812 // Handling depends on whether we are INPUT or DUPLEX.
4813 // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
4814 // then a wait here will drag the write pointers into the forbidden zone.
4816 // In DUPLEX mode, rather than wait, we will back off the read pointer until
4817 // it's in a safe position. This causes dropouts, but it seems to be the only
4818 // practical way to sync up the read and write pointers reliably, given the
4819 // the very complex relationship between phase and increment of the read and write
4820 // pointers.
4822 // In order to minimize audible dropouts in DUPLEX mode, we will
4823 // provide a pre-roll period of 0.5 seconds in which we return
4824 // zeros from the read buffer while the pointers sync up.
4826 if ( stream_.mode == DUPLEX ) {
4827 if ( safeReadPointer < endRead ) {
4828 if ( duplexPrerollBytes <= 0 ) {
4829 // Pre-roll time over. Be more agressive.
4830 int adjustment = endRead-safeReadPointer;
4832 handle->xrun[1] = true;
4833 // Two cases:
4834 // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
4835 // and perform fine adjustments later.
4836 // - small adjustments: back off by twice as much.
4837 if ( adjustment >= 2*bufferBytes )
4838 nextReadPointer = safeReadPointer-2*bufferBytes;
4839 else
4840 nextReadPointer = safeReadPointer-bufferBytes-adjustment;
4842 if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
4845 else {
4846 // In pre=roll time. Just do it.
4847 nextReadPointer = safeReadPointer - bufferBytes;
4848 while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
4850 endRead = nextReadPointer + bufferBytes;
4853 else { // mode == INPUT
4854 while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {
4855 // See comments for playback.
4856 double millis = (endRead - safeReadPointer) * 1000.0;
4857 millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
4858 if ( millis < 1.0 ) millis = 1.0;
4859 Sleep( (DWORD) millis );
4861 // Wake up and find out where we are now.
4862 result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );
4863 if ( FAILED( result ) ) {
4864 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4865 errorText_ = errorStream_.str();
4866 error( RtError::SYSTEM_ERROR );
4869 if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
4873 // Lock free space in the buffer
4874 result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,
4875 &bufferSize1, &buffer2, &bufferSize2, 0 );
4876 if ( FAILED( result ) ) {
4877 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
4878 errorText_ = errorStream_.str();
4879 error( RtError::SYSTEM_ERROR );
4882 if ( duplexPrerollBytes <= 0 ) {
4883 // Copy our buffer into the DS buffer
4884 CopyMemory( buffer, buffer1, bufferSize1 );
4885 if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );
4887 else {
4888 memset( buffer, 0, bufferSize1 );
4889 if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );
4890 duplexPrerollBytes -= bufferSize1 + bufferSize2;
4893 // Update our buffer offset and unlock sound buffer
4894 nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
4895 dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
4896 if ( FAILED( result ) ) {
4897 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
4898 errorText_ = errorStream_.str();
4899 error( RtError::SYSTEM_ERROR );
4901 handle->bufferPointer[1] = nextReadPointer;
4903 // No byte swapping necessary in DirectSound implementation.
4905 // If necessary, convert 8-bit data from unsigned to signed.
4906 if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
4907 for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
4909 // Do buffer conversion if necessary.
4910 if ( stream_.doConvertBuffer[1] )
4911 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
4914 unlock:
4915 RtApi::tickStreamTime();
4918 // Definitions for utility functions and callbacks
4919 // specific to the DirectSound implementation.
4921 extern "C" unsigned __stdcall callbackHandler( void *ptr )
4923 CallbackInfo *info = (CallbackInfo *) ptr;
4924 RtApiDs *object = (RtApiDs *) info->object;
4925 bool* isRunning = &info->isRunning;
4927 while ( *isRunning == true ) {
4928 object->callbackEvent();
4931 _endthreadex( 0 );
4932 return 0;
4935 #include "tchar.h"
4937 std::string convertTChar( LPCTSTR name )
4939 #if defined( UNICODE ) || defined( _UNICODE )
4940 int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
4941 std::string s( length, 0 );
4942 length = WideCharToMultiByte(CP_UTF8, 0, name, wcslen(name), &s[0], length, NULL, NULL);
4943 #else
4944 std::string s( name );
4945 #endif
4947 return s;
4950 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
4951 LPCTSTR description,
4952 LPCTSTR module,
4953 LPVOID lpContext )
4955 bool *isInput = (bool *) lpContext;
4957 HRESULT hr;
4958 bool validDevice = false;
4959 if ( *isInput == true ) {
4960 DSCCAPS caps;
4961 LPDIRECTSOUNDCAPTURE object;
4963 hr = DirectSoundCaptureCreate( lpguid, &object, NULL );
4964 if ( hr != DS_OK ) return TRUE;
4966 caps.dwSize = sizeof(caps);
4967 hr = object->GetCaps( &caps );
4968 if ( hr == DS_OK ) {
4969 if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
4970 validDevice = true;
4972 object->Release();
4974 else {
4975 DSCAPS caps;
4976 LPDIRECTSOUND object;
4977 hr = DirectSoundCreate( lpguid, &object, NULL );
4978 if ( hr != DS_OK ) return TRUE;
4980 caps.dwSize = sizeof(caps);
4981 hr = object->GetCaps( &caps );
4982 if ( hr == DS_OK ) {
4983 if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
4984 validDevice = true;
4986 object->Release();
4989 // If good device, then save its name and guid.
4990 std::string name = convertTChar( description );
4991 if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )
4992 name = "Default Device";
4993 if ( validDevice ) {
4994 for ( unsigned int i=0; i<dsDevices.size(); i++ ) {
4995 if ( dsDevices[i].name == name ) {
4996 dsDevices[i].found = true;
4997 if ( *isInput ) {
4998 dsDevices[i].id[1] = lpguid;
4999 dsDevices[i].validId[1] = true;
5001 else {
5002 dsDevices[i].id[0] = lpguid;
5003 dsDevices[i].validId[0] = true;
5005 return TRUE;
5009 DsDevice device;
5010 device.name = name;
5011 device.found = true;
5012 if ( *isInput ) {
5013 device.id[1] = lpguid;
5014 device.validId[1] = true;
5016 else {
5017 device.id[0] = lpguid;
5018 device.validId[0] = true;
5020 dsDevices.push_back( device );
5023 return TRUE;
5026 static const char* getErrorString( int code )
5028 switch ( code ) {
5030 case DSERR_ALLOCATED:
5031 return "Already allocated";
5033 case DSERR_CONTROLUNAVAIL:
5034 return "Control unavailable";
5036 case DSERR_INVALIDPARAM:
5037 return "Invalid parameter";
5039 case DSERR_INVALIDCALL:
5040 return "Invalid call";
5042 case DSERR_GENERIC:
5043 return "Generic error";
5045 case DSERR_PRIOLEVELNEEDED:
5046 return "Priority level needed";
5048 case DSERR_OUTOFMEMORY:
5049 return "Out of memory";
5051 case DSERR_BADFORMAT:
5052 return "The sample rate or the channel format is not supported";
5054 case DSERR_UNSUPPORTED:
5055 return "Not supported";
5057 case DSERR_NODRIVER:
5058 return "No driver";
5060 case DSERR_ALREADYINITIALIZED:
5061 return "Already initialized";
5063 case DSERR_NOAGGREGATION:
5064 return "No aggregation";
5066 case DSERR_BUFFERLOST:
5067 return "Buffer lost";
5069 case DSERR_OTHERAPPHASPRIO:
5070 return "Another application already has priority";
5072 case DSERR_UNINITIALIZED:
5073 return "Uninitialized";
5075 default:
5076 return "DirectSound unknown error";
5079 //******************** End of __WINDOWS_DS__ *********************//
5080 #endif
5083 #if defined(__LINUX_ALSA__)
5085 #include <alsa/asoundlib.h>
5086 #include <unistd.h>
5088 // A structure to hold various information related to the ALSA API
5089 // implementation.
5090 struct AlsaHandle {
5091 snd_pcm_t *handles[2];
5092 bool synchronized;
5093 bool xrun[2];
5094 pthread_cond_t runnable_cv;
5095 bool runnable;
5097 AlsaHandle()
5098 :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }
5101 extern "C" void *alsaCallbackHandler( void * ptr );
5103 RtApiAlsa :: RtApiAlsa()
5105 // Nothing to do here.
5108 RtApiAlsa :: ~RtApiAlsa()
5110 if ( stream_.state != STREAM_CLOSED ) closeStream();
5113 unsigned int RtApiAlsa :: getDeviceCount( void )
5115 unsigned nDevices = 0;
5116 int result, subdevice, card;
5117 char name[64];
5118 snd_ctl_t *handle;
5120 // Count cards and devices
5121 card = -1;
5122 snd_card_next( &card );
5123 while ( card >= 0 ) {
5124 sprintf( name, "hw:%d", card );
5125 result = snd_ctl_open( &handle, name, 0 );
5126 if ( result < 0 ) {
5127 errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
5128 errorText_ = errorStream_.str();
5129 error( RtError::WARNING );
5130 goto nextcard;
5132 subdevice = -1;
5133 while( 1 ) {
5134 result = snd_ctl_pcm_next_device( handle, &subdevice );
5135 if ( result < 0 ) {
5136 errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
5137 errorText_ = errorStream_.str();
5138 error( RtError::WARNING );
5139 break;
5141 if ( subdevice < 0 )
5142 break;
5143 nDevices++;
5145 nextcard:
5146 snd_ctl_close( handle );
5147 snd_card_next( &card );
5150 return nDevices;
5153 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
5155 RtAudio::DeviceInfo info;
5156 info.probed = false;
5158 unsigned nDevices = 0;
5159 int result, subdevice, card;
5160 char name[64];
5161 snd_ctl_t *chandle;
5163 // Count cards and devices
5164 card = -1;
5165 snd_card_next( &card );
5166 while ( card >= 0 ) {
5167 sprintf( name, "hw:%d", card );
5168 result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
5169 if ( result < 0 ) {
5170 errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
5171 errorText_ = errorStream_.str();
5172 error( RtError::WARNING );
5173 goto nextcard;
5175 subdevice = -1;
5176 while( 1 ) {
5177 result = snd_ctl_pcm_next_device( chandle, &subdevice );
5178 if ( result < 0 ) {
5179 errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
5180 errorText_ = errorStream_.str();
5181 error( RtError::WARNING );
5182 break;
5184 if ( subdevice < 0 ) break;
5185 if ( nDevices == device ) {
5186 sprintf( name, "hw:%d,%d", card, subdevice );
5187 goto foundDevice;
5189 nDevices++;
5191 nextcard:
5192 snd_ctl_close( chandle );
5193 snd_card_next( &card );
5196 if ( nDevices == 0 ) {
5197 errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
5198 error( RtError::INVALID_USE );
5201 if ( device >= nDevices ) {
5202 errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
5203 error( RtError::INVALID_USE );
5206 foundDevice:
5208 // If a stream is already open, we cannot probe the stream devices.
5209 // Thus, use the saved results.
5210 if ( stream_.state != STREAM_CLOSED &&
5211 ( stream_.device[0] == device || stream_.device[1] == device ) ) {
5212 snd_ctl_close( chandle );
5213 if ( device >= devices_.size() ) {
5214 errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";
5215 error( RtError::WARNING );
5216 return info;
5218 return devices_[ device ];
5221 int openMode = SND_PCM_ASYNC;
5222 snd_pcm_stream_t stream;
5223 snd_pcm_info_t *pcminfo;
5224 snd_pcm_info_alloca( &pcminfo );
5225 snd_pcm_t *phandle;
5226 snd_pcm_hw_params_t *params;
5227 snd_pcm_hw_params_alloca( &params );
5229 // First try for playback
5230 stream = SND_PCM_STREAM_PLAYBACK;
5231 snd_pcm_info_set_device( pcminfo, subdevice );
5232 snd_pcm_info_set_subdevice( pcminfo, 0 );
5233 snd_pcm_info_set_stream( pcminfo, stream );
5235 result = snd_ctl_pcm_info( chandle, pcminfo );
5236 if ( result < 0 ) {
5237 // Device probably doesn't support playback.
5238 goto captureProbe;
5241 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );
5242 if ( result < 0 ) {
5243 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5244 errorText_ = errorStream_.str();
5245 error( RtError::WARNING );
5246 goto captureProbe;
5249 // The device is open ... fill the parameter structure.
5250 result = snd_pcm_hw_params_any( phandle, params );
5251 if ( result < 0 ) {
5252 snd_pcm_close( phandle );
5253 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5254 errorText_ = errorStream_.str();
5255 error( RtError::WARNING );
5256 goto captureProbe;
5259 // Get output channel information.
5260 unsigned int value;
5261 result = snd_pcm_hw_params_get_channels_max( params, &value );
5262 if ( result < 0 ) {
5263 snd_pcm_close( phandle );
5264 errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";
5265 errorText_ = errorStream_.str();
5266 error( RtError::WARNING );
5267 goto captureProbe;
5269 info.outputChannels = value;
5270 snd_pcm_close( phandle );
5272 captureProbe:
5273 // Now try for capture
5274 stream = SND_PCM_STREAM_CAPTURE;
5275 snd_pcm_info_set_stream( pcminfo, stream );
5277 result = snd_ctl_pcm_info( chandle, pcminfo );
5278 snd_ctl_close( chandle );
5279 if ( result < 0 ) {
5280 // Device probably doesn't support capture.
5281 if ( info.outputChannels == 0 ) return info;
5282 goto probeParameters;
5285 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
5286 if ( result < 0 ) {
5287 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5288 errorText_ = errorStream_.str();
5289 error( RtError::WARNING );
5290 if ( info.outputChannels == 0 ) return info;
5291 goto probeParameters;
5294 // The device is open ... fill the parameter structure.
5295 result = snd_pcm_hw_params_any( phandle, params );
5296 if ( result < 0 ) {
5297 snd_pcm_close( phandle );
5298 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5299 errorText_ = errorStream_.str();
5300 error( RtError::WARNING );
5301 if ( info.outputChannels == 0 ) return info;
5302 goto probeParameters;
5305 result = snd_pcm_hw_params_get_channels_max( params, &value );
5306 if ( result < 0 ) {
5307 snd_pcm_close( phandle );
5308 errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";
5309 errorText_ = errorStream_.str();
5310 error( RtError::WARNING );
5311 if ( info.outputChannels == 0 ) return info;
5312 goto probeParameters;
5314 info.inputChannels = value;
5315 snd_pcm_close( phandle );
5317 // If device opens for both playback and capture, we determine the channels.
5318 if ( info.outputChannels > 0 && info.inputChannels > 0 )
5319 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
5321 // ALSA doesn't provide default devices so we'll use the first available one.
5322 if ( device == 0 && info.outputChannels > 0 )
5323 info.isDefaultOutput = true;
5324 if ( device == 0 && info.inputChannels > 0 )
5325 info.isDefaultInput = true;
5327 probeParameters:
5328 // At this point, we just need to figure out the supported data
5329 // formats and sample rates. We'll proceed by opening the device in
5330 // the direction with the maximum number of channels, or playback if
5331 // they are equal. This might limit our sample rate options, but so
5332 // be it.
5334 if ( info.outputChannels >= info.inputChannels )
5335 stream = SND_PCM_STREAM_PLAYBACK;
5336 else
5337 stream = SND_PCM_STREAM_CAPTURE;
5338 snd_pcm_info_set_stream( pcminfo, stream );
5340 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
5341 if ( result < 0 ) {
5342 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5343 errorText_ = errorStream_.str();
5344 error( RtError::WARNING );
5345 return info;
5348 // The device is open ... fill the parameter structure.
5349 result = snd_pcm_hw_params_any( phandle, params );
5350 if ( result < 0 ) {
5351 snd_pcm_close( phandle );
5352 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5353 errorText_ = errorStream_.str();
5354 error( RtError::WARNING );
5355 return info;
5358 // Test our discrete set of sample rate values.
5359 info.sampleRates.clear();
5360 for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
5361 if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )
5362 info.sampleRates.push_back( SAMPLE_RATES[i] );
5364 if ( info.sampleRates.size() == 0 ) {
5365 snd_pcm_close( phandle );
5366 errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
5367 errorText_ = errorStream_.str();
5368 error( RtError::WARNING );
5369 return info;
5372 // Probe the supported data formats ... we don't care about endian-ness just yet
5373 snd_pcm_format_t format;
5374 info.nativeFormats = 0;
5375 format = SND_PCM_FORMAT_S8;
5376 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5377 info.nativeFormats |= RTAUDIO_SINT8;
5378 format = SND_PCM_FORMAT_S16;
5379 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5380 info.nativeFormats |= RTAUDIO_SINT16;
5381 format = SND_PCM_FORMAT_S24;
5382 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5383 info.nativeFormats |= RTAUDIO_SINT24;
5384 format = SND_PCM_FORMAT_S32;
5385 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5386 info.nativeFormats |= RTAUDIO_SINT32;
5387 format = SND_PCM_FORMAT_FLOAT;
5388 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5389 info.nativeFormats |= RTAUDIO_FLOAT32;
5390 format = SND_PCM_FORMAT_FLOAT64;
5391 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5392 info.nativeFormats |= RTAUDIO_FLOAT64;
5394 // Check that we have at least one supported format
5395 if ( info.nativeFormats == 0 ) {
5396 errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
5397 errorText_ = errorStream_.str();
5398 error( RtError::WARNING );
5399 return info;
5402 // Get the device name
5403 char *cardname;
5404 result = snd_card_get_name( card, &cardname );
5405 if ( result >= 0 )
5406 sprintf( name, "hw:%s,%d", cardname, subdevice );
5407 info.name = name;
5409 // That's all ... close the device and return
5410 snd_pcm_close( phandle );
5411 info.probed = true;
5412 return info;
5415 void RtApiAlsa :: saveDeviceInfo( void )
5417 devices_.clear();
5419 unsigned int nDevices = getDeviceCount();
5420 devices_.resize( nDevices );
5421 for ( unsigned int i=0; i<nDevices; i++ )
5422 devices_[i] = getDeviceInfo( i );
5425 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
5426 unsigned int firstChannel, unsigned int sampleRate,
5427 RtAudioFormat format, unsigned int *bufferSize,
5428 RtAudio::StreamOptions *options )
5431 #if defined(__RTAUDIO_DEBUG__)
5432 snd_output_t *out;
5433 snd_output_stdio_attach(&out, stderr, 0);
5434 #endif
5436 // I'm not using the "plug" interface ... too much inconsistent behavior.
5438 unsigned nDevices = 0;
5439 int result, subdevice, card;
5440 char name[64];
5441 snd_ctl_t *chandle;
5443 if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )
5444 snprintf(name, sizeof(name), "%s", "default");
5445 else {
5446 // Count cards and devices
5447 card = -1;
5448 snd_card_next( &card );
5449 while ( card >= 0 ) {
5450 sprintf( name, "hw:%d", card );
5451 result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
5452 if ( result < 0 ) {
5453 errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
5454 errorText_ = errorStream_.str();
5455 return FAILURE;
5457 subdevice = -1;
5458 while( 1 ) {
5459 result = snd_ctl_pcm_next_device( chandle, &subdevice );
5460 if ( result < 0 ) break;
5461 if ( subdevice < 0 ) break;
5462 if ( nDevices == device ) {
5463 sprintf( name, "hw:%d,%d", card, subdevice );
5464 snd_ctl_close( chandle );
5465 goto foundDevice;
5467 nDevices++;
5469 snd_ctl_close( chandle );
5470 snd_card_next( &card );
5473 if ( nDevices == 0 ) {
5474 // This should not happen because a check is made before this function is called.
5475 errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";
5476 return FAILURE;
5479 if ( device >= nDevices ) {
5480 // This should not happen because a check is made before this function is called.
5481 errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";
5482 return FAILURE;
5486 foundDevice:
5488 // The getDeviceInfo() function will not work for a device that is
5489 // already open. Thus, we'll probe the system before opening a
5490 // stream and save the results for use by getDeviceInfo().
5491 if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
5492 this->saveDeviceInfo();
5494 snd_pcm_stream_t stream;
5495 if ( mode == OUTPUT )
5496 stream = SND_PCM_STREAM_PLAYBACK;
5497 else
5498 stream = SND_PCM_STREAM_CAPTURE;
5500 snd_pcm_t *phandle;
5501 int openMode = SND_PCM_ASYNC;
5502 result = snd_pcm_open( &phandle, name, stream, openMode );
5503 if ( result < 0 ) {
5504 if ( mode == OUTPUT )
5505 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";
5506 else
5507 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";
5508 errorText_ = errorStream_.str();
5509 return FAILURE;
5512 // Fill the parameter structure.
5513 snd_pcm_hw_params_t *hw_params;
5514 snd_pcm_hw_params_alloca( &hw_params );
5515 result = snd_pcm_hw_params_any( phandle, hw_params );
5516 if ( result < 0 ) {
5517 snd_pcm_close( phandle );
5518 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";
5519 errorText_ = errorStream_.str();
5520 return FAILURE;
5523 #if defined(__RTAUDIO_DEBUG__)
5524 fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );
5525 snd_pcm_hw_params_dump( hw_params, out );
5526 #endif
5528 // Set access ... check user preference.
5529 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {
5530 stream_.userInterleaved = false;
5531 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
5532 if ( result < 0 ) {
5533 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
5534 stream_.deviceInterleaved[mode] = true;
5536 else
5537 stream_.deviceInterleaved[mode] = false;
5539 else {
5540 stream_.userInterleaved = true;
5541 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
5542 if ( result < 0 ) {
5543 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
5544 stream_.deviceInterleaved[mode] = false;
5546 else
5547 stream_.deviceInterleaved[mode] = true;
5550 if ( result < 0 ) {
5551 snd_pcm_close( phandle );
5552 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
5553 errorText_ = errorStream_.str();
5554 return FAILURE;
5557 // Determine how to set the device format.
5558 stream_.userFormat = format;
5559 snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
5561 if ( format == RTAUDIO_SINT8 )
5562 deviceFormat = SND_PCM_FORMAT_S8;
5563 else if ( format == RTAUDIO_SINT16 )
5564 deviceFormat = SND_PCM_FORMAT_S16;
5565 else if ( format == RTAUDIO_SINT24 )
5566 deviceFormat = SND_PCM_FORMAT_S24;
5567 else if ( format == RTAUDIO_SINT32 )
5568 deviceFormat = SND_PCM_FORMAT_S32;
5569 else if ( format == RTAUDIO_FLOAT32 )
5570 deviceFormat = SND_PCM_FORMAT_FLOAT;
5571 else if ( format == RTAUDIO_FLOAT64 )
5572 deviceFormat = SND_PCM_FORMAT_FLOAT64;
5574 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
5575 stream_.deviceFormat[mode] = format;
5576 goto setFormat;
5579 // The user requested format is not natively supported by the device.
5580 deviceFormat = SND_PCM_FORMAT_FLOAT64;
5581 if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {
5582 stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
5583 goto setFormat;
5586 deviceFormat = SND_PCM_FORMAT_FLOAT;
5587 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5588 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
5589 goto setFormat;
5592 deviceFormat = SND_PCM_FORMAT_S32;
5593 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5594 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
5595 goto setFormat;
5598 deviceFormat = SND_PCM_FORMAT_S24;
5599 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5600 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
5601 goto setFormat;
5604 deviceFormat = SND_PCM_FORMAT_S16;
5605 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5606 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
5607 goto setFormat;
5610 deviceFormat = SND_PCM_FORMAT_S8;
5611 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5612 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
5613 goto setFormat;
5616 // If we get here, no supported format was found.
5617 snd_pcm_close( phandle );
5618 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";
5619 errorText_ = errorStream_.str();
5620 return FAILURE;
5622 setFormat:
5623 result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );
5624 if ( result < 0 ) {
5625 snd_pcm_close( phandle );
5626 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";
5627 errorText_ = errorStream_.str();
5628 return FAILURE;
5631 // Determine whether byte-swaping is necessary.
5632 stream_.doByteSwap[mode] = false;
5633 if ( deviceFormat != SND_PCM_FORMAT_S8 ) {
5634 result = snd_pcm_format_cpu_endian( deviceFormat );
5635 if ( result == 0 )
5636 stream_.doByteSwap[mode] = true;
5637 else if (result < 0) {
5638 snd_pcm_close( phandle );
5639 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";
5640 errorText_ = errorStream_.str();
5641 return FAILURE;
5645 // Set the sample rate.
5646 result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );
5647 if ( result < 0 ) {
5648 snd_pcm_close( phandle );
5649 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";
5650 errorText_ = errorStream_.str();
5651 return FAILURE;
5654 // Determine the number of channels for this device. We support a possible
5655 // minimum device channel number > than the value requested by the user.
5656 stream_.nUserChannels[mode] = channels;
5657 unsigned int value;
5658 result = snd_pcm_hw_params_get_channels_max( hw_params, &value );
5659 unsigned int deviceChannels = value;
5660 if ( result < 0 || deviceChannels < channels + firstChannel ) {
5661 snd_pcm_close( phandle );
5662 errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";
5663 errorText_ = errorStream_.str();
5664 return FAILURE;
5667 result = snd_pcm_hw_params_get_channels_min( hw_params, &value );
5668 if ( result < 0 ) {
5669 snd_pcm_close( phandle );
5670 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";
5671 errorText_ = errorStream_.str();
5672 return FAILURE;
5674 deviceChannels = value;
5675 if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;
5676 stream_.nDeviceChannels[mode] = deviceChannels;
5678 // Set the device channels.
5679 result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );
5680 if ( result < 0 ) {
5681 snd_pcm_close( phandle );
5682 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";
5683 errorText_ = errorStream_.str();
5684 return FAILURE;
5687 // Set the buffer (or period) size.
5688 int dir = 0;
5689 snd_pcm_uframes_t periodSize = *bufferSize;
5690 result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );
5691 if ( result < 0 ) {
5692 snd_pcm_close( phandle );
5693 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";
5694 errorText_ = errorStream_.str();
5695 return FAILURE;
5697 *bufferSize = periodSize;
5699 // Set the buffer number, which in ALSA is referred to as the "period".
5700 unsigned int periods = 0;
5701 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
5702 if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;
5703 if ( periods < 2 ) periods = 4; // a fairly safe default value
5704 result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
5705 if ( result < 0 ) {
5706 snd_pcm_close( phandle );
5707 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
5708 errorText_ = errorStream_.str();
5709 return FAILURE;
5712 // If attempting to setup a duplex stream, the bufferSize parameter
5713 // MUST be the same in both directions!
5714 if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
5715 snd_pcm_close( phandle );
5716 errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
5717 errorText_ = errorStream_.str();
5718 return FAILURE;
5721 stream_.bufferSize = *bufferSize;
5723 // Install the hardware configuration
5724 result = snd_pcm_hw_params( phandle, hw_params );
5725 if ( result < 0 ) {
5726 snd_pcm_close( phandle );
5727 errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";
5728 errorText_ = errorStream_.str();
5729 return FAILURE;
5732 #if defined(__RTAUDIO_DEBUG__)
5733 fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
5734 snd_pcm_hw_params_dump( hw_params, out );
5735 #endif
5737 // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
5738 snd_pcm_sw_params_t *sw_params = NULL;
5739 snd_pcm_sw_params_alloca( &sw_params );
5740 snd_pcm_sw_params_current( phandle, sw_params );
5741 snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
5742 snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
5743 snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
5745 // The following two settings were suggested by Theo Veenker
5746 //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );
5747 //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );
5749 // here are two options for a fix
5750 //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );
5751 snd_pcm_uframes_t val;
5752 snd_pcm_sw_params_get_boundary( sw_params, &val );
5753 snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );
5755 result = snd_pcm_sw_params( phandle, sw_params );
5756 if ( result < 0 ) {
5757 snd_pcm_close( phandle );
5758 errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
5759 errorText_ = errorStream_.str();
5760 return FAILURE;
5763 #if defined(__RTAUDIO_DEBUG__)
5764 fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
5765 snd_pcm_sw_params_dump( sw_params, out );
5766 #endif
5768 // Set flags for buffer conversion
5769 stream_.doConvertBuffer[mode] = false;
5770 if ( stream_.userFormat != stream_.deviceFormat[mode] )
5771 stream_.doConvertBuffer[mode] = true;
5772 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
5773 stream_.doConvertBuffer[mode] = true;
5774 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
5775 stream_.nUserChannels[mode] > 1 )
5776 stream_.doConvertBuffer[mode] = true;
5778 // Allocate the ApiHandle if necessary and then save.
5779 AlsaHandle *apiInfo = 0;
5780 if ( stream_.apiHandle == 0 ) {
5781 try {
5782 apiInfo = (AlsaHandle *) new AlsaHandle;
5784 catch ( std::bad_alloc& ) {
5785 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
5786 goto error;
5789 if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {
5790 errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
5791 goto error;
5794 stream_.apiHandle = (void *) apiInfo;
5795 apiInfo->handles[0] = 0;
5796 apiInfo->handles[1] = 0;
5798 else {
5799 apiInfo = (AlsaHandle *) stream_.apiHandle;
5801 apiInfo->handles[mode] = phandle;
5802 phandle = 0;
5804 // Allocate necessary internal buffers.
5805 unsigned long bufferBytes;
5806 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
5807 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
5808 if ( stream_.userBuffer[mode] == NULL ) {
5809 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";
5810 goto error;
5813 if ( stream_.doConvertBuffer[mode] ) {
5815 bool makeBuffer = true;
5816 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
5817 if ( mode == INPUT ) {
5818 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
5819 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
5820 if ( bufferBytes <= bytesOut ) makeBuffer = false;
5824 if ( makeBuffer ) {
5825 bufferBytes *= *bufferSize;
5826 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
5827 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
5828 if ( stream_.deviceBuffer == NULL ) {
5829 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";
5830 goto error;
5835 stream_.sampleRate = sampleRate;
5836 stream_.nBuffers = periods;
5837 stream_.device[mode] = device;
5838 stream_.state = STREAM_STOPPED;
5840 // Setup the buffer conversion information structure.
5841 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
5843 // Setup thread if necessary.
5844 if ( stream_.mode == OUTPUT && mode == INPUT ) {
5845 // We had already set up an output stream.
5846 stream_.mode = DUPLEX;
5847 // Link the streams if possible.
5848 apiInfo->synchronized = false;
5849 if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )
5850 apiInfo->synchronized = true;
5851 else {
5852 errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";
5853 error( RtError::WARNING );
5856 else {
5857 stream_.mode = mode;
5859 // Setup callback thread.
5860 stream_.callbackInfo.object = (void *) this;
5862 // Set the thread attributes for joinable and realtime scheduling
5863 // priority (optional). The higher priority will only take affect
5864 // if the program is run as root or suid. Note, under Linux
5865 // processes with CAP_SYS_NICE privilege, a user can change
5866 // scheduling policy and priority (thus need not be root). See
5867 // POSIX "capabilities".
5868 pthread_attr_t attr;
5869 pthread_attr_init( &attr );
5870 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
5871 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
5872 if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
5873 struct sched_param param;
5874 int priority = options->priority;
5875 int min = sched_get_priority_min( SCHED_RR );
5876 int max = sched_get_priority_max( SCHED_RR );
5877 if ( priority < min ) priority = min;
5878 else if ( priority > max ) priority = max;
5879 param.sched_priority = priority;
5880 pthread_attr_setschedparam( &attr, &param );
5881 pthread_attr_setschedpolicy( &attr, SCHED_RR );
5883 else
5884 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
5885 #else
5886 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
5887 #endif
5889 stream_.callbackInfo.isRunning = true;
5890 result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
5891 pthread_attr_destroy( &attr );
5892 if ( result ) {
5893 stream_.callbackInfo.isRunning = false;
5894 errorText_ = "RtApiAlsa::error creating callback thread!";
5895 goto error;
5899 return SUCCESS;
5901 error:
5902 if ( apiInfo ) {
5903 pthread_cond_destroy( &apiInfo->runnable_cv );
5904 if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
5905 if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
5906 delete apiInfo;
5907 stream_.apiHandle = 0;
5910 if ( phandle) snd_pcm_close( phandle );
5912 for ( int i=0; i<2; i++ ) {
5913 if ( stream_.userBuffer[i] ) {
5914 free( stream_.userBuffer[i] );
5915 stream_.userBuffer[i] = 0;
5919 if ( stream_.deviceBuffer ) {
5920 free( stream_.deviceBuffer );
5921 stream_.deviceBuffer = 0;
5924 return FAILURE;
5927 void RtApiAlsa :: closeStream()
5929 if ( stream_.state == STREAM_CLOSED ) {
5930 errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
5931 error( RtError::WARNING );
5932 return;
5935 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5936 stream_.callbackInfo.isRunning = false;
5937 MUTEX_LOCK( &stream_.mutex );
5938 if ( stream_.state == STREAM_STOPPED ) {
5939 apiInfo->runnable = true;
5940 pthread_cond_signal( &apiInfo->runnable_cv );
5942 MUTEX_UNLOCK( &stream_.mutex );
5943 pthread_join( stream_.callbackInfo.thread, NULL );
5945 if ( stream_.state == STREAM_RUNNING ) {
5946 stream_.state = STREAM_STOPPED;
5947 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
5948 snd_pcm_drop( apiInfo->handles[0] );
5949 if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
5950 snd_pcm_drop( apiInfo->handles[1] );
5953 if ( apiInfo ) {
5954 pthread_cond_destroy( &apiInfo->runnable_cv );
5955 if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
5956 if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
5957 delete apiInfo;
5958 stream_.apiHandle = 0;
5961 for ( int i=0; i<2; i++ ) {
5962 if ( stream_.userBuffer[i] ) {
5963 free( stream_.userBuffer[i] );
5964 stream_.userBuffer[i] = 0;
5968 if ( stream_.deviceBuffer ) {
5969 free( stream_.deviceBuffer );
5970 stream_.deviceBuffer = 0;
5973 stream_.mode = UNINITIALIZED;
5974 stream_.state = STREAM_CLOSED;
5977 void RtApiAlsa :: startStream()
5979 // This method calls snd_pcm_prepare if the device isn't already in that state.
5981 verifyStream();
5982 if ( stream_.state == STREAM_RUNNING ) {
5983 errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
5984 error( RtError::WARNING );
5985 return;
5988 MUTEX_LOCK( &stream_.mutex );
5990 int result = 0;
5991 snd_pcm_state_t state;
5992 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5993 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5994 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5995 state = snd_pcm_state( handle[0] );
5996 if ( state != SND_PCM_STATE_PREPARED ) {
5997 result = snd_pcm_prepare( handle[0] );
5998 if ( result < 0 ) {
5999 errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";
6000 errorText_ = errorStream_.str();
6001 goto unlock;
6006 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
6007 state = snd_pcm_state( handle[1] );
6008 if ( state != SND_PCM_STATE_PREPARED ) {
6009 result = snd_pcm_prepare( handle[1] );
6010 if ( result < 0 ) {
6011 errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";
6012 errorText_ = errorStream_.str();
6013 goto unlock;
6018 stream_.state = STREAM_RUNNING;
6020 unlock:
6021 apiInfo->runnable = true;
6022 pthread_cond_signal( &apiInfo->runnable_cv );
6023 MUTEX_UNLOCK( &stream_.mutex );
6025 if ( result >= 0 ) return;
6026 error( RtError::SYSTEM_ERROR );
6029 void RtApiAlsa :: stopStream()
6031 verifyStream();
6032 if ( stream_.state == STREAM_STOPPED ) {
6033 errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
6034 error( RtError::WARNING );
6035 return;
6038 stream_.state = STREAM_STOPPED;
6039 MUTEX_LOCK( &stream_.mutex );
6041 int result = 0;
6042 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
6043 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
6044 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6045 if ( apiInfo->synchronized )
6046 result = snd_pcm_drop( handle[0] );
6047 else
6048 result = snd_pcm_drain( handle[0] );
6049 if ( result < 0 ) {
6050 errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";
6051 errorText_ = errorStream_.str();
6052 goto unlock;
6056 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
6057 result = snd_pcm_drop( handle[1] );
6058 if ( result < 0 ) {
6059 errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";
6060 errorText_ = errorStream_.str();
6061 goto unlock;
6065 unlock:
6066 MUTEX_UNLOCK( &stream_.mutex );
6068 if ( result >= 0 ) return;
6069 error( RtError::SYSTEM_ERROR );
6072 void RtApiAlsa :: abortStream()
6074 verifyStream();
6075 if ( stream_.state == STREAM_STOPPED ) {
6076 errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
6077 error( RtError::WARNING );
6078 return;
6081 stream_.state = STREAM_STOPPED;
6082 MUTEX_LOCK( &stream_.mutex );
6084 int result = 0;
6085 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
6086 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
6087 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6088 result = snd_pcm_drop( handle[0] );
6089 if ( result < 0 ) {
6090 errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";
6091 errorText_ = errorStream_.str();
6092 goto unlock;
6096 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
6097 result = snd_pcm_drop( handle[1] );
6098 if ( result < 0 ) {
6099 errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";
6100 errorText_ = errorStream_.str();
6101 goto unlock;
6105 unlock:
6106 MUTEX_UNLOCK( &stream_.mutex );
6108 if ( result >= 0 ) return;
6109 error( RtError::SYSTEM_ERROR );
6112 void RtApiAlsa :: callbackEvent()
6114 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
6115 if ( stream_.state == STREAM_STOPPED ) {
6116 MUTEX_LOCK( &stream_.mutex );
6117 while ( !apiInfo->runnable )
6118 pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );
6120 if ( stream_.state != STREAM_RUNNING ) {
6121 MUTEX_UNLOCK( &stream_.mutex );
6122 return;
6124 MUTEX_UNLOCK( &stream_.mutex );
6127 if ( stream_.state == STREAM_CLOSED ) {
6128 errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
6129 error( RtError::WARNING );
6130 return;
6133 int doStopStream = 0;
6134 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
6135 double streamTime = getStreamTime();
6136 RtAudioStreamStatus status = 0;
6137 if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
6138 status |= RTAUDIO_OUTPUT_UNDERFLOW;
6139 apiInfo->xrun[0] = false;
6141 if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
6142 status |= RTAUDIO_INPUT_OVERFLOW;
6143 apiInfo->xrun[1] = false;
6145 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
6146 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
6148 if ( doStopStream == 2 ) {
6149 abortStream();
6150 return;
6153 MUTEX_LOCK( &stream_.mutex );
6155 // The state might change while waiting on a mutex.
6156 if ( stream_.state == STREAM_STOPPED ) goto unlock;
6158 int result;
6159 char *buffer;
6160 int channels;
6161 snd_pcm_t **handle;
6162 snd_pcm_sframes_t frames;
6163 RtAudioFormat format;
6164 handle = (snd_pcm_t **) apiInfo->handles;
6166 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
6168 // Setup parameters.
6169 if ( stream_.doConvertBuffer[1] ) {
6170 buffer = stream_.deviceBuffer;
6171 channels = stream_.nDeviceChannels[1];
6172 format = stream_.deviceFormat[1];
6174 else {
6175 buffer = stream_.userBuffer[1];
6176 channels = stream_.nUserChannels[1];
6177 format = stream_.userFormat;
6180 // Read samples from device in interleaved/non-interleaved format.
6181 if ( stream_.deviceInterleaved[1] )
6182 result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
6183 else {
6184 void *bufs[channels];
6185 size_t offset = stream_.bufferSize * formatBytes( format );
6186 for ( int i=0; i<channels; i++ )
6187 bufs[i] = (void *) (buffer + (i * offset));
6188 result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
6191 if ( result < (int) stream_.bufferSize ) {
6192 // Either an error or overrun occured.
6193 if ( result == -EPIPE ) {
6194 snd_pcm_state_t state = snd_pcm_state( handle[1] );
6195 if ( state == SND_PCM_STATE_XRUN ) {
6196 apiInfo->xrun[1] = true;
6197 result = snd_pcm_prepare( handle[1] );
6198 if ( result < 0 ) {
6199 errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
6200 errorText_ = errorStream_.str();
6203 else {
6204 errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
6205 errorText_ = errorStream_.str();
6208 else {
6209 errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
6210 errorText_ = errorStream_.str();
6212 error( RtError::WARNING );
6213 goto tryOutput;
6216 // Do byte swapping if necessary.
6217 if ( stream_.doByteSwap[1] )
6218 byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
6220 // Do buffer conversion if necessary.
6221 if ( stream_.doConvertBuffer[1] )
6222 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
6224 // Check stream latency
6225 result = snd_pcm_delay( handle[1], &frames );
6226 if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
6229 tryOutput:
6231 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6233 // Setup parameters and do buffer conversion if necessary.
6234 if ( stream_.doConvertBuffer[0] ) {
6235 buffer = stream_.deviceBuffer;
6236 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
6237 channels = stream_.nDeviceChannels[0];
6238 format = stream_.deviceFormat[0];
6240 else {
6241 buffer = stream_.userBuffer[0];
6242 channels = stream_.nUserChannels[0];
6243 format = stream_.userFormat;
6246 // Do byte swapping if necessary.
6247 if ( stream_.doByteSwap[0] )
6248 byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
6250 // Write samples to device in interleaved/non-interleaved format.
6251 if ( stream_.deviceInterleaved[0] )
6252 result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );
6253 else {
6254 void *bufs[channels];
6255 size_t offset = stream_.bufferSize * formatBytes( format );
6256 for ( int i=0; i<channels; i++ )
6257 bufs[i] = (void *) (buffer + (i * offset));
6258 result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );
6261 if ( result < (int) stream_.bufferSize ) {
6262 // Either an error or underrun occured.
6263 if ( result == -EPIPE ) {
6264 snd_pcm_state_t state = snd_pcm_state( handle[0] );
6265 if ( state == SND_PCM_STATE_XRUN ) {
6266 apiInfo->xrun[0] = true;
6267 result = snd_pcm_prepare( handle[0] );
6268 if ( result < 0 ) {
6269 errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
6270 errorText_ = errorStream_.str();
6273 else {
6274 errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
6275 errorText_ = errorStream_.str();
6278 else {
6279 errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
6280 errorText_ = errorStream_.str();
6282 error( RtError::WARNING );
6283 goto unlock;
6286 // Check stream latency
6287 result = snd_pcm_delay( handle[0], &frames );
6288 if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
6291 unlock:
6292 MUTEX_UNLOCK( &stream_.mutex );
6294 RtApi::tickStreamTime();
6295 if ( doStopStream == 1 ) this->stopStream();
6298 extern "C" void *alsaCallbackHandler( void *ptr )
6300 CallbackInfo *info = (CallbackInfo *) ptr;
6301 RtApiAlsa *object = (RtApiAlsa *) info->object;
6302 bool *isRunning = &info->isRunning;
6304 while ( *isRunning == true ) {
6305 pthread_testcancel();
6306 object->callbackEvent();
6309 pthread_exit( NULL );
6312 //******************** End of __LINUX_ALSA__ *********************//
6313 #endif
6315 #if defined(__LINUX_PULSE__)
6317 // Code written by Peter Meerwald, pmeerw@pmeerw.net
6318 // and Tristan Matthews.
6320 #include <pulse/error.h>
6321 #include <pulse/simple.h>
6322 #include <cstdio>
6324 namespace {
6325 const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,
6326 44100, 48000, 96000, 0}; }
6328 struct rtaudio_pa_format_mapping_t {
6329 RtAudioFormat rtaudio_format;
6330 pa_sample_format_t pa_format;
6333 static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
6334 {RTAUDIO_SINT16, PA_SAMPLE_S16LE},
6335 {RTAUDIO_SINT32, PA_SAMPLE_S32LE},
6336 {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},
6337 {0, PA_SAMPLE_INVALID}};
6339 struct PulseAudioHandle {
6340 pa_simple *s_play;
6341 pa_simple *s_rec;
6342 pthread_t thread;
6343 pthread_cond_t runnable_cv;
6344 bool runnable;
6345 PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }
6348 RtApiPulse::~RtApiPulse()
6350 if ( stream_.state != STREAM_CLOSED )
6351 closeStream();
6354 unsigned int RtApiPulse::getDeviceCount( void )
6356 return 1;
6359 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int device )
6361 RtAudio::DeviceInfo info;
6362 info.probed = true;
6363 info.name = "PulseAudio";
6364 info.outputChannels = 2;
6365 info.inputChannels = 2;
6366 info.duplexChannels = 2;
6367 info.isDefaultOutput = true;
6368 info.isDefaultInput = true;
6370 for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )
6371 info.sampleRates.push_back( *sr );
6373 info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
6375 return info;
6378 extern "C" void *pulseaudio_callback( void * user )
6380 CallbackInfo *cbi = static_cast<CallbackInfo *>( user );
6381 RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );
6382 volatile bool *isRunning = &cbi->isRunning;
6384 while ( *isRunning ) {
6385 pthread_testcancel();
6386 context->callbackEvent();
6389 pthread_exit( NULL );
6392 void RtApiPulse::closeStream( void )
6394 PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
6396 stream_.callbackInfo.isRunning = false;
6397 if ( pah ) {
6398 MUTEX_LOCK( &stream_.mutex );
6399 if ( stream_.state == STREAM_STOPPED ) {
6400 pah->runnable = true;
6401 pthread_cond_signal( &pah->runnable_cv );
6403 MUTEX_UNLOCK( &stream_.mutex );
6405 pthread_join( pah->thread, 0 );
6406 if ( pah->s_play ) {
6407 pa_simple_flush( pah->s_play, NULL );
6408 pa_simple_free( pah->s_play );
6410 if ( pah->s_rec )
6411 pa_simple_free( pah->s_rec );
6413 pthread_cond_destroy( &pah->runnable_cv );
6414 delete pah;
6415 stream_.apiHandle = 0;
6418 if ( stream_.userBuffer[0] ) {
6419 free( stream_.userBuffer[0] );
6420 stream_.userBuffer[0] = 0;
6422 if ( stream_.userBuffer[1] ) {
6423 free( stream_.userBuffer[1] );
6424 stream_.userBuffer[1] = 0;
6427 stream_.state = STREAM_CLOSED;
6428 stream_.mode = UNINITIALIZED;
6431 void RtApiPulse::callbackEvent( void )
6433 PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
6435 if ( stream_.state == STREAM_STOPPED ) {
6436 MUTEX_LOCK( &stream_.mutex );
6437 while ( !pah->runnable )
6438 pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );
6440 if ( stream_.state != STREAM_RUNNING ) {
6441 MUTEX_UNLOCK( &stream_.mutex );
6442 return;
6444 MUTEX_UNLOCK( &stream_.mutex );
6447 if ( stream_.state == STREAM_CLOSED ) {
6448 errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "
6449 "this shouldn't happen!";
6450 error( RtError::WARNING );
6451 return;
6454 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
6455 double streamTime = getStreamTime();
6456 RtAudioStreamStatus status = 0;
6457 int doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
6458 stream_.bufferSize, streamTime, status,
6459 stream_.callbackInfo.userData );
6461 if ( doStopStream == 2 ) {
6462 abortStream();
6463 return;
6466 MUTEX_LOCK( &stream_.mutex );
6468 if ( stream_.state != STREAM_RUNNING )
6469 goto unlock;
6471 int pa_error;
6472 size_t bytes;
6473 switch ( stream_.mode ) {
6474 case INPUT:
6475 bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );
6476 if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) {
6477 errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
6478 pa_strerror( pa_error ) << ".";
6479 errorText_ = errorStream_.str();
6480 error( RtError::WARNING );
6482 break;
6483 case OUTPUT:
6484 bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat );
6485 if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0 ) {
6486 errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
6487 pa_strerror( pa_error ) << ".";
6488 errorText_ = errorStream_.str();
6489 error( RtError::WARNING );
6491 break;
6492 case DUPLEX:
6493 bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );
6494 if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) {
6495 errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
6496 pa_strerror( pa_error ) << ".";
6497 errorText_ = errorStream_.str();
6498 error( RtError::WARNING );
6500 bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat );
6501 if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0) {
6502 errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
6503 pa_strerror( pa_error ) << ".";
6504 errorText_ = errorStream_.str();
6505 error( RtError::WARNING );
6507 break;
6508 default:
6509 // ERROR
6510 break;
6513 unlock:
6514 MUTEX_UNLOCK( &stream_.mutex );
6515 RtApi::tickStreamTime();
6517 if ( doStopStream == 1 )
6518 stopStream();
6521 void RtApiPulse::startStream( void )
6523 PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
6525 if ( stream_.state == STREAM_CLOSED ) {
6526 errorText_ = "RtApiPulse::startStream(): the stream is not open!";
6527 error( RtError::INVALID_USE );
6528 return;
6530 if ( stream_.state == STREAM_RUNNING ) {
6531 errorText_ = "RtApiPulse::startStream(): the stream is already running!";
6532 error( RtError::WARNING );
6533 return;
6536 MUTEX_LOCK( &stream_.mutex );
6538 stream_.state = STREAM_RUNNING;
6540 pah->runnable = true;
6541 pthread_cond_signal( &pah->runnable_cv );
6542 MUTEX_UNLOCK( &stream_.mutex );
6545 void RtApiPulse::stopStream( void )
6547 PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
6549 if ( stream_.state == STREAM_CLOSED ) {
6550 errorText_ = "RtApiPulse::stopStream(): the stream is not open!";
6551 error( RtError::INVALID_USE );
6552 return;
6554 if ( stream_.state == STREAM_STOPPED ) {
6555 errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";
6556 error( RtError::WARNING );
6557 return;
6560 stream_.state = STREAM_STOPPED;
6561 MUTEX_LOCK( &stream_.mutex );
6563 if ( pah && pah->s_play ) {
6564 int pa_error;
6565 if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
6566 errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
6567 pa_strerror( pa_error ) << ".";
6568 errorText_ = errorStream_.str();
6569 MUTEX_UNLOCK( &stream_.mutex );
6570 error( RtError::SYSTEM_ERROR );
6574 stream_.state = STREAM_STOPPED;
6575 MUTEX_UNLOCK( &stream_.mutex );
6578 void RtApiPulse::abortStream( void )
6580 PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );
6582 if ( stream_.state == STREAM_CLOSED ) {
6583 errorText_ = "RtApiPulse::abortStream(): the stream is not open!";
6584 error( RtError::INVALID_USE );
6585 return;
6587 if ( stream_.state == STREAM_STOPPED ) {
6588 errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";
6589 error( RtError::WARNING );
6590 return;
6593 stream_.state = STREAM_STOPPED;
6594 MUTEX_LOCK( &stream_.mutex );
6596 if ( pah && pah->s_play ) {
6597 int pa_error;
6598 if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
6599 errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
6600 pa_strerror( pa_error ) << ".";
6601 errorText_ = errorStream_.str();
6602 MUTEX_UNLOCK( &stream_.mutex );
6603 error( RtError::SYSTEM_ERROR );
6607 stream_.state = STREAM_STOPPED;
6608 MUTEX_UNLOCK( &stream_.mutex );
6611 bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
6612 unsigned int channels, unsigned int firstChannel,
6613 unsigned int sampleRate, RtAudioFormat format,
6614 unsigned int *bufferSize, RtAudio::StreamOptions *options )
6616 PulseAudioHandle *pah = 0;
6617 unsigned long bufferBytes = 0;
6618 pa_sample_spec ss;
6620 if ( device != 0 ) return false;
6621 if ( mode != INPUT && mode != OUTPUT ) return false;
6622 if ( channels != 1 && channels != 2 ) {
6623 errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";
6624 return false;
6626 ss.channels = channels;
6628 if ( firstChannel != 0 ) return false;
6630 bool sr_found = false;
6631 for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {
6632 if ( sampleRate == *sr ) {
6633 sr_found = true;
6634 stream_.sampleRate = sampleRate;
6635 ss.rate = sampleRate;
6636 break;
6639 if ( !sr_found ) {
6640 errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";
6641 return false;
6644 bool sf_found = 0;
6645 for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;
6646 sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {
6647 if ( format == sf->rtaudio_format ) {
6648 sf_found = true;
6649 stream_.userFormat = sf->rtaudio_format;
6650 ss.format = sf->pa_format;
6651 break;
6654 if ( !sf_found ) {
6655 errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample format.";
6656 return false;
6659 if ( options && ( options->flags & RTAUDIO_NONINTERLEAVED ) ) {
6660 errorText_ = "RtApiPulse::probeDeviceOpen: only interleaved audio data supported.";
6661 return false;
6664 stream_.userInterleaved = true;
6665 stream_.nBuffers = 1;
6667 stream_.deviceInterleaved[mode] = true;
6668 stream_.doByteSwap[mode] = false;
6669 stream_.doConvertBuffer[mode] = false;
6670 stream_.deviceFormat[mode] = stream_.userFormat;
6671 stream_.nUserChannels[mode] = channels;
6672 stream_.nDeviceChannels[mode] = channels;
6673 stream_.channelOffset[mode] = 0;
6675 // Allocate necessary internal buffers.
6676 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
6677 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
6678 if ( stream_.userBuffer[mode] == NULL ) {
6679 errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";
6680 goto error;
6682 stream_.bufferSize = *bufferSize;
6684 if ( !stream_.apiHandle ) {
6685 PulseAudioHandle *pah = new PulseAudioHandle;
6686 if ( !pah ) {
6687 errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";
6688 goto error;
6691 stream_.apiHandle = pah;
6692 if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {
6693 errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";
6694 goto error;
6697 pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
6699 int error;
6700 switch ( mode ) {
6701 case INPUT:
6702 pah->s_rec = pa_simple_new( NULL, "RtAudio", PA_STREAM_RECORD, NULL, "Record", &ss, NULL, NULL, &error );
6703 if ( !pah->s_rec ) {
6704 errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";
6705 goto error;
6707 break;
6708 case OUTPUT:
6709 pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
6710 if ( !pah->s_play ) {
6711 errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
6712 goto error;
6714 break;
6715 default:
6716 goto error;
6719 if ( stream_.mode == UNINITIALIZED )
6720 stream_.mode = mode;
6721 else if ( stream_.mode == mode )
6722 goto error;
6723 else
6724 stream_.mode = DUPLEX;
6726 stream_.state = STREAM_STOPPED;
6728 if ( !stream_.callbackInfo.isRunning ) {
6729 stream_.callbackInfo.object = this;
6730 stream_.callbackInfo.isRunning = true;
6731 if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {
6732 errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";
6733 goto error;
6736 return true;
6738 error:
6739 closeStream();
6740 return false;
6743 //******************** End of __LINUX_PULSE__ *********************//
6744 #endif
6746 #if defined(__LINUX_OSS__)
6748 #include <unistd.h>
6749 #include <sys/ioctl.h>
6750 #include <unistd.h>
6751 #include <fcntl.h>
6752 #include "soundcard.h"
6753 #include <errno.h>
6754 #include <math.h>
6756 extern "C" void *ossCallbackHandler(void * ptr);
6758 // A structure to hold various information related to the OSS API
6759 // implementation.
6760 struct OssHandle {
6761 int id[2]; // device ids
6762 bool xrun[2];
6763 bool triggered;
6764 pthread_cond_t runnable;
6766 OssHandle()
6767 :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
6770 RtApiOss :: RtApiOss()
6772 // Nothing to do here.
6775 RtApiOss :: ~RtApiOss()
6777 if ( stream_.state != STREAM_CLOSED ) closeStream();
6780 unsigned int RtApiOss :: getDeviceCount( void )
6782 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6783 if ( mixerfd == -1 ) {
6784 errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
6785 error( RtError::WARNING );
6786 return 0;
6789 oss_sysinfo sysinfo;
6790 if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
6791 close( mixerfd );
6792 errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
6793 error( RtError::WARNING );
6794 return 0;
6797 close( mixerfd );
6798 return sysinfo.numaudios;
6801 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
6803 RtAudio::DeviceInfo info;
6804 info.probed = false;
6806 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6807 if ( mixerfd == -1 ) {
6808 errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
6809 error( RtError::WARNING );
6810 return info;
6813 oss_sysinfo sysinfo;
6814 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
6815 if ( result == -1 ) {
6816 close( mixerfd );
6817 errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
6818 error( RtError::WARNING );
6819 return info;
6822 unsigned nDevices = sysinfo.numaudios;
6823 if ( nDevices == 0 ) {
6824 close( mixerfd );
6825 errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
6826 error( RtError::INVALID_USE );
6829 if ( device >= nDevices ) {
6830 close( mixerfd );
6831 errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
6832 error( RtError::INVALID_USE );
6835 oss_audioinfo ainfo;
6836 ainfo.dev = device;
6837 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6838 close( mixerfd );
6839 if ( result == -1 ) {
6840 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6841 errorText_ = errorStream_.str();
6842 error( RtError::WARNING );
6843 return info;
6846 // Probe channels
6847 if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
6848 if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
6849 if ( ainfo.caps & PCM_CAP_DUPLEX ) {
6850 if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
6851 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
6854 // Probe data formats ... do for input
6855 unsigned long mask = ainfo.iformats;
6856 if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
6857 info.nativeFormats |= RTAUDIO_SINT16;
6858 if ( mask & AFMT_S8 )
6859 info.nativeFormats |= RTAUDIO_SINT8;
6860 if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
6861 info.nativeFormats |= RTAUDIO_SINT32;
6862 if ( mask & AFMT_FLOAT )
6863 info.nativeFormats |= RTAUDIO_FLOAT32;
6864 if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
6865 info.nativeFormats |= RTAUDIO_SINT24;
6867 // Check that we have at least one supported format
6868 if ( info.nativeFormats == 0 ) {
6869 errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
6870 errorText_ = errorStream_.str();
6871 error( RtError::WARNING );
6872 return info;
6875 // Probe the supported sample rates.
6876 info.sampleRates.clear();
6877 if ( ainfo.nrates ) {
6878 for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
6879 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6880 if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
6881 info.sampleRates.push_back( SAMPLE_RATES[k] );
6882 break;
6887 else {
6888 // Check min and max rate values;
6889 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6890 if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )
6891 info.sampleRates.push_back( SAMPLE_RATES[k] );
6895 if ( info.sampleRates.size() == 0 ) {
6896 errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
6897 errorText_ = errorStream_.str();
6898 error( RtError::WARNING );
6900 else {
6901 info.probed = true;
6902 info.name = ainfo.name;
6905 return info;
6909 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
6910 unsigned int firstChannel, unsigned int sampleRate,
6911 RtAudioFormat format, unsigned int *bufferSize,
6912 RtAudio::StreamOptions *options )
6914 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6915 if ( mixerfd == -1 ) {
6916 errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
6917 return FAILURE;
6920 oss_sysinfo sysinfo;
6921 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
6922 if ( result == -1 ) {
6923 close( mixerfd );
6924 errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
6925 return FAILURE;
6928 unsigned nDevices = sysinfo.numaudios;
6929 if ( nDevices == 0 ) {
6930 // This should not happen because a check is made before this function is called.
6931 close( mixerfd );
6932 errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
6933 return FAILURE;
6936 if ( device >= nDevices ) {
6937 // This should not happen because a check is made before this function is called.
6938 close( mixerfd );
6939 errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
6940 return FAILURE;
6943 oss_audioinfo ainfo;
6944 ainfo.dev = device;
6945 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6946 close( mixerfd );
6947 if ( result == -1 ) {
6948 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6949 errorText_ = errorStream_.str();
6950 return FAILURE;
6953 // Check if device supports input or output
6954 if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
6955 ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
6956 if ( mode == OUTPUT )
6957 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
6958 else
6959 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
6960 errorText_ = errorStream_.str();
6961 return FAILURE;
6964 int flags = 0;
6965 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6966 if ( mode == OUTPUT )
6967 flags |= O_WRONLY;
6968 else { // mode == INPUT
6969 if (stream_.mode == OUTPUT && stream_.device[0] == device) {
6970 // We just set the same device for playback ... close and reopen for duplex (OSS only).
6971 close( handle->id[0] );
6972 handle->id[0] = 0;
6973 if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
6974 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
6975 errorText_ = errorStream_.str();
6976 return FAILURE;
6978 // Check that the number previously set channels is the same.
6979 if ( stream_.nUserChannels[0] != channels ) {
6980 errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
6981 errorText_ = errorStream_.str();
6982 return FAILURE;
6984 flags |= O_RDWR;
6986 else
6987 flags |= O_RDONLY;
6990 // Set exclusive access if specified.
6991 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
6993 // Try to open the device.
6994 int fd;
6995 fd = open( ainfo.devnode, flags, 0 );
6996 if ( fd == -1 ) {
6997 if ( errno == EBUSY )
6998 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
6999 else
7000 errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
7001 errorText_ = errorStream_.str();
7002 return FAILURE;
7005 // For duplex operation, specifically set this mode (this doesn't seem to work).
7007 if ( flags | O_RDWR ) {
7008 result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
7009 if ( result == -1) {
7010 errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
7011 errorText_ = errorStream_.str();
7012 return FAILURE;
7017 // Check the device channel support.
7018 stream_.nUserChannels[mode] = channels;
7019 if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
7020 close( fd );
7021 errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
7022 errorText_ = errorStream_.str();
7023 return FAILURE;
7026 // Set the number of channels.
7027 int deviceChannels = channels + firstChannel;
7028 result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
7029 if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
7030 close( fd );
7031 errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
7032 errorText_ = errorStream_.str();
7033 return FAILURE;
7035 stream_.nDeviceChannels[mode] = deviceChannels;
7037 // Get the data format mask
7038 int mask;
7039 result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
7040 if ( result == -1 ) {
7041 close( fd );
7042 errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
7043 errorText_ = errorStream_.str();
7044 return FAILURE;
7047 // Determine how to set the device format.
7048 stream_.userFormat = format;
7049 int deviceFormat = -1;
7050 stream_.doByteSwap[mode] = false;
7051 if ( format == RTAUDIO_SINT8 ) {
7052 if ( mask & AFMT_S8 ) {
7053 deviceFormat = AFMT_S8;
7054 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
7057 else if ( format == RTAUDIO_SINT16 ) {
7058 if ( mask & AFMT_S16_NE ) {
7059 deviceFormat = AFMT_S16_NE;
7060 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
7062 else if ( mask & AFMT_S16_OE ) {
7063 deviceFormat = AFMT_S16_OE;
7064 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
7065 stream_.doByteSwap[mode] = true;
7068 else if ( format == RTAUDIO_SINT24 ) {
7069 if ( mask & AFMT_S24_NE ) {
7070 deviceFormat = AFMT_S24_NE;
7071 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
7073 else if ( mask & AFMT_S24_OE ) {
7074 deviceFormat = AFMT_S24_OE;
7075 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
7076 stream_.doByteSwap[mode] = true;
7079 else if ( format == RTAUDIO_SINT32 ) {
7080 if ( mask & AFMT_S32_NE ) {
7081 deviceFormat = AFMT_S32_NE;
7082 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
7084 else if ( mask & AFMT_S32_OE ) {
7085 deviceFormat = AFMT_S32_OE;
7086 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
7087 stream_.doByteSwap[mode] = true;
7091 if ( deviceFormat == -1 ) {
7092 // The user requested format is not natively supported by the device.
7093 if ( mask & AFMT_S16_NE ) {
7094 deviceFormat = AFMT_S16_NE;
7095 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
7097 else if ( mask & AFMT_S32_NE ) {
7098 deviceFormat = AFMT_S32_NE;
7099 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
7101 else if ( mask & AFMT_S24_NE ) {
7102 deviceFormat = AFMT_S24_NE;
7103 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
7105 else if ( mask & AFMT_S16_OE ) {
7106 deviceFormat = AFMT_S16_OE;
7107 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
7108 stream_.doByteSwap[mode] = true;
7110 else if ( mask & AFMT_S32_OE ) {
7111 deviceFormat = AFMT_S32_OE;
7112 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
7113 stream_.doByteSwap[mode] = true;
7115 else if ( mask & AFMT_S24_OE ) {
7116 deviceFormat = AFMT_S24_OE;
7117 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
7118 stream_.doByteSwap[mode] = true;
7120 else if ( mask & AFMT_S8) {
7121 deviceFormat = AFMT_S8;
7122 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
7126 if ( stream_.deviceFormat[mode] == 0 ) {
7127 // This really shouldn't happen ...
7128 close( fd );
7129 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
7130 errorText_ = errorStream_.str();
7131 return FAILURE;
7134 // Set the data format.
7135 int temp = deviceFormat;
7136 result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
7137 if ( result == -1 || deviceFormat != temp ) {
7138 close( fd );
7139 errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
7140 errorText_ = errorStream_.str();
7141 return FAILURE;
7144 // Attempt to set the buffer size. According to OSS, the minimum
7145 // number of buffers is two. The supposed minimum buffer size is 16
7146 // bytes, so that will be our lower bound. The argument to this
7147 // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
7148 // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
7149 // We'll check the actual value used near the end of the setup
7150 // procedure.
7151 int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
7152 if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
7153 int buffers = 0;
7154 if ( options ) buffers = options->numberOfBuffers;
7155 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
7156 if ( buffers < 2 ) buffers = 3;
7157 temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
7158 result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
7159 if ( result == -1 ) {
7160 close( fd );
7161 errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
7162 errorText_ = errorStream_.str();
7163 return FAILURE;
7165 stream_.nBuffers = buffers;
7167 // Save buffer size (in sample frames).
7168 *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
7169 stream_.bufferSize = *bufferSize;
7171 // Set the sample rate.
7172 int srate = sampleRate;
7173 result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
7174 if ( result == -1 ) {
7175 close( fd );
7176 errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
7177 errorText_ = errorStream_.str();
7178 return FAILURE;
7181 // Verify the sample rate setup worked.
7182 if ( abs( srate - sampleRate ) > 100 ) {
7183 close( fd );
7184 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
7185 errorText_ = errorStream_.str();
7186 return FAILURE;
7188 stream_.sampleRate = sampleRate;
7190 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
7191 // We're doing duplex setup here.
7192 stream_.deviceFormat[0] = stream_.deviceFormat[1];
7193 stream_.nDeviceChannels[0] = deviceChannels;
7196 // Set interleaving parameters.
7197 stream_.userInterleaved = true;
7198 stream_.deviceInterleaved[mode] = true;
7199 if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
7200 stream_.userInterleaved = false;
7202 // Set flags for buffer conversion
7203 stream_.doConvertBuffer[mode] = false;
7204 if ( stream_.userFormat != stream_.deviceFormat[mode] )
7205 stream_.doConvertBuffer[mode] = true;
7206 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
7207 stream_.doConvertBuffer[mode] = true;
7208 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
7209 stream_.nUserChannels[mode] > 1 )
7210 stream_.doConvertBuffer[mode] = true;
7212 // Allocate the stream handles if necessary and then save.
7213 if ( stream_.apiHandle == 0 ) {
7214 try {
7215 handle = new OssHandle;
7217 catch ( std::bad_alloc& ) {
7218 errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
7219 goto error;
7222 if ( pthread_cond_init( &handle->runnable, NULL ) ) {
7223 errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
7224 goto error;
7227 stream_.apiHandle = (void *) handle;
7229 else {
7230 handle = (OssHandle *) stream_.apiHandle;
7232 handle->id[mode] = fd;
7234 // Allocate necessary internal buffers.
7235 unsigned long bufferBytes;
7236 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
7237 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
7238 if ( stream_.userBuffer[mode] == NULL ) {
7239 errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
7240 goto error;
7243 if ( stream_.doConvertBuffer[mode] ) {
7245 bool makeBuffer = true;
7246 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
7247 if ( mode == INPUT ) {
7248 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
7249 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
7250 if ( bufferBytes <= bytesOut ) makeBuffer = false;
7254 if ( makeBuffer ) {
7255 bufferBytes *= *bufferSize;
7256 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
7257 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
7258 if ( stream_.deviceBuffer == NULL ) {
7259 errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
7260 goto error;
7265 stream_.device[mode] = device;
7266 stream_.state = STREAM_STOPPED;
7268 // Setup the buffer conversion information structure.
7269 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
7271 // Setup thread if necessary.
7272 if ( stream_.mode == OUTPUT && mode == INPUT ) {
7273 // We had already set up an output stream.
7274 stream_.mode = DUPLEX;
7275 if ( stream_.device[0] == device ) handle->id[0] = fd;
7277 else {
7278 stream_.mode = mode;
7280 // Setup callback thread.
7281 stream_.callbackInfo.object = (void *) this;
7283 // Set the thread attributes for joinable and realtime scheduling
7284 // priority. The higher priority will only take affect if the
7285 // program is run as root or suid.
7286 pthread_attr_t attr;
7287 pthread_attr_init( &attr );
7288 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
7289 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
7290 if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
7291 struct sched_param param;
7292 int priority = options->priority;
7293 int min = sched_get_priority_min( SCHED_RR );
7294 int max = sched_get_priority_max( SCHED_RR );
7295 if ( priority < min ) priority = min;
7296 else if ( priority > max ) priority = max;
7297 param.sched_priority = priority;
7298 pthread_attr_setschedparam( &attr, &param );
7299 pthread_attr_setschedpolicy( &attr, SCHED_RR );
7301 else
7302 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
7303 #else
7304 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
7305 #endif
7307 stream_.callbackInfo.isRunning = true;
7308 result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
7309 pthread_attr_destroy( &attr );
7310 if ( result ) {
7311 stream_.callbackInfo.isRunning = false;
7312 errorText_ = "RtApiOss::error creating callback thread!";
7313 goto error;
7317 return SUCCESS;
7319 error:
7320 if ( handle ) {
7321 pthread_cond_destroy( &handle->runnable );
7322 if ( handle->id[0] ) close( handle->id[0] );
7323 if ( handle->id[1] ) close( handle->id[1] );
7324 delete handle;
7325 stream_.apiHandle = 0;
7328 for ( int i=0; i<2; i++ ) {
7329 if ( stream_.userBuffer[i] ) {
7330 free( stream_.userBuffer[i] );
7331 stream_.userBuffer[i] = 0;
7335 if ( stream_.deviceBuffer ) {
7336 free( stream_.deviceBuffer );
7337 stream_.deviceBuffer = 0;
7340 return FAILURE;
7343 void RtApiOss :: closeStream()
7345 if ( stream_.state == STREAM_CLOSED ) {
7346 errorText_ = "RtApiOss::closeStream(): no open stream to close!";
7347 error( RtError::WARNING );
7348 return;
7351 OssHandle *handle = (OssHandle *) stream_.apiHandle;
7352 stream_.callbackInfo.isRunning = false;
7353 MUTEX_LOCK( &stream_.mutex );
7354 if ( stream_.state == STREAM_STOPPED )
7355 pthread_cond_signal( &handle->runnable );
7356 MUTEX_UNLOCK( &stream_.mutex );
7357 pthread_join( stream_.callbackInfo.thread, NULL );
7359 if ( stream_.state == STREAM_RUNNING ) {
7360 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
7361 ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
7362 else
7363 ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
7364 stream_.state = STREAM_STOPPED;
7367 if ( handle ) {
7368 pthread_cond_destroy( &handle->runnable );
7369 if ( handle->id[0] ) close( handle->id[0] );
7370 if ( handle->id[1] ) close( handle->id[1] );
7371 delete handle;
7372 stream_.apiHandle = 0;
7375 for ( int i=0; i<2; i++ ) {
7376 if ( stream_.userBuffer[i] ) {
7377 free( stream_.userBuffer[i] );
7378 stream_.userBuffer[i] = 0;
7382 if ( stream_.deviceBuffer ) {
7383 free( stream_.deviceBuffer );
7384 stream_.deviceBuffer = 0;
7387 stream_.mode = UNINITIALIZED;
7388 stream_.state = STREAM_CLOSED;
7391 void RtApiOss :: startStream()
7393 verifyStream();
7394 if ( stream_.state == STREAM_RUNNING ) {
7395 errorText_ = "RtApiOss::startStream(): the stream is already running!";
7396 error( RtError::WARNING );
7397 return;
7400 MUTEX_LOCK( &stream_.mutex );
7402 stream_.state = STREAM_RUNNING;
7404 // No need to do anything else here ... OSS automatically starts
7405 // when fed samples.
7407 MUTEX_UNLOCK( &stream_.mutex );
7409 OssHandle *handle = (OssHandle *) stream_.apiHandle;
7410 pthread_cond_signal( &handle->runnable );
7413 void RtApiOss :: stopStream()
7415 verifyStream();
7416 if ( stream_.state == STREAM_STOPPED ) {
7417 errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
7418 error( RtError::WARNING );
7419 return;
7422 MUTEX_LOCK( &stream_.mutex );
7424 // The state might change while waiting on a mutex.
7425 if ( stream_.state == STREAM_STOPPED ) {
7426 MUTEX_UNLOCK( &stream_.mutex );
7427 return;
7430 int result = 0;
7431 OssHandle *handle = (OssHandle *) stream_.apiHandle;
7432 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
7434 // Flush the output with zeros a few times.
7435 char *buffer;
7436 int samples;
7437 RtAudioFormat format;
7439 if ( stream_.doConvertBuffer[0] ) {
7440 buffer = stream_.deviceBuffer;
7441 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
7442 format = stream_.deviceFormat[0];
7444 else {
7445 buffer = stream_.userBuffer[0];
7446 samples = stream_.bufferSize * stream_.nUserChannels[0];
7447 format = stream_.userFormat;
7450 memset( buffer, 0, samples * formatBytes(format) );
7451 for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
7452 result = write( handle->id[0], buffer, samples * formatBytes(format) );
7453 if ( result == -1 ) {
7454 errorText_ = "RtApiOss::stopStream: audio write error.";
7455 error( RtError::WARNING );
7459 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
7460 if ( result == -1 ) {
7461 errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
7462 errorText_ = errorStream_.str();
7463 goto unlock;
7465 handle->triggered = false;
7468 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
7469 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
7470 if ( result == -1 ) {
7471 errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
7472 errorText_ = errorStream_.str();
7473 goto unlock;
7477 unlock:
7478 stream_.state = STREAM_STOPPED;
7479 MUTEX_UNLOCK( &stream_.mutex );
7481 if ( result != -1 ) return;
7482 error( RtError::SYSTEM_ERROR );
7485 void RtApiOss :: abortStream()
7487 verifyStream();
7488 if ( stream_.state == STREAM_STOPPED ) {
7489 errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
7490 error( RtError::WARNING );
7491 return;
7494 MUTEX_LOCK( &stream_.mutex );
7496 // The state might change while waiting on a mutex.
7497 if ( stream_.state == STREAM_STOPPED ) {
7498 MUTEX_UNLOCK( &stream_.mutex );
7499 return;
7502 int result = 0;
7503 OssHandle *handle = (OssHandle *) stream_.apiHandle;
7504 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
7505 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
7506 if ( result == -1 ) {
7507 errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
7508 errorText_ = errorStream_.str();
7509 goto unlock;
7511 handle->triggered = false;
7514 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
7515 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
7516 if ( result == -1 ) {
7517 errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
7518 errorText_ = errorStream_.str();
7519 goto unlock;
7523 unlock:
7524 stream_.state = STREAM_STOPPED;
7525 MUTEX_UNLOCK( &stream_.mutex );
7527 if ( result != -1 ) return;
7528 error( RtError::SYSTEM_ERROR );
7531 void RtApiOss :: callbackEvent()
7533 OssHandle *handle = (OssHandle *) stream_.apiHandle;
7534 if ( stream_.state == STREAM_STOPPED ) {
7535 MUTEX_LOCK( &stream_.mutex );
7536 pthread_cond_wait( &handle->runnable, &stream_.mutex );
7537 if ( stream_.state != STREAM_RUNNING ) {
7538 MUTEX_UNLOCK( &stream_.mutex );
7539 return;
7541 MUTEX_UNLOCK( &stream_.mutex );
7544 if ( stream_.state == STREAM_CLOSED ) {
7545 errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
7546 error( RtError::WARNING );
7547 return;
7550 // Invoke user callback to get fresh output data.
7551 int doStopStream = 0;
7552 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
7553 double streamTime = getStreamTime();
7554 RtAudioStreamStatus status = 0;
7555 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
7556 status |= RTAUDIO_OUTPUT_UNDERFLOW;
7557 handle->xrun[0] = false;
7559 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
7560 status |= RTAUDIO_INPUT_OVERFLOW;
7561 handle->xrun[1] = false;
7563 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
7564 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
7565 if ( doStopStream == 2 ) {
7566 this->abortStream();
7567 return;
7570 MUTEX_LOCK( &stream_.mutex );
7572 // The state might change while waiting on a mutex.
7573 if ( stream_.state == STREAM_STOPPED ) goto unlock;
7575 int result;
7576 char *buffer;
7577 int samples;
7578 RtAudioFormat format;
7580 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
7582 // Setup parameters and do buffer conversion if necessary.
7583 if ( stream_.doConvertBuffer[0] ) {
7584 buffer = stream_.deviceBuffer;
7585 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
7586 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
7587 format = stream_.deviceFormat[0];
7589 else {
7590 buffer = stream_.userBuffer[0];
7591 samples = stream_.bufferSize * stream_.nUserChannels[0];
7592 format = stream_.userFormat;
7595 // Do byte swapping if necessary.
7596 if ( stream_.doByteSwap[0] )
7597 byteSwapBuffer( buffer, samples, format );
7599 if ( stream_.mode == DUPLEX && handle->triggered == false ) {
7600 int trig = 0;
7601 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
7602 result = write( handle->id[0], buffer, samples * formatBytes(format) );
7603 trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
7604 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
7605 handle->triggered = true;
7607 else
7608 // Write samples to device.
7609 result = write( handle->id[0], buffer, samples * formatBytes(format) );
7611 if ( result == -1 ) {
7612 // We'll assume this is an underrun, though there isn't a
7613 // specific means for determining that.
7614 handle->xrun[0] = true;
7615 errorText_ = "RtApiOss::callbackEvent: audio write error.";
7616 error( RtError::WARNING );
7617 // Continue on to input section.
7621 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
7623 // Setup parameters.
7624 if ( stream_.doConvertBuffer[1] ) {
7625 buffer = stream_.deviceBuffer;
7626 samples = stream_.bufferSize * stream_.nDeviceChannels[1];
7627 format = stream_.deviceFormat[1];
7629 else {
7630 buffer = stream_.userBuffer[1];
7631 samples = stream_.bufferSize * stream_.nUserChannels[1];
7632 format = stream_.userFormat;
7635 // Read samples from device.
7636 result = read( handle->id[1], buffer, samples * formatBytes(format) );
7638 if ( result == -1 ) {
7639 // We'll assume this is an overrun, though there isn't a
7640 // specific means for determining that.
7641 handle->xrun[1] = true;
7642 errorText_ = "RtApiOss::callbackEvent: audio read error.";
7643 error( RtError::WARNING );
7644 goto unlock;
7647 // Do byte swapping if necessary.
7648 if ( stream_.doByteSwap[1] )
7649 byteSwapBuffer( buffer, samples, format );
7651 // Do buffer conversion if necessary.
7652 if ( stream_.doConvertBuffer[1] )
7653 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
7656 unlock:
7657 MUTEX_UNLOCK( &stream_.mutex );
7659 RtApi::tickStreamTime();
7660 if ( doStopStream == 1 ) this->stopStream();
7663 extern "C" void *ossCallbackHandler( void *ptr )
7665 CallbackInfo *info = (CallbackInfo *) ptr;
7666 RtApiOss *object = (RtApiOss *) info->object;
7667 bool *isRunning = &info->isRunning;
7669 while ( *isRunning == true ) {
7670 pthread_testcancel();
7671 object->callbackEvent();
7674 pthread_exit( NULL );
7677 //******************** End of __LINUX_OSS__ *********************//
7678 #endif
7681 // *************************************************** //
7683 // Protected common (OS-independent) RtAudio methods.
7685 // *************************************************** //
7687 // This method can be modified to control the behavior of error
7688 // message printing.
7689 void RtApi :: error( RtError::Type type )
7691 errorStream_.str(""); // clear the ostringstream
7692 if ( type == RtError::WARNING && showWarnings_ == true )
7693 std::cerr << '\n' << errorText_ << "\n\n";
7694 else if ( type != RtError::WARNING )
7695 throw( RtError( errorText_, type ) );
7698 void RtApi :: verifyStream()
7700 if ( stream_.state == STREAM_CLOSED ) {
7701 errorText_ = "RtApi:: a stream is not open!";
7702 error( RtError::INVALID_USE );
7706 void RtApi :: clearStreamInfo()
7708 stream_.mode = UNINITIALIZED;
7709 stream_.state = STREAM_CLOSED;
7710 stream_.sampleRate = 0;
7711 stream_.bufferSize = 0;
7712 stream_.nBuffers = 0;
7713 stream_.userFormat = 0;
7714 stream_.userInterleaved = true;
7715 stream_.streamTime = 0.0;
7716 stream_.apiHandle = 0;
7717 stream_.deviceBuffer = 0;
7718 stream_.callbackInfo.callback = 0;
7719 stream_.callbackInfo.userData = 0;
7720 stream_.callbackInfo.isRunning = false;
7721 for ( int i=0; i<2; i++ ) {
7722 stream_.device[i] = 11111;
7723 stream_.doConvertBuffer[i] = false;
7724 stream_.deviceInterleaved[i] = true;
7725 stream_.doByteSwap[i] = false;
7726 stream_.nUserChannels[i] = 0;
7727 stream_.nDeviceChannels[i] = 0;
7728 stream_.channelOffset[i] = 0;
7729 stream_.deviceFormat[i] = 0;
7730 stream_.latency[i] = 0;
7731 stream_.userBuffer[i] = 0;
7732 stream_.convertInfo[i].channels = 0;
7733 stream_.convertInfo[i].inJump = 0;
7734 stream_.convertInfo[i].outJump = 0;
7735 stream_.convertInfo[i].inFormat = 0;
7736 stream_.convertInfo[i].outFormat = 0;
7737 stream_.convertInfo[i].inOffset.clear();
7738 stream_.convertInfo[i].outOffset.clear();
7742 unsigned int RtApi :: formatBytes( RtAudioFormat format )
7744 if ( format == RTAUDIO_SINT16 )
7745 return 2;
7746 else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
7747 format == RTAUDIO_FLOAT32 )
7748 return 4;
7749 else if ( format == RTAUDIO_FLOAT64 )
7750 return 8;
7751 else if ( format == RTAUDIO_SINT8 )
7752 return 1;
7754 errorText_ = "RtApi::formatBytes: undefined format.";
7755 error( RtError::WARNING );
7757 return 0;
7760 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
7762 if ( mode == INPUT ) { // convert device to user buffer
7763 stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
7764 stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
7765 stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
7766 stream_.convertInfo[mode].outFormat = stream_.userFormat;
7768 else { // convert user to device buffer
7769 stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
7770 stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
7771 stream_.convertInfo[mode].inFormat = stream_.userFormat;
7772 stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
7775 if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
7776 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
7777 else
7778 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
7780 // Set up the interleave/deinterleave offsets.
7781 if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
7782 if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
7783 ( mode == INPUT && stream_.userInterleaved ) ) {
7784 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7785 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
7786 stream_.convertInfo[mode].outOffset.push_back( k );
7787 stream_.convertInfo[mode].inJump = 1;
7790 else {
7791 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7792 stream_.convertInfo[mode].inOffset.push_back( k );
7793 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
7794 stream_.convertInfo[mode].outJump = 1;
7798 else { // no (de)interleaving
7799 if ( stream_.userInterleaved ) {
7800 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7801 stream_.convertInfo[mode].inOffset.push_back( k );
7802 stream_.convertInfo[mode].outOffset.push_back( k );
7805 else {
7806 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7807 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
7808 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
7809 stream_.convertInfo[mode].inJump = 1;
7810 stream_.convertInfo[mode].outJump = 1;
7815 // Add channel offset.
7816 if ( firstChannel > 0 ) {
7817 if ( stream_.deviceInterleaved[mode] ) {
7818 if ( mode == OUTPUT ) {
7819 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7820 stream_.convertInfo[mode].outOffset[k] += firstChannel;
7822 else {
7823 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7824 stream_.convertInfo[mode].inOffset[k] += firstChannel;
7827 else {
7828 if ( mode == OUTPUT ) {
7829 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7830 stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
7832 else {
7833 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7834 stream_.convertInfo[mode].inOffset[k] += ( firstChannel * stream_.bufferSize );
7840 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
7842 // This function does format conversion, input/output channel compensation, and
7843 // data interleaving/deinterleaving. 24-bit integers are assumed to occupy
7844 // the lower three bytes of a 32-bit integer.
7846 // Clear our device buffer when in/out duplex device channels are different
7847 if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
7848 ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
7849 memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
7851 int j;
7852 if (info.outFormat == RTAUDIO_FLOAT64) {
7853 Float64 scale;
7854 Float64 *out = (Float64 *)outBuffer;
7856 if (info.inFormat == RTAUDIO_SINT8) {
7857 signed char *in = (signed char *)inBuffer;
7858 scale = 1.0 / 127.5;
7859 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7860 for (j=0; j<info.channels; j++) {
7861 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7862 out[info.outOffset[j]] += 0.5;
7863 out[info.outOffset[j]] *= scale;
7865 in += info.inJump;
7866 out += info.outJump;
7869 else if (info.inFormat == RTAUDIO_SINT16) {
7870 Int16 *in = (Int16 *)inBuffer;
7871 scale = 1.0 / 32767.5;
7872 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7873 for (j=0; j<info.channels; j++) {
7874 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7875 out[info.outOffset[j]] += 0.5;
7876 out[info.outOffset[j]] *= scale;
7878 in += info.inJump;
7879 out += info.outJump;
7882 else if (info.inFormat == RTAUDIO_SINT24) {
7883 Int32 *in = (Int32 *)inBuffer;
7884 scale = 1.0 / 8388607.5;
7885 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7886 for (j=0; j<info.channels; j++) {
7887 out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
7888 out[info.outOffset[j]] += 0.5;
7889 out[info.outOffset[j]] *= scale;
7891 in += info.inJump;
7892 out += info.outJump;
7895 else if (info.inFormat == RTAUDIO_SINT32) {
7896 Int32 *in = (Int32 *)inBuffer;
7897 scale = 1.0 / 2147483647.5;
7898 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7899 for (j=0; j<info.channels; j++) {
7900 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7901 out[info.outOffset[j]] += 0.5;
7902 out[info.outOffset[j]] *= scale;
7904 in += info.inJump;
7905 out += info.outJump;
7908 else if (info.inFormat == RTAUDIO_FLOAT32) {
7909 Float32 *in = (Float32 *)inBuffer;
7910 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7911 for (j=0; j<info.channels; j++) {
7912 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7914 in += info.inJump;
7915 out += info.outJump;
7918 else if (info.inFormat == RTAUDIO_FLOAT64) {
7919 // Channel compensation and/or (de)interleaving only.
7920 Float64 *in = (Float64 *)inBuffer;
7921 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7922 for (j=0; j<info.channels; j++) {
7923 out[info.outOffset[j]] = in[info.inOffset[j]];
7925 in += info.inJump;
7926 out += info.outJump;
7930 else if (info.outFormat == RTAUDIO_FLOAT32) {
7931 Float32 scale;
7932 Float32 *out = (Float32 *)outBuffer;
7934 if (info.inFormat == RTAUDIO_SINT8) {
7935 signed char *in = (signed char *)inBuffer;
7936 scale = (Float32) ( 1.0 / 127.5 );
7937 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7938 for (j=0; j<info.channels; j++) {
7939 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7940 out[info.outOffset[j]] += 0.5;
7941 out[info.outOffset[j]] *= scale;
7943 in += info.inJump;
7944 out += info.outJump;
7947 else if (info.inFormat == RTAUDIO_SINT16) {
7948 Int16 *in = (Int16 *)inBuffer;
7949 scale = (Float32) ( 1.0 / 32767.5 );
7950 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7951 for (j=0; j<info.channels; j++) {
7952 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7953 out[info.outOffset[j]] += 0.5;
7954 out[info.outOffset[j]] *= scale;
7956 in += info.inJump;
7957 out += info.outJump;
7960 else if (info.inFormat == RTAUDIO_SINT24) {
7961 Int32 *in = (Int32 *)inBuffer;
7962 scale = (Float32) ( 1.0 / 8388607.5 );
7963 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7964 for (j=0; j<info.channels; j++) {
7965 out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
7966 out[info.outOffset[j]] += 0.5;
7967 out[info.outOffset[j]] *= scale;
7969 in += info.inJump;
7970 out += info.outJump;
7973 else if (info.inFormat == RTAUDIO_SINT32) {
7974 Int32 *in = (Int32 *)inBuffer;
7975 scale = (Float32) ( 1.0 / 2147483647.5 );
7976 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7977 for (j=0; j<info.channels; j++) {
7978 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7979 out[info.outOffset[j]] += 0.5;
7980 out[info.outOffset[j]] *= scale;
7982 in += info.inJump;
7983 out += info.outJump;
7986 else if (info.inFormat == RTAUDIO_FLOAT32) {
7987 // Channel compensation and/or (de)interleaving only.
7988 Float32 *in = (Float32 *)inBuffer;
7989 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7990 for (j=0; j<info.channels; j++) {
7991 out[info.outOffset[j]] = in[info.inOffset[j]];
7993 in += info.inJump;
7994 out += info.outJump;
7997 else if (info.inFormat == RTAUDIO_FLOAT64) {
7998 Float64 *in = (Float64 *)inBuffer;
7999 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8000 for (j=0; j<info.channels; j++) {
8001 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
8003 in += info.inJump;
8004 out += info.outJump;
8008 else if (info.outFormat == RTAUDIO_SINT32) {
8009 Int32 *out = (Int32 *)outBuffer;
8010 if (info.inFormat == RTAUDIO_SINT8) {
8011 signed char *in = (signed char *)inBuffer;
8012 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8013 for (j=0; j<info.channels; j++) {
8014 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
8015 out[info.outOffset[j]] <<= 24;
8017 in += info.inJump;
8018 out += info.outJump;
8021 else if (info.inFormat == RTAUDIO_SINT16) {
8022 Int16 *in = (Int16 *)inBuffer;
8023 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8024 for (j=0; j<info.channels; j++) {
8025 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
8026 out[info.outOffset[j]] <<= 16;
8028 in += info.inJump;
8029 out += info.outJump;
8032 else if (info.inFormat == RTAUDIO_SINT24) { // Hmmm ... we could just leave it in the lower 3 bytes
8033 Int32 *in = (Int32 *)inBuffer;
8034 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8035 for (j=0; j<info.channels; j++) {
8036 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
8037 out[info.outOffset[j]] <<= 8;
8039 in += info.inJump;
8040 out += info.outJump;
8043 else if (info.inFormat == RTAUDIO_SINT32) {
8044 // Channel compensation and/or (de)interleaving only.
8045 Int32 *in = (Int32 *)inBuffer;
8046 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8047 for (j=0; j<info.channels; j++) {
8048 out[info.outOffset[j]] = in[info.inOffset[j]];
8050 in += info.inJump;
8051 out += info.outJump;
8054 else if (info.inFormat == RTAUDIO_FLOAT32) {
8055 Float32 *in = (Float32 *)inBuffer;
8056 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8057 for (j=0; j<info.channels; j++) {
8058 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
8060 in += info.inJump;
8061 out += info.outJump;
8064 else if (info.inFormat == RTAUDIO_FLOAT64) {
8065 Float64 *in = (Float64 *)inBuffer;
8066 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8067 for (j=0; j<info.channels; j++) {
8068 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
8070 in += info.inJump;
8071 out += info.outJump;
8075 else if (info.outFormat == RTAUDIO_SINT24) {
8076 Int32 *out = (Int32 *)outBuffer;
8077 if (info.inFormat == RTAUDIO_SINT8) {
8078 signed char *in = (signed char *)inBuffer;
8079 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8080 for (j=0; j<info.channels; j++) {
8081 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
8082 out[info.outOffset[j]] <<= 16;
8084 in += info.inJump;
8085 out += info.outJump;
8088 else if (info.inFormat == RTAUDIO_SINT16) {
8089 Int16 *in = (Int16 *)inBuffer;
8090 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8091 for (j=0; j<info.channels; j++) {
8092 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
8093 out[info.outOffset[j]] <<= 8;
8095 in += info.inJump;
8096 out += info.outJump;
8099 else if (info.inFormat == RTAUDIO_SINT24) {
8100 // Channel compensation and/or (de)interleaving only.
8101 Int32 *in = (Int32 *)inBuffer;
8102 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8103 for (j=0; j<info.channels; j++) {
8104 out[info.outOffset[j]] = in[info.inOffset[j]];
8106 in += info.inJump;
8107 out += info.outJump;
8110 else if (info.inFormat == RTAUDIO_SINT32) {
8111 Int32 *in = (Int32 *)inBuffer;
8112 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8113 for (j=0; j<info.channels; j++) {
8114 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
8115 out[info.outOffset[j]] >>= 8;
8117 in += info.inJump;
8118 out += info.outJump;
8121 else if (info.inFormat == RTAUDIO_FLOAT32) {
8122 Float32 *in = (Float32 *)inBuffer;
8123 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8124 for (j=0; j<info.channels; j++) {
8125 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
8127 in += info.inJump;
8128 out += info.outJump;
8131 else if (info.inFormat == RTAUDIO_FLOAT64) {
8132 Float64 *in = (Float64 *)inBuffer;
8133 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8134 for (j=0; j<info.channels; j++) {
8135 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
8137 in += info.inJump;
8138 out += info.outJump;
8142 else if (info.outFormat == RTAUDIO_SINT16) {
8143 Int16 *out = (Int16 *)outBuffer;
8144 if (info.inFormat == RTAUDIO_SINT8) {
8145 signed char *in = (signed char *)inBuffer;
8146 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8147 for (j=0; j<info.channels; j++) {
8148 out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
8149 out[info.outOffset[j]] <<= 8;
8151 in += info.inJump;
8152 out += info.outJump;
8155 else if (info.inFormat == RTAUDIO_SINT16) {
8156 // Channel compensation and/or (de)interleaving only.
8157 Int16 *in = (Int16 *)inBuffer;
8158 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8159 for (j=0; j<info.channels; j++) {
8160 out[info.outOffset[j]] = in[info.inOffset[j]];
8162 in += info.inJump;
8163 out += info.outJump;
8166 else if (info.inFormat == RTAUDIO_SINT24) {
8167 Int32 *in = (Int32 *)inBuffer;
8168 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8169 for (j=0; j<info.channels; j++) {
8170 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 8) & 0x0000ffff);
8172 in += info.inJump;
8173 out += info.outJump;
8176 else if (info.inFormat == RTAUDIO_SINT32) {
8177 Int32 *in = (Int32 *)inBuffer;
8178 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8179 for (j=0; j<info.channels; j++) {
8180 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
8182 in += info.inJump;
8183 out += info.outJump;
8186 else if (info.inFormat == RTAUDIO_FLOAT32) {
8187 Float32 *in = (Float32 *)inBuffer;
8188 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8189 for (j=0; j<info.channels; j++) {
8190 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
8192 in += info.inJump;
8193 out += info.outJump;
8196 else if (info.inFormat == RTAUDIO_FLOAT64) {
8197 Float64 *in = (Float64 *)inBuffer;
8198 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8199 for (j=0; j<info.channels; j++) {
8200 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
8202 in += info.inJump;
8203 out += info.outJump;
8207 else if (info.outFormat == RTAUDIO_SINT8) {
8208 signed char *out = (signed char *)outBuffer;
8209 if (info.inFormat == RTAUDIO_SINT8) {
8210 // Channel compensation and/or (de)interleaving only.
8211 signed char *in = (signed char *)inBuffer;
8212 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8213 for (j=0; j<info.channels; j++) {
8214 out[info.outOffset[j]] = in[info.inOffset[j]];
8216 in += info.inJump;
8217 out += info.outJump;
8220 if (info.inFormat == RTAUDIO_SINT16) {
8221 Int16 *in = (Int16 *)inBuffer;
8222 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8223 for (j=0; j<info.channels; j++) {
8224 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
8226 in += info.inJump;
8227 out += info.outJump;
8230 else if (info.inFormat == RTAUDIO_SINT24) {
8231 Int32 *in = (Int32 *)inBuffer;
8232 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8233 for (j=0; j<info.channels; j++) {
8234 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 16) & 0x000000ff);
8236 in += info.inJump;
8237 out += info.outJump;
8240 else if (info.inFormat == RTAUDIO_SINT32) {
8241 Int32 *in = (Int32 *)inBuffer;
8242 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8243 for (j=0; j<info.channels; j++) {
8244 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
8246 in += info.inJump;
8247 out += info.outJump;
8250 else if (info.inFormat == RTAUDIO_FLOAT32) {
8251 Float32 *in = (Float32 *)inBuffer;
8252 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8253 for (j=0; j<info.channels; j++) {
8254 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
8256 in += info.inJump;
8257 out += info.outJump;
8260 else if (info.inFormat == RTAUDIO_FLOAT64) {
8261 Float64 *in = (Float64 *)inBuffer;
8262 for (unsigned int i=0; i<stream_.bufferSize; i++) {
8263 for (j=0; j<info.channels; j++) {
8264 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
8266 in += info.inJump;
8267 out += info.outJump;
8273 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }
8274 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }
8275 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }
8277 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
8279 register char val;
8280 register char *ptr;
8282 ptr = buffer;
8283 if ( format == RTAUDIO_SINT16 ) {
8284 for ( unsigned int i=0; i<samples; i++ ) {
8285 // Swap 1st and 2nd bytes.
8286 val = *(ptr);
8287 *(ptr) = *(ptr+1);
8288 *(ptr+1) = val;
8290 // Increment 2 bytes.
8291 ptr += 2;
8294 else if ( format == RTAUDIO_SINT24 ||
8295 format == RTAUDIO_SINT32 ||
8296 format == RTAUDIO_FLOAT32 ) {
8297 for ( unsigned int i=0; i<samples; i++ ) {
8298 // Swap 1st and 4th bytes.
8299 val = *(ptr);
8300 *(ptr) = *(ptr+3);
8301 *(ptr+3) = val;
8303 // Swap 2nd and 3rd bytes.
8304 ptr += 1;
8305 val = *(ptr);
8306 *(ptr) = *(ptr+1);
8307 *(ptr+1) = val;
8309 // Increment 3 more bytes.
8310 ptr += 3;
8313 else if ( format == RTAUDIO_FLOAT64 ) {
8314 for ( unsigned int i=0; i<samples; i++ ) {
8315 // Swap 1st and 8th bytes
8316 val = *(ptr);
8317 *(ptr) = *(ptr+7);
8318 *(ptr+7) = val;
8320 // Swap 2nd and 7th bytes
8321 ptr += 1;
8322 val = *(ptr);
8323 *(ptr) = *(ptr+5);
8324 *(ptr+5) = val;
8326 // Swap 3rd and 6th bytes
8327 ptr += 1;
8328 val = *(ptr);
8329 *(ptr) = *(ptr+3);
8330 *(ptr+3) = val;
8332 // Swap 4th and 5th bytes
8333 ptr += 1;
8334 val = *(ptr);
8335 *(ptr) = *(ptr+1);
8336 *(ptr+1) = val;
8338 // Increment 5 more bytes.
8339 ptr += 5;
8344 // Indentation settings for Vim and Emacs
8346 // Local Variables:
8347 // c-basic-offset: 2
8348 // indent-tabs-mode: nil
8349 // End:
8351 // vim: et sts=2 sw=2