1 /************************************************************************/
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), SGI, 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-2008 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.4
46 // Static variable definitions.
47 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
48 const unsigned int RtApi::SAMPLE_RATES[] = {
49 4000, 5512, 8000, 9600, 11025, 16000, 22050,
50 32000, 44100, 48000, 88200, 96000, 176400, 192000
53 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
54 #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
55 #define MUTEX_DESTROY(A) DeleteCriticalSection(A)
56 #define MUTEX_LOCK(A) EnterCriticalSection(A)
57 #define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
58 #elif defined(__LINUX_ALSA__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
60 #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
61 #define MUTEX_DESTROY(A) pthread_mutex_destroy(A)
62 #define MUTEX_LOCK(A) pthread_mutex_lock(A)
63 #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A)
65 #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions
66 #define MUTEX_DESTROY(A) abs(*A) // dummy definitions
69 // *************************************************** //
71 // RtAudio definitions.
73 // *************************************************** //
75 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
79 // The order here will control the order of RtAudio's API search in
81 #if defined(__UNIX_JACK__)
82 apis.push_back( UNIX_JACK );
84 #if defined(__LINUX_ALSA__)
85 apis.push_back( LINUX_ALSA );
87 #if defined(__LINUX_OSS__)
88 apis.push_back( LINUX_OSS );
90 #if defined(__WINDOWS_ASIO__)
91 apis.push_back( WINDOWS_ASIO );
93 #if defined(__WINDOWS_DS__)
94 apis.push_back( WINDOWS_DS );
96 #if defined(__MACOSX_CORE__)
97 apis.push_back( MACOSX_CORE );
99 #if defined(__RTAUDIO_DUMMY__)
100 apis.push_back( RTAUDIO_DUMMY );
104 void RtAudio :: openRtApi( RtAudio::Api api )
106 #if defined(__UNIX_JACK__)
107 if ( api == UNIX_JACK )
108 rtapi_ = new RtApiJack();
110 #if defined(__LINUX_ALSA__)
111 if ( api == LINUX_ALSA )
112 rtapi_ = new RtApiAlsa();
114 #if defined(__LINUX_OSS__)
115 if ( api == LINUX_OSS )
116 rtapi_ = new RtApiOss();
118 #if defined(__WINDOWS_ASIO__)
119 if ( api == WINDOWS_ASIO )
120 rtapi_ = new RtApiAsio();
122 #if defined(__WINDOWS_DS__)
123 if ( api == WINDOWS_DS )
124 rtapi_ = new RtApiDs();
126 #if defined(__MACOSX_CORE__)
127 if ( api == MACOSX_CORE )
128 rtapi_ = new RtApiCore();
130 #if defined(__RTAUDIO_DUMMY__)
131 if ( api == RTAUDIO_DUMMY )
132 rtapi_ = new RtApiDummy();
136 RtAudio :: RtAudio( RtAudio::Api api ) throw()
140 if ( api != UNSPECIFIED ) {
141 // Attempt to open the specified API.
143 if ( rtapi_ ) return;
145 // No compiled support for specified API value. Issue a debug
146 // warning and continue as if no API was specified.
147 std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
150 // Iterate through the compiled APIs and return as soon as we find
151 // one with at least one device or we reach the end of the list.
152 std::vector< RtAudio::Api > apis;
153 getCompiledApi( apis );
154 for ( unsigned int i=0; i<apis.size(); i++ ) {
155 openRtApi( apis[i] );
156 if ( rtapi_->getDeviceCount() ) break;
159 if ( rtapi_ ) return;
161 // It should not be possible to get here because the preprocessor
162 // definition __RTAUDIO_DUMMY__ is automatically defined if no
163 // API-specific definitions are passed to the compiler. But just in
164 // case something weird happens, we'll print out an error message.
165 std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n";
168 RtAudio :: ~RtAudio() throw()
173 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
174 RtAudio::StreamParameters *inputParameters,
175 RtAudioFormat format, unsigned int sampleRate,
176 unsigned int *bufferFrames,
177 RtAudioCallback callback, void *userData,
178 RtAudio::StreamOptions *options )
180 return rtapi_->openStream( outputParameters, inputParameters, format,
181 sampleRate, bufferFrames, callback,
185 // *************************************************** //
187 // Public RtApi definitions (see end of file for
188 // private or protected utility functions).
190 // *************************************************** //
194 stream_.state = STREAM_CLOSED;
195 stream_.mode = UNINITIALIZED;
196 stream_.apiHandle = 0;
197 stream_.userBuffer[0] = 0;
198 stream_.userBuffer[1] = 0;
199 MUTEX_INITIALIZE( &stream_.mutex );
200 showWarnings_ = true;
205 MUTEX_DESTROY( &stream_.mutex );
208 void RtApi :: openStream( RtAudio::StreamParameters *oParams,
209 RtAudio::StreamParameters *iParams,
210 RtAudioFormat format, unsigned int sampleRate,
211 unsigned int *bufferFrames,
212 RtAudioCallback callback, void *userData,
213 RtAudio::StreamOptions *options )
215 if ( stream_.state != STREAM_CLOSED ) {
216 errorText_ = "RtApi::openStream: a stream is already open!";
217 error( RtError::INVALID_USE );
220 if ( oParams && oParams->nChannels < 1 ) {
221 errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
222 error( RtError::INVALID_USE );
225 if ( iParams && iParams->nChannels < 1 ) {
226 errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
227 error( RtError::INVALID_USE );
230 if ( oParams == NULL && iParams == NULL ) {
231 errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
232 error( RtError::INVALID_USE );
235 if ( formatBytes(format) == 0 ) {
236 errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
237 error( RtError::INVALID_USE );
240 unsigned int nDevices = getDeviceCount();
241 unsigned int oChannels = 0;
243 oChannels = oParams->nChannels;
244 if ( oParams->deviceId >= nDevices ) {
245 errorText_ = "RtApi::openStream: output device parameter value is invalid.";
246 error( RtError::INVALID_USE );
250 unsigned int iChannels = 0;
252 iChannels = iParams->nChannels;
253 if ( iParams->deviceId >= nDevices ) {
254 errorText_ = "RtApi::openStream: input device parameter value is invalid.";
255 error( RtError::INVALID_USE );
262 if ( oChannels > 0 ) {
264 result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
265 sampleRate, format, bufferFrames, options );
266 if ( result == false ) error( RtError::SYSTEM_ERROR );
269 if ( iChannels > 0 ) {
271 result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
272 sampleRate, format, bufferFrames, options );
273 if ( result == false ) {
274 if ( oChannels > 0 ) closeStream();
275 error( RtError::SYSTEM_ERROR );
279 stream_.callbackInfo.callback = (void *) callback;
280 stream_.callbackInfo.userData = userData;
282 if ( options ) options->numberOfBuffers = stream_.nBuffers;
283 stream_.state = STREAM_STOPPED;
286 unsigned int RtApi :: getDefaultInputDevice( void )
288 // Should be implemented in subclasses if possible.
292 unsigned int RtApi :: getDefaultOutputDevice( void )
294 // Should be implemented in subclasses if possible.
298 void RtApi :: closeStream( void )
300 // MUST be implemented in subclasses!
304 bool RtApi :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
305 unsigned int firstChannel, unsigned int sampleRate,
306 RtAudioFormat format, unsigned int *bufferSize,
307 RtAudio::StreamOptions *options )
309 // MUST be implemented in subclasses!
313 void RtApi :: tickStreamTime( void )
315 // Subclasses that do not provide their own implementation of
316 // getStreamTime should call this function once per buffer I/O to
317 // provide basic stream time support.
319 stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
321 #if defined( HAVE_GETTIMEOFDAY )
322 gettimeofday( &stream_.lastTickTimestamp, NULL );
326 long RtApi :: getStreamLatency( void )
330 long totalLatency = 0;
331 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
332 totalLatency = stream_.latency[0];
333 if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
334 totalLatency += stream_.latency[1];
339 double RtApi :: getStreamTime( void )
343 #if defined( HAVE_GETTIMEOFDAY )
344 // Return a very accurate estimate of the stream time by
345 // adding in the elapsed time since the last tick.
349 if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )
350 return stream_.streamTime;
352 gettimeofday( &now, NULL );
353 then = stream_.lastTickTimestamp;
354 return stream_.streamTime +
355 ((now.tv_sec + 0.000001 * now.tv_usec) -
356 (then.tv_sec + 0.000001 * then.tv_usec));
358 return stream_.streamTime;
362 unsigned int RtApi :: getStreamSampleRate( void )
366 return stream_.sampleRate;
370 // *************************************************** //
372 // OS/API-specific methods.
374 // *************************************************** //
376 #if defined(__MACOSX_CORE__)
378 // The OS X CoreAudio API is designed to use a separate callback
379 // procedure for each of its audio devices. A single RtAudio duplex
380 // stream using two different devices is supported here, though it
381 // cannot be guaranteed to always behave correctly because we cannot
382 // synchronize these two callbacks.
384 // A property listener is installed for over/underrun information.
385 // However, no functionality is currently provided to allow property
386 // listeners to trigger user handlers because it is unclear what could
387 // be done if a critical stream parameter (buffer size, sample rate,
388 // device disconnect) notification arrived. The listeners entail
389 // quite a bit of extra code and most likely, a user program wouldn't
390 // be prepared for the result anyway. However, we do provide a flag
391 // to the client callback function to inform of an over/underrun.
393 // The mechanism for querying and setting system parameters was
394 // updated (and perhaps simplified) in OS-X version 10.4. However,
395 // since 10.4 support is not necessarily available to all users, I've
396 // decided not to update the respective code at this time. Perhaps
397 // this will happen when Apple makes 10.4 free for everyone. :-)
399 // A structure to hold various information related to the CoreAudio API
402 AudioDeviceID id[2]; // device ids
403 AudioDeviceIOProcID procId[2];
404 UInt32 iStream[2]; // device stream index (first for mono mode)
407 pthread_cond_t condition;
408 int drainCounter; // Tracks callback counts when draining
409 bool internalDrain; // Indicates if stop is initiated from callback or not.
412 :deviceBuffer(0), drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
415 RtApiCore :: RtApiCore()
417 // Nothing to do here.
420 RtApiCore :: ~RtApiCore()
422 // The subclass destructor gets called before the base class
423 // destructor, so close an existing stream before deallocating
424 // apiDeviceId memory.
425 if ( stream_.state != STREAM_CLOSED ) closeStream();
428 unsigned int RtApiCore :: getDeviceCount( void )
430 // Find out how many audio devices there are, if any.
432 OSStatus result = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &dataSize, NULL );
433 if ( result != noErr ) {
434 errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
435 error( RtError::WARNING );
439 return dataSize / sizeof( AudioDeviceID );
442 unsigned int RtApiCore :: getDefaultInputDevice( void )
444 unsigned int nDevices = getDeviceCount();
445 if ( nDevices <= 1 ) return 0;
448 UInt32 dataSize = sizeof( AudioDeviceID );
449 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
452 if ( result != noErr ) {
453 errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
454 error( RtError::WARNING );
458 dataSize *= nDevices;
459 AudioDeviceID deviceList[ nDevices ];
460 result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
461 if ( result != noErr ) {
462 errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
463 error( RtError::WARNING );
467 for ( unsigned int i=0; i<nDevices; i++ )
468 if ( id == deviceList[i] ) return i;
470 errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
471 error( RtError::WARNING );
475 unsigned int RtApiCore :: getDefaultOutputDevice( void )
477 unsigned int nDevices = getDeviceCount();
478 if ( nDevices <= 1 ) return 0;
481 UInt32 dataSize = sizeof( AudioDeviceID );
482 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
485 if ( result != noErr ) {
486 errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
487 error( RtError::WARNING );
491 dataSize *= nDevices;
492 AudioDeviceID deviceList[ nDevices ];
493 result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
494 if ( result != noErr ) {
495 errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
496 error( RtError::WARNING );
500 for ( unsigned int i=0; i<nDevices; i++ )
501 if ( id == deviceList[i] ) return i;
503 errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
504 error( RtError::WARNING );
508 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
510 RtAudio::DeviceInfo info;
514 unsigned int nDevices = getDeviceCount();
515 if ( nDevices == 0 ) {
516 errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
517 error( RtError::INVALID_USE );
520 if ( device >= nDevices ) {
521 errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
522 error( RtError::INVALID_USE );
525 AudioDeviceID deviceList[ nDevices ];
526 UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
527 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
528 if ( result != noErr ) {
529 errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
530 error( RtError::WARNING );
534 AudioDeviceID id = deviceList[ device ];
536 // Get the device name.
540 result = AudioDeviceGetProperty( id, 0, false,
541 kAudioDevicePropertyDeviceManufacturer,
544 if ( result != noErr ) {
545 errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
546 errorText_ = errorStream_.str();
547 error( RtError::WARNING );
550 info.name.append( (const char *)name, strlen(name) );
551 info.name.append( ": " );
554 result = AudioDeviceGetProperty( id, 0, false,
555 kAudioDevicePropertyDeviceName,
557 if ( result != noErr ) {
558 errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
559 errorText_ = errorStream_.str();
560 error( RtError::WARNING );
563 info.name.append( (const char *)name, strlen(name) );
565 // Get the output stream "configuration".
566 AudioBufferList *bufferList = nil;
567 result = AudioDeviceGetPropertyInfo( id, 0, false,
568 kAudioDevicePropertyStreamConfiguration,
570 if (result != noErr || dataSize == 0) {
571 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
572 errorText_ = errorStream_.str();
573 error( RtError::WARNING );
577 // Allocate the AudioBufferList.
578 bufferList = (AudioBufferList *) malloc( dataSize );
579 if ( bufferList == NULL ) {
580 errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
581 error( RtError::WARNING );
585 result = AudioDeviceGetProperty( id, 0, false,
586 kAudioDevicePropertyStreamConfiguration,
587 &dataSize, bufferList );
588 if ( result != noErr ) {
590 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
591 errorText_ = errorStream_.str();
592 error( RtError::WARNING );
596 // Get output channel information.
597 unsigned int i, nStreams = bufferList->mNumberBuffers;
598 for ( i=0; i<nStreams; i++ )
599 info.outputChannels += bufferList->mBuffers[i].mNumberChannels;
602 // Get the input stream "configuration".
603 result = AudioDeviceGetPropertyInfo( id, 0, true,
604 kAudioDevicePropertyStreamConfiguration,
606 if (result != noErr || dataSize == 0) {
607 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
608 errorText_ = errorStream_.str();
609 error( RtError::WARNING );
613 // Allocate the AudioBufferList.
614 bufferList = (AudioBufferList *) malloc( dataSize );
615 if ( bufferList == NULL ) {
616 errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
617 error( RtError::WARNING );
621 result = AudioDeviceGetProperty( id, 0, true,
622 kAudioDevicePropertyStreamConfiguration,
623 &dataSize, bufferList );
624 if ( result != noErr ) {
626 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
627 errorText_ = errorStream_.str();
628 error( RtError::WARNING );
632 // Get input channel information.
633 nStreams = bufferList->mNumberBuffers;
634 for ( i=0; i<nStreams; i++ )
635 info.inputChannels += bufferList->mBuffers[i].mNumberChannels;
638 // If device opens for both playback and capture, we determine the channels.
639 if ( info.outputChannels > 0 && info.inputChannels > 0 )
640 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
642 // Probe the device sample rates.
643 bool isInput = false;
644 if ( info.outputChannels == 0 ) isInput = true;
646 // Determine the supported sample rates.
647 result = AudioDeviceGetPropertyInfo( id, 0, isInput,
648 kAudioDevicePropertyAvailableNominalSampleRates,
651 if ( result != kAudioHardwareNoError || dataSize == 0 ) {
652 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
653 errorText_ = errorStream_.str();
654 error( RtError::WARNING );
658 UInt32 nRanges = dataSize / sizeof( AudioValueRange );
659 AudioValueRange rangeList[ nRanges ];
660 result = AudioDeviceGetProperty( id, 0, isInput,
661 kAudioDevicePropertyAvailableNominalSampleRates,
662 &dataSize, &rangeList );
664 if ( result != kAudioHardwareNoError ) {
665 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
666 errorText_ = errorStream_.str();
667 error( RtError::WARNING );
671 Float64 minimumRate = 100000000.0, maximumRate = 0.0;
672 for ( UInt32 i=0; i<nRanges; i++ ) {
673 if ( rangeList[i].mMinimum < minimumRate ) minimumRate = rangeList[i].mMinimum;
674 if ( rangeList[i].mMaximum > maximumRate ) maximumRate = rangeList[i].mMaximum;
677 info.sampleRates.clear();
678 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
679 if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )
680 info.sampleRates.push_back( SAMPLE_RATES[k] );
683 if ( info.sampleRates.size() == 0 ) {
684 errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
685 errorText_ = errorStream_.str();
686 error( RtError::WARNING );
690 // CoreAudio always uses 32-bit floating point data for PCM streams.
691 // Thus, any other "physical" formats supported by the device are of
692 // no interest to the client.
693 info.nativeFormats = RTAUDIO_FLOAT32;
695 if ( getDefaultOutputDevice() == device )
696 info.isDefaultOutput = true;
697 if ( getDefaultInputDevice() == device )
698 info.isDefaultInput = true;
704 OSStatus callbackHandler( AudioDeviceID inDevice,
705 const AudioTimeStamp* inNow,
706 const AudioBufferList* inInputData,
707 const AudioTimeStamp* inInputTime,
708 AudioBufferList* outOutputData,
709 const AudioTimeStamp* inOutputTime,
712 CallbackInfo *info = (CallbackInfo *) infoPointer;
714 RtApiCore *object = (RtApiCore *) info->object;
715 if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )
716 return kAudioHardwareUnspecifiedError;
718 return kAudioHardwareNoError;
721 OSStatus deviceListener( AudioDeviceID inDevice,
724 AudioDevicePropertyID propertyID,
725 void* handlePointer )
727 CoreHandle *handle = (CoreHandle *) handlePointer;
728 if ( propertyID == kAudioDeviceProcessorOverload ) {
730 handle->xrun[1] = true;
732 handle->xrun[0] = true;
735 return kAudioHardwareNoError;
738 static bool hasProperty( AudioDeviceID id, UInt32 channel, bool isInput, AudioDevicePropertyID property )
740 OSStatus result = AudioDeviceGetPropertyInfo( id, channel, isInput, property, NULL, NULL );
744 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
745 unsigned int firstChannel, unsigned int sampleRate,
746 RtAudioFormat format, unsigned int *bufferSize,
747 RtAudio::StreamOptions *options )
750 unsigned int nDevices = getDeviceCount();
751 if ( nDevices == 0 ) {
752 // This should not happen because a check is made before this function is called.
753 errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";
757 if ( device >= nDevices ) {
758 // This should not happen because a check is made before this function is called.
759 errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";
763 AudioDeviceID deviceList[ nDevices ];
764 UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
765 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
766 if ( result != noErr ) {
767 errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
771 AudioDeviceID id = deviceList[ device ];
773 // Setup for stream mode.
774 bool isInput = false;
775 if ( mode == INPUT ) isInput = true;
777 // Set or disable "hog" mode.
778 dataSize = sizeof( UInt32 );
780 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) doHog = 1;
781 result = AudioHardwareSetProperty( kAudioHardwarePropertyHogModeIsAllowed, dataSize, &doHog );
782 if ( result != noErr ) {
783 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
784 errorText_ = errorStream_.str();
788 // Get the stream "configuration".
789 AudioBufferList *bufferList;
790 result = AudioDeviceGetPropertyInfo( id, 0, isInput,
791 kAudioDevicePropertyStreamConfiguration,
793 if (result != noErr || dataSize == 0) {
794 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
795 errorText_ = errorStream_.str();
799 // Allocate the AudioBufferList.
800 bufferList = (AudioBufferList *) malloc( dataSize );
801 if ( bufferList == NULL ) {
802 errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
806 result = AudioDeviceGetProperty( id, 0, isInput,
807 kAudioDevicePropertyStreamConfiguration,
808 &dataSize, bufferList );
809 if ( result != noErr ) {
811 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
812 errorText_ = errorStream_.str();
816 // Search for a stream that contains the desired number of
817 // channels. CoreAudio devices can have an arbitrary number of
818 // streams and each stream can have an arbitrary number of channels.
819 // For each stream, a single buffer of interleaved samples is
820 // provided. RtAudio currently only supports the use of one stream
821 // of interleaved data or multiple consecutive single-channel
822 // streams. Thus, our search below is limited to these two
824 unsigned int streamChannels = 0, nStreams = 0;
825 UInt32 iChannel = 0, iStream = 0;
826 unsigned int offsetCounter = firstChannel;
827 stream_.deviceInterleaved[mode] = true;
828 nStreams = bufferList->mNumberBuffers;
829 bool foundStream = false;
831 for ( iStream=0; iStream<nStreams; iStream++ ) {
832 streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
833 if ( streamChannels >= channels + offsetCounter ) {
834 iChannel += offsetCounter;
838 if ( streamChannels > offsetCounter ) break;
839 offsetCounter -= streamChannels;
840 iChannel += streamChannels;
843 // If we didn't find a single stream above, see if we can meet
844 // the channel specification in mono mode (i.e. using separate
845 // non-interleaved buffers). This can only work if there are N
846 // consecutive one-channel streams, where N is the number of
847 // desired channels (+ channel offset).
848 if ( foundStream == false ) {
849 unsigned int counter = 0;
850 offsetCounter = firstChannel;
852 for ( iStream=0; iStream<nStreams; iStream++ ) {
853 streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
854 if ( offsetCounter ) {
855 if ( streamChannels > offsetCounter ) break;
856 offsetCounter -= streamChannels;
858 else if ( streamChannels == 1 )
862 if ( counter == channels ) {
863 iStream -= channels - 1;
864 iChannel -= channels - 1;
865 stream_.deviceInterleaved[mode] = false;
869 iChannel += streamChannels;
874 if ( foundStream == false ) {
875 errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels.";
876 errorText_ = errorStream_.str();
880 // Determine the buffer size.
881 AudioValueRange bufferRange;
882 dataSize = sizeof( AudioValueRange );
883 result = AudioDeviceGetProperty( id, 0, isInput,
884 kAudioDevicePropertyBufferFrameSizeRange,
885 &dataSize, &bufferRange );
886 if ( result != noErr ) {
887 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
888 errorText_ = errorStream_.str();
892 if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
893 else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
894 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
896 // Set the buffer size. For mono mode, I'm assuming we only need to
897 // make this setting for the master channel.
898 UInt32 theSize = (UInt32) *bufferSize;
899 dataSize = sizeof( UInt32 );
900 result = AudioDeviceSetProperty( id, NULL, 0, isInput,
901 kAudioDevicePropertyBufferFrameSize,
902 dataSize, &theSize );
904 if ( result != noErr ) {
905 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
906 errorText_ = errorStream_.str();
910 // If attempting to setup a duplex stream, the bufferSize parameter
911 // MUST be the same in both directions!
912 *bufferSize = theSize;
913 if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
914 errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";
915 errorText_ = errorStream_.str();
919 stream_.bufferSize = *bufferSize;
920 stream_.nBuffers = 1;
922 // Get the stream ID(s) so we can set the stream format. In mono
923 // mode, we'll have to do this for each stream (channel).
924 AudioStreamID streamIDs[ nStreams ];
925 dataSize = nStreams * sizeof( AudioStreamID );
926 result = AudioDeviceGetProperty( id, 0, isInput,
927 kAudioDevicePropertyStreams,
928 &dataSize, &streamIDs );
929 if ( result != noErr ) {
930 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ").";
931 errorText_ = errorStream_.str();
935 // Now set the stream format. Also, check the physical format of the
936 // device and change that if necessary.
937 AudioStreamBasicDescription description;
938 dataSize = sizeof( AudioStreamBasicDescription );
939 if ( stream_.deviceInterleaved[mode] ) nStreams = 1;
940 else nStreams = channels;
943 for ( unsigned int i=0; i<nStreams; i++ ) {
945 result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
946 kAudioStreamPropertyVirtualFormat,
947 &dataSize, &description );
949 if ( result != noErr ) {
950 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
951 errorText_ = errorStream_.str();
955 // Set the sample rate and data format id. However, only make the
956 // change if the sample rate is not within 1.0 of the desired
957 // rate and the format is not linear pcm.
958 updateFormat = false;
959 if ( fabs( description.mSampleRate - (double)sampleRate ) > 1.0 ) {
960 description.mSampleRate = (double) sampleRate;
964 if ( description.mFormatID != kAudioFormatLinearPCM ) {
965 description.mFormatID = kAudioFormatLinearPCM;
969 if ( updateFormat ) {
970 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0,
971 kAudioStreamPropertyVirtualFormat,
972 dataSize, &description );
973 if ( result != noErr ) {
974 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
975 errorText_ = errorStream_.str();
980 // Now check the physical format.
981 result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
982 kAudioStreamPropertyPhysicalFormat,
983 &dataSize, &description );
984 if ( result != noErr ) {
985 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
986 errorText_ = errorStream_.str();
990 if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 24 ) {
991 description.mFormatID = kAudioFormatLinearPCM;
992 AudioStreamBasicDescription testDescription = description;
993 unsigned long formatFlags;
995 // We'll try higher bit rates first and then work our way down.
996 testDescription.mBitsPerChannel = 32;
997 formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;
998 testDescription.mFormatFlags = formatFlags;
999 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1000 if ( result == noErr ) continue;
1002 testDescription = description;
1003 testDescription.mBitsPerChannel = 32;
1004 formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat;
1005 testDescription.mFormatFlags = formatFlags;
1006 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1007 if ( result == noErr ) continue;
1009 testDescription = description;
1010 testDescription.mBitsPerChannel = 24;
1011 testDescription.mFormatFlags = formatFlags;
1012 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1013 if ( result == noErr ) continue;
1015 testDescription = description;
1016 testDescription.mBitsPerChannel = 16;
1017 testDescription.mFormatFlags = formatFlags;
1018 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1019 if ( result == noErr ) continue;
1021 testDescription = description;
1022 testDescription.mBitsPerChannel = 8;
1023 testDescription.mFormatFlags = formatFlags;
1024 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1025 if ( result != noErr ) {
1026 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
1027 errorText_ = errorStream_.str();
1033 // Get the stream latency. There can be latency in both the device
1034 // and the stream. First, attempt to get the device latency on the
1035 // master channel or the first open channel. Errors that might
1036 // occur here are not deemed critical.
1037 UInt32 latency, channel = 0;
1038 dataSize = sizeof( UInt32 );
1039 AudioDevicePropertyID property = kAudioDevicePropertyLatency;
1040 for ( int i=0; i<2; i++ ) {
1041 if ( hasProperty( id, channel, isInput, property ) == true ) break;
1042 channel = iChannel + 1 + i;
1044 if ( channel <= iChannel + 1 ) {
1045 result = AudioDeviceGetProperty( id, channel, isInput, property, &dataSize, &latency );
1046 if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
1048 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
1049 errorText_ = errorStream_.str();
1050 error( RtError::WARNING );
1054 // Now try to get the stream latency. For "mono" mode, I assume the
1055 // latency is equal for all single-channel streams.
1056 result = AudioStreamGetProperty( streamIDs[iStream], 0, property, &dataSize, &latency );
1057 if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency;
1059 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";
1060 errorText_ = errorStream_.str();
1061 error( RtError::WARNING );
1064 // Byte-swapping: According to AudioHardware.h, the stream data will
1065 // always be presented in native-endian format, so we should never
1066 // need to byte swap.
1067 stream_.doByteSwap[mode] = false;
1069 // From the CoreAudio documentation, PCM data must be supplied as
1071 stream_.userFormat = format;
1072 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
1074 if ( stream_.deviceInterleaved[mode] )
1075 stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
1077 stream_.nDeviceChannels[mode] = channels;
1078 stream_.nUserChannels[mode] = channels;
1079 stream_.channelOffset[mode] = iChannel; // offset within a CoreAudio stream
1080 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
1081 else stream_.userInterleaved = true;
1083 // Set flags for buffer conversion.
1084 stream_.doConvertBuffer[mode] = false;
1085 if ( stream_.userFormat != stream_.deviceFormat[mode] )
1086 stream_.doConvertBuffer[mode] = true;
1087 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
1088 stream_.doConvertBuffer[mode] = true;
1089 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
1090 stream_.nUserChannels[mode] > 1 )
1091 stream_.doConvertBuffer[mode] = true;
1093 // Allocate our CoreHandle structure for the stream.
1094 CoreHandle *handle = 0;
1095 if ( stream_.apiHandle == 0 ) {
1097 handle = new CoreHandle;
1099 catch ( std::bad_alloc& ) {
1100 errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
1104 if ( pthread_cond_init( &handle->condition, NULL ) ) {
1105 errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
1108 stream_.apiHandle = (void *) handle;
1111 handle = (CoreHandle *) stream_.apiHandle;
1112 handle->iStream[mode] = iStream;
1113 handle->id[mode] = id;
1115 // Allocate necessary internal buffers.
1116 unsigned long bufferBytes;
1117 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
1118 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
1119 if ( stream_.userBuffer[mode] == NULL ) {
1120 errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
1124 // If possible, we will make use of the CoreAudio stream buffers as
1125 // "device buffers". However, we can't do this if the device
1126 // buffers are non-interleaved ("mono" mode).
1127 if ( !stream_.deviceInterleaved[mode] && stream_.doConvertBuffer[mode] ) {
1129 bool makeBuffer = true;
1130 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
1131 if ( mode == INPUT ) {
1132 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
1133 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
1134 if ( bufferBytes <= bytesOut ) makeBuffer = false;
1139 bufferBytes *= *bufferSize;
1140 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
1141 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
1142 if ( stream_.deviceBuffer == NULL ) {
1143 errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
1147 // Save a pointer to our own device buffer in the CoreHandle
1148 // structure because we may need to use the stream_.deviceBuffer
1149 // variable to point to the CoreAudio buffer before buffer
1150 // conversion (if we have a duplex stream with two different
1151 // conversion schemes).
1152 handle->deviceBuffer = stream_.deviceBuffer;
1156 stream_.sampleRate = sampleRate;
1157 stream_.device[mode] = device;
1158 stream_.state = STREAM_STOPPED;
1159 stream_.callbackInfo.object = (void *) this;
1161 // Setup the buffer conversion information structure. We override
1162 // the channel offset value and perform our own setting for that
1164 if ( stream_.doConvertBuffer[mode] ) {
1165 setConvertInfo( mode, 0 );
1167 // Add channel offset for interleaved channels.
1168 if ( firstChannel > 0 && stream_.deviceInterleaved[mode] ) {
1169 if ( mode == OUTPUT ) {
1170 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
1171 stream_.convertInfo[mode].outOffset[k] += firstChannel;
1174 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
1175 stream_.convertInfo[mode].inOffset[k] += firstChannel;
1180 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
1181 // Only one callback procedure per device.
1182 stream_.mode = DUPLEX;
1184 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
1185 result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );
1187 // deprecated in favor of AudioDeviceCreateIOProcID()
1188 result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
1190 if ( result != noErr ) {
1191 errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
1192 errorText_ = errorStream_.str();
1195 if ( stream_.mode == OUTPUT && mode == INPUT )
1196 stream_.mode = DUPLEX;
1198 stream_.mode = mode;
1201 // Setup the device property listener for over/underload.
1202 result = AudioDeviceAddPropertyListener( id, 0, isInput,
1203 kAudioDeviceProcessorOverload,
1204 deviceListener, (void *) handle );
1210 pthread_cond_destroy( &handle->condition );
1212 stream_.apiHandle = 0;
1215 for ( int i=0; i<2; i++ ) {
1216 if ( stream_.userBuffer[i] ) {
1217 free( stream_.userBuffer[i] );
1218 stream_.userBuffer[i] = 0;
1222 if ( stream_.deviceBuffer ) {
1223 free( stream_.deviceBuffer );
1224 stream_.deviceBuffer = 0;
1230 void RtApiCore :: closeStream( void )
1232 if ( stream_.state == STREAM_CLOSED ) {
1233 errorText_ = "RtApiCore::closeStream(): no open stream to close!";
1234 error( RtError::WARNING );
1238 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1239 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1240 if ( stream_.state == STREAM_RUNNING )
1241 AudioDeviceStop( handle->id[0], callbackHandler );
1242 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
1243 AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
1245 // deprecated in favor of AudioDeviceDestroyIOProcID()
1246 AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
1250 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1251 if ( stream_.state == STREAM_RUNNING )
1252 AudioDeviceStop( handle->id[1], callbackHandler );
1253 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
1254 AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
1256 // deprecated in favor of AudioDeviceDestroyIOProcID()
1257 AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
1261 for ( int i=0; i<2; i++ ) {
1262 if ( stream_.userBuffer[i] ) {
1263 free( stream_.userBuffer[i] );
1264 stream_.userBuffer[i] = 0;
1268 if ( handle->deviceBuffer ) {
1269 free( handle->deviceBuffer );
1270 stream_.deviceBuffer = 0;
1273 // Destroy pthread condition variable.
1274 pthread_cond_destroy( &handle->condition );
1276 stream_.apiHandle = 0;
1278 stream_.mode = UNINITIALIZED;
1279 stream_.state = STREAM_CLOSED;
1282 void RtApiCore :: startStream( void )
1285 if ( stream_.state == STREAM_RUNNING ) {
1286 errorText_ = "RtApiCore::startStream(): the stream is already running!";
1287 error( RtError::WARNING );
1291 MUTEX_LOCK( &stream_.mutex );
1293 OSStatus result = noErr;
1294 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1295 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1297 result = AudioDeviceStart( handle->id[0], callbackHandler );
1298 if ( result != noErr ) {
1299 errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
1300 errorText_ = errorStream_.str();
1305 if ( stream_.mode == INPUT ||
1306 ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1308 result = AudioDeviceStart( handle->id[1], callbackHandler );
1309 if ( result != noErr ) {
1310 errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
1311 errorText_ = errorStream_.str();
1316 handle->drainCounter = 0;
1317 handle->internalDrain = false;
1318 stream_.state = STREAM_RUNNING;
1321 MUTEX_UNLOCK( &stream_.mutex );
1323 if ( result == noErr ) return;
1324 error( RtError::SYSTEM_ERROR );
1327 void RtApiCore :: stopStream( void )
1330 if ( stream_.state == STREAM_STOPPED ) {
1331 errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
1332 error( RtError::WARNING );
1336 MUTEX_LOCK( &stream_.mutex );
1338 OSStatus result = noErr;
1339 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1340 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1342 if ( handle->drainCounter == 0 ) {
1343 handle->drainCounter = 1;
1344 pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
1347 result = AudioDeviceStop( handle->id[0], callbackHandler );
1348 if ( result != noErr ) {
1349 errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
1350 errorText_ = errorStream_.str();
1355 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1357 result = AudioDeviceStop( handle->id[1], callbackHandler );
1358 if ( result != noErr ) {
1359 errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
1360 errorText_ = errorStream_.str();
1366 MUTEX_UNLOCK( &stream_.mutex );
1368 stream_.state = STREAM_STOPPED;
1369 if ( result == noErr ) return;
1370 error( RtError::SYSTEM_ERROR );
1373 void RtApiCore :: abortStream( void )
1376 if ( stream_.state == STREAM_STOPPED ) {
1377 errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
1378 error( RtError::WARNING );
1382 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1383 handle->drainCounter = 1;
1388 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
1389 const AudioBufferList *inBufferList,
1390 const AudioBufferList *outBufferList )
1392 if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
1393 if ( stream_.state == STREAM_CLOSED ) {
1394 errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
1395 error( RtError::WARNING );
1399 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
1400 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1402 // Check if we were draining the stream and signal is finished.
1403 if ( handle->drainCounter > 3 ) {
1404 if ( handle->internalDrain == false )
1405 pthread_cond_signal( &handle->condition );
1411 MUTEX_LOCK( &stream_.mutex );
1413 AudioDeviceID outputDevice = handle->id[0];
1415 // Invoke user callback to get fresh output data UNLESS we are
1416 // draining stream or duplex mode AND the input/output devices are
1417 // different AND this function is called for the input device.
1418 if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {
1419 RtAudioCallback callback = (RtAudioCallback) info->callback;
1420 double streamTime = getStreamTime();
1421 RtAudioStreamStatus status = 0;
1422 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
1423 status |= RTAUDIO_OUTPUT_UNDERFLOW;
1424 handle->xrun[0] = false;
1426 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
1427 status |= RTAUDIO_INPUT_OVERFLOW;
1428 handle->xrun[1] = false;
1430 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
1431 stream_.bufferSize, streamTime, status, info->userData );
1432 if ( handle->drainCounter == 2 ) {
1433 MUTEX_UNLOCK( &stream_.mutex );
1437 else if ( handle->drainCounter == 1 )
1438 handle->internalDrain = true;
1441 if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
1443 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
1445 if ( stream_.deviceInterleaved[0] ) {
1446 memset( outBufferList->mBuffers[handle->iStream[0]].mData,
1448 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
1451 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
1452 memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1454 outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
1458 else if ( stream_.doConvertBuffer[0] ) {
1460 if ( stream_.deviceInterleaved[0] )
1461 stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData;
1463 stream_.deviceBuffer = handle->deviceBuffer;
1465 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
1467 if ( !stream_.deviceInterleaved[0] ) {
1468 UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
1469 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
1470 memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1471 &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
1477 if ( stream_.deviceInterleaved[0] ) {
1478 memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
1479 stream_.userBuffer[0],
1480 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
1483 UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
1484 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
1485 memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1486 &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
1491 if ( handle->drainCounter ) {
1492 handle->drainCounter++;
1497 AudioDeviceID inputDevice;
1498 inputDevice = handle->id[1];
1499 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
1501 if ( stream_.doConvertBuffer[1] ) {
1503 if ( stream_.deviceInterleaved[1] )
1504 stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData;
1506 stream_.deviceBuffer = (char *) handle->deviceBuffer;
1507 UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
1508 for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
1509 memcpy( &stream_.deviceBuffer[i*bufferBytes],
1510 inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
1514 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
1518 memcpy( stream_.userBuffer[1],
1519 inBufferList->mBuffers[handle->iStream[1]].mData,
1520 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
1525 MUTEX_UNLOCK( &stream_.mutex );
1527 RtApi::tickStreamTime();
1531 const char* RtApiCore :: getErrorCode( OSStatus code )
1535 case kAudioHardwareNotRunningError:
1536 return "kAudioHardwareNotRunningError";
1538 case kAudioHardwareUnspecifiedError:
1539 return "kAudioHardwareUnspecifiedError";
1541 case kAudioHardwareUnknownPropertyError:
1542 return "kAudioHardwareUnknownPropertyError";
1544 case kAudioHardwareBadPropertySizeError:
1545 return "kAudioHardwareBadPropertySizeError";
1547 case kAudioHardwareIllegalOperationError:
1548 return "kAudioHardwareIllegalOperationError";
1550 case kAudioHardwareBadObjectError:
1551 return "kAudioHardwareBadObjectError";
1553 case kAudioHardwareBadDeviceError:
1554 return "kAudioHardwareBadDeviceError";
1556 case kAudioHardwareBadStreamError:
1557 return "kAudioHardwareBadStreamError";
1559 case kAudioHardwareUnsupportedOperationError:
1560 return "kAudioHardwareUnsupportedOperationError";
1562 case kAudioDeviceUnsupportedFormatError:
1563 return "kAudioDeviceUnsupportedFormatError";
1565 case kAudioDevicePermissionsError:
1566 return "kAudioDevicePermissionsError";
1569 return "CoreAudio unknown error";
1573 //******************** End of __MACOSX_CORE__ *********************//
1576 #if defined(__UNIX_JACK__)
1578 // JACK is a low-latency audio server, originally written for the
1579 // GNU/Linux operating system and now also ported to OS-X. It can
1580 // connect a number of different applications to an audio device, as
1581 // well as allowing them to share audio between themselves.
1583 // When using JACK with RtAudio, "devices" refer to JACK clients that
1584 // have ports connected to the server. The JACK server is typically
1585 // started in a terminal as follows:
1587 // .jackd -d alsa -d hw:0
1589 // or through an interface program such as qjackctl. Many of the
1590 // parameters normally set for a stream are fixed by the JACK server
1591 // and can be specified when the JACK server is started. In
1594 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
1596 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample
1597 // frames, and number of buffers = 4. Once the server is running, it
1598 // is not possible to override these values. If the values are not
1599 // specified in the command-line, the JACK server uses default values.
1601 // The JACK server does not have to be running when an instance of
1602 // RtApiJack is created, though the function getDeviceCount() will
1603 // report 0 devices found until JACK has been started. When no
1604 // devices are available (i.e., the JACK server is not running), a
1605 // stream cannot be opened.
1607 #include <jack/jack.h>
1610 // A structure to hold various information related to the Jack API
1613 jack_client_t *client;
1614 jack_port_t **ports[2];
1615 std::string deviceName[2];
1617 pthread_cond_t condition;
1618 int drainCounter; // Tracks callback counts when draining
1619 bool internalDrain; // Indicates if stop is initiated from callback or not.
1622 :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
1625 RtApiJack :: RtApiJack()
1627 // Nothing to do here.
1630 RtApiJack :: ~RtApiJack()
1632 if ( stream_.state != STREAM_CLOSED ) closeStream();
1635 unsigned int RtApiJack :: getDeviceCount( void )
1637 // See if we can become a jack client.
1638 jack_options_t options = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption;
1639 jack_status_t *status = NULL;
1640 jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );
1641 if ( client == 0 ) return 0;
1644 std::string port, previousPort;
1645 unsigned int nChannels = 0, nDevices = 0;
1646 ports = jack_get_ports( client, NULL, NULL, 0 );
1648 // Parse the port names up to the first colon (:).
1651 port = (char *) ports[ nChannels ];
1652 iColon = port.find(":");
1653 if ( iColon != std::string::npos ) {
1654 port = port.substr( 0, iColon + 1 );
1655 if ( port != previousPort ) {
1657 previousPort = port;
1660 } while ( ports[++nChannels] );
1664 jack_client_close( client );
1668 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
1670 RtAudio::DeviceInfo info;
1671 info.probed = false;
1673 jack_options_t options = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption
1674 jack_status_t *status = NULL;
1675 jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
1676 if ( client == 0 ) {
1677 errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
1678 error( RtError::WARNING );
1683 std::string port, previousPort;
1684 unsigned int nPorts = 0, nDevices = 0;
1685 ports = jack_get_ports( client, NULL, NULL, 0 );
1687 // Parse the port names up to the first colon (:).
1690 port = (char *) ports[ nPorts ];
1691 iColon = port.find(":");
1692 if ( iColon != std::string::npos ) {
1693 port = port.substr( 0, iColon );
1694 if ( port != previousPort ) {
1695 if ( nDevices == device ) info.name = port;
1697 previousPort = port;
1700 } while ( ports[++nPorts] );
1704 if ( device >= nDevices ) {
1705 errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
1706 error( RtError::INVALID_USE );
1709 // Get the current jack server sample rate.
1710 info.sampleRates.clear();
1711 info.sampleRates.push_back( jack_get_sample_rate( client ) );
1713 // Count the available ports containing the client name as device
1714 // channels. Jack "input ports" equal RtAudio output channels.
1715 unsigned int nChannels = 0;
1716 ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );
1718 while ( ports[ nChannels ] ) nChannels++;
1720 info.outputChannels = nChannels;
1723 // Jack "output ports" equal RtAudio input channels.
1725 ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );
1727 while ( ports[ nChannels ] ) nChannels++;
1729 info.inputChannels = nChannels;
1732 if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
1733 jack_client_close(client);
1734 errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
1735 error( RtError::WARNING );
1739 // If device opens for both playback and capture, we determine the channels.
1740 if ( info.outputChannels > 0 && info.inputChannels > 0 )
1741 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
1743 // Jack always uses 32-bit floats.
1744 info.nativeFormats = RTAUDIO_FLOAT32;
1746 // Jack doesn't provide default devices so we'll use the first available one.
1747 if ( device == 0 && info.outputChannels > 0 )
1748 info.isDefaultOutput = true;
1749 if ( device == 0 && info.inputChannels > 0 )
1750 info.isDefaultInput = true;
1752 jack_client_close(client);
1757 int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
1759 CallbackInfo *info = (CallbackInfo *) infoPointer;
1761 RtApiJack *object = (RtApiJack *) info->object;
1762 if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
1767 void jackShutdown( void *infoPointer )
1769 CallbackInfo *info = (CallbackInfo *) infoPointer;
1770 RtApiJack *object = (RtApiJack *) info->object;
1772 // Check current stream state. If stopped, then we'll assume this
1773 // was called as a result of a call to RtApiJack::stopStream (the
1774 // deactivation of a client handle causes this function to be called).
1775 // If not, we'll assume the Jack server is shutting down or some
1776 // other problem occurred and we should close the stream.
1777 if ( object->isStreamRunning() == false ) return;
1779 object->closeStream();
1780 std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
1783 int jackXrun( void *infoPointer )
1785 JackHandle *handle = (JackHandle *) infoPointer;
1787 if ( handle->ports[0] ) handle->xrun[0] = true;
1788 if ( handle->ports[1] ) handle->xrun[1] = true;
1793 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
1794 unsigned int firstChannel, unsigned int sampleRate,
1795 RtAudioFormat format, unsigned int *bufferSize,
1796 RtAudio::StreamOptions *options )
1798 JackHandle *handle = (JackHandle *) stream_.apiHandle;
1800 // Look for jack server and try to become a client (only do once per stream).
1801 jack_client_t *client = 0;
1802 if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
1803 jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption;
1804 jack_status_t *status = NULL;
1805 if ( options && !options->streamName.empty() )
1806 client = jack_client_open( options->streamName.c_str(), jackoptions, status );
1808 client = jack_client_open( "RtApiJack", jackoptions, status );
1809 if ( client == 0 ) {
1810 errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
1811 error( RtError::WARNING );
1816 // The handle must have been created on an earlier pass.
1817 client = handle->client;
1821 std::string port, previousPort, deviceName;
1822 unsigned int nPorts = 0, nDevices = 0;
1823 ports = jack_get_ports( client, NULL, NULL, 0 );
1825 // Parse the port names up to the first colon (:).
1828 port = (char *) ports[ nPorts ];
1829 iColon = port.find(":");
1830 if ( iColon != std::string::npos ) {
1831 port = port.substr( 0, iColon );
1832 if ( port != previousPort ) {
1833 if ( nDevices == device ) deviceName = port;
1835 previousPort = port;
1838 } while ( ports[++nPorts] );
1842 if ( device >= nDevices ) {
1843 errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
1847 // Count the available ports containing the client name as device
1848 // channels. Jack "input ports" equal RtAudio output channels.
1849 unsigned int nChannels = 0;
1850 unsigned long flag = JackPortIsInput;
1851 if ( mode == INPUT ) flag = JackPortIsOutput;
1852 ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
1854 while ( ports[ nChannels ] ) nChannels++;
1858 // Compare the jack ports for specified client to the requested number of channels.
1859 if ( nChannels < (channels + firstChannel) ) {
1860 errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
1861 errorText_ = errorStream_.str();
1865 // Check the jack server sample rate.
1866 unsigned int jackRate = jack_get_sample_rate( client );
1867 if ( sampleRate != jackRate ) {
1868 jack_client_close( client );
1869 errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
1870 errorText_ = errorStream_.str();
1873 stream_.sampleRate = jackRate;
1875 // Get the latency of the JACK port.
1876 ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
1877 if ( ports[ firstChannel ] )
1878 stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
1881 // The jack server always uses 32-bit floating-point data.
1882 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
1883 stream_.userFormat = format;
1885 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
1886 else stream_.userInterleaved = true;
1888 // Jack always uses non-interleaved buffers.
1889 stream_.deviceInterleaved[mode] = false;
1891 // Jack always provides host byte-ordered data.
1892 stream_.doByteSwap[mode] = false;
1894 // Get the buffer size. The buffer size and number of buffers
1895 // (periods) is set when the jack server is started.
1896 stream_.bufferSize = (int) jack_get_buffer_size( client );
1897 *bufferSize = stream_.bufferSize;
1899 stream_.nDeviceChannels[mode] = channels;
1900 stream_.nUserChannels[mode] = channels;
1902 // Set flags for buffer conversion.
1903 stream_.doConvertBuffer[mode] = false;
1904 if ( stream_.userFormat != stream_.deviceFormat[mode] )
1905 stream_.doConvertBuffer[mode] = true;
1906 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
1907 stream_.nUserChannels[mode] > 1 )
1908 stream_.doConvertBuffer[mode] = true;
1910 // Allocate our JackHandle structure for the stream.
1911 if ( handle == 0 ) {
1913 handle = new JackHandle;
1915 catch ( std::bad_alloc& ) {
1916 errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";
1920 if ( pthread_cond_init(&handle->condition, NULL) ) {
1921 errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
1924 stream_.apiHandle = (void *) handle;
1925 handle->client = client;
1927 handle->deviceName[mode] = deviceName;
1929 // Allocate necessary internal buffers.
1930 unsigned long bufferBytes;
1931 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
1932 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
1933 if ( stream_.userBuffer[mode] == NULL ) {
1934 errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";
1938 if ( stream_.doConvertBuffer[mode] ) {
1940 bool makeBuffer = true;
1941 if ( mode == OUTPUT )
1942 bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
1943 else { // mode == INPUT
1944 bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );
1945 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
1946 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
1947 if ( bufferBytes < bytesOut ) makeBuffer = false;
1952 bufferBytes *= *bufferSize;
1953 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
1954 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
1955 if ( stream_.deviceBuffer == NULL ) {
1956 errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";
1962 // Allocate memory for the Jack ports (channels) identifiers.
1963 handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
1964 if ( handle->ports[mode] == NULL ) {
1965 errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
1969 stream_.device[mode] = device;
1970 stream_.channelOffset[mode] = firstChannel;
1971 stream_.state = STREAM_STOPPED;
1972 stream_.callbackInfo.object = (void *) this;
1974 if ( stream_.mode == OUTPUT && mode == INPUT )
1975 // We had already set up the stream for output.
1976 stream_.mode = DUPLEX;
1978 stream_.mode = mode;
1979 jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
1980 jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );
1981 jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
1984 // Register our ports.
1986 if ( mode == OUTPUT ) {
1987 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
1988 snprintf( label, 64, "outport %d", i );
1989 handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
1990 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
1994 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
1995 snprintf( label, 64, "inport %d", i );
1996 handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
1997 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
2001 // Setup the buffer conversion information structure. We don't use
2002 // buffers to do channel offsets, so we override that parameter
2004 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
2010 pthread_cond_destroy( &handle->condition );
2011 jack_client_close( handle->client );
2013 if ( handle->ports[0] ) free( handle->ports[0] );
2014 if ( handle->ports[1] ) free( handle->ports[1] );
2017 stream_.apiHandle = 0;
2020 for ( int i=0; i<2; i++ ) {
2021 if ( stream_.userBuffer[i] ) {
2022 free( stream_.userBuffer[i] );
2023 stream_.userBuffer[i] = 0;
2027 if ( stream_.deviceBuffer ) {
2028 free( stream_.deviceBuffer );
2029 stream_.deviceBuffer = 0;
2035 void RtApiJack :: closeStream( void )
2037 if ( stream_.state == STREAM_CLOSED ) {
2038 errorText_ = "RtApiJack::closeStream(): no open stream to close!";
2039 error( RtError::WARNING );
2043 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2046 if ( stream_.state == STREAM_RUNNING )
2047 jack_deactivate( handle->client );
2049 jack_client_close( handle->client );
2053 if ( handle->ports[0] ) free( handle->ports[0] );
2054 if ( handle->ports[1] ) free( handle->ports[1] );
2055 pthread_cond_destroy( &handle->condition );
2057 stream_.apiHandle = 0;
2060 for ( int i=0; i<2; i++ ) {
2061 if ( stream_.userBuffer[i] ) {
2062 free( stream_.userBuffer[i] );
2063 stream_.userBuffer[i] = 0;
2067 if ( stream_.deviceBuffer ) {
2068 free( stream_.deviceBuffer );
2069 stream_.deviceBuffer = 0;
2072 stream_.mode = UNINITIALIZED;
2073 stream_.state = STREAM_CLOSED;
2076 void RtApiJack :: startStream( void )
2079 if ( stream_.state == STREAM_RUNNING ) {
2080 errorText_ = "RtApiJack::startStream(): the stream is already running!";
2081 error( RtError::WARNING );
2085 MUTEX_LOCK(&stream_.mutex);
2087 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2088 int result = jack_activate( handle->client );
2090 errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
2096 // Get the list of available ports.
2097 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2099 ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);
2100 if ( ports == NULL) {
2101 errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
2105 // Now make the port connections. Since RtAudio wasn't designed to
2106 // allow the user to select particular channels of a device, we'll
2107 // just open the first "nChannels" ports with offset.
2108 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2110 if ( ports[ stream_.channelOffset[0] + i ] )
2111 result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
2114 errorText_ = "RtApiJack::startStream(): error connecting output ports!";
2121 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
2123 ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );
2124 if ( ports == NULL) {
2125 errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
2129 // Now make the port connections. See note above.
2130 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2132 if ( ports[ stream_.channelOffset[1] + i ] )
2133 result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
2136 errorText_ = "RtApiJack::startStream(): error connecting input ports!";
2143 handle->drainCounter = 0;
2144 handle->internalDrain = false;
2145 stream_.state = STREAM_RUNNING;
2148 MUTEX_UNLOCK(&stream_.mutex);
2150 if ( result == 0 ) return;
2151 error( RtError::SYSTEM_ERROR );
2154 void RtApiJack :: stopStream( void )
2157 if ( stream_.state == STREAM_STOPPED ) {
2158 errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
2159 error( RtError::WARNING );
2163 MUTEX_LOCK( &stream_.mutex );
2165 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2166 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2168 if ( handle->drainCounter == 0 ) {
2169 handle->drainCounter = 1;
2170 pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
2174 jack_deactivate( handle->client );
2175 stream_.state = STREAM_STOPPED;
2177 MUTEX_UNLOCK( &stream_.mutex );
2180 void RtApiJack :: abortStream( void )
2183 if ( stream_.state == STREAM_STOPPED ) {
2184 errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
2185 error( RtError::WARNING );
2189 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2190 handle->drainCounter = 1;
2195 bool RtApiJack :: callbackEvent( unsigned long nframes )
2197 if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
2198 if ( stream_.state == STREAM_CLOSED ) {
2199 errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
2200 error( RtError::WARNING );
2203 if ( stream_.bufferSize != nframes ) {
2204 errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
2205 error( RtError::WARNING );
2209 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
2210 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2212 // Check if we were draining the stream and signal is finished.
2213 if ( handle->drainCounter > 3 ) {
2214 if ( handle->internalDrain == false )
2215 pthread_cond_signal( &handle->condition );
2221 MUTEX_LOCK( &stream_.mutex );
2223 // Invoke user callback first, to get fresh output data.
2224 if ( handle->drainCounter == 0 ) {
2225 RtAudioCallback callback = (RtAudioCallback) info->callback;
2226 double streamTime = getStreamTime();
2227 RtAudioStreamStatus status = 0;
2228 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
2229 status |= RTAUDIO_OUTPUT_UNDERFLOW;
2230 handle->xrun[0] = false;
2232 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
2233 status |= RTAUDIO_INPUT_OVERFLOW;
2234 handle->xrun[1] = false;
2236 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
2237 stream_.bufferSize, streamTime, status, info->userData );
2238 if ( handle->drainCounter == 2 ) {
2239 MUTEX_UNLOCK( &stream_.mutex );
2243 else if ( handle->drainCounter == 1 )
2244 handle->internalDrain = true;
2247 jack_default_audio_sample_t *jackbuffer;
2248 unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
2249 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2251 if ( handle->drainCounter > 0 ) { // write zeros to the output stream
2253 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2254 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2255 memset( jackbuffer, 0, bufferBytes );
2259 else if ( stream_.doConvertBuffer[0] ) {
2261 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
2263 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2264 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2265 memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
2268 else { // no buffer conversion
2269 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2270 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2271 memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
2275 if ( handle->drainCounter ) {
2276 handle->drainCounter++;
2281 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
2283 if ( stream_.doConvertBuffer[1] ) {
2284 for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
2285 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2286 memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
2288 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
2290 else { // no buffer conversion
2291 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2292 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2293 memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
2299 MUTEX_UNLOCK(&stream_.mutex);
2301 RtApi::tickStreamTime();
2304 //******************** End of __UNIX_JACK__ *********************//
2307 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows
2309 // The ASIO API is designed around a callback scheme, so this
2310 // implementation is similar to that used for OS-X CoreAudio and Linux
2311 // Jack. The primary constraint with ASIO is that it only allows
2312 // access to a single driver at a time. Thus, it is not possible to
2313 // have more than one simultaneous RtAudio stream.
2315 // This implementation also requires a number of external ASIO files
2316 // and a few global variables. The ASIO callback scheme does not
2317 // allow for the passing of user data, so we must create a global
2318 // pointer to our callbackInfo structure.
2320 // On unix systems, we make use of a pthread condition variable.
2321 // Since there is no equivalent in Windows, I hacked something based
2322 // on information found in
2323 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
2325 #include "asiosys.h"
2327 #include "iasiothiscallresolver.h"
2328 #include "asiodrivers.h"
2331 AsioDrivers drivers;
2332 ASIOCallbacks asioCallbacks;
2333 ASIODriverInfo driverInfo;
2334 CallbackInfo *asioCallbackInfo;
2338 int drainCounter; // Tracks callback counts when draining
2339 bool internalDrain; // Indicates if stop is initiated from callback or not.
2340 ASIOBufferInfo *bufferInfos;
2344 :drainCounter(0), internalDrain(false), bufferInfos(0) {}
2347 // Function declarations (definitions at end of section)
2348 static const char* getAsioErrorString( ASIOError result );
2349 void sampleRateChanged( ASIOSampleRate sRate );
2350 long asioMessages( long selector, long value, void* message, double* opt );
2352 RtApiAsio :: RtApiAsio()
2354 // ASIO cannot run on a multi-threaded appartment. You can call
2355 // CoInitialize beforehand, but it must be for appartment threading
2356 // (in which case, CoInitilialize will return S_FALSE here).
2357 coInitialized_ = false;
2358 HRESULT hr = CoInitialize( NULL );
2360 errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";
2361 error( RtError::WARNING );
2363 coInitialized_ = true;
2365 drivers.removeCurrentDriver();
2366 driverInfo.asioVersion = 2;
2368 // See note in DirectSound implementation about GetDesktopWindow().
2369 driverInfo.sysRef = GetForegroundWindow();
2372 RtApiAsio :: ~RtApiAsio()
2374 if ( stream_.state != STREAM_CLOSED ) closeStream();
2375 if ( coInitialized_ ) CoUninitialize();
2378 unsigned int RtApiAsio :: getDeviceCount( void )
2380 return (unsigned int) drivers.asioGetNumDev();
2383 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
2385 RtAudio::DeviceInfo info;
2386 info.probed = false;
2389 unsigned int nDevices = getDeviceCount();
2390 if ( nDevices == 0 ) {
2391 errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
2392 error( RtError::INVALID_USE );
2395 if ( device >= nDevices ) {
2396 errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
2397 error( RtError::INVALID_USE );
2400 // If a stream is already open, we cannot probe other devices. Thus, use the saved results.
2401 if ( stream_.state != STREAM_CLOSED ) {
2402 if ( device >= devices_.size() ) {
2403 errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";
2404 error( RtError::WARNING );
2407 return devices_[ device ];
2410 char driverName[32];
2411 ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
2412 if ( result != ASE_OK ) {
2413 errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";
2414 errorText_ = errorStream_.str();
2415 error( RtError::WARNING );
2419 info.name = driverName;
2421 if ( !drivers.loadDriver( driverName ) ) {
2422 errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
2423 errorText_ = errorStream_.str();
2424 error( RtError::WARNING );
2428 result = ASIOInit( &driverInfo );
2429 if ( result != ASE_OK ) {
2430 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
2431 errorText_ = errorStream_.str();
2432 error( RtError::WARNING );
2436 // Determine the device channel information.
2437 long inputChannels, outputChannels;
2438 result = ASIOGetChannels( &inputChannels, &outputChannels );
2439 if ( result != ASE_OK ) {
2440 drivers.removeCurrentDriver();
2441 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
2442 errorText_ = errorStream_.str();
2443 error( RtError::WARNING );
2447 info.outputChannels = outputChannels;
2448 info.inputChannels = inputChannels;
2449 if ( info.outputChannels > 0 && info.inputChannels > 0 )
2450 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
2452 // Determine the supported sample rates.
2453 info.sampleRates.clear();
2454 for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
2455 result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
2456 if ( result == ASE_OK )
2457 info.sampleRates.push_back( SAMPLE_RATES[i] );
2460 // Determine supported data types ... just check first channel and assume rest are the same.
2461 ASIOChannelInfo channelInfo;
2462 channelInfo.channel = 0;
2463 channelInfo.isInput = true;
2464 if ( info.inputChannels <= 0 ) channelInfo.isInput = false;
2465 result = ASIOGetChannelInfo( &channelInfo );
2466 if ( result != ASE_OK ) {
2467 drivers.removeCurrentDriver();
2468 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";
2469 errorText_ = errorStream_.str();
2470 error( RtError::WARNING );
2474 info.nativeFormats = 0;
2475 if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
2476 info.nativeFormats |= RTAUDIO_SINT16;
2477 else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
2478 info.nativeFormats |= RTAUDIO_SINT32;
2479 else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
2480 info.nativeFormats |= RTAUDIO_FLOAT32;
2481 else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
2482 info.nativeFormats |= RTAUDIO_FLOAT64;
2484 if ( getDefaultOutputDevice() == device )
2485 info.isDefaultOutput = true;
2486 if ( getDefaultInputDevice() == device )
2487 info.isDefaultInput = true;
2490 drivers.removeCurrentDriver();
2494 void bufferSwitch( long index, ASIOBool processNow )
2496 RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
2497 object->callbackEvent( index );
2500 void RtApiAsio :: saveDeviceInfo( void )
2504 unsigned int nDevices = getDeviceCount();
2505 devices_.resize( nDevices );
2506 for ( unsigned int i=0; i<nDevices; i++ )
2507 devices_[i] = getDeviceInfo( i );
2510 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
2511 unsigned int firstChannel, unsigned int sampleRate,
2512 RtAudioFormat format, unsigned int *bufferSize,
2513 RtAudio::StreamOptions *options )
2515 // For ASIO, a duplex stream MUST use the same driver.
2516 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {
2517 errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
2521 char driverName[32];
2522 ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
2523 if ( result != ASE_OK ) {
2524 errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";
2525 errorText_ = errorStream_.str();
2529 // The getDeviceInfo() function will not work when a stream is open
2530 // because ASIO does not allow multiple devices to run at the same
2531 // time. Thus, we'll probe the system before opening a stream and
2532 // save the results for use by getDeviceInfo().
2533 this->saveDeviceInfo();
2535 // Only load the driver once for duplex stream.
2536 if ( mode != INPUT || stream_.mode != OUTPUT ) {
2537 if ( !drivers.loadDriver( driverName ) ) {
2538 errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
2539 errorText_ = errorStream_.str();
2543 result = ASIOInit( &driverInfo );
2544 if ( result != ASE_OK ) {
2545 errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
2546 errorText_ = errorStream_.str();
2551 // Check the device channel count.
2552 long inputChannels, outputChannels;
2553 result = ASIOGetChannels( &inputChannels, &outputChannels );
2554 if ( result != ASE_OK ) {
2555 drivers.removeCurrentDriver();
2556 errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
2557 errorText_ = errorStream_.str();
2561 if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
2562 ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
2563 drivers.removeCurrentDriver();
2564 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
2565 errorText_ = errorStream_.str();
2568 stream_.nDeviceChannels[mode] = channels;
2569 stream_.nUserChannels[mode] = channels;
2570 stream_.channelOffset[mode] = firstChannel;
2572 // Verify the sample rate is supported.
2573 result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
2574 if ( result != ASE_OK ) {
2575 drivers.removeCurrentDriver();
2576 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
2577 errorText_ = errorStream_.str();
2581 // Get the current sample rate
2582 ASIOSampleRate currentRate;
2583 result = ASIOGetSampleRate( ¤tRate );
2584 if ( result != ASE_OK ) {
2585 drivers.removeCurrentDriver();
2586 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
2587 errorText_ = errorStream_.str();
2591 // Set the sample rate only if necessary
2592 if ( currentRate != sampleRate ) {
2593 result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
2594 if ( result != ASE_OK ) {
2595 drivers.removeCurrentDriver();
2596 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
2597 errorText_ = errorStream_.str();
2602 // Determine the driver data type.
2603 ASIOChannelInfo channelInfo;
2604 channelInfo.channel = 0;
2605 if ( mode == OUTPUT ) channelInfo.isInput = false;
2606 else channelInfo.isInput = true;
2607 result = ASIOGetChannelInfo( &channelInfo );
2608 if ( result != ASE_OK ) {
2609 drivers.removeCurrentDriver();
2610 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
2611 errorText_ = errorStream_.str();
2615 // Assuming WINDOWS host is always little-endian.
2616 stream_.doByteSwap[mode] = false;
2617 stream_.userFormat = format;
2618 stream_.deviceFormat[mode] = 0;
2619 if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
2620 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
2621 if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
2623 else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
2624 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
2625 if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
2627 else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
2628 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
2629 if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
2631 else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
2632 stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
2633 if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
2636 if ( stream_.deviceFormat[mode] == 0 ) {
2637 drivers.removeCurrentDriver();
2638 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
2639 errorText_ = errorStream_.str();
2643 // Set the buffer size. For a duplex stream, this will end up
2644 // setting the buffer size based on the input constraints, which
2646 long minSize, maxSize, preferSize, granularity;
2647 result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
2648 if ( result != ASE_OK ) {
2649 drivers.removeCurrentDriver();
2650 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
2651 errorText_ = errorStream_.str();
2655 if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
2656 else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
2657 else if ( granularity == -1 ) {
2658 // Make sure bufferSize is a power of two.
2659 int log2_of_min_size = 0;
2660 int log2_of_max_size = 0;
2662 for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
2663 if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
2664 if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
2667 long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
2668 int min_delta_num = log2_of_min_size;
2670 for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
2671 long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
2672 if (current_delta < min_delta) {
2673 min_delta = current_delta;
2678 *bufferSize = ( (unsigned int)1 << min_delta_num );
2679 if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
2680 else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
2682 else if ( granularity != 0 ) {
2683 // Set to an even multiple of granularity, rounding up.
2684 *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
2687 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {
2688 drivers.removeCurrentDriver();
2689 errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
2693 stream_.bufferSize = *bufferSize;
2694 stream_.nBuffers = 2;
2696 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
2697 else stream_.userInterleaved = true;
2699 // ASIO always uses non-interleaved buffers.
2700 stream_.deviceInterleaved[mode] = false;
2702 // Allocate, if necessary, our AsioHandle structure for the stream.
2703 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2704 if ( handle == 0 ) {
2706 handle = new AsioHandle;
2708 catch ( std::bad_alloc& ) {
2709 //if ( handle == NULL ) {
2710 drivers.removeCurrentDriver();
2711 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
2714 handle->bufferInfos = 0;
2716 // Create a manual-reset event.
2717 handle->condition = CreateEvent( NULL, // no security
2718 TRUE, // manual-reset
2719 FALSE, // non-signaled initially
2721 stream_.apiHandle = (void *) handle;
2724 // Create the ASIO internal buffers. Since RtAudio sets up input
2725 // and output separately, we'll have to dispose of previously
2726 // created output buffers for a duplex stream.
2727 long inputLatency, outputLatency;
2728 if ( mode == INPUT && stream_.mode == OUTPUT ) {
2729 ASIODisposeBuffers();
2730 if ( handle->bufferInfos ) free( handle->bufferInfos );
2733 // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
2734 bool buffersAllocated = false;
2735 unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
2736 handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
2737 if ( handle->bufferInfos == NULL ) {
2738 errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
2739 errorText_ = errorStream_.str();
2743 ASIOBufferInfo *infos;
2744 infos = handle->bufferInfos;
2745 for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
2746 infos->isInput = ASIOFalse;
2747 infos->channelNum = i + stream_.channelOffset[0];
2748 infos->buffers[0] = infos->buffers[1] = 0;
2750 for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
2751 infos->isInput = ASIOTrue;
2752 infos->channelNum = i + stream_.channelOffset[1];
2753 infos->buffers[0] = infos->buffers[1] = 0;
2756 // Set up the ASIO callback structure and create the ASIO data buffers.
2757 asioCallbacks.bufferSwitch = &bufferSwitch;
2758 asioCallbacks.sampleRateDidChange = &sampleRateChanged;
2759 asioCallbacks.asioMessage = &asioMessages;
2760 asioCallbacks.bufferSwitchTimeInfo = NULL;
2761 result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
2762 if ( result != ASE_OK ) {
2763 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
2764 errorText_ = errorStream_.str();
2767 buffersAllocated = true;
2769 // Set flags for buffer conversion.
2770 stream_.doConvertBuffer[mode] = false;
2771 if ( stream_.userFormat != stream_.deviceFormat[mode] )
2772 stream_.doConvertBuffer[mode] = true;
2773 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
2774 stream_.nUserChannels[mode] > 1 )
2775 stream_.doConvertBuffer[mode] = true;
2777 // Allocate necessary internal buffers
2778 unsigned long bufferBytes;
2779 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
2780 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
2781 if ( stream_.userBuffer[mode] == NULL ) {
2782 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
2786 if ( stream_.doConvertBuffer[mode] ) {
2788 bool makeBuffer = true;
2789 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
2790 if ( mode == INPUT ) {
2791 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
2792 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
2793 if ( bufferBytes <= bytesOut ) makeBuffer = false;
2798 bufferBytes *= *bufferSize;
2799 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
2800 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
2801 if ( stream_.deviceBuffer == NULL ) {
2802 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
2808 stream_.sampleRate = sampleRate;
2809 stream_.device[mode] = device;
2810 stream_.state = STREAM_STOPPED;
2811 asioCallbackInfo = &stream_.callbackInfo;
2812 stream_.callbackInfo.object = (void *) this;
2813 if ( stream_.mode == OUTPUT && mode == INPUT )
2814 // We had already set up an output stream.
2815 stream_.mode = DUPLEX;
2817 stream_.mode = mode;
2819 // Determine device latencies
2820 result = ASIOGetLatencies( &inputLatency, &outputLatency );
2821 if ( result != ASE_OK ) {
2822 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
2823 errorText_ = errorStream_.str();
2824 error( RtError::WARNING); // warn but don't fail
2827 stream_.latency[0] = outputLatency;
2828 stream_.latency[1] = inputLatency;
2831 // Setup the buffer conversion information structure. We don't use
2832 // buffers to do channel offsets, so we override that parameter
2834 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
2839 if ( buffersAllocated )
2840 ASIODisposeBuffers();
2841 drivers.removeCurrentDriver();
2844 CloseHandle( handle->condition );
2845 if ( handle->bufferInfos )
2846 free( handle->bufferInfos );
2848 stream_.apiHandle = 0;
2851 for ( int i=0; i<2; i++ ) {
2852 if ( stream_.userBuffer[i] ) {
2853 free( stream_.userBuffer[i] );
2854 stream_.userBuffer[i] = 0;
2858 if ( stream_.deviceBuffer ) {
2859 free( stream_.deviceBuffer );
2860 stream_.deviceBuffer = 0;
2866 void RtApiAsio :: closeStream()
2868 if ( stream_.state == STREAM_CLOSED ) {
2869 errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
2870 error( RtError::WARNING );
2874 if ( stream_.state == STREAM_RUNNING ) {
2875 stream_.state = STREAM_STOPPED;
2878 ASIODisposeBuffers();
2879 drivers.removeCurrentDriver();
2881 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2883 CloseHandle( handle->condition );
2884 if ( handle->bufferInfos )
2885 free( handle->bufferInfos );
2887 stream_.apiHandle = 0;
2890 for ( int i=0; i<2; i++ ) {
2891 if ( stream_.userBuffer[i] ) {
2892 free( stream_.userBuffer[i] );
2893 stream_.userBuffer[i] = 0;
2897 if ( stream_.deviceBuffer ) {
2898 free( stream_.deviceBuffer );
2899 stream_.deviceBuffer = 0;
2902 stream_.mode = UNINITIALIZED;
2903 stream_.state = STREAM_CLOSED;
2906 void RtApiAsio :: startStream()
2909 if ( stream_.state == STREAM_RUNNING ) {
2910 errorText_ = "RtApiAsio::startStream(): the stream is already running!";
2911 error( RtError::WARNING );
2915 MUTEX_LOCK( &stream_.mutex );
2917 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2918 ASIOError result = ASIOStart();
2919 if ( result != ASE_OK ) {
2920 errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";
2921 errorText_ = errorStream_.str();
2925 handle->drainCounter = 0;
2926 handle->internalDrain = false;
2927 stream_.state = STREAM_RUNNING;
2931 MUTEX_UNLOCK( &stream_.mutex );
2933 if ( result == ASE_OK ) return;
2934 error( RtError::SYSTEM_ERROR );
2937 void RtApiAsio :: stopStream()
2940 if ( stream_.state == STREAM_STOPPED ) {
2941 errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
2942 error( RtError::WARNING );
2946 MUTEX_LOCK( &stream_.mutex );
2948 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2949 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2950 if ( handle->drainCounter == 0 ) {
2951 handle->drainCounter = 1;
2952 MUTEX_UNLOCK( &stream_.mutex );
2953 WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE ); // block until signaled
2954 ResetEvent( handle->condition );
2955 MUTEX_LOCK( &stream_.mutex );
2959 ASIOError result = ASIOStop();
2960 if ( result != ASE_OK ) {
2961 errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
2962 errorText_ = errorStream_.str();
2965 stream_.state = STREAM_STOPPED;
2966 MUTEX_UNLOCK( &stream_.mutex );
2968 if ( result == ASE_OK ) return;
2969 error( RtError::SYSTEM_ERROR );
2972 void RtApiAsio :: abortStream()
2975 if ( stream_.state == STREAM_STOPPED ) {
2976 errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
2977 error( RtError::WARNING );
2981 // The following lines were commented-out because some behavior was
2982 // noted where the device buffers need to be zeroed to avoid
2983 // continuing sound, even when the device buffers are completed
2984 // disposed. So now, calling abort is the same as calling stop.
2985 //AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2986 //handle->drainCounter = 1;
2990 bool RtApiAsio :: callbackEvent( long bufferIndex )
2992 if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
2993 if ( stream_.state == STREAM_CLOSED ) {
2994 errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
2995 error( RtError::WARNING );
2999 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
3000 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
3002 // Check if we were draining the stream and signal is finished.
3003 if ( handle->drainCounter > 3 ) {
3004 if ( handle->internalDrain == false )
3005 SetEvent( handle->condition );
3011 MUTEX_LOCK( &stream_.mutex );
3013 // The state might change while waiting on a mutex.
3014 if ( stream_.state == STREAM_STOPPED ) goto unlock;
3016 // Invoke user callback to get fresh output data UNLESS we are
3018 if ( handle->drainCounter == 0 ) {
3019 RtAudioCallback callback = (RtAudioCallback) info->callback;
3020 double streamTime = getStreamTime();
3021 RtAudioStreamStatus status = 0;
3022 if ( stream_.mode != INPUT && asioXRun == true ) {
3023 status |= RTAUDIO_OUTPUT_UNDERFLOW;
3026 if ( stream_.mode != OUTPUT && asioXRun == true ) {
3027 status |= RTAUDIO_INPUT_OVERFLOW;
3030 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
3031 stream_.bufferSize, streamTime, status, info->userData );
3032 if ( handle->drainCounter == 2 ) {
3033 MUTEX_UNLOCK( &stream_.mutex );
3037 else if ( handle->drainCounter == 1 )
3038 handle->internalDrain = true;
3041 unsigned int nChannels, bufferBytes, i, j;
3042 nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
3043 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
3045 bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
3047 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
3049 for ( i=0, j=0; i<nChannels; i++ ) {
3050 if ( handle->bufferInfos[i].isInput != ASIOTrue )
3051 memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
3055 else if ( stream_.doConvertBuffer[0] ) {
3057 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
3058 if ( stream_.doByteSwap[0] )
3059 byteSwapBuffer( stream_.deviceBuffer,
3060 stream_.bufferSize * stream_.nDeviceChannels[0],
3061 stream_.deviceFormat[0] );
3063 for ( i=0, j=0; i<nChannels; i++ ) {
3064 if ( handle->bufferInfos[i].isInput != ASIOTrue )
3065 memcpy( handle->bufferInfos[i].buffers[bufferIndex],
3066 &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
3072 if ( stream_.doByteSwap[0] )
3073 byteSwapBuffer( stream_.userBuffer[0],
3074 stream_.bufferSize * stream_.nUserChannels[0],
3075 stream_.userFormat );
3077 for ( i=0, j=0; i<nChannels; i++ ) {
3078 if ( handle->bufferInfos[i].isInput != ASIOTrue )
3079 memcpy( handle->bufferInfos[i].buffers[bufferIndex],
3080 &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );
3085 if ( handle->drainCounter ) {
3086 handle->drainCounter++;
3091 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
3093 bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
3095 if (stream_.doConvertBuffer[1]) {
3097 // Always interleave ASIO input data.
3098 for ( i=0, j=0; i<nChannels; i++ ) {
3099 if ( handle->bufferInfos[i].isInput == ASIOTrue )
3100 memcpy( &stream_.deviceBuffer[j++*bufferBytes],
3101 handle->bufferInfos[i].buffers[bufferIndex],
3105 if ( stream_.doByteSwap[1] )
3106 byteSwapBuffer( stream_.deviceBuffer,
3107 stream_.bufferSize * stream_.nDeviceChannels[1],
3108 stream_.deviceFormat[1] );
3109 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
3113 for ( i=0, j=0; i<nChannels; i++ ) {
3114 if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
3115 memcpy( &stream_.userBuffer[1][bufferBytes*j++],
3116 handle->bufferInfos[i].buffers[bufferIndex],
3121 if ( stream_.doByteSwap[1] )
3122 byteSwapBuffer( stream_.userBuffer[1],
3123 stream_.bufferSize * stream_.nUserChannels[1],
3124 stream_.userFormat );
3129 // The following call was suggested by Malte Clasen. While the API
3130 // documentation indicates it should not be required, some device
3131 // drivers apparently do not function correctly without it.
3134 MUTEX_UNLOCK( &stream_.mutex );
3136 RtApi::tickStreamTime();
3140 void sampleRateChanged( ASIOSampleRate sRate )
3142 // The ASIO documentation says that this usually only happens during
3143 // external sync. Audio processing is not stopped by the driver,
3144 // actual sample rate might not have even changed, maybe only the
3145 // sample rate status of an AES/EBU or S/PDIF digital input at the
3148 RtApi *object = (RtApi *) asioCallbackInfo->object;
3150 object->stopStream();
3152 catch ( RtError &exception ) {
3153 std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
3157 std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
3160 long asioMessages( long selector, long value, void* message, double* opt )
3164 switch( selector ) {
3165 case kAsioSelectorSupported:
3166 if ( value == kAsioResetRequest
3167 || value == kAsioEngineVersion
3168 || value == kAsioResyncRequest
3169 || value == kAsioLatenciesChanged
3170 // The following three were added for ASIO 2.0, you don't
3171 // necessarily have to support them.
3172 || value == kAsioSupportsTimeInfo
3173 || value == kAsioSupportsTimeCode
3174 || value == kAsioSupportsInputMonitor)
3177 case kAsioResetRequest:
3178 // Defer the task and perform the reset of the driver during the
3179 // next "safe" situation. You cannot reset the driver right now,
3180 // as this code is called from the driver. Reset the driver is
3181 // done by completely destruct is. I.e. ASIOStop(),
3182 // ASIODisposeBuffers(), Destruction Afterwards you initialize the
3184 std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
3187 case kAsioResyncRequest:
3188 // This informs the application that the driver encountered some
3189 // non-fatal data loss. It is used for synchronization purposes
3190 // of different media. Added mainly to work around the Win16Mutex
3191 // problems in Windows 95/98 with the Windows Multimedia system,
3192 // which could lose data because the Mutex was held too long by
3193 // another thread. However a driver can issue it in other
3195 // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
3199 case kAsioLatenciesChanged:
3200 // This will inform the host application that the drivers were
3201 // latencies changed. Beware, it this does not mean that the
3202 // buffer sizes have changed! You might need to update internal
3204 std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
3207 case kAsioEngineVersion:
3208 // Return the supported ASIO version of the host application. If
3209 // a host application does not implement this selector, ASIO 1.0
3210 // is assumed by the driver.
3213 case kAsioSupportsTimeInfo:
3214 // Informs the driver whether the
3215 // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
3216 // For compatibility with ASIO 1.0 drivers the host application
3217 // should always support the "old" bufferSwitch method, too.
3220 case kAsioSupportsTimeCode:
3221 // Informs the driver whether application is interested in time
3222 // code info. If an application does not need to know about time
3223 // code, the driver has less work to do.
3230 static const char* getAsioErrorString( ASIOError result )
3238 static Messages m[] =
3240 { ASE_NotPresent, "Hardware input or output is not present or available." },
3241 { ASE_HWMalfunction, "Hardware is malfunctioning." },
3242 { ASE_InvalidParameter, "Invalid input parameter." },
3243 { ASE_InvalidMode, "Invalid mode." },
3244 { ASE_SPNotAdvancing, "Sample position not advancing." },
3245 { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },
3246 { ASE_NoMemory, "Not enough memory to complete the request." }
3249 for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
3250 if ( m[i].value == result ) return m[i].message;
3252 return "Unknown error.";
3254 //******************** End of __WINDOWS_ASIO__ *********************//
3258 #if defined(__WINDOWS_DS__) // Windows DirectSound API
3260 // Modified by Robin Davies, October 2005
3261 // - Improvements to DirectX pointer chasing.
3262 // - Backdoor RtDsStatistics hook provides DirectX performance information.
3263 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
3264 // - Auto-call CoInitialize for DSOUND and ASIO platforms.
3265 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
3270 #if defined(__MINGW32__)
3271 // missing from latest mingw winapi
3272 #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
3273 #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
3274 #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
3275 #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
3278 #define MINIMUM_DEVICE_BUFFER_SIZE 32768
3280 #ifdef _MSC_VER // if Microsoft Visual C++
3281 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.
3284 static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
3286 if ( laterPointer > earlierPointer )
3287 return laterPointer - earlierPointer;
3289 return laterPointer - earlierPointer + bufferSize;
3292 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
3294 if ( pointer > bufferSize ) pointer -= bufferSize;
3295 if ( laterPointer < earlierPointer ) laterPointer += bufferSize;
3296 if ( pointer < earlierPointer ) pointer += bufferSize;
3297 return pointer >= earlierPointer && pointer < laterPointer;
3300 // A structure to hold various information related to the DirectSound
3301 // API implementation.
3303 unsigned int drainCounter; // Tracks callback counts when draining
3304 bool internalDrain; // Indicates if stop is initiated from callback or not.
3308 UINT bufferPointer[2];
3309 DWORD dsBufferSize[2];
3310 DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
3314 :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; }
3318 RtApiDs::RtDsStatistics RtApiDs::statistics;
3320 // Provides a backdoor hook to monitor for DirectSound read overruns and write underruns.
3321 RtApiDs::RtDsStatistics RtApiDs::getDsStatistics()
3323 RtDsStatistics s = statistics;
3325 // update the calculated fields.
3326 if ( s.inputFrameSize != 0 )
3327 s.latency += s.readDeviceSafeLeadBytes * 1.0 / s.inputFrameSize / s.sampleRate;
3329 if ( s.outputFrameSize != 0 )
3330 s.latency += (s.writeDeviceSafeLeadBytes + s.writeDeviceBufferLeadBytes) * 1.0 / s.outputFrameSize / s.sampleRate;
3336 // Declarations for utility functions, callbacks, and structures
3337 // specific to the DirectSound implementation.
3338 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
3339 LPCTSTR description,
3343 static char* getErrorString( int code );
3345 extern "C" unsigned __stdcall callbackHandler( void *ptr );
3351 unsigned int counter;
3357 : isInput(false), getDefault(false), findIndex(false), counter(0), index(0) {}
3360 RtApiDs :: RtApiDs()
3362 // Dsound will run both-threaded. If CoInitialize fails, then just
3363 // accept whatever the mainline chose for a threading model.
3364 coInitialized_ = false;
3365 HRESULT hr = CoInitialize( NULL );
3366 if ( !FAILED( hr ) ) coInitialized_ = true;
3369 RtApiDs :: ~RtApiDs()
3371 if ( coInitialized_ ) CoUninitialize(); // balanced call.
3372 if ( stream_.state != STREAM_CLOSED ) closeStream();
3375 unsigned int RtApiDs :: getDefaultInputDevice( void )
3377 // Count output devices.
3379 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
3380 if ( FAILED( result ) ) {
3381 errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") counting output devices!";
3382 errorText_ = errorStream_.str();
3383 error( RtError::WARNING );
3387 // Now enumerate input devices until we find the id = NULL.
3388 info.isInput = true;
3389 info.getDefault = true;
3390 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
3391 if ( FAILED( result ) ) {
3392 errorStream_ << "RtApiDs::getDefaultInputDevice: error (" << getErrorString( result ) << ") enumerating input devices!";
3393 errorText_ = errorStream_.str();
3394 error( RtError::WARNING );
3398 if ( info.counter > 0 ) return info.counter - 1;
3402 unsigned int RtApiDs :: getDefaultOutputDevice( void )
3404 // Enumerate output devices until we find the id = NULL.
3406 info.getDefault = true;
3407 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
3408 if ( FAILED( result ) ) {
3409 errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") enumerating output devices!";
3410 errorText_ = errorStream_.str();
3411 error( RtError::WARNING );
3415 if ( info.counter > 0 ) return info.counter - 1;
3419 unsigned int RtApiDs :: getDeviceCount( void )
3421 // Count DirectSound devices.
3423 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
3424 if ( FAILED( result ) ) {
3425 errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
3426 errorText_ = errorStream_.str();
3427 error( RtError::WARNING );
3430 // Count DirectSoundCapture devices.
3431 info.isInput = true;
3432 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
3433 if ( FAILED( result ) ) {
3434 errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
3435 errorText_ = errorStream_.str();
3436 error( RtError::WARNING );
3439 return info.counter;
3442 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
3444 // Because DirectSound always enumerates input and output devices
3445 // separately (and because we don't attempt to combine devices
3446 // internally), none of our "devices" will ever be duplex.
3448 RtAudio::DeviceInfo info;
3449 info.probed = false;
3451 // Enumerate through devices to find the id (if it exists). Note
3452 // that we have to do the output enumeration first, even if this is
3453 // an input device, in order for the device counter to be correct.
3455 dsinfo.findIndex = true;
3456 dsinfo.index = device;
3457 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
3458 if ( FAILED( result ) ) {
3459 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating output devices!";
3460 errorText_ = errorStream_.str();
3461 error( RtError::WARNING );
3464 if ( dsinfo.name.empty() ) goto probeInput;
3466 LPDIRECTSOUND output;
3468 result = DirectSoundCreate( dsinfo.id, &output, NULL );
3469 if ( FAILED( result ) ) {
3470 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
3471 errorText_ = errorStream_.str();
3472 error( RtError::WARNING );
3476 outCaps.dwSize = sizeof( outCaps );
3477 result = output->GetCaps( &outCaps );
3478 if ( FAILED( result ) ) {
3480 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
3481 errorText_ = errorStream_.str();
3482 error( RtError::WARNING );
3486 // Get output channel information.
3487 info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
3489 // Get sample rate information.
3490 info.sampleRates.clear();
3491 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
3492 if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
3493 SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )
3494 info.sampleRates.push_back( SAMPLE_RATES[k] );
3497 // Get format information.
3498 if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
3499 if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
3503 if ( getDefaultOutputDevice() == device )
3504 info.isDefaultOutput = true;
3506 // Copy name and return.
3507 info.name = dsinfo.name;
3514 dsinfo.isInput = true;
3515 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
3516 if ( FAILED( result ) ) {
3517 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating input devices!";
3518 errorText_ = errorStream_.str();
3519 error( RtError::WARNING );
3522 if ( dsinfo.name.empty() ) return info;
3524 LPDIRECTSOUNDCAPTURE input;
3525 result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
3526 if ( FAILED( result ) ) {
3527 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
3528 errorText_ = errorStream_.str();
3529 error( RtError::WARNING );
3534 inCaps.dwSize = sizeof( inCaps );
3535 result = input->GetCaps( &inCaps );
3536 if ( FAILED( result ) ) {
3538 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsinfo.name << ")!";
3539 errorText_ = errorStream_.str();
3540 error( RtError::WARNING );
3544 // Get input channel information.
3545 info.inputChannels = inCaps.dwChannels;
3547 // Get sample rate and format information.
3548 if ( inCaps.dwChannels == 2 ) {
3549 if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3550 if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3551 if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3552 if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3553 if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3554 if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3555 if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3556 if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3558 if ( info.nativeFormats & RTAUDIO_SINT16 ) {
3559 if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.sampleRates.push_back( 11025 );
3560 if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.sampleRates.push_back( 22050 );
3561 if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.sampleRates.push_back( 44100 );
3562 if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.sampleRates.push_back( 96000 );
3564 else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
3565 if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.sampleRates.push_back( 11025 );
3566 if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.sampleRates.push_back( 22050 );
3567 if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.sampleRates.push_back( 44100 );
3568 if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.sampleRates.push_back( 44100 );
3571 else if ( inCaps.dwChannels == 1 ) {
3572 if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3573 if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3574 if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3575 if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3576 if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3577 if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3578 if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3579 if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3581 if ( info.nativeFormats & RTAUDIO_SINT16 ) {
3582 if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.sampleRates.push_back( 11025 );
3583 if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.sampleRates.push_back( 22050 );
3584 if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.sampleRates.push_back( 44100 );
3585 if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.sampleRates.push_back( 96000 );
3587 else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
3588 if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.sampleRates.push_back( 11025 );
3589 if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.sampleRates.push_back( 22050 );
3590 if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.sampleRates.push_back( 44100 );
3591 if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.sampleRates.push_back( 96000 );
3594 else info.inputChannels = 0; // technically, this would be an error
3598 if ( info.inputChannels == 0 ) return info;
3600 if ( getDefaultInputDevice() == device )
3601 info.isDefaultInput = true;
3603 // Copy name and return.
3604 info.name = dsinfo.name;
3609 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
3610 unsigned int firstChannel, unsigned int sampleRate,
3611 RtAudioFormat format, unsigned int *bufferSize,
3612 RtAudio::StreamOptions *options )
3614 if ( channels + firstChannel > 2 ) {
3615 errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
3619 // Enumerate through devices to find the id (if it exists). Note
3620 // that we have to do the output enumeration first, even if this is
3621 // an input device, in order for the device counter to be correct.
3623 dsinfo.findIndex = true;
3624 dsinfo.index = device;
3625 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
3626 if ( FAILED( result ) ) {
3627 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating output devices!";
3628 errorText_ = errorStream_.str();
3632 if ( mode == OUTPUT ) {
3633 if ( dsinfo.name.empty() ) {
3634 errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
3635 errorText_ = errorStream_.str();
3639 else { // mode == INPUT
3640 dsinfo.isInput = true;
3641 HRESULT result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
3642 if ( FAILED( result ) ) {
3643 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating input devices!";
3644 errorText_ = errorStream_.str();
3647 if ( dsinfo.name.empty() ) {
3648 errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
3649 errorText_ = errorStream_.str();
3654 // According to a note in PortAudio, using GetDesktopWindow()
3655 // instead of GetForegroundWindow() is supposed to avoid problems
3656 // that occur when the application's window is not the foreground
3657 // window. Also, if the application window closes before the
3658 // DirectSound buffer, DirectSound can crash. However, for console
3659 // applications, no sound was produced when using GetDesktopWindow().
3660 HWND hWnd = GetForegroundWindow();
3662 // Check the numberOfBuffers parameter and limit the lowest value to
3663 // two. This is a judgement call and a value of two is probably too
3664 // low for capture, but it should work for playback.
3666 if ( options ) nBuffers = options->numberOfBuffers;
3667 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
3668 if ( nBuffers < 2 ) nBuffers = 3;
3670 // Create the wave format structure. The data format setting will
3671 // be determined later.
3672 WAVEFORMATEX waveFormat;
3673 ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
3674 waveFormat.wFormatTag = WAVE_FORMAT_PCM;
3675 waveFormat.nChannels = channels + firstChannel;
3676 waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
3678 // Determine the device buffer size. By default, 32k, but we will
3679 // grow it to make allowances for very large software buffer sizes.
3680 DWORD dsBufferSize = 0;
3681 DWORD dsPointerLeadTime = 0;
3682 long bufferBytes = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
3684 void *ohandle = 0, *bhandle = 0;
3685 if ( mode == OUTPUT ) {
3687 LPDIRECTSOUND output;
3688 result = DirectSoundCreate( dsinfo.id, &output, NULL );
3689 if ( FAILED( result ) ) {
3690 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
3691 errorText_ = errorStream_.str();
3696 outCaps.dwSize = sizeof( outCaps );
3697 result = output->GetCaps( &outCaps );
3698 if ( FAILED( result ) ) {
3700 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsinfo.name << ")!";
3701 errorText_ = errorStream_.str();
3705 // Check channel information.
3706 if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
3707 errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsinfo.name << ") does not support stereo playback.";
3708 errorText_ = errorStream_.str();
3712 // Check format information. Use 16-bit format unless not
3713 // supported or user requests 8-bit.
3714 if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
3715 !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
3716 waveFormat.wBitsPerSample = 16;
3717 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3720 waveFormat.wBitsPerSample = 8;
3721 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3723 stream_.userFormat = format;
3725 // Update wave format structure and buffer information.
3726 waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
3727 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
3728 dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
3730 // If the user wants an even bigger buffer, increase the device buffer size accordingly.
3731 while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes )
3734 // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.
3735 //result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
3736 // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.
3737 result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );
3738 if ( FAILED( result ) ) {
3740 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsinfo.name << ")!";
3741 errorText_ = errorStream_.str();
3745 // Even though we will write to the secondary buffer, we need to
3746 // access the primary buffer to set the correct output format
3747 // (since the default is 8-bit, 22 kHz!). Setup the DS primary
3748 // buffer description.
3749 DSBUFFERDESC bufferDescription;
3750 ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
3751 bufferDescription.dwSize = sizeof( DSBUFFERDESC );
3752 bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
3754 // Obtain the primary buffer
3755 LPDIRECTSOUNDBUFFER buffer;
3756 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3757 if ( FAILED( result ) ) {
3759 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsinfo.name << ")!";
3760 errorText_ = errorStream_.str();
3764 // Set the primary DS buffer sound format.
3765 result = buffer->SetFormat( &waveFormat );
3766 if ( FAILED( result ) ) {
3768 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsinfo.name << ")!";
3769 errorText_ = errorStream_.str();
3773 // Setup the secondary DS buffer description.
3774 dsBufferSize = (DWORD) bufferBytes;
3775 ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
3776 bufferDescription.dwSize = sizeof( DSBUFFERDESC );
3777 bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
3778 DSBCAPS_GLOBALFOCUS |
3779 DSBCAPS_GETCURRENTPOSITION2 |
3780 DSBCAPS_LOCHARDWARE ); // Force hardware mixing
3781 bufferDescription.dwBufferBytes = bufferBytes;
3782 bufferDescription.lpwfxFormat = &waveFormat;
3784 // Try to create the secondary DS buffer. If that doesn't work,
3785 // try to use software mixing. Otherwise, there's a problem.
3786 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3787 if ( FAILED( result ) ) {
3788 bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
3789 DSBCAPS_GLOBALFOCUS |
3790 DSBCAPS_GETCURRENTPOSITION2 |
3791 DSBCAPS_LOCSOFTWARE ); // Force software mixing
3792 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3793 if ( FAILED( result ) ) {
3795 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsinfo.name << ")!";
3796 errorText_ = errorStream_.str();
3801 // Get the buffer size ... might be different from what we specified.
3803 dsbcaps.dwSize = sizeof( DSBCAPS );
3804 result = buffer->GetCaps( &dsbcaps );
3805 if ( FAILED( result ) ) {
3808 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!";
3809 errorText_ = errorStream_.str();
3813 bufferBytes = dsbcaps.dwBufferBytes;
3815 // Lock the DS buffer
3818 result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
3819 if ( FAILED( result ) ) {
3822 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsinfo.name << ")!";
3823 errorText_ = errorStream_.str();
3827 // Zero the DS buffer
3828 ZeroMemory( audioPtr, dataLen );
3830 // Unlock the DS buffer
3831 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
3832 if ( FAILED( result ) ) {
3835 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsinfo.name << ")!";
3836 errorText_ = errorStream_.str();
3840 dsBufferSize = bufferBytes;
3841 ohandle = (void *) output;
3842 bhandle = (void *) buffer;
3845 if ( mode == INPUT ) {
3847 LPDIRECTSOUNDCAPTURE input;
3848 result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
3849 if ( FAILED( result ) ) {
3850 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
3851 errorText_ = errorStream_.str();
3856 inCaps.dwSize = sizeof( inCaps );
3857 result = input->GetCaps( &inCaps );
3858 if ( FAILED( result ) ) {
3860 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsinfo.name << ")!";
3861 errorText_ = errorStream_.str();
3865 // Check channel information.
3866 if ( inCaps.dwChannels < channels + firstChannel ) {
3867 errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
3871 // Check format information. Use 16-bit format unless user
3873 DWORD deviceFormats;
3874 if ( channels + firstChannel == 2 ) {
3875 deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
3876 if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
3877 waveFormat.wBitsPerSample = 8;
3878 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3880 else { // assume 16-bit is supported
3881 waveFormat.wBitsPerSample = 16;
3882 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3885 else { // channel == 1
3886 deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
3887 if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
3888 waveFormat.wBitsPerSample = 8;
3889 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3891 else { // assume 16-bit is supported
3892 waveFormat.wBitsPerSample = 16;
3893 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3896 stream_.userFormat = format;
3898 // Update wave format structure and buffer information.
3899 waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
3900 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
3902 // Setup the secondary DS buffer description.
3903 dsBufferSize = bufferBytes;
3904 DSCBUFFERDESC bufferDescription;
3905 ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
3906 bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
3907 bufferDescription.dwFlags = 0;
3908 bufferDescription.dwReserved = 0;
3909 bufferDescription.dwBufferBytes = bufferBytes;
3910 bufferDescription.lpwfxFormat = &waveFormat;
3912 // Create the capture buffer.
3913 LPDIRECTSOUNDCAPTUREBUFFER buffer;
3914 result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
3915 if ( FAILED( result ) ) {
3917 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsinfo.name << ")!";
3918 errorText_ = errorStream_.str();
3922 // Lock the capture buffer
3925 result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
3926 if ( FAILED( result ) ) {
3929 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsinfo.name << ")!";
3930 errorText_ = errorStream_.str();
3935 ZeroMemory( audioPtr, dataLen );
3937 // Unlock the buffer
3938 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
3939 if ( FAILED( result ) ) {
3942 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsinfo.name << ")!";
3943 errorText_ = errorStream_.str();
3947 dsBufferSize = bufferBytes;
3948 ohandle = (void *) input;
3949 bhandle = (void *) buffer;
3952 // Set various stream parameters
3953 DsHandle *handle = 0;
3954 stream_.nDeviceChannels[mode] = channels + firstChannel;
3955 stream_.nUserChannels[mode] = channels;
3956 stream_.bufferSize = *bufferSize;
3957 stream_.channelOffset[mode] = firstChannel;
3958 stream_.deviceInterleaved[mode] = true;
3959 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
3960 else stream_.userInterleaved = true;
3962 // Set flag for buffer conversion
3963 stream_.doConvertBuffer[mode] = false;
3964 if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])
3965 stream_.doConvertBuffer[mode] = true;
3966 if (stream_.userFormat != stream_.deviceFormat[mode])
3967 stream_.doConvertBuffer[mode] = true;
3968 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
3969 stream_.nUserChannels[mode] > 1 )
3970 stream_.doConvertBuffer[mode] = true;
3972 // Allocate necessary internal buffers
3973 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
3974 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
3975 if ( stream_.userBuffer[mode] == NULL ) {
3976 errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
3980 if ( stream_.doConvertBuffer[mode] ) {
3982 bool makeBuffer = true;
3983 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
3984 if ( mode == INPUT ) {
3985 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
3986 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
3987 if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
3992 bufferBytes *= *bufferSize;
3993 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
3994 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
3995 if ( stream_.deviceBuffer == NULL ) {
3996 errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
4002 // Allocate our DsHandle structures for the stream.
4003 if ( stream_.apiHandle == 0 ) {
4005 handle = new DsHandle;
4007 catch ( std::bad_alloc& ) {
4008 errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
4012 // Create a manual-reset event.
4013 handle->condition = CreateEvent( NULL, // no security
4014 TRUE, // manual-reset
4015 FALSE, // non-signaled initially
4017 stream_.apiHandle = (void *) handle;
4020 handle = (DsHandle *) stream_.apiHandle;
4021 handle->id[mode] = ohandle;
4022 handle->buffer[mode] = bhandle;
4023 handle->dsBufferSize[mode] = dsBufferSize;
4024 handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
4026 stream_.device[mode] = device;
4027 stream_.state = STREAM_STOPPED;
4028 if ( stream_.mode == OUTPUT && mode == INPUT )
4029 // We had already set up an output stream.
4030 stream_.mode = DUPLEX;
4032 stream_.mode = mode;
4033 stream_.nBuffers = nBuffers;
4034 stream_.sampleRate = sampleRate;
4036 // Setup the buffer conversion information structure.
4037 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
4039 // Setup the callback thread.
4041 stream_.callbackInfo.object = (void *) this;
4042 stream_.callbackInfo.isRunning = true;
4043 stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,
4044 &stream_.callbackInfo, 0, &threadId );
4045 if ( stream_.callbackInfo.thread == 0 ) {
4046 errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";
4050 // Boost DS thread priority
4051 SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
4056 if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
4057 LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
4058 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4059 if ( buffer ) buffer->Release();
4062 if ( handle->buffer[1] ) {
4063 LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
4064 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4065 if ( buffer ) buffer->Release();
4068 CloseHandle( handle->condition );
4070 stream_.apiHandle = 0;
4073 for ( int i=0; i<2; i++ ) {
4074 if ( stream_.userBuffer[i] ) {
4075 free( stream_.userBuffer[i] );
4076 stream_.userBuffer[i] = 0;
4080 if ( stream_.deviceBuffer ) {
4081 free( stream_.deviceBuffer );
4082 stream_.deviceBuffer = 0;
4088 void RtApiDs :: closeStream()
4090 if ( stream_.state == STREAM_CLOSED ) {
4091 errorText_ = "RtApiDs::closeStream(): no open stream to close!";
4092 error( RtError::WARNING );
4096 // Stop the callback thread.
4097 stream_.callbackInfo.isRunning = false;
4098 WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
4099 CloseHandle( (HANDLE) stream_.callbackInfo.thread );
4101 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4103 if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
4104 LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
4105 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4112 if ( handle->buffer[1] ) {
4113 LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
4114 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4121 CloseHandle( handle->condition );
4123 stream_.apiHandle = 0;
4126 for ( int i=0; i<2; i++ ) {
4127 if ( stream_.userBuffer[i] ) {
4128 free( stream_.userBuffer[i] );
4129 stream_.userBuffer[i] = 0;
4133 if ( stream_.deviceBuffer ) {
4134 free( stream_.deviceBuffer );
4135 stream_.deviceBuffer = 0;
4138 stream_.mode = UNINITIALIZED;
4139 stream_.state = STREAM_CLOSED;
4142 void RtApiDs :: startStream()
4145 if ( stream_.state == STREAM_RUNNING ) {
4146 errorText_ = "RtApiDs::startStream(): the stream is already running!";
4147 error( RtError::WARNING );
4151 // Increase scheduler frequency on lesser windows (a side-effect of
4152 // increasing timer accuracy). On greater windows (Win2K or later),
4153 // this is already in effect.
4155 MUTEX_LOCK( &stream_.mutex );
4157 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4159 timeBeginPeriod( 1 );
4162 memset( &statistics, 0, sizeof( statistics ) );
4163 statistics.sampleRate = stream_.sampleRate;
4164 statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0];
4167 buffersRolling = false;
4168 duplexPrerollBytes = 0;
4170 if ( stream_.mode == DUPLEX ) {
4171 // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
4172 duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
4176 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4177 //statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0] ) * stream_.nDeviceChannels[0];
4179 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4180 result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
4181 if ( FAILED( result ) ) {
4182 errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
4183 errorText_ = errorStream_.str();
4188 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4189 //statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) * stream_.nDeviceChannels[1];
4191 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4192 result = buffer->Start( DSCBSTART_LOOPING );
4193 if ( FAILED( result ) ) {
4194 errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";
4195 errorText_ = errorStream_.str();
4200 handle->drainCounter = 0;
4201 handle->internalDrain = false;
4202 stream_.state = STREAM_RUNNING;
4205 MUTEX_UNLOCK( &stream_.mutex );
4207 if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
4210 void RtApiDs :: stopStream()
4213 if ( stream_.state == STREAM_STOPPED ) {
4214 errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
4215 error( RtError::WARNING );
4219 MUTEX_LOCK( &stream_.mutex );
4224 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4225 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4226 if ( handle->drainCounter == 0 ) {
4227 handle->drainCounter = 1;
4228 MUTEX_UNLOCK( &stream_.mutex );
4229 WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE ); // block until signaled
4230 ResetEvent( handle->condition );
4231 MUTEX_LOCK( &stream_.mutex );
4234 // Stop the buffer and clear memory
4235 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4236 result = buffer->Stop();
4237 if ( FAILED( result ) ) {
4238 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!";
4239 errorText_ = errorStream_.str();
4243 // Lock the buffer and clear it so that if we start to play again,
4244 // we won't have old data playing.
4245 result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
4246 if ( FAILED( result ) ) {
4247 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking output buffer!";
4248 errorText_ = errorStream_.str();
4252 // Zero the DS buffer
4253 ZeroMemory( audioPtr, dataLen );
4255 // Unlock the DS buffer
4256 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4257 if ( FAILED( result ) ) {
4258 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
4259 errorText_ = errorStream_.str();
4263 // If we start playing again, we must begin at beginning of buffer.
4264 handle->bufferPointer[0] = 0;
4267 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4268 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4272 result = buffer->Stop();
4273 if ( FAILED( result ) ) {
4274 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping input buffer!";
4275 errorText_ = errorStream_.str();
4279 // Lock the buffer and clear it so that if we start to play again,
4280 // we won't have old data playing.
4281 result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
4282 if ( FAILED( result ) ) {
4283 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking input buffer!";
4284 errorText_ = errorStream_.str();
4288 // Zero the DS buffer
4289 ZeroMemory( audioPtr, dataLen );
4291 // Unlock the DS buffer
4292 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4293 if ( FAILED( result ) ) {
4294 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
4295 errorText_ = errorStream_.str();
4299 // If we start recording again, we must begin at beginning of buffer.
4300 handle->bufferPointer[1] = 0;
4304 timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
4305 stream_.state = STREAM_STOPPED;
4306 MUTEX_UNLOCK( &stream_.mutex );
4307 if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
4310 void RtApiDs :: abortStream()
4313 if ( stream_.state == STREAM_STOPPED ) {
4314 errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
4315 error( RtError::WARNING );
4319 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4320 handle->drainCounter = 1;
4325 void RtApiDs :: callbackEvent()
4327 if ( stream_.state == STREAM_STOPPED ) {
4328 Sleep(50); // sleep 50 milliseconds
4332 if ( stream_.state == STREAM_CLOSED ) {
4333 errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
4334 error( RtError::WARNING );
4338 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
4339 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4341 // Check if we were draining the stream and signal is finished.
4342 if ( handle->drainCounter > stream_.nBuffers + 2 ) {
4343 if ( handle->internalDrain == false )
4344 SetEvent( handle->condition );
4350 MUTEX_LOCK( &stream_.mutex );
4352 // Invoke user callback to get fresh output data UNLESS we are
4354 if ( handle->drainCounter == 0 ) {
4355 RtAudioCallback callback = (RtAudioCallback) info->callback;
4356 double streamTime = getStreamTime();
4357 RtAudioStreamStatus status = 0;
4358 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
4359 status |= RTAUDIO_OUTPUT_UNDERFLOW;
4360 handle->xrun[0] = false;
4362 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
4363 status |= RTAUDIO_INPUT_OVERFLOW;
4364 handle->xrun[1] = false;
4366 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
4367 stream_.bufferSize, streamTime, status, info->userData );
4368 if ( handle->drainCounter == 2 ) {
4369 MUTEX_UNLOCK( &stream_.mutex );
4373 else if ( handle->drainCounter == 1 )
4374 handle->internalDrain = true;
4378 DWORD currentWritePos, safeWritePos;
4379 DWORD currentReadPos, safeReadPos;
4383 #ifdef GENERATE_DEBUG_LOG
4384 DWORD writeTime, readTime;
4387 LPVOID buffer1 = NULL;
4388 LPVOID buffer2 = NULL;
4389 DWORD bufferSize1 = 0;
4390 DWORD bufferSize2 = 0;
4395 if ( stream_.mode == DUPLEX && !buffersRolling ) {
4396 assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
4398 // It takes a while for the devices to get rolling. As a result,
4399 // there's no guarantee that the capture and write device pointers
4400 // will move in lockstep. Wait here for both devices to start
4401 // rolling, and then set our buffer pointers accordingly.
4402 // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600
4403 // bytes later than the write buffer.
4405 // Stub: a serious risk of having a pre-emptive scheduling round
4406 // take place between the two GetCurrentPosition calls... but I'm
4407 // really not sure how to solve the problem. Temporarily boost to
4408 // Realtime priority, maybe; but I'm not sure what priority the
4409 // DirectSound service threads run at. We *should* be roughly
4410 // within a ms or so of correct.
4412 LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4413 LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4415 DWORD initialWritePos, initialSafeWritePos;
4416 DWORD initialReadPos, initialSafeReadPos;
4418 result = dsWriteBuffer->GetCurrentPosition( &initialWritePos, &initialSafeWritePos );
4419 if ( FAILED( result ) ) {
4420 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4421 errorText_ = errorStream_.str();
4422 error( RtError::SYSTEM_ERROR );
4424 result = dsCaptureBuffer->GetCurrentPosition( &initialReadPos, &initialSafeReadPos );
4425 if ( FAILED( result ) ) {
4426 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4427 errorText_ = errorStream_.str();
4428 error( RtError::SYSTEM_ERROR );
4431 result = dsWriteBuffer->GetCurrentPosition( ¤tWritePos, &safeWritePos );
4432 if ( FAILED( result ) ) {
4433 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4434 errorText_ = errorStream_.str();
4435 error( RtError::SYSTEM_ERROR );
4437 result = dsCaptureBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos );
4438 if ( FAILED( result ) ) {
4439 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4440 errorText_ = errorStream_.str();
4441 error( RtError::SYSTEM_ERROR );
4443 if ( safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos ) break;
4447 assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
4449 buffersRolling = true;
4450 handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] );
4451 handle->bufferPointer[1] = safeReadPos;
4454 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4456 LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4458 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
4459 bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
4460 bufferBytes *= formatBytes( stream_.userFormat );
4461 memset( stream_.userBuffer[0], 0, bufferBytes );
4464 // Setup parameters and do buffer conversion if necessary.
4465 if ( stream_.doConvertBuffer[0] ) {
4466 buffer = stream_.deviceBuffer;
4467 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
4468 bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
4469 bufferBytes *= formatBytes( stream_.deviceFormat[0] );
4472 buffer = stream_.userBuffer[0];
4473 bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
4474 bufferBytes *= formatBytes( stream_.userFormat );
4477 // No byte swapping necessary in DirectSound implementation.
4479 // Ahhh ... windoze. 16-bit data is signed but 8-bit data is
4480 // unsigned. So, we need to convert our signed 8-bit data here to
4482 if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
4483 for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
4485 DWORD dsBufferSize = handle->dsBufferSize[0];
4486 nextWritePos = handle->bufferPointer[0];
4490 // Find out where the read and "safe write" pointers are.
4491 result = dsBuffer->GetCurrentPosition( ¤tWritePos, &safeWritePos );
4492 if ( FAILED( result ) ) {
4493 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4494 errorText_ = errorStream_.str();
4495 error( RtError::SYSTEM_ERROR );
4498 leadPos = safeWritePos + handle->dsPointerLeadTime[0];
4499 if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize;
4500 if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
4501 endWrite = nextWritePos + bufferBytes;
4503 // Check whether the entire write region is behind the play pointer.
4504 if ( leadPos >= endWrite ) break;
4506 // If we are here, then we must wait until the play pointer gets
4507 // beyond the write region. The approach here is to use the
4508 // Sleep() function to suspend operation until safePos catches
4509 // up. Calculate number of milliseconds to wait as:
4510 // time = distance * (milliseconds/second) * fudgefactor /
4511 // ((bytes/sample) * (samples/second))
4512 // A "fudgefactor" less than 1 is used because it was found
4513 // that sleeping too long was MUCH worse than sleeping for
4514 // several shorter periods.
4515 double millis = ( endWrite - leadPos ) * 900.0;
4516 millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
4517 if ( millis < 1.0 ) millis = 1.0;
4518 if ( millis > 50.0 ) {
4519 static int nOverruns = 0;
4522 Sleep( (DWORD) millis );
4525 //if ( statistics.writeDeviceSafeLeadBytes < dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ) ) {
4526 // statistics.writeDeviceSafeLeadBytes = dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] );
4529 if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize )
4530 || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) {
4531 // We've strayed into the forbidden zone ... resync the read pointer.
4532 //++statistics.numberOfWriteUnderruns;
4533 handle->xrun[0] = true;
4534 nextWritePos = safeWritePos + handle->dsPointerLeadTime[0] - bufferBytes + dsBufferSize;
4535 while ( nextWritePos >= dsBufferSize ) nextWritePos -= dsBufferSize;
4536 handle->bufferPointer[0] = nextWritePos;
4537 endWrite = nextWritePos + bufferBytes;
4540 // Lock free space in the buffer
4541 result = dsBuffer->Lock( nextWritePos, bufferBytes, &buffer1,
4542 &bufferSize1, &buffer2, &bufferSize2, 0 );
4543 if ( FAILED( result ) ) {
4544 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
4545 errorText_ = errorStream_.str();
4546 error( RtError::SYSTEM_ERROR );
4549 // Copy our buffer into the DS buffer
4550 CopyMemory( buffer1, buffer, bufferSize1 );
4551 if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );
4553 // Update our buffer offset and unlock sound buffer
4554 dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
4555 if ( FAILED( result ) ) {
4556 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
4557 errorText_ = errorStream_.str();
4558 error( RtError::SYSTEM_ERROR );
4560 nextWritePos = ( nextWritePos + bufferSize1 + bufferSize2 ) % dsBufferSize;
4561 handle->bufferPointer[0] = nextWritePos;
4563 if ( handle->drainCounter ) {
4564 handle->drainCounter++;
4569 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4571 // Setup parameters.
4572 if ( stream_.doConvertBuffer[1] ) {
4573 buffer = stream_.deviceBuffer;
4574 bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];
4575 bufferBytes *= formatBytes( stream_.deviceFormat[1] );
4578 buffer = stream_.userBuffer[1];
4579 bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];
4580 bufferBytes *= formatBytes( stream_.userFormat );
4583 LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4584 long nextReadPos = handle->bufferPointer[1];
4585 DWORD dsBufferSize = handle->dsBufferSize[1];
4587 // Find out where the write and "safe read" pointers are.
4588 result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos );
4589 if ( FAILED( result ) ) {
4590 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4591 errorText_ = errorStream_.str();
4592 error( RtError::SYSTEM_ERROR );
4595 if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
4596 DWORD endRead = nextReadPos + bufferBytes;
4598 // Handling depends on whether we are INPUT or DUPLEX.
4599 // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
4600 // then a wait here will drag the write pointers into the forbidden zone.
4602 // In DUPLEX mode, rather than wait, we will back off the read pointer until
4603 // it's in a safe position. This causes dropouts, but it seems to be the only
4604 // practical way to sync up the read and write pointers reliably, given the
4605 // the very complex relationship between phase and increment of the read and write
4608 // In order to minimize audible dropouts in DUPLEX mode, we will
4609 // provide a pre-roll period of 0.5 seconds in which we return
4610 // zeros from the read buffer while the pointers sync up.
4612 if ( stream_.mode == DUPLEX ) {
4613 if ( safeReadPos < endRead ) {
4614 if ( duplexPrerollBytes <= 0 ) {
4615 // Pre-roll time over. Be more agressive.
4616 int adjustment = endRead-safeReadPos;
4618 handle->xrun[1] = true;
4619 //++statistics.numberOfReadOverruns;
4621 // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
4622 // and perform fine adjustments later.
4623 // - small adjustments: back off by twice as much.
4624 if ( adjustment >= 2*bufferBytes )
4625 nextReadPos = safeReadPos-2*bufferBytes;
4627 nextReadPos = safeReadPos-bufferBytes-adjustment;
4629 //statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos;
4630 //if ( statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize;
4631 if ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
4635 // In pre=roll time. Just do it.
4636 nextReadPos = safeReadPos-bufferBytes;
4637 while ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
4639 endRead = nextReadPos + bufferBytes;
4642 else { // mode == INPUT
4643 while ( safeReadPos < endRead ) {
4644 // See comments for playback.
4645 double millis = (endRead - safeReadPos) * 900.0;
4646 millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
4647 if ( millis < 1.0 ) millis = 1.0;
4648 Sleep( (DWORD) millis );
4650 // Wake up, find out where we are now
4651 result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos );
4652 if ( FAILED( result ) ) {
4653 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4654 errorText_ = errorStream_.str();
4655 error( RtError::SYSTEM_ERROR );
4658 if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
4662 //if (statistics.readDeviceSafeLeadBytes < dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ) )
4663 // statistics.readDeviceSafeLeadBytes = dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize );
4665 // Lock free space in the buffer
4666 result = dsBuffer->Lock( nextReadPos, bufferBytes, &buffer1,
4667 &bufferSize1, &buffer2, &bufferSize2, 0 );
4668 if ( FAILED( result ) ) {
4669 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
4670 errorText_ = errorStream_.str();
4671 error( RtError::SYSTEM_ERROR );
4674 if ( duplexPrerollBytes <= 0 ) {
4675 // Copy our buffer into the DS buffer
4676 CopyMemory( buffer, buffer1, bufferSize1 );
4677 if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );
4680 memset( buffer, 0, bufferSize1 );
4681 if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );
4682 duplexPrerollBytes -= bufferSize1 + bufferSize2;
4685 // Update our buffer offset and unlock sound buffer
4686 nextReadPos = ( nextReadPos + bufferSize1 + bufferSize2 ) % dsBufferSize;
4687 dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
4688 if ( FAILED( result ) ) {
4689 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
4690 errorText_ = errorStream_.str();
4691 error( RtError::SYSTEM_ERROR );
4693 handle->bufferPointer[1] = nextReadPos;
4695 // No byte swapping necessary in DirectSound implementation.
4697 // If necessary, convert 8-bit data from unsigned to signed.
4698 if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
4699 for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
4701 // Do buffer conversion if necessary.
4702 if ( stream_.doConvertBuffer[1] )
4703 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
4705 #ifdef GENERATE_DEBUG_LOG
4706 if ( currentDebugLogEntry < debugLog.size() )
4708 TTickRecord &r = debugLog[currentDebugLogEntry++];
4709 r.currentReadPointer = currentReadPos;
4710 r.safeReadPointer = safeReadPos;
4711 r.currentWritePointer = currentWritePos;
4712 r.safeWritePointer = safeWritePos;
4713 r.readTime = readTime;
4714 r.writeTime = writeTime;
4715 r.nextReadPointer = handles[1].bufferPointer;
4716 r.nextWritePointer = handles[0].bufferPointer;
4721 MUTEX_UNLOCK( &stream_.mutex );
4723 RtApi::tickStreamTime();
4726 // Definitions for utility functions and callbacks
4727 // specific to the DirectSound implementation.
4729 extern "C" unsigned __stdcall callbackHandler( void *ptr )
4731 CallbackInfo *info = (CallbackInfo *) ptr;
4732 RtApiDs *object = (RtApiDs *) info->object;
4733 bool* isRunning = &info->isRunning;
4735 while ( *isRunning == true ) {
4736 object->callbackEvent();
4745 std::string convertTChar( LPCTSTR name )
4749 #if defined( UNICODE ) || defined( _UNICODE )
4750 // Yes, this conversion doesn't make sense for two-byte characters
4751 // but RtAudio is currently written to return an std::string of
4752 // one-byte chars for the device name.
4753 for ( unsigned int i=0; i<wcslen( name ); i++ )
4754 s.push_back( name[i] );
4756 s.append( std::string( name ) );
4762 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
4763 LPCTSTR description,
4767 EnumInfo *info = (EnumInfo *) lpContext;
4770 if ( info->isInput == true ) {
4772 LPDIRECTSOUNDCAPTURE object;
4774 hr = DirectSoundCaptureCreate( lpguid, &object, NULL );
4775 if ( hr != DS_OK ) return TRUE;
4777 caps.dwSize = sizeof(caps);
4778 hr = object->GetCaps( &caps );
4779 if ( hr == DS_OK ) {
4780 if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
4787 LPDIRECTSOUND object;
4788 hr = DirectSoundCreate( lpguid, &object, NULL );
4789 if ( hr != DS_OK ) return TRUE;
4791 caps.dwSize = sizeof(caps);
4792 hr = object->GetCaps( &caps );
4793 if ( hr == DS_OK ) {
4794 if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
4800 if ( info->getDefault && lpguid == NULL ) return FALSE;
4802 if ( info->findIndex && info->counter > info->index ) {
4804 info->name = convertTChar( description );
4811 static char* getErrorString( int code )
4815 case DSERR_ALLOCATED:
4816 return "Already allocated";
4818 case DSERR_CONTROLUNAVAIL:
4819 return "Control unavailable";
4821 case DSERR_INVALIDPARAM:
4822 return "Invalid parameter";
4824 case DSERR_INVALIDCALL:
4825 return "Invalid call";
4828 return "Generic error";
4830 case DSERR_PRIOLEVELNEEDED:
4831 return "Priority level needed";
4833 case DSERR_OUTOFMEMORY:
4834 return "Out of memory";
4836 case DSERR_BADFORMAT:
4837 return "The sample rate or the channel format is not supported";
4839 case DSERR_UNSUPPORTED:
4840 return "Not supported";
4842 case DSERR_NODRIVER:
4845 case DSERR_ALREADYINITIALIZED:
4846 return "Already initialized";
4848 case DSERR_NOAGGREGATION:
4849 return "No aggregation";
4851 case DSERR_BUFFERLOST:
4852 return "Buffer lost";
4854 case DSERR_OTHERAPPHASPRIO:
4855 return "Another application already has priority";
4857 case DSERR_UNINITIALIZED:
4858 return "Uninitialized";
4861 return "DirectSound unknown error";
4864 //******************** End of __WINDOWS_DS__ *********************//
4868 #if defined(__LINUX_ALSA__)
4870 #include <alsa/asoundlib.h>
4873 // A structure to hold various information related to the ALSA API
4876 snd_pcm_t *handles[2];
4879 pthread_cond_t runnable;
4882 :synchronized(false) { xrun[0] = false; xrun[1] = false; }
4885 extern "C" void *alsaCallbackHandler( void * ptr );
4887 RtApiAlsa :: RtApiAlsa()
4889 // Nothing to do here.
4892 RtApiAlsa :: ~RtApiAlsa()
4894 if ( stream_.state != STREAM_CLOSED ) closeStream();
4897 unsigned int RtApiAlsa :: getDeviceCount( void )
4899 unsigned nDevices = 0;
4900 int result, subdevice, card;
4904 // Count cards and devices
4906 snd_card_next( &card );
4907 while ( card >= 0 ) {
4908 sprintf( name, "hw:%d", card );
4909 result = snd_ctl_open( &handle, name, 0 );
4911 errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
4912 errorText_ = errorStream_.str();
4913 error( RtError::WARNING );
4918 result = snd_ctl_pcm_next_device( handle, &subdevice );
4920 errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
4921 errorText_ = errorStream_.str();
4922 error( RtError::WARNING );
4925 if ( subdevice < 0 )
4930 snd_ctl_close( handle );
4931 snd_card_next( &card );
4937 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
4939 RtAudio::DeviceInfo info;
4940 info.probed = false;
4942 unsigned nDevices = 0;
4943 int result, subdevice, card;
4947 // Count cards and devices
4949 snd_card_next( &card );
4950 while ( card >= 0 ) {
4951 sprintf( name, "hw:%d", card );
4952 result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
4954 errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
4955 errorText_ = errorStream_.str();
4956 error( RtError::WARNING );
4961 result = snd_ctl_pcm_next_device( chandle, &subdevice );
4963 errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
4964 errorText_ = errorStream_.str();
4965 error( RtError::WARNING );
4968 if ( subdevice < 0 ) break;
4969 if ( nDevices == device ) {
4970 sprintf( name, "hw:%d,%d", card, subdevice );
4976 snd_ctl_close( chandle );
4977 snd_card_next( &card );
4980 if ( nDevices == 0 ) {
4981 errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
4982 error( RtError::INVALID_USE );
4985 if ( device >= nDevices ) {
4986 errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
4987 error( RtError::INVALID_USE );
4992 // If a stream is already open, we cannot probe the stream devices.
4993 // Thus, use the saved results.
4994 if ( stream_.state != STREAM_CLOSED &&
4995 ( stream_.device[0] == device || stream_.device[1] == device ) ) {
4996 if ( device >= devices_.size() ) {
4997 errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";
4998 error( RtError::WARNING );
5001 return devices_[ device ];
5004 int openMode = SND_PCM_ASYNC;
5005 snd_pcm_stream_t stream;
5006 snd_pcm_info_t *pcminfo;
5007 snd_pcm_info_alloca( &pcminfo );
5009 snd_pcm_hw_params_t *params;
5010 snd_pcm_hw_params_alloca( ¶ms );
5012 // First try for playback
5013 stream = SND_PCM_STREAM_PLAYBACK;
5014 snd_pcm_info_set_device( pcminfo, subdevice );
5015 snd_pcm_info_set_subdevice( pcminfo, 0 );
5016 snd_pcm_info_set_stream( pcminfo, stream );
5018 result = snd_ctl_pcm_info( chandle, pcminfo );
5020 // Device probably doesn't support playback.
5024 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );
5026 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5027 errorText_ = errorStream_.str();
5028 error( RtError::WARNING );
5032 // The device is open ... fill the parameter structure.
5033 result = snd_pcm_hw_params_any( phandle, params );
5035 snd_pcm_close( phandle );
5036 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5037 errorText_ = errorStream_.str();
5038 error( RtError::WARNING );
5042 // Get output channel information.
5044 result = snd_pcm_hw_params_get_channels_max( params, &value );
5046 snd_pcm_close( phandle );
5047 errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";
5048 errorText_ = errorStream_.str();
5049 error( RtError::WARNING );
5052 info.outputChannels = value;
5053 snd_pcm_close( phandle );
5056 // Now try for capture
5057 stream = SND_PCM_STREAM_CAPTURE;
5058 snd_pcm_info_set_stream( pcminfo, stream );
5060 result = snd_ctl_pcm_info( chandle, pcminfo );
5061 snd_ctl_close( chandle );
5063 // Device probably doesn't support capture.
5064 if ( info.outputChannels == 0 ) return info;
5065 goto probeParameters;
5068 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
5070 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5071 errorText_ = errorStream_.str();
5072 error( RtError::WARNING );
5073 if ( info.outputChannels == 0 ) return info;
5074 goto probeParameters;
5077 // The device is open ... fill the parameter structure.
5078 result = snd_pcm_hw_params_any( phandle, params );
5080 snd_pcm_close( phandle );
5081 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5082 errorText_ = errorStream_.str();
5083 error( RtError::WARNING );
5084 if ( info.outputChannels == 0 ) return info;
5085 goto probeParameters;
5088 result = snd_pcm_hw_params_get_channels_max( params, &value );
5090 snd_pcm_close( phandle );
5091 errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";
5092 errorText_ = errorStream_.str();
5093 error( RtError::WARNING );
5094 if ( info.outputChannels == 0 ) return info;
5095 goto probeParameters;
5097 info.inputChannels = value;
5098 snd_pcm_close( phandle );
5100 // If device opens for both playback and capture, we determine the channels.
5101 if ( info.outputChannels > 0 && info.inputChannels > 0 )
5102 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
5104 // ALSA doesn't provide default devices so we'll use the first available one.
5105 if ( device == 0 && info.outputChannels > 0 )
5106 info.isDefaultOutput = true;
5107 if ( device == 0 && info.inputChannels > 0 )
5108 info.isDefaultInput = true;
5111 // At this point, we just need to figure out the supported data
5112 // formats and sample rates. We'll proceed by opening the device in
5113 // the direction with the maximum number of channels, or playback if
5114 // they are equal. This might limit our sample rate options, but so
5117 if ( info.outputChannels >= info.inputChannels )
5118 stream = SND_PCM_STREAM_PLAYBACK;
5120 stream = SND_PCM_STREAM_CAPTURE;
5121 snd_pcm_info_set_stream( pcminfo, stream );
5123 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
5125 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5126 errorText_ = errorStream_.str();
5127 error( RtError::WARNING );
5131 // The device is open ... fill the parameter structure.
5132 result = snd_pcm_hw_params_any( phandle, params );
5134 snd_pcm_close( phandle );
5135 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5136 errorText_ = errorStream_.str();
5137 error( RtError::WARNING );
5141 // Test our discrete set of sample rate values.
5142 info.sampleRates.clear();
5143 for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
5144 if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )
5145 info.sampleRates.push_back( SAMPLE_RATES[i] );
5147 if ( info.sampleRates.size() == 0 ) {
5148 snd_pcm_close( phandle );
5149 errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
5150 errorText_ = errorStream_.str();
5151 error( RtError::WARNING );
5155 // Probe the supported data formats ... we don't care about endian-ness just yet
5156 snd_pcm_format_t format;
5157 info.nativeFormats = 0;
5158 format = SND_PCM_FORMAT_S8;
5159 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5160 info.nativeFormats |= RTAUDIO_SINT8;
5161 format = SND_PCM_FORMAT_S16;
5162 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5163 info.nativeFormats |= RTAUDIO_SINT16;
5164 format = SND_PCM_FORMAT_S24;
5165 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5166 info.nativeFormats |= RTAUDIO_SINT24;
5167 format = SND_PCM_FORMAT_S32;
5168 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5169 info.nativeFormats |= RTAUDIO_SINT32;
5170 format = SND_PCM_FORMAT_FLOAT;
5171 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5172 info.nativeFormats |= RTAUDIO_FLOAT32;
5173 format = SND_PCM_FORMAT_FLOAT64;
5174 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5175 info.nativeFormats |= RTAUDIO_FLOAT64;
5177 // Check that we have at least one supported format
5178 if ( info.nativeFormats == 0 ) {
5179 errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
5180 errorText_ = errorStream_.str();
5181 error( RtError::WARNING );
5185 // Get the device name
5187 result = snd_card_get_name( card, &cardname );
5189 sprintf( name, "hw:%s,%d", cardname, subdevice );
5192 // That's all ... close the device and return
5193 snd_pcm_close( phandle );
5198 void RtApiAlsa :: saveDeviceInfo( void )
5202 unsigned int nDevices = getDeviceCount();
5203 devices_.resize( nDevices );
5204 for ( unsigned int i=0; i<nDevices; i++ )
5205 devices_[i] = getDeviceInfo( i );
5208 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
5209 unsigned int firstChannel, unsigned int sampleRate,
5210 RtAudioFormat format, unsigned int *bufferSize,
5211 RtAudio::StreamOptions *options )
5214 #if defined(__RTAUDIO_DEBUG__)
5216 snd_output_stdio_attach(&out, stderr, 0);
5219 // I'm not using the "plug" interface ... too much inconsistent behavior.
5221 unsigned nDevices = 0;
5222 int result, subdevice, card;
5226 // Count cards and devices
5228 snd_card_next( &card );
5229 while ( card >= 0 ) {
5230 sprintf( name, "hw:%d", card );
5231 result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
5233 errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
5234 errorText_ = errorStream_.str();
5239 result = snd_ctl_pcm_next_device( chandle, &subdevice );
5240 if ( result < 0 ) break;
5241 if ( subdevice < 0 ) break;
5242 if ( nDevices == device ) {
5243 sprintf( name, "hw:%d,%d", card, subdevice );
5244 snd_ctl_close( chandle );
5249 snd_ctl_close( chandle );
5250 snd_card_next( &card );
5253 if ( nDevices == 0 ) {
5254 // This should not happen because a check is made before this function is called.
5255 errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";
5259 if ( device >= nDevices ) {
5260 // This should not happen because a check is made before this function is called.
5261 errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";
5267 // The getDeviceInfo() function will not work for a device that is
5268 // already open. Thus, we'll probe the system before opening a
5269 // stream and save the results for use by getDeviceInfo().
5270 if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
5271 this->saveDeviceInfo();
5273 snd_pcm_stream_t stream;
5274 if ( mode == OUTPUT )
5275 stream = SND_PCM_STREAM_PLAYBACK;
5277 stream = SND_PCM_STREAM_CAPTURE;
5280 int openMode = SND_PCM_ASYNC;
5281 result = snd_pcm_open( &phandle, name, stream, openMode );
5283 if ( mode == OUTPUT )
5284 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";
5286 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";
5287 errorText_ = errorStream_.str();
5291 // Fill the parameter structure.
5292 snd_pcm_hw_params_t *hw_params;
5293 snd_pcm_hw_params_alloca( &hw_params );
5294 result = snd_pcm_hw_params_any( phandle, hw_params );
5296 snd_pcm_close( phandle );
5297 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";
5298 errorText_ = errorStream_.str();
5302 #if defined(__RTAUDIO_DEBUG__)
5303 fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );
5304 snd_pcm_hw_params_dump( hw_params, out );
5307 // Set access ... check user preference.
5308 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {
5309 stream_.userInterleaved = false;
5310 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
5312 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
5313 stream_.deviceInterleaved[mode] = true;
5316 stream_.deviceInterleaved[mode] = false;
5319 stream_.userInterleaved = true;
5320 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
5322 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
5323 stream_.deviceInterleaved[mode] = false;
5326 stream_.deviceInterleaved[mode] = true;
5330 snd_pcm_close( phandle );
5331 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
5332 errorText_ = errorStream_.str();
5336 // Determine how to set the device format.
5337 stream_.userFormat = format;
5338 snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
5340 if ( format == RTAUDIO_SINT8 )
5341 deviceFormat = SND_PCM_FORMAT_S8;
5342 else if ( format == RTAUDIO_SINT16 )
5343 deviceFormat = SND_PCM_FORMAT_S16;
5344 else if ( format == RTAUDIO_SINT24 )
5345 deviceFormat = SND_PCM_FORMAT_S24;
5346 else if ( format == RTAUDIO_SINT32 )
5347 deviceFormat = SND_PCM_FORMAT_S32;
5348 else if ( format == RTAUDIO_FLOAT32 )
5349 deviceFormat = SND_PCM_FORMAT_FLOAT;
5350 else if ( format == RTAUDIO_FLOAT64 )
5351 deviceFormat = SND_PCM_FORMAT_FLOAT64;
5353 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
5354 stream_.deviceFormat[mode] = format;
5358 // The user requested format is not natively supported by the device.
5359 deviceFormat = SND_PCM_FORMAT_FLOAT64;
5360 if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {
5361 stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
5365 deviceFormat = SND_PCM_FORMAT_FLOAT;
5366 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5367 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
5371 deviceFormat = SND_PCM_FORMAT_S32;
5372 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5373 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
5377 deviceFormat = SND_PCM_FORMAT_S24;
5378 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5379 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
5383 deviceFormat = SND_PCM_FORMAT_S16;
5384 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5385 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
5389 deviceFormat = SND_PCM_FORMAT_S8;
5390 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5391 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
5395 // If we get here, no supported format was found.
5396 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";
5397 errorText_ = errorStream_.str();
5401 result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );
5403 snd_pcm_close( phandle );
5404 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";
5405 errorText_ = errorStream_.str();
5409 // Determine whether byte-swaping is necessary.
5410 stream_.doByteSwap[mode] = false;
5411 if ( deviceFormat != SND_PCM_FORMAT_S8 ) {
5412 result = snd_pcm_format_cpu_endian( deviceFormat );
5414 stream_.doByteSwap[mode] = true;
5415 else if (result < 0) {
5416 snd_pcm_close( phandle );
5417 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";
5418 errorText_ = errorStream_.str();
5423 // Set the sample rate.
5424 result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );
5426 snd_pcm_close( phandle );
5427 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";
5428 errorText_ = errorStream_.str();
5432 // Determine the number of channels for this device. We support a possible
5433 // minimum device channel number > than the value requested by the user.
5434 stream_.nUserChannels[mode] = channels;
5436 result = snd_pcm_hw_params_get_channels_max( hw_params, &value );
5437 unsigned int deviceChannels = value;
5438 if ( result < 0 || deviceChannels < channels + firstChannel ) {
5439 snd_pcm_close( phandle );
5440 errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";
5441 errorText_ = errorStream_.str();
5445 result = snd_pcm_hw_params_get_channels_min( hw_params, &value );
5447 snd_pcm_close( phandle );
5448 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";
5449 errorText_ = errorStream_.str();
5452 deviceChannels = value;
5453 if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;
5454 stream_.nDeviceChannels[mode] = deviceChannels;
5456 // Set the device channels.
5457 result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );
5459 snd_pcm_close( phandle );
5460 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";
5461 errorText_ = errorStream_.str();
5465 // Set the buffer number, which in ALSA is referred to as the "period".
5467 unsigned int periods = 0;
5468 if ( options ) periods = options->numberOfBuffers;
5469 totalSize = *bufferSize * periods;
5471 // Set the buffer (or period) size.
5472 snd_pcm_uframes_t periodSize = *bufferSize;
5473 result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );
5475 snd_pcm_close( phandle );
5476 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";
5477 errorText_ = errorStream_.str();
5480 *bufferSize = periodSize;
5482 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
5483 else periods = totalSize / *bufferSize;
5484 // Even though the hardware might allow 1 buffer, it won't work reliably.
5485 if ( periods < 2 ) periods = 2;
5486 result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
5488 snd_pcm_close( phandle );
5489 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
5490 errorText_ = errorStream_.str();
5494 // If attempting to setup a duplex stream, the bufferSize parameter
5495 // MUST be the same in both directions!
5496 if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
5497 errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
5498 errorText_ = errorStream_.str();
5502 stream_.bufferSize = *bufferSize;
5504 // Install the hardware configuration
5505 result = snd_pcm_hw_params( phandle, hw_params );
5507 snd_pcm_close( phandle );
5508 errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";
5509 errorText_ = errorStream_.str();
5513 #if defined(__RTAUDIO_DEBUG__)
5514 fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
5515 snd_pcm_hw_params_dump( hw_params, out );
5518 // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
5519 snd_pcm_sw_params_t *sw_params = NULL;
5520 snd_pcm_sw_params_alloca( &sw_params );
5521 snd_pcm_sw_params_current( phandle, sw_params );
5522 snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
5523 snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
5524 snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
5526 // The following two settings were suggested by Theo Veenker
5527 //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );
5528 //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );
5530 // here are two options for a fix
5531 //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );
5532 snd_pcm_uframes_t val;
5533 snd_pcm_sw_params_get_boundary( sw_params, &val );
5534 snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );
5536 result = snd_pcm_sw_params( phandle, sw_params );
5538 snd_pcm_close( phandle );
5539 errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
5540 errorText_ = errorStream_.str();
5544 #if defined(__RTAUDIO_DEBUG__)
5545 fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
5546 snd_pcm_sw_params_dump( sw_params, out );
5549 // Set flags for buffer conversion
5550 stream_.doConvertBuffer[mode] = false;
5551 if ( stream_.userFormat != stream_.deviceFormat[mode] )
5552 stream_.doConvertBuffer[mode] = true;
5553 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
5554 stream_.doConvertBuffer[mode] = true;
5555 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
5556 stream_.nUserChannels[mode] > 1 )
5557 stream_.doConvertBuffer[mode] = true;
5559 // Allocate the ApiHandle if necessary and then save.
5560 AlsaHandle *apiInfo = 0;
5561 if ( stream_.apiHandle == 0 ) {
5563 apiInfo = (AlsaHandle *) new AlsaHandle;
5565 catch ( std::bad_alloc& ) {
5566 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
5570 if ( pthread_cond_init( &apiInfo->runnable, NULL ) ) {
5571 errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
5575 stream_.apiHandle = (void *) apiInfo;
5576 apiInfo->handles[0] = 0;
5577 apiInfo->handles[1] = 0;
5580 apiInfo = (AlsaHandle *) stream_.apiHandle;
5582 apiInfo->handles[mode] = phandle;
5584 // Allocate necessary internal buffers.
5585 unsigned long bufferBytes;
5586 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
5587 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
5588 if ( stream_.userBuffer[mode] == NULL ) {
5589 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";
5593 if ( stream_.doConvertBuffer[mode] ) {
5595 bool makeBuffer = true;
5596 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
5597 if ( mode == INPUT ) {
5598 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
5599 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
5600 if ( bufferBytes <= bytesOut ) makeBuffer = false;
5605 bufferBytes *= *bufferSize;
5606 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
5607 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
5608 if ( stream_.deviceBuffer == NULL ) {
5609 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";
5615 stream_.sampleRate = sampleRate;
5616 stream_.nBuffers = periods;
5617 stream_.device[mode] = device;
5618 stream_.state = STREAM_STOPPED;
5620 // Setup the buffer conversion information structure.
5621 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
5623 // Setup thread if necessary.
5624 if ( stream_.mode == OUTPUT && mode == INPUT ) {
5625 // We had already set up an output stream.
5626 stream_.mode = DUPLEX;
5627 // Link the streams if possible.
5628 apiInfo->synchronized = false;
5629 if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )
5630 apiInfo->synchronized = true;
5632 errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";
5633 error( RtError::WARNING );
5637 stream_.mode = mode;
5639 // Setup callback thread.
5640 stream_.callbackInfo.object = (void *) this;
5642 // Set the thread attributes for joinable and realtime scheduling
5643 // priority (optional). The higher priority will only take affect
5644 // if the program is run as root or suid. Note, under Linux
5645 // processes with CAP_SYS_NICE privilege, a user can change
5646 // scheduling policy and priority (thus need not be root). See
5647 // POSIX "capabilities".
5648 pthread_attr_t attr;
5649 pthread_attr_init( &attr );
5650 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
5651 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
5652 if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
5653 struct sched_param param;
5654 int priority = options->priority;
5655 int min = sched_get_priority_min( SCHED_RR );
5656 int max = sched_get_priority_max( SCHED_RR );
5657 if ( priority < min ) priority = min;
5658 else if ( priority > max ) priority = max;
5659 param.sched_priority = priority;
5660 pthread_attr_setschedparam( &attr, ¶m );
5661 pthread_attr_setschedpolicy( &attr, SCHED_RR );
5664 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
5666 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
5669 stream_.callbackInfo.isRunning = true;
5670 result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
5671 pthread_attr_destroy( &attr );
5673 stream_.callbackInfo.isRunning = false;
5674 errorText_ = "RtApiAlsa::error creating callback thread!";
5683 pthread_cond_destroy( &apiInfo->runnable );
5684 if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
5685 if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
5687 stream_.apiHandle = 0;
5690 for ( int i=0; i<2; i++ ) {
5691 if ( stream_.userBuffer[i] ) {
5692 free( stream_.userBuffer[i] );
5693 stream_.userBuffer[i] = 0;
5697 if ( stream_.deviceBuffer ) {
5698 free( stream_.deviceBuffer );
5699 stream_.deviceBuffer = 0;
5705 void RtApiAlsa :: closeStream()
5707 if ( stream_.state == STREAM_CLOSED ) {
5708 errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
5709 error( RtError::WARNING );
5713 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5714 stream_.callbackInfo.isRunning = false;
5715 MUTEX_LOCK( &stream_.mutex );
5716 if ( stream_.state == STREAM_STOPPED )
5717 pthread_cond_signal( &apiInfo->runnable );
5718 MUTEX_UNLOCK( &stream_.mutex );
5719 pthread_join( stream_.callbackInfo.thread, NULL );
5721 if ( stream_.state == STREAM_RUNNING ) {
5722 stream_.state = STREAM_STOPPED;
5723 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
5724 snd_pcm_drop( apiInfo->handles[0] );
5725 if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
5726 snd_pcm_drop( apiInfo->handles[1] );
5730 pthread_cond_destroy( &apiInfo->runnable );
5731 if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
5732 if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
5734 stream_.apiHandle = 0;
5737 for ( int i=0; i<2; i++ ) {
5738 if ( stream_.userBuffer[i] ) {
5739 free( stream_.userBuffer[i] );
5740 stream_.userBuffer[i] = 0;
5744 if ( stream_.deviceBuffer ) {
5745 free( stream_.deviceBuffer );
5746 stream_.deviceBuffer = 0;
5749 stream_.mode = UNINITIALIZED;
5750 stream_.state = STREAM_CLOSED;
5753 void RtApiAlsa :: startStream()
5755 // This method calls snd_pcm_prepare if the device isn't already in that state.
5758 if ( stream_.state == STREAM_RUNNING ) {
5759 errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
5760 error( RtError::WARNING );
5764 MUTEX_LOCK( &stream_.mutex );
5767 snd_pcm_state_t state;
5768 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5769 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5770 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5771 state = snd_pcm_state( handle[0] );
5772 if ( state != SND_PCM_STATE_PREPARED ) {
5773 result = snd_pcm_prepare( handle[0] );
5775 errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";
5776 errorText_ = errorStream_.str();
5782 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
5783 state = snd_pcm_state( handle[1] );
5784 if ( state != SND_PCM_STATE_PREPARED ) {
5785 result = snd_pcm_prepare( handle[1] );
5787 errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";
5788 errorText_ = errorStream_.str();
5794 stream_.state = STREAM_RUNNING;
5797 MUTEX_UNLOCK( &stream_.mutex );
5799 pthread_cond_signal( &apiInfo->runnable );
5801 if ( result >= 0 ) return;
5802 error( RtError::SYSTEM_ERROR );
5805 void RtApiAlsa :: stopStream()
5808 if ( stream_.state == STREAM_STOPPED ) {
5809 errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
5810 error( RtError::WARNING );
5814 // Change the state before the lock to improve shutdown response.
5815 stream_.state = STREAM_STOPPED;
5816 MUTEX_LOCK( &stream_.mutex );
5819 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5820 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5821 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5822 if ( apiInfo->synchronized )
5823 result = snd_pcm_drop( handle[0] );
5825 result = snd_pcm_drain( handle[0] );
5827 errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";
5828 errorText_ = errorStream_.str();
5833 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
5834 result = snd_pcm_drop( handle[1] );
5836 errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";
5837 errorText_ = errorStream_.str();
5843 MUTEX_UNLOCK( &stream_.mutex );
5845 if ( result >= 0 ) return;
5846 error( RtError::SYSTEM_ERROR );
5849 void RtApiAlsa :: abortStream()
5852 if ( stream_.state == STREAM_STOPPED ) {
5853 errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
5854 error( RtError::WARNING );
5858 // Change the state before the lock to improve shutdown response.
5859 stream_.state = STREAM_STOPPED;
5860 MUTEX_LOCK( &stream_.mutex );
5863 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5864 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5865 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5866 result = snd_pcm_drop( handle[0] );
5868 errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";
5869 errorText_ = errorStream_.str();
5874 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
5875 result = snd_pcm_drop( handle[1] );
5877 errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";
5878 errorText_ = errorStream_.str();
5884 MUTEX_UNLOCK( &stream_.mutex );
5886 if ( result >= 0 ) return;
5887 error( RtError::SYSTEM_ERROR );
5890 void RtApiAlsa :: callbackEvent()
5892 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5893 if ( stream_.state == STREAM_STOPPED ) {
5894 MUTEX_LOCK( &stream_.mutex );
5895 pthread_cond_wait( &apiInfo->runnable, &stream_.mutex );
5896 if ( stream_.state != STREAM_RUNNING ) {
5897 MUTEX_UNLOCK( &stream_.mutex );
5900 MUTEX_UNLOCK( &stream_.mutex );
5903 if ( stream_.state == STREAM_CLOSED ) {
5904 errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
5905 error( RtError::WARNING );
5909 int doStopStream = 0;
5910 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
5911 double streamTime = getStreamTime();
5912 RtAudioStreamStatus status = 0;
5913 if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
5914 status |= RTAUDIO_OUTPUT_UNDERFLOW;
5915 apiInfo->xrun[0] = false;
5917 if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
5918 status |= RTAUDIO_INPUT_OVERFLOW;
5919 apiInfo->xrun[1] = false;
5921 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
5922 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
5924 if ( doStopStream == 2 ) {
5929 MUTEX_LOCK( &stream_.mutex );
5931 // The state might change while waiting on a mutex.
5932 if ( stream_.state == STREAM_STOPPED ) goto unlock;
5938 snd_pcm_sframes_t frames;
5939 RtAudioFormat format;
5940 handle = (snd_pcm_t **) apiInfo->handles;
5942 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
5944 // Setup parameters.
5945 if ( stream_.doConvertBuffer[1] ) {
5946 buffer = stream_.deviceBuffer;
5947 channels = stream_.nDeviceChannels[1];
5948 format = stream_.deviceFormat[1];
5951 buffer = stream_.userBuffer[1];
5952 channels = stream_.nUserChannels[1];
5953 format = stream_.userFormat;
5956 // Read samples from device in interleaved/non-interleaved format.
5957 if ( stream_.deviceInterleaved[1] )
5958 result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
5960 void *bufs[channels];
5961 size_t offset = stream_.bufferSize * formatBytes( format );
5962 for ( int i=0; i<channels; i++ )
5963 bufs[i] = (void *) (buffer + (i * offset));
5964 result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
5967 if ( result < (int) stream_.bufferSize ) {
5968 // Either an error or overrun occured.
5969 if ( result == -EPIPE ) {
5970 snd_pcm_state_t state = snd_pcm_state( handle[1] );
5971 if ( state == SND_PCM_STATE_XRUN ) {
5972 apiInfo->xrun[1] = true;
5973 result = snd_pcm_prepare( handle[1] );
5975 errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
5976 errorText_ = errorStream_.str();
5980 errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
5981 errorText_ = errorStream_.str();
5985 errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
5986 errorText_ = errorStream_.str();
5988 error( RtError::WARNING );
5992 // Do byte swapping if necessary.
5993 if ( stream_.doByteSwap[1] )
5994 byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
5996 // Do buffer conversion if necessary.
5997 if ( stream_.doConvertBuffer[1] )
5998 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
6000 // Check stream latency
6001 result = snd_pcm_delay( handle[1], &frames );
6002 if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
6007 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6009 // Setup parameters and do buffer conversion if necessary.
6010 if ( stream_.doConvertBuffer[0] ) {
6011 buffer = stream_.deviceBuffer;
6012 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
6013 channels = stream_.nDeviceChannels[0];
6014 format = stream_.deviceFormat[0];
6017 buffer = stream_.userBuffer[0];
6018 channels = stream_.nUserChannels[0];
6019 format = stream_.userFormat;
6022 // Do byte swapping if necessary.
6023 if ( stream_.doByteSwap[0] )
6024 byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
6026 // Write samples to device in interleaved/non-interleaved format.
6027 if ( stream_.deviceInterleaved[0] )
6028 result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );
6030 void *bufs[channels];
6031 size_t offset = stream_.bufferSize * formatBytes( format );
6032 for ( int i=0; i<channels; i++ )
6033 bufs[i] = (void *) (buffer + (i * offset));
6034 result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );
6037 if ( result < (int) stream_.bufferSize ) {
6038 // Either an error or underrun occured.
6039 if ( result == -EPIPE ) {
6040 snd_pcm_state_t state = snd_pcm_state( handle[0] );
6041 if ( state == SND_PCM_STATE_XRUN ) {
6042 apiInfo->xrun[0] = true;
6043 result = snd_pcm_prepare( handle[0] );
6045 errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
6046 errorText_ = errorStream_.str();
6050 errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
6051 errorText_ = errorStream_.str();
6055 errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
6056 errorText_ = errorStream_.str();
6058 error( RtError::WARNING );
6062 // Check stream latency
6063 result = snd_pcm_delay( handle[0], &frames );
6064 if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
6068 MUTEX_UNLOCK( &stream_.mutex );
6070 RtApi::tickStreamTime();
6071 if ( doStopStream == 1 ) this->stopStream();
6074 extern "C" void *alsaCallbackHandler( void *ptr )
6076 CallbackInfo *info = (CallbackInfo *) ptr;
6077 RtApiAlsa *object = (RtApiAlsa *) info->object;
6078 bool *isRunning = &info->isRunning;
6080 while ( *isRunning == true ) {
6081 pthread_testcancel();
6082 object->callbackEvent();
6085 pthread_exit( NULL );
6088 //******************** End of __LINUX_ALSA__ *********************//
6092 #if defined(__LINUX_OSS__)
6095 #include <sys/ioctl.h>
6098 #include "soundcard.h"
6102 extern "C" void *ossCallbackHandler(void * ptr);
6104 // A structure to hold various information related to the OSS API
6107 int id[2]; // device ids
6110 pthread_cond_t runnable;
6113 :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
6116 RtApiOss :: RtApiOss()
6118 // Nothing to do here.
6121 RtApiOss :: ~RtApiOss()
6123 if ( stream_.state != STREAM_CLOSED ) closeStream();
6126 unsigned int RtApiOss :: getDeviceCount( void )
6128 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6129 if ( mixerfd == -1 ) {
6130 errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
6131 error( RtError::WARNING );
6135 oss_sysinfo sysinfo;
6136 if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
6138 errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
6139 error( RtError::WARNING );
6144 return sysinfo.numaudios;
6147 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
6149 RtAudio::DeviceInfo info;
6150 info.probed = false;
6152 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6153 if ( mixerfd == -1 ) {
6154 errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
6155 error( RtError::WARNING );
6159 oss_sysinfo sysinfo;
6160 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
6161 if ( result == -1 ) {
6163 errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
6164 error( RtError::WARNING );
6168 unsigned nDevices = sysinfo.numaudios;
6169 if ( nDevices == 0 ) {
6171 errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
6172 error( RtError::INVALID_USE );
6175 if ( device >= nDevices ) {
6177 errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
6178 error( RtError::INVALID_USE );
6181 oss_audioinfo ainfo;
6183 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6185 if ( result == -1 ) {
6186 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6187 errorText_ = errorStream_.str();
6188 error( RtError::WARNING );
6193 if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
6194 if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
6195 if ( ainfo.caps & PCM_CAP_DUPLEX ) {
6196 if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
6197 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
6200 // Probe data formats ... do for input
6201 unsigned long mask = ainfo.iformats;
6202 if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
6203 info.nativeFormats |= RTAUDIO_SINT16;
6204 if ( mask & AFMT_S8 )
6205 info.nativeFormats |= RTAUDIO_SINT8;
6206 if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
6207 info.nativeFormats |= RTAUDIO_SINT32;
6208 if ( mask & AFMT_FLOAT )
6209 info.nativeFormats |= RTAUDIO_FLOAT32;
6210 if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
6211 info.nativeFormats |= RTAUDIO_SINT24;
6213 // Check that we have at least one supported format
6214 if ( info.nativeFormats == 0 ) {
6215 errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
6216 errorText_ = errorStream_.str();
6217 error( RtError::WARNING );
6221 // Probe the supported sample rates.
6222 info.sampleRates.clear();
6223 if ( ainfo.nrates ) {
6224 for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
6225 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6226 if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
6227 info.sampleRates.push_back( SAMPLE_RATES[k] );
6234 // Check min and max rate values;
6235 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6236 if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )
6237 info.sampleRates.push_back( SAMPLE_RATES[k] );
6241 if ( info.sampleRates.size() == 0 ) {
6242 errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
6243 errorText_ = errorStream_.str();
6244 error( RtError::WARNING );
6248 info.name = ainfo.name;
6255 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
6256 unsigned int firstChannel, unsigned int sampleRate,
6257 RtAudioFormat format, unsigned int *bufferSize,
6258 RtAudio::StreamOptions *options )
6260 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6261 if ( mixerfd == -1 ) {
6262 errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
6266 oss_sysinfo sysinfo;
6267 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
6268 if ( result == -1 ) {
6270 errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
6274 unsigned nDevices = sysinfo.numaudios;
6275 if ( nDevices == 0 ) {
6276 // This should not happen because a check is made before this function is called.
6278 errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
6282 if ( device >= nDevices ) {
6283 // This should not happen because a check is made before this function is called.
6285 errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
6289 oss_audioinfo ainfo;
6291 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6293 if ( result == -1 ) {
6294 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6295 errorText_ = errorStream_.str();
6299 // Check if device supports input or output
6300 if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
6301 ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
6302 if ( mode == OUTPUT )
6303 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
6305 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
6306 errorText_ = errorStream_.str();
6311 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6312 if ( mode == OUTPUT )
6314 else { // mode == INPUT
6315 if (stream_.mode == OUTPUT && stream_.device[0] == device) {
6316 // We just set the same device for playback ... close and reopen for duplex (OSS only).
6317 close( handle->id[0] );
6319 if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
6320 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
6321 errorText_ = errorStream_.str();
6324 // Check that the number previously set channels is the same.
6325 if ( stream_.nUserChannels[0] != channels ) {
6326 errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
6327 errorText_ = errorStream_.str();
6336 // Set exclusive access if specified.
6337 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
6339 // Try to open the device.
6341 fd = open( ainfo.devnode, flags, 0 );
6343 if ( errno == EBUSY )
6344 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
6346 errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
6347 errorText_ = errorStream_.str();
6351 // For duplex operation, specifically set this mode (this doesn't seem to work).
6353 if ( flags | O_RDWR ) {
6354 result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
6355 if ( result == -1) {
6356 errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
6357 errorText_ = errorStream_.str();
6363 // Check the device channel support.
6364 stream_.nUserChannels[mode] = channels;
6365 if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
6367 errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
6368 errorText_ = errorStream_.str();
6372 // Set the number of channels.
6373 int deviceChannels = channels + firstChannel;
6374 result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
6375 if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
6377 errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
6378 errorText_ = errorStream_.str();
6381 stream_.nDeviceChannels[mode] = deviceChannels;
6383 // Get the data format mask
6385 result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
6386 if ( result == -1 ) {
6388 errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
6389 errorText_ = errorStream_.str();
6393 // Determine how to set the device format.
6394 stream_.userFormat = format;
6395 int deviceFormat = -1;
6396 stream_.doByteSwap[mode] = false;
6397 if ( format == RTAUDIO_SINT8 ) {
6398 if ( mask & AFMT_S8 ) {
6399 deviceFormat = AFMT_S8;
6400 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
6403 else if ( format == RTAUDIO_SINT16 ) {
6404 if ( mask & AFMT_S16_NE ) {
6405 deviceFormat = AFMT_S16_NE;
6406 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6408 else if ( mask & AFMT_S16_OE ) {
6409 deviceFormat = AFMT_S16_OE;
6410 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6411 stream_.doByteSwap[mode] = true;
6414 else if ( format == RTAUDIO_SINT24 ) {
6415 if ( mask & AFMT_S24_NE ) {
6416 deviceFormat = AFMT_S24_NE;
6417 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6419 else if ( mask & AFMT_S24_OE ) {
6420 deviceFormat = AFMT_S24_OE;
6421 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6422 stream_.doByteSwap[mode] = true;
6425 else if ( format == RTAUDIO_SINT32 ) {
6426 if ( mask & AFMT_S32_NE ) {
6427 deviceFormat = AFMT_S32_NE;
6428 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6430 else if ( mask & AFMT_S32_OE ) {
6431 deviceFormat = AFMT_S32_OE;
6432 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6433 stream_.doByteSwap[mode] = true;
6437 if ( deviceFormat == -1 ) {
6438 // The user requested format is not natively supported by the device.
6439 if ( mask & AFMT_S16_NE ) {
6440 deviceFormat = AFMT_S16_NE;
6441 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6443 else if ( mask & AFMT_S32_NE ) {
6444 deviceFormat = AFMT_S32_NE;
6445 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6447 else if ( mask & AFMT_S24_NE ) {
6448 deviceFormat = AFMT_S24_NE;
6449 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6451 else if ( mask & AFMT_S16_OE ) {
6452 deviceFormat = AFMT_S16_OE;
6453 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6454 stream_.doByteSwap[mode] = true;
6456 else if ( mask & AFMT_S32_OE ) {
6457 deviceFormat = AFMT_S32_OE;
6458 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6459 stream_.doByteSwap[mode] = true;
6461 else if ( mask & AFMT_S24_OE ) {
6462 deviceFormat = AFMT_S24_OE;
6463 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6464 stream_.doByteSwap[mode] = true;
6466 else if ( mask & AFMT_S8) {
6467 deviceFormat = AFMT_S8;
6468 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
6472 if ( stream_.deviceFormat[mode] == 0 ) {
6473 // This really shouldn't happen ...
6475 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
6476 errorText_ = errorStream_.str();
6480 // Set the data format.
6481 int temp = deviceFormat;
6482 result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
6483 if ( result == -1 || deviceFormat != temp ) {
6485 errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
6486 errorText_ = errorStream_.str();
6490 // Attempt to set the buffer size. According to OSS, the minimum
6491 // number of buffers is two. The supposed minimum buffer size is 16
6492 // bytes, so that will be our lower bound. The argument to this
6493 // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
6494 // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
6495 // We'll check the actual value used near the end of the setup
6497 int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
6498 if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
6500 if ( options ) buffers = options->numberOfBuffers;
6501 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
6502 if ( buffers < 2 ) buffers = 3;
6503 temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
6504 result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
6505 if ( result == -1 ) {
6507 errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
6508 errorText_ = errorStream_.str();
6511 stream_.nBuffers = buffers;
6513 // Save buffer size (in sample frames).
6514 *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
6515 stream_.bufferSize = *bufferSize;
6517 // Set the sample rate.
6518 int srate = sampleRate;
6519 result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
6520 if ( result == -1 ) {
6522 errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
6523 errorText_ = errorStream_.str();
6527 // Verify the sample rate setup worked.
6528 if ( abs( srate - sampleRate ) > 100 ) {
6530 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
6531 errorText_ = errorStream_.str();
6534 stream_.sampleRate = sampleRate;
6536 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
6537 // We're doing duplex setup here.
6538 stream_.deviceFormat[0] = stream_.deviceFormat[1];
6539 stream_.nDeviceChannels[0] = deviceChannels;
6542 // Set interleaving parameters.
6543 stream_.userInterleaved = true;
6544 stream_.deviceInterleaved[mode] = true;
6545 if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
6546 stream_.userInterleaved = false;
6548 // Set flags for buffer conversion
6549 stream_.doConvertBuffer[mode] = false;
6550 if ( stream_.userFormat != stream_.deviceFormat[mode] )
6551 stream_.doConvertBuffer[mode] = true;
6552 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
6553 stream_.doConvertBuffer[mode] = true;
6554 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
6555 stream_.nUserChannels[mode] > 1 )
6556 stream_.doConvertBuffer[mode] = true;
6558 // Allocate the stream handles if necessary and then save.
6559 if ( stream_.apiHandle == 0 ) {
6561 handle = new OssHandle;
6563 catch ( std::bad_alloc& ) {
6564 errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
6568 if ( pthread_cond_init( &handle->runnable, NULL ) ) {
6569 errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
6573 stream_.apiHandle = (void *) handle;
6576 handle = (OssHandle *) stream_.apiHandle;
6578 handle->id[mode] = fd;
6580 // Allocate necessary internal buffers.
6581 unsigned long bufferBytes;
6582 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
6583 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
6584 if ( stream_.userBuffer[mode] == NULL ) {
6585 errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
6589 if ( stream_.doConvertBuffer[mode] ) {
6591 bool makeBuffer = true;
6592 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
6593 if ( mode == INPUT ) {
6594 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
6595 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
6596 if ( bufferBytes <= bytesOut ) makeBuffer = false;
6601 bufferBytes *= *bufferSize;
6602 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
6603 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
6604 if ( stream_.deviceBuffer == NULL ) {
6605 errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
6611 stream_.device[mode] = device;
6612 stream_.state = STREAM_STOPPED;
6614 // Setup the buffer conversion information structure.
6615 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
6617 // Setup thread if necessary.
6618 if ( stream_.mode == OUTPUT && mode == INPUT ) {
6619 // We had already set up an output stream.
6620 stream_.mode = DUPLEX;
6621 if ( stream_.device[0] == device ) handle->id[0] = fd;
6624 stream_.mode = mode;
6626 // Setup callback thread.
6627 stream_.callbackInfo.object = (void *) this;
6629 // Set the thread attributes for joinable and realtime scheduling
6630 // priority. The higher priority will only take affect if the
6631 // program is run as root or suid.
6632 pthread_attr_t attr;
6633 pthread_attr_init( &attr );
6634 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
6635 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
6636 if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
6637 struct sched_param param;
6638 int priority = options->priority;
6639 int min = sched_get_priority_min( SCHED_RR );
6640 int max = sched_get_priority_max( SCHED_RR );
6641 if ( priority < min ) priority = min;
6642 else if ( priority > max ) priority = max;
6643 param.sched_priority = priority;
6644 pthread_attr_setschedparam( &attr, ¶m );
6645 pthread_attr_setschedpolicy( &attr, SCHED_RR );
6648 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
6650 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
6653 stream_.callbackInfo.isRunning = true;
6654 result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
6655 pthread_attr_destroy( &attr );
6657 stream_.callbackInfo.isRunning = false;
6658 errorText_ = "RtApiOss::error creating callback thread!";
6667 pthread_cond_destroy( &handle->runnable );
6668 if ( handle->id[0] ) close( handle->id[0] );
6669 if ( handle->id[1] ) close( handle->id[1] );
6671 stream_.apiHandle = 0;
6674 for ( int i=0; i<2; i++ ) {
6675 if ( stream_.userBuffer[i] ) {
6676 free( stream_.userBuffer[i] );
6677 stream_.userBuffer[i] = 0;
6681 if ( stream_.deviceBuffer ) {
6682 free( stream_.deviceBuffer );
6683 stream_.deviceBuffer = 0;
6689 void RtApiOss :: closeStream()
6691 if ( stream_.state == STREAM_CLOSED ) {
6692 errorText_ = "RtApiOss::closeStream(): no open stream to close!";
6693 error( RtError::WARNING );
6697 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6698 stream_.callbackInfo.isRunning = false;
6699 MUTEX_LOCK( &stream_.mutex );
6700 if ( stream_.state == STREAM_STOPPED )
6701 pthread_cond_signal( &handle->runnable );
6702 MUTEX_UNLOCK( &stream_.mutex );
6703 pthread_join( stream_.callbackInfo.thread, NULL );
6705 if ( stream_.state == STREAM_RUNNING ) {
6706 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
6707 ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6709 ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6710 stream_.state = STREAM_STOPPED;
6714 pthread_cond_destroy( &handle->runnable );
6715 if ( handle->id[0] ) close( handle->id[0] );
6716 if ( handle->id[1] ) close( handle->id[1] );
6718 stream_.apiHandle = 0;
6721 for ( int i=0; i<2; i++ ) {
6722 if ( stream_.userBuffer[i] ) {
6723 free( stream_.userBuffer[i] );
6724 stream_.userBuffer[i] = 0;
6728 if ( stream_.deviceBuffer ) {
6729 free( stream_.deviceBuffer );
6730 stream_.deviceBuffer = 0;
6733 stream_.mode = UNINITIALIZED;
6734 stream_.state = STREAM_CLOSED;
6737 void RtApiOss :: startStream()
6740 if ( stream_.state == STREAM_RUNNING ) {
6741 errorText_ = "RtApiOss::startStream(): the stream is already running!";
6742 error( RtError::WARNING );
6746 MUTEX_LOCK( &stream_.mutex );
6748 stream_.state = STREAM_RUNNING;
6750 // No need to do anything else here ... OSS automatically starts
6751 // when fed samples.
6753 MUTEX_UNLOCK( &stream_.mutex );
6755 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6756 pthread_cond_signal( &handle->runnable );
6759 void RtApiOss :: stopStream()
6762 if ( stream_.state == STREAM_STOPPED ) {
6763 errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
6764 error( RtError::WARNING );
6768 // Change the state before the lock to improve shutdown response
6769 // when using a callback.
6770 stream_.state = STREAM_STOPPED;
6771 MUTEX_LOCK( &stream_.mutex );
6774 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6775 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6777 // Flush the output with zeros a few times.
6780 RtAudioFormat format;
6782 if ( stream_.doConvertBuffer[0] ) {
6783 buffer = stream_.deviceBuffer;
6784 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
6785 format = stream_.deviceFormat[0];
6788 buffer = stream_.userBuffer[0];
6789 samples = stream_.bufferSize * stream_.nUserChannels[0];
6790 format = stream_.userFormat;
6793 memset( buffer, 0, samples * formatBytes(format) );
6794 for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
6795 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6796 if ( result == -1 ) {
6797 errorText_ = "RtApiOss::stopStream: audio write error.";
6798 error( RtError::WARNING );
6802 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6803 if ( result == -1 ) {
6804 errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
6805 errorText_ = errorStream_.str();
6808 handle->triggered = false;
6811 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
6812 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6813 if ( result == -1 ) {
6814 errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
6815 errorText_ = errorStream_.str();
6821 MUTEX_UNLOCK( &stream_.mutex );
6823 stream_.state = STREAM_STOPPED;
6824 if ( result != -1 ) return;
6825 error( RtError::SYSTEM_ERROR );
6828 void RtApiOss :: abortStream()
6831 if ( stream_.state == STREAM_STOPPED ) {
6832 errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
6833 error( RtError::WARNING );
6837 // Change the state before the lock to improve shutdown response
6838 // when using a callback.
6839 stream_.state = STREAM_STOPPED;
6840 MUTEX_LOCK( &stream_.mutex );
6843 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6844 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6845 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6846 if ( result == -1 ) {
6847 errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
6848 errorText_ = errorStream_.str();
6851 handle->triggered = false;
6854 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
6855 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6856 if ( result == -1 ) {
6857 errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
6858 errorText_ = errorStream_.str();
6864 MUTEX_UNLOCK( &stream_.mutex );
6866 stream_.state = STREAM_STOPPED;
6867 if ( result != -1 ) return;
6868 error( RtError::SYSTEM_ERROR );
6871 void RtApiOss :: callbackEvent()
6873 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6874 if ( stream_.state == STREAM_STOPPED ) {
6875 MUTEX_LOCK( &stream_.mutex );
6876 pthread_cond_wait( &handle->runnable, &stream_.mutex );
6877 if ( stream_.state != STREAM_RUNNING ) {
6878 MUTEX_UNLOCK( &stream_.mutex );
6881 MUTEX_UNLOCK( &stream_.mutex );
6884 if ( stream_.state == STREAM_CLOSED ) {
6885 errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
6886 error( RtError::WARNING );
6890 // Invoke user callback to get fresh output data.
6891 int doStopStream = 0;
6892 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
6893 double streamTime = getStreamTime();
6894 RtAudioStreamStatus status = 0;
6895 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
6896 status |= RTAUDIO_OUTPUT_UNDERFLOW;
6897 handle->xrun[0] = false;
6899 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
6900 status |= RTAUDIO_INPUT_OVERFLOW;
6901 handle->xrun[1] = false;
6903 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
6904 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
6905 if ( doStopStream == 2 ) {
6906 this->abortStream();
6910 MUTEX_LOCK( &stream_.mutex );
6912 // The state might change while waiting on a mutex.
6913 if ( stream_.state == STREAM_STOPPED ) goto unlock;
6918 RtAudioFormat format;
6920 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6922 // Setup parameters and do buffer conversion if necessary.
6923 if ( stream_.doConvertBuffer[0] ) {
6924 buffer = stream_.deviceBuffer;
6925 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
6926 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
6927 format = stream_.deviceFormat[0];
6930 buffer = stream_.userBuffer[0];
6931 samples = stream_.bufferSize * stream_.nUserChannels[0];
6932 format = stream_.userFormat;
6935 // Do byte swapping if necessary.
6936 if ( stream_.doByteSwap[0] )
6937 byteSwapBuffer( buffer, samples, format );
6939 if ( stream_.mode == DUPLEX && handle->triggered == false ) {
6941 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
6942 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6943 trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
6944 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
6945 handle->triggered = true;
6948 // Write samples to device.
6949 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6951 if ( result == -1 ) {
6952 // We'll assume this is an underrun, though there isn't a
6953 // specific means for determining that.
6954 handle->xrun[0] = true;
6955 errorText_ = "RtApiOss::callbackEvent: audio write error.";
6956 error( RtError::WARNING );
6957 // Continue on to input section.
6961 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
6963 // Setup parameters.
6964 if ( stream_.doConvertBuffer[1] ) {
6965 buffer = stream_.deviceBuffer;
6966 samples = stream_.bufferSize * stream_.nDeviceChannels[1];
6967 format = stream_.deviceFormat[1];
6970 buffer = stream_.userBuffer[1];
6971 samples = stream_.bufferSize * stream_.nUserChannels[1];
6972 format = stream_.userFormat;
6975 // Read samples from device.
6976 result = read( handle->id[1], buffer, samples * formatBytes(format) );
6978 if ( result == -1 ) {
6979 // We'll assume this is an overrun, though there isn't a
6980 // specific means for determining that.
6981 handle->xrun[1] = true;
6982 errorText_ = "RtApiOss::callbackEvent: audio read error.";
6983 error( RtError::WARNING );
6987 // Do byte swapping if necessary.
6988 if ( stream_.doByteSwap[1] )
6989 byteSwapBuffer( buffer, samples, format );
6991 // Do buffer conversion if necessary.
6992 if ( stream_.doConvertBuffer[1] )
6993 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
6997 MUTEX_UNLOCK( &stream_.mutex );
6999 RtApi::tickStreamTime();
7000 if ( doStopStream == 1 ) this->stopStream();
7003 extern "C" void *ossCallbackHandler( void *ptr )
7005 CallbackInfo *info = (CallbackInfo *) ptr;
7006 RtApiOss *object = (RtApiOss *) info->object;
7007 bool *isRunning = &info->isRunning;
7009 while ( *isRunning == true ) {
7010 pthread_testcancel();
7011 object->callbackEvent();
7014 pthread_exit( NULL );
7017 //******************** End of __LINUX_OSS__ *********************//
7021 // *************************************************** //
7023 // Protected common (OS-independent) RtAudio methods.
7025 // *************************************************** //
7027 // This method can be modified to control the behavior of error
7028 // message printing.
7029 void RtApi :: error( RtError::Type type )
7031 errorStream_.str(""); // clear the ostringstream
7032 if ( type == RtError::WARNING && showWarnings_ == true )
7033 std::cerr << '\n' << errorText_ << "\n\n";
7035 throw( RtError( errorText_, type ) );
7038 void RtApi :: verifyStream()
7040 if ( stream_.state == STREAM_CLOSED ) {
7041 errorText_ = "RtApi:: a stream is not open!";
7042 error( RtError::INVALID_USE );
7046 void RtApi :: clearStreamInfo()
7048 stream_.mode = UNINITIALIZED;
7049 stream_.state = STREAM_CLOSED;
7050 stream_.sampleRate = 0;
7051 stream_.bufferSize = 0;
7052 stream_.nBuffers = 0;
7053 stream_.userFormat = 0;
7054 stream_.userInterleaved = true;
7055 stream_.streamTime = 0.0;
7056 stream_.apiHandle = 0;
7057 stream_.deviceBuffer = 0;
7058 stream_.callbackInfo.callback = 0;
7059 stream_.callbackInfo.userData = 0;
7060 stream_.callbackInfo.isRunning = false;
7061 for ( int i=0; i<2; i++ ) {
7062 stream_.device[i] = 11111;
7063 stream_.doConvertBuffer[i] = false;
7064 stream_.deviceInterleaved[i] = true;
7065 stream_.doByteSwap[i] = false;
7066 stream_.nUserChannels[i] = 0;
7067 stream_.nDeviceChannels[i] = 0;
7068 stream_.channelOffset[i] = 0;
7069 stream_.deviceFormat[i] = 0;
7070 stream_.latency[i] = 0;
7071 stream_.userBuffer[i] = 0;
7072 stream_.convertInfo[i].channels = 0;
7073 stream_.convertInfo[i].inJump = 0;
7074 stream_.convertInfo[i].outJump = 0;
7075 stream_.convertInfo[i].inFormat = 0;
7076 stream_.convertInfo[i].outFormat = 0;
7077 stream_.convertInfo[i].inOffset.clear();
7078 stream_.convertInfo[i].outOffset.clear();
7082 unsigned int RtApi :: formatBytes( RtAudioFormat format )
7084 if ( format == RTAUDIO_SINT16 )
7086 else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
7087 format == RTAUDIO_FLOAT32 )
7089 else if ( format == RTAUDIO_FLOAT64 )
7091 else if ( format == RTAUDIO_SINT8 )
7094 errorText_ = "RtApi::formatBytes: undefined format.";
7095 error( RtError::WARNING );
7100 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
7102 if ( mode == INPUT ) { // convert device to user buffer
7103 stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
7104 stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
7105 stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
7106 stream_.convertInfo[mode].outFormat = stream_.userFormat;
7108 else { // convert user to device buffer
7109 stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
7110 stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
7111 stream_.convertInfo[mode].inFormat = stream_.userFormat;
7112 stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
7115 if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
7116 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
7118 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
7120 // Set up the interleave/deinterleave offsets.
7121 if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
7122 if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
7123 ( mode == INPUT && stream_.userInterleaved ) ) {
7124 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7125 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
7126 stream_.convertInfo[mode].outOffset.push_back( k );
7127 stream_.convertInfo[mode].inJump = 1;
7131 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7132 stream_.convertInfo[mode].inOffset.push_back( k );
7133 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
7134 stream_.convertInfo[mode].outJump = 1;
7138 else { // no (de)interleaving
7139 if ( stream_.userInterleaved ) {
7140 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7141 stream_.convertInfo[mode].inOffset.push_back( k );
7142 stream_.convertInfo[mode].outOffset.push_back( k );
7146 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
7147 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
7148 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
7149 stream_.convertInfo[mode].inJump = 1;
7150 stream_.convertInfo[mode].outJump = 1;
7155 // Add channel offset.
7156 if ( firstChannel > 0 ) {
7157 if ( stream_.deviceInterleaved[mode] ) {
7158 if ( mode == OUTPUT ) {
7159 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7160 stream_.convertInfo[mode].outOffset[k] += firstChannel;
7163 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7164 stream_.convertInfo[mode].inOffset[k] += firstChannel;
7168 if ( mode == OUTPUT ) {
7169 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7170 stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
7173 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
7174 stream_.convertInfo[mode].inOffset[k] += ( firstChannel * stream_.bufferSize );
7180 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
7182 // This function does format conversion, input/output channel compensation, and
7183 // data interleaving/deinterleaving. 24-bit integers are assumed to occupy
7184 // the upper three bytes of a 32-bit integer.
7186 // Clear our device buffer when in/out duplex device channels are different
7187 if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
7188 ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
7189 memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
7192 if (info.outFormat == RTAUDIO_FLOAT64) {
7194 Float64 *out = (Float64 *)outBuffer;
7196 if (info.inFormat == RTAUDIO_SINT8) {
7197 signed char *in = (signed char *)inBuffer;
7198 scale = 1.0 / 127.5;
7199 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7200 for (j=0; j<info.channels; j++) {
7201 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7202 out[info.outOffset[j]] += 0.5;
7203 out[info.outOffset[j]] *= scale;
7206 out += info.outJump;
7209 else if (info.inFormat == RTAUDIO_SINT16) {
7210 Int16 *in = (Int16 *)inBuffer;
7211 scale = 1.0 / 32767.5;
7212 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7213 for (j=0; j<info.channels; j++) {
7214 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7215 out[info.outOffset[j]] += 0.5;
7216 out[info.outOffset[j]] *= scale;
7219 out += info.outJump;
7222 else if (info.inFormat == RTAUDIO_SINT24) {
7223 Int32 *in = (Int32 *)inBuffer;
7224 scale = 1.0 / 8388607.5;
7225 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7226 for (j=0; j<info.channels; j++) {
7227 out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
7228 out[info.outOffset[j]] += 0.5;
7229 out[info.outOffset[j]] *= scale;
7232 out += info.outJump;
7235 else if (info.inFormat == RTAUDIO_SINT32) {
7236 Int32 *in = (Int32 *)inBuffer;
7237 scale = 1.0 / 2147483647.5;
7238 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7239 for (j=0; j<info.channels; j++) {
7240 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7241 out[info.outOffset[j]] += 0.5;
7242 out[info.outOffset[j]] *= scale;
7245 out += info.outJump;
7248 else if (info.inFormat == RTAUDIO_FLOAT32) {
7249 Float32 *in = (Float32 *)inBuffer;
7250 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7251 for (j=0; j<info.channels; j++) {
7252 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7255 out += info.outJump;
7258 else if (info.inFormat == RTAUDIO_FLOAT64) {
7259 // Channel compensation and/or (de)interleaving only.
7260 Float64 *in = (Float64 *)inBuffer;
7261 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7262 for (j=0; j<info.channels; j++) {
7263 out[info.outOffset[j]] = in[info.inOffset[j]];
7266 out += info.outJump;
7270 else if (info.outFormat == RTAUDIO_FLOAT32) {
7272 Float32 *out = (Float32 *)outBuffer;
7274 if (info.inFormat == RTAUDIO_SINT8) {
7275 signed char *in = (signed char *)inBuffer;
7276 scale = (Float32) ( 1.0 / 127.5 );
7277 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7278 for (j=0; j<info.channels; j++) {
7279 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7280 out[info.outOffset[j]] += 0.5;
7281 out[info.outOffset[j]] *= scale;
7284 out += info.outJump;
7287 else if (info.inFormat == RTAUDIO_SINT16) {
7288 Int16 *in = (Int16 *)inBuffer;
7289 scale = (Float32) ( 1.0 / 32767.5 );
7290 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7291 for (j=0; j<info.channels; j++) {
7292 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7293 out[info.outOffset[j]] += 0.5;
7294 out[info.outOffset[j]] *= scale;
7297 out += info.outJump;
7300 else if (info.inFormat == RTAUDIO_SINT24) {
7301 Int32 *in = (Int32 *)inBuffer;
7302 scale = (Float32) ( 1.0 / 8388607.5 );
7303 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7304 for (j=0; j<info.channels; j++) {
7305 out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
7306 out[info.outOffset[j]] += 0.5;
7307 out[info.outOffset[j]] *= scale;
7310 out += info.outJump;
7313 else if (info.inFormat == RTAUDIO_SINT32) {
7314 Int32 *in = (Int32 *)inBuffer;
7315 scale = (Float32) ( 1.0 / 2147483647.5 );
7316 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7317 for (j=0; j<info.channels; j++) {
7318 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7319 out[info.outOffset[j]] += 0.5;
7320 out[info.outOffset[j]] *= scale;
7323 out += info.outJump;
7326 else if (info.inFormat == RTAUDIO_FLOAT32) {
7327 // Channel compensation and/or (de)interleaving only.
7328 Float32 *in = (Float32 *)inBuffer;
7329 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7330 for (j=0; j<info.channels; j++) {
7331 out[info.outOffset[j]] = in[info.inOffset[j]];
7334 out += info.outJump;
7337 else if (info.inFormat == RTAUDIO_FLOAT64) {
7338 Float64 *in = (Float64 *)inBuffer;
7339 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7340 for (j=0; j<info.channels; j++) {
7341 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7344 out += info.outJump;
7348 else if (info.outFormat == RTAUDIO_SINT32) {
7349 Int32 *out = (Int32 *)outBuffer;
7350 if (info.inFormat == RTAUDIO_SINT8) {
7351 signed char *in = (signed char *)inBuffer;
7352 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7353 for (j=0; j<info.channels; j++) {
7354 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7355 out[info.outOffset[j]] <<= 24;
7358 out += info.outJump;
7361 else if (info.inFormat == RTAUDIO_SINT16) {
7362 Int16 *in = (Int16 *)inBuffer;
7363 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7364 for (j=0; j<info.channels; j++) {
7365 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7366 out[info.outOffset[j]] <<= 16;
7369 out += info.outJump;
7372 else if (info.inFormat == RTAUDIO_SINT24) {
7373 Int32 *in = (Int32 *)inBuffer;
7374 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7375 for (j=0; j<info.channels; j++) {
7376 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7377 out[info.outOffset[j]] <<= 8;
7380 out += info.outJump;
7383 else if (info.inFormat == RTAUDIO_SINT32) {
7384 // Channel compensation and/or (de)interleaving only.
7385 Int32 *in = (Int32 *)inBuffer;
7386 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7387 for (j=0; j<info.channels; j++) {
7388 out[info.outOffset[j]] = in[info.inOffset[j]];
7391 out += info.outJump;
7394 else if (info.inFormat == RTAUDIO_FLOAT32) {
7395 Float32 *in = (Float32 *)inBuffer;
7396 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7397 for (j=0; j<info.channels; j++) {
7398 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
7401 out += info.outJump;
7404 else if (info.inFormat == RTAUDIO_FLOAT64) {
7405 Float64 *in = (Float64 *)inBuffer;
7406 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7407 for (j=0; j<info.channels; j++) {
7408 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
7411 out += info.outJump;
7415 else if (info.outFormat == RTAUDIO_SINT24) {
7416 Int32 *out = (Int32 *)outBuffer;
7417 if (info.inFormat == RTAUDIO_SINT8) {
7418 signed char *in = (signed char *)inBuffer;
7419 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7420 for (j=0; j<info.channels; j++) {
7421 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7422 out[info.outOffset[j]] <<= 16;
7425 out += info.outJump;
7428 else if (info.inFormat == RTAUDIO_SINT16) {
7429 Int16 *in = (Int16 *)inBuffer;
7430 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7431 for (j=0; j<info.channels; j++) {
7432 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7433 out[info.outOffset[j]] <<= 8;
7436 out += info.outJump;
7439 else if (info.inFormat == RTAUDIO_SINT24) {
7440 // Channel compensation and/or (de)interleaving only.
7441 Int32 *in = (Int32 *)inBuffer;
7442 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7443 for (j=0; j<info.channels; j++) {
7444 out[info.outOffset[j]] = in[info.inOffset[j]];
7447 out += info.outJump;
7450 else if (info.inFormat == RTAUDIO_SINT32) {
7451 Int32 *in = (Int32 *)inBuffer;
7452 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7453 for (j=0; j<info.channels; j++) {
7454 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7455 out[info.outOffset[j]] >>= 8;
7458 out += info.outJump;
7461 else if (info.inFormat == RTAUDIO_FLOAT32) {
7462 Float32 *in = (Float32 *)inBuffer;
7463 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7464 for (j=0; j<info.channels; j++) {
7465 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
7468 out += info.outJump;
7471 else if (info.inFormat == RTAUDIO_FLOAT64) {
7472 Float64 *in = (Float64 *)inBuffer;
7473 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7474 for (j=0; j<info.channels; j++) {
7475 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
7478 out += info.outJump;
7482 else if (info.outFormat == RTAUDIO_SINT16) {
7483 Int16 *out = (Int16 *)outBuffer;
7484 if (info.inFormat == RTAUDIO_SINT8) {
7485 signed char *in = (signed char *)inBuffer;
7486 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7487 for (j=0; j<info.channels; j++) {
7488 out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
7489 out[info.outOffset[j]] <<= 8;
7492 out += info.outJump;
7495 else if (info.inFormat == RTAUDIO_SINT16) {
7496 // Channel compensation and/or (de)interleaving only.
7497 Int16 *in = (Int16 *)inBuffer;
7498 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7499 for (j=0; j<info.channels; j++) {
7500 out[info.outOffset[j]] = in[info.inOffset[j]];
7503 out += info.outJump;
7506 else if (info.inFormat == RTAUDIO_SINT24) {
7507 Int32 *in = (Int32 *)inBuffer;
7508 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7509 for (j=0; j<info.channels; j++) {
7510 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 8) & 0x0000ffff);
7513 out += info.outJump;
7516 else if (info.inFormat == RTAUDIO_SINT32) {
7517 Int32 *in = (Int32 *)inBuffer;
7518 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7519 for (j=0; j<info.channels; j++) {
7520 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
7523 out += info.outJump;
7526 else if (info.inFormat == RTAUDIO_FLOAT32) {
7527 Float32 *in = (Float32 *)inBuffer;
7528 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7529 for (j=0; j<info.channels; j++) {
7530 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
7533 out += info.outJump;
7536 else if (info.inFormat == RTAUDIO_FLOAT64) {
7537 Float64 *in = (Float64 *)inBuffer;
7538 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7539 for (j=0; j<info.channels; j++) {
7540 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
7543 out += info.outJump;
7547 else if (info.outFormat == RTAUDIO_SINT8) {
7548 signed char *out = (signed char *)outBuffer;
7549 if (info.inFormat == RTAUDIO_SINT8) {
7550 // Channel compensation and/or (de)interleaving only.
7551 signed char *in = (signed char *)inBuffer;
7552 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7553 for (j=0; j<info.channels; j++) {
7554 out[info.outOffset[j]] = in[info.inOffset[j]];
7557 out += info.outJump;
7560 if (info.inFormat == RTAUDIO_SINT16) {
7561 Int16 *in = (Int16 *)inBuffer;
7562 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7563 for (j=0; j<info.channels; j++) {
7564 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
7567 out += info.outJump;
7570 else if (info.inFormat == RTAUDIO_SINT24) {
7571 Int32 *in = (Int32 *)inBuffer;
7572 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7573 for (j=0; j<info.channels; j++) {
7574 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 16) & 0x000000ff);
7577 out += info.outJump;
7580 else if (info.inFormat == RTAUDIO_SINT32) {
7581 Int32 *in = (Int32 *)inBuffer;
7582 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7583 for (j=0; j<info.channels; j++) {
7584 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
7587 out += info.outJump;
7590 else if (info.inFormat == RTAUDIO_FLOAT32) {
7591 Float32 *in = (Float32 *)inBuffer;
7592 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7593 for (j=0; j<info.channels; j++) {
7594 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
7597 out += info.outJump;
7600 else if (info.inFormat == RTAUDIO_FLOAT64) {
7601 Float64 *in = (Float64 *)inBuffer;
7602 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7603 for (j=0; j<info.channels; j++) {
7604 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
7607 out += info.outJump;
7613 //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }
7614 //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }
7615 //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }
7617 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
7623 if ( format == RTAUDIO_SINT16 ) {
7624 for ( unsigned int i=0; i<samples; i++ ) {
7625 // Swap 1st and 2nd bytes.
7630 // Increment 2 bytes.
7634 else if ( format == RTAUDIO_SINT24 ||
7635 format == RTAUDIO_SINT32 ||
7636 format == RTAUDIO_FLOAT32 ) {
7637 for ( unsigned int i=0; i<samples; i++ ) {
7638 // Swap 1st and 4th bytes.
7643 // Swap 2nd and 3rd bytes.
7649 // Increment 3 more bytes.
7653 else if ( format == RTAUDIO_FLOAT64 ) {
7654 for ( unsigned int i=0; i<samples; i++ ) {
7655 // Swap 1st and 8th bytes
7660 // Swap 2nd and 7th bytes
7666 // Swap 3rd and 6th bytes
7672 // Swap 4th and 5th bytes
7678 // Increment 5 more bytes.
7684 // Indentation settings for Vim and Emacs
7687 // c-basic-offset: 2
7688 // indent-tabs-mode: nil
7691 // vim: et sts=2 sw=2