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-2007 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
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;
363 // *************************************************** //
365 // OS/API-specific methods.
367 // *************************************************** //
369 #if defined(__MACOSX_CORE__)
371 // The OS X CoreAudio API is designed to use a separate callback
372 // procedure for each of its audio devices. A single RtAudio duplex
373 // stream using two different devices is supported here, though it
374 // cannot be guaranteed to always behave correctly because we cannot
375 // synchronize these two callbacks.
377 // A property listener is installed for over/underrun information.
378 // However, no functionality is currently provided to allow property
379 // listeners to trigger user handlers because it is unclear what could
380 // be done if a critical stream parameter (buffer size, sample rate,
381 // device disconnect) notification arrived. The listeners entail
382 // quite a bit of extra code and most likely, a user program wouldn't
383 // be prepared for the result anyway. However, we do provide a flag
384 // to the client callback function to inform of an over/underrun.
386 // The mechanism for querying and setting system parameters was
387 // updated (and perhaps simplified) in OS-X version 10.4. However,
388 // since 10.4 support is not necessarily available to all users, I've
389 // decided not to update the respective code at this time. Perhaps
390 // this will happen when Apple makes 10.4 free for everyone. :-)
392 // A structure to hold various information related to the CoreAudio API
395 AudioDeviceID id[2]; // device ids
396 UInt32 iStream[2]; // device stream index (first for mono mode)
399 pthread_cond_t condition;
400 int drainCounter; // Tracks callback counts when draining
401 bool internalDrain; // Indicates if stop is initiated from callback or not.
404 :deviceBuffer(0), drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
407 RtApiCore :: RtApiCore()
409 // Nothing to do here.
412 RtApiCore :: ~RtApiCore()
414 // The subclass destructor gets called before the base class
415 // destructor, so close an existing stream before deallocating
416 // apiDeviceId memory.
417 if ( stream_.state != STREAM_CLOSED ) closeStream();
420 unsigned int RtApiCore :: getDeviceCount( void )
422 // Find out how many audio devices there are, if any.
424 OSStatus result = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &dataSize, NULL );
425 if ( result != noErr ) {
426 errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
427 error( RtError::WARNING );
431 return dataSize / sizeof( AudioDeviceID );
434 unsigned int RtApiCore :: getDefaultInputDevice( void )
436 unsigned int nDevices = getDeviceCount();
437 if ( nDevices <= 1 ) return 0;
440 UInt32 dataSize = sizeof( AudioDeviceID );
441 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
444 if ( result != noErr ) {
445 errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
446 error( RtError::WARNING );
450 dataSize *= nDevices;
451 AudioDeviceID deviceList[ nDevices ];
452 result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
453 if ( result != noErr ) {
454 errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
455 error( RtError::WARNING );
459 for ( unsigned int i=0; i<nDevices; i++ )
460 if ( id == deviceList[i] ) return i;
462 errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
463 error( RtError::WARNING );
467 unsigned int RtApiCore :: getDefaultOutputDevice( void )
469 unsigned int nDevices = getDeviceCount();
470 if ( nDevices <= 1 ) return 0;
473 UInt32 dataSize = sizeof( AudioDeviceID );
474 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
477 if ( result != noErr ) {
478 errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
479 error( RtError::WARNING );
483 dataSize *= nDevices;
484 AudioDeviceID deviceList[ nDevices ];
485 result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
486 if ( result != noErr ) {
487 errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
488 error( RtError::WARNING );
492 for ( unsigned int i=0; i<nDevices; i++ )
493 if ( id == deviceList[i] ) return i;
495 errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
496 error( RtError::WARNING );
500 RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
502 RtAudio::DeviceInfo info;
506 unsigned int nDevices = getDeviceCount();
507 if ( nDevices == 0 ) {
508 errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
509 error( RtError::INVALID_USE );
512 if ( device >= nDevices ) {
513 errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
514 error( RtError::INVALID_USE );
517 AudioDeviceID deviceList[ nDevices ];
518 UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
519 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
520 if ( result != noErr ) {
521 errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
522 error( RtError::WARNING );
526 AudioDeviceID id = deviceList[ device ];
528 // Get the device name.
532 result = AudioDeviceGetProperty( id, 0, false,
533 kAudioDevicePropertyDeviceManufacturer,
536 if ( result != noErr ) {
537 errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
538 errorText_ = errorStream_.str();
539 error( RtError::WARNING );
542 info.name.append( (const char *)name, strlen(name) + 1 );
543 info.name.append( ": " );
546 result = AudioDeviceGetProperty( id, 0, false,
547 kAudioDevicePropertyDeviceName,
549 if ( result != noErr ) {
550 errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
551 errorText_ = errorStream_.str();
552 error( RtError::WARNING );
555 info.name.append( (const char *)name, strlen(name) + 1 );
557 // Get the output stream "configuration".
558 AudioBufferList *bufferList = nil;
559 result = AudioDeviceGetPropertyInfo( id, 0, false,
560 kAudioDevicePropertyStreamConfiguration,
562 if (result != noErr || dataSize == 0) {
563 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
564 errorText_ = errorStream_.str();
565 error( RtError::WARNING );
569 // Allocate the AudioBufferList.
570 bufferList = (AudioBufferList *) malloc( dataSize );
571 if ( bufferList == NULL ) {
572 errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
573 error( RtError::WARNING );
577 result = AudioDeviceGetProperty( id, 0, false,
578 kAudioDevicePropertyStreamConfiguration,
579 &dataSize, bufferList );
580 if ( result != noErr ) {
582 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
583 errorText_ = errorStream_.str();
584 error( RtError::WARNING );
588 // Get output channel information.
589 unsigned int i, nStreams = bufferList->mNumberBuffers;
590 for ( i=0; i<nStreams; i++ )
591 info.outputChannels += bufferList->mBuffers[i].mNumberChannels;
594 // Get the input stream "configuration".
595 result = AudioDeviceGetPropertyInfo( id, 0, true,
596 kAudioDevicePropertyStreamConfiguration,
598 if (result != noErr || dataSize == 0) {
599 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
600 errorText_ = errorStream_.str();
601 error( RtError::WARNING );
605 // Allocate the AudioBufferList.
606 bufferList = (AudioBufferList *) malloc( dataSize );
607 if ( bufferList == NULL ) {
608 errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
609 error( RtError::WARNING );
613 result = AudioDeviceGetProperty( id, 0, true,
614 kAudioDevicePropertyStreamConfiguration,
615 &dataSize, bufferList );
616 if ( result != noErr ) {
618 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
619 errorText_ = errorStream_.str();
620 error( RtError::WARNING );
624 // Get input channel information.
625 nStreams = bufferList->mNumberBuffers;
626 for ( i=0; i<nStreams; i++ )
627 info.inputChannels += bufferList->mBuffers[i].mNumberChannels;
630 // If device opens for both playback and capture, we determine the channels.
631 if ( info.outputChannels > 0 && info.inputChannels > 0 )
632 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
634 // Probe the device sample rates.
635 bool isInput = false;
636 if ( info.outputChannels == 0 ) isInput = true;
638 // Determine the supported sample rates.
639 result = AudioDeviceGetPropertyInfo( id, 0, isInput,
640 kAudioDevicePropertyAvailableNominalSampleRates,
643 if ( result != kAudioHardwareNoError || dataSize == 0 ) {
644 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
645 errorText_ = errorStream_.str();
646 error( RtError::WARNING );
650 UInt32 nRanges = dataSize / sizeof( AudioValueRange );
651 AudioValueRange rangeList[ nRanges ];
652 result = AudioDeviceGetProperty( id, 0, isInput,
653 kAudioDevicePropertyAvailableNominalSampleRates,
654 &dataSize, &rangeList );
656 if ( result != kAudioHardwareNoError ) {
657 errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
658 errorText_ = errorStream_.str();
659 error( RtError::WARNING );
663 Float64 minimumRate = 100000000.0, maximumRate = 0.0;
664 for ( UInt32 i=0; i<nRanges; i++ ) {
665 if ( rangeList[i].mMinimum < minimumRate ) minimumRate = rangeList[i].mMinimum;
666 if ( rangeList[i].mMaximum > maximumRate ) maximumRate = rangeList[i].mMaximum;
669 info.sampleRates.clear();
670 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
671 if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )
672 info.sampleRates.push_back( SAMPLE_RATES[k] );
675 if ( info.sampleRates.size() == 0 ) {
676 errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
677 errorText_ = errorStream_.str();
678 error( RtError::WARNING );
682 // CoreAudio always uses 32-bit floating point data for PCM streams.
683 // Thus, any other "physical" formats supported by the device are of
684 // no interest to the client.
685 info.nativeFormats = RTAUDIO_FLOAT32;
687 if ( getDefaultOutputDevice() == device )
688 info.isDefaultOutput = true;
689 if ( getDefaultInputDevice() == device )
690 info.isDefaultInput = true;
696 OSStatus callbackHandler( AudioDeviceID inDevice,
697 const AudioTimeStamp* inNow,
698 const AudioBufferList* inInputData,
699 const AudioTimeStamp* inInputTime,
700 AudioBufferList* outOutputData,
701 const AudioTimeStamp* inOutputTime,
704 CallbackInfo *info = (CallbackInfo *) infoPointer;
706 RtApiCore *object = (RtApiCore *) info->object;
707 if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )
708 return kAudioHardwareUnspecifiedError;
710 return kAudioHardwareNoError;
713 OSStatus deviceListener( AudioDeviceID inDevice,
716 AudioDevicePropertyID propertyID,
717 void* handlePointer )
719 CoreHandle *handle = (CoreHandle *) handlePointer;
720 if ( propertyID == kAudioDeviceProcessorOverload ) {
722 handle->xrun[1] = true;
724 handle->xrun[0] = true;
727 return kAudioHardwareNoError;
730 static bool hasProperty( AudioDeviceID id, UInt32 channel, bool isInput, AudioDevicePropertyID property )
732 OSStatus result = AudioDeviceGetPropertyInfo( id, channel, isInput, property, NULL, NULL );
736 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
737 unsigned int firstChannel, unsigned int sampleRate,
738 RtAudioFormat format, unsigned int *bufferSize,
739 RtAudio::StreamOptions *options )
742 unsigned int nDevices = getDeviceCount();
743 if ( nDevices == 0 ) {
744 // This should not happen because a check is made before this function is called.
745 errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";
749 if ( device >= nDevices ) {
750 // This should not happen because a check is made before this function is called.
751 errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";
755 AudioDeviceID deviceList[ nDevices ];
756 UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
757 OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
758 if ( result != noErr ) {
759 errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
763 AudioDeviceID id = deviceList[ device ];
765 // Setup for stream mode.
766 bool isInput = false;
767 if ( mode == INPUT ) isInput = true;
769 // Set or disable "hog" mode.
770 dataSize = sizeof( UInt32 );
772 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) doHog = 1;
773 result = AudioHardwareSetProperty( kAudioHardwarePropertyHogModeIsAllowed, dataSize, &doHog );
774 if ( result != noErr ) {
775 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
776 errorText_ = errorStream_.str();
780 // Get the stream "configuration".
781 AudioBufferList *bufferList;
782 result = AudioDeviceGetPropertyInfo( id, 0, isInput,
783 kAudioDevicePropertyStreamConfiguration,
785 if (result != noErr || dataSize == 0) {
786 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
787 errorText_ = errorStream_.str();
791 // Allocate the AudioBufferList.
792 bufferList = (AudioBufferList *) malloc( dataSize );
793 if ( bufferList == NULL ) {
794 errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
798 result = AudioDeviceGetProperty( id, 0, isInput,
799 kAudioDevicePropertyStreamConfiguration,
800 &dataSize, bufferList );
801 if ( result != noErr ) {
803 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
804 errorText_ = errorStream_.str();
808 // Search for a stream that contains the desired number of
809 // channels. CoreAudio devices can have an arbitrary number of
810 // streams and each stream can have an arbitrary number of channels.
811 // For each stream, a single buffer of interleaved samples is
812 // provided. RtAudio currently only supports the use of one stream
813 // of interleaved data or multiple consecutive single-channel
814 // streams. Thus, our search below is limited to these two
816 unsigned int streamChannels = 0, nStreams = 0;
817 UInt32 iChannel = 0, iStream = 0;
818 unsigned int offsetCounter = firstChannel;
819 stream_.deviceInterleaved[mode] = true;
820 nStreams = bufferList->mNumberBuffers;
821 bool foundStream = false;
823 for ( iStream=0; iStream<nStreams; iStream++ ) {
824 streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
825 if ( streamChannels >= channels + offsetCounter ) {
826 iChannel += offsetCounter;
830 if ( streamChannels > offsetCounter ) break;
831 offsetCounter -= streamChannels;
832 iChannel += streamChannels;
835 // If we didn't find a single stream above, see if we can meet
836 // the channel specification in mono mode (i.e. using separate
837 // non-interleaved buffers). This can only work if there are N
838 // consecutive one-channel streams, where N is the number of
839 // desired channels (+ channel offset).
840 if ( foundStream == false ) {
841 unsigned int counter = 0;
842 offsetCounter = firstChannel;
844 for ( iStream=0; iStream<nStreams; iStream++ ) {
845 streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
846 if ( offsetCounter ) {
847 if ( streamChannels > offsetCounter ) break;
848 offsetCounter -= streamChannels;
850 else if ( streamChannels == 1 )
854 if ( counter == channels ) {
855 iStream -= channels - 1;
856 iChannel -= channels - 1;
857 stream_.deviceInterleaved[mode] = false;
861 iChannel += streamChannels;
866 if ( foundStream == false ) {
867 errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels.";
868 errorText_ = errorStream_.str();
872 // Determine the buffer size.
873 AudioValueRange bufferRange;
874 dataSize = sizeof( AudioValueRange );
875 result = AudioDeviceGetProperty( id, 0, isInput,
876 kAudioDevicePropertyBufferFrameSizeRange,
877 &dataSize, &bufferRange );
878 if ( result != noErr ) {
879 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
880 errorText_ = errorStream_.str();
884 if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
885 else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
886 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
888 // Set the buffer size. For mono mode, I'm assuming we only need to
889 // make this setting for the master channel.
890 UInt32 theSize = (UInt32) *bufferSize;
891 dataSize = sizeof( UInt32 );
892 result = AudioDeviceSetProperty( id, NULL, 0, isInput,
893 kAudioDevicePropertyBufferFrameSize,
894 dataSize, &theSize );
896 if ( result != noErr ) {
897 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
898 errorText_ = errorStream_.str();
902 // If attempting to setup a duplex stream, the bufferSize parameter
903 // MUST be the same in both directions!
904 *bufferSize = theSize;
905 if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
906 errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";
907 errorText_ = errorStream_.str();
911 stream_.bufferSize = *bufferSize;
912 stream_.nBuffers = 1;
914 // Get the stream ID(s) so we can set the stream format. In mono
915 // mode, we'll have to do this for each stream (channel).
916 AudioStreamID streamIDs[ nStreams ];
917 dataSize = nStreams * sizeof( AudioStreamID );
918 result = AudioDeviceGetProperty( id, 0, isInput,
919 kAudioDevicePropertyStreams,
920 &dataSize, &streamIDs );
921 if ( result != noErr ) {
922 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ").";
923 errorText_ = errorStream_.str();
927 // Now set the stream format. Also, check the physical format of the
928 // device and change that if necessary.
929 AudioStreamBasicDescription description;
930 dataSize = sizeof( AudioStreamBasicDescription );
931 if ( stream_.deviceInterleaved[mode] ) nStreams = 1;
932 else nStreams = channels;
935 for ( unsigned int i=0; i<nStreams; i++ ) {
937 result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
938 kAudioStreamPropertyVirtualFormat,
939 &dataSize, &description );
941 if ( result != noErr ) {
942 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
943 errorText_ = errorStream_.str();
947 // Set the sample rate and data format id. However, only make the
948 // change if the sample rate is not within 1.0 of the desired
949 // rate and the format is not linear pcm.
950 updateFormat = false;
951 if ( fabs( description.mSampleRate - (double)sampleRate ) > 1.0 ) {
952 description.mSampleRate = (double) sampleRate;
956 if ( description.mFormatID != kAudioFormatLinearPCM ) {
957 description.mFormatID = kAudioFormatLinearPCM;
961 if ( updateFormat ) {
962 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0,
963 kAudioStreamPropertyVirtualFormat,
964 dataSize, &description );
965 if ( result != noErr ) {
966 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
967 errorText_ = errorStream_.str();
972 // Now check the physical format.
973 result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
974 kAudioStreamPropertyPhysicalFormat,
975 &dataSize, &description );
976 if ( result != noErr ) {
977 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
978 errorText_ = errorStream_.str();
982 if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 24 ) {
983 description.mFormatID = kAudioFormatLinearPCM;
984 AudioStreamBasicDescription testDescription = description;
985 unsigned long formatFlags;
987 // We'll try higher bit rates first and then work our way down.
988 testDescription.mBitsPerChannel = 32;
989 formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;
990 testDescription.mFormatFlags = formatFlags;
991 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
992 if ( result == noErr ) continue;
994 testDescription = description;
995 testDescription.mBitsPerChannel = 32;
996 formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat;
997 testDescription.mFormatFlags = formatFlags;
998 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
999 if ( result == noErr ) continue;
1001 testDescription = description;
1002 testDescription.mBitsPerChannel = 24;
1003 testDescription.mFormatFlags = formatFlags;
1004 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1005 if ( result == noErr ) continue;
1007 testDescription = description;
1008 testDescription.mBitsPerChannel = 16;
1009 testDescription.mFormatFlags = formatFlags;
1010 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1011 if ( result == noErr ) continue;
1013 testDescription = description;
1014 testDescription.mBitsPerChannel = 8;
1015 testDescription.mFormatFlags = formatFlags;
1016 result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
1017 if ( result != noErr ) {
1018 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
1019 errorText_ = errorStream_.str();
1025 // Get the stream latency. There can be latency in both the device
1026 // and the stream. First, attempt to get the device latency on the
1027 // master channel or the first open channel. Errors that might
1028 // occur here are not deemed critical.
1029 UInt32 latency, channel = 0;
1030 dataSize = sizeof( UInt32 );
1031 AudioDevicePropertyID property = kAudioDevicePropertyLatency;
1032 for ( int i=0; i<2; i++ ) {
1033 if ( hasProperty( id, channel, isInput, property ) == true ) break;
1034 channel = iChannel + 1 + i;
1036 if ( channel <= iChannel + 1 ) {
1037 result = AudioDeviceGetProperty( id, channel, isInput, property, &dataSize, &latency );
1038 if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
1040 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
1041 errorText_ = errorStream_.str();
1042 error( RtError::WARNING );
1046 // Now try to get the stream latency. For "mono" mode, I assume the
1047 // latency is equal for all single-channel streams.
1048 result = AudioStreamGetProperty( streamIDs[iStream], 0, property, &dataSize, &latency );
1049 if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency;
1051 errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";
1052 errorText_ = errorStream_.str();
1053 error( RtError::WARNING );
1056 // Byte-swapping: According to AudioHardware.h, the stream data will
1057 // always be presented in native-endian format, so we should never
1058 // need to byte swap.
1059 stream_.doByteSwap[mode] = false;
1061 // From the CoreAudio documentation, PCM data must be supplied as
1063 stream_.userFormat = format;
1064 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
1066 if ( stream_.deviceInterleaved[mode] )
1067 stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
1069 stream_.nDeviceChannels[mode] = channels;
1070 stream_.nUserChannels[mode] = channels;
1071 stream_.channelOffset[mode] = iChannel; // offset within a CoreAudio stream
1072 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
1073 else stream_.userInterleaved = true;
1075 // Set flags for buffer conversion.
1076 stream_.doConvertBuffer[mode] = false;
1077 if ( stream_.userFormat != stream_.deviceFormat[mode] )
1078 stream_.doConvertBuffer[mode] = true;
1079 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
1080 stream_.doConvertBuffer[mode] = true;
1081 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
1082 stream_.nUserChannels[mode] > 1 )
1083 stream_.doConvertBuffer[mode] = true;
1085 // Allocate our CoreHandle structure for the stream.
1086 CoreHandle *handle = 0;
1087 if ( stream_.apiHandle == 0 ) {
1089 handle = new CoreHandle;
1091 catch ( std::bad_alloc& ) {
1092 errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
1096 if ( pthread_cond_init( &handle->condition, NULL ) ) {
1097 errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
1100 stream_.apiHandle = (void *) handle;
1103 handle = (CoreHandle *) stream_.apiHandle;
1104 handle->iStream[mode] = iStream;
1105 handle->id[mode] = id;
1107 // Allocate necessary internal buffers.
1108 unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
1109 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
1110 if ( stream_.userBuffer[mode] == NULL ) {
1111 errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
1115 // If possible, we will make use of the CoreAudio stream buffers as
1116 // "device buffers". However, we can't do this if the device
1117 // buffers are non-interleaved ("mono" mode).
1118 if ( !stream_.deviceInterleaved[mode] && stream_.doConvertBuffer[mode] ) {
1120 bool makeBuffer = true;
1121 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
1122 if ( mode == INPUT ) {
1123 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
1124 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
1125 if ( bufferBytes <= bytesOut ) makeBuffer = false;
1130 bufferBytes *= *bufferSize;
1131 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
1132 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
1133 if ( stream_.deviceBuffer == NULL ) {
1134 errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
1138 // Save a pointer to our own device buffer in the CoreHandle
1139 // structure because we may need to use the stream_.deviceBuffer
1140 // variable to point to the CoreAudio buffer before buffer
1141 // conversion (if we have a duplex stream with two different
1142 // conversion schemes).
1143 handle->deviceBuffer = stream_.deviceBuffer;
1147 stream_.sampleRate = sampleRate;
1148 stream_.device[mode] = device;
1149 stream_.state = STREAM_STOPPED;
1150 stream_.callbackInfo.object = (void *) this;
1152 // Setup the buffer conversion information structure. We override
1153 // the channel offset value and perform our own setting for that
1155 if ( stream_.doConvertBuffer[mode] ) {
1156 setConvertInfo( mode, 0 );
1158 // Add channel offset for interleaved channels.
1159 if ( firstChannel > 0 && stream_.deviceInterleaved[mode] ) {
1160 if ( mode == OUTPUT ) {
1161 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
1162 stream_.convertInfo[mode].outOffset[k] += firstChannel;
1165 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
1166 stream_.convertInfo[mode].inOffset[k] += firstChannel;
1171 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
1172 // Only one callback procedure per device.
1173 stream_.mode = DUPLEX;
1175 result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
1176 if ( result != noErr ) {
1177 errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
1178 errorText_ = errorStream_.str();
1181 if ( stream_.mode == OUTPUT && mode == INPUT )
1182 stream_.mode = DUPLEX;
1184 stream_.mode = mode;
1187 // Setup the device property listener for over/underload.
1188 result = AudioDeviceAddPropertyListener( id, 0, isInput,
1189 kAudioDeviceProcessorOverload,
1190 deviceListener, (void *) handle );
1196 pthread_cond_destroy( &handle->condition );
1198 stream_.apiHandle = 0;
1201 for ( int i=0; i<2; i++ ) {
1202 if ( stream_.userBuffer[i] ) {
1203 free( stream_.userBuffer[i] );
1204 stream_.userBuffer[i] = 0;
1208 if ( stream_.deviceBuffer ) {
1209 free( stream_.deviceBuffer );
1210 stream_.deviceBuffer = 0;
1216 void RtApiCore :: closeStream( void )
1218 if ( stream_.state == STREAM_CLOSED ) {
1219 errorText_ = "RtApiCore::closeStream(): no open stream to close!";
1220 error( RtError::WARNING );
1224 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1225 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1226 if ( stream_.state == STREAM_RUNNING )
1227 AudioDeviceStop( handle->id[0], callbackHandler );
1228 AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
1231 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1232 if ( stream_.state == STREAM_RUNNING )
1233 AudioDeviceStop( handle->id[1], callbackHandler );
1234 AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
1237 for ( int i=0; i<2; i++ ) {
1238 if ( stream_.userBuffer[i] ) {
1239 free( stream_.userBuffer[i] );
1240 stream_.userBuffer[i] = 0;
1244 if ( handle->deviceBuffer ) {
1245 free( handle->deviceBuffer );
1246 stream_.deviceBuffer = 0;
1249 // Destroy pthread condition variable.
1250 pthread_cond_destroy( &handle->condition );
1252 stream_.apiHandle = 0;
1254 stream_.mode = UNINITIALIZED;
1255 stream_.state = STREAM_CLOSED;
1258 void RtApiCore :: startStream( void )
1261 if ( stream_.state == STREAM_RUNNING ) {
1262 errorText_ = "RtApiCore::startStream(): the stream is already running!";
1263 error( RtError::WARNING );
1267 MUTEX_LOCK( &stream_.mutex );
1269 OSStatus result = noErr;
1270 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1271 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1273 result = AudioDeviceStart( handle->id[0], callbackHandler );
1274 if ( result != noErr ) {
1275 errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
1276 errorText_ = errorStream_.str();
1281 if ( stream_.mode == INPUT ||
1282 ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1284 result = AudioDeviceStart( handle->id[1], callbackHandler );
1285 if ( result != noErr ) {
1286 errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
1287 errorText_ = errorStream_.str();
1292 handle->drainCounter = 0;
1293 handle->internalDrain = false;
1294 stream_.state = STREAM_RUNNING;
1297 MUTEX_UNLOCK( &stream_.mutex );
1299 if ( result == noErr ) return;
1300 error( RtError::SYSTEM_ERROR );
1303 void RtApiCore :: stopStream( void )
1306 if ( stream_.state == STREAM_STOPPED ) {
1307 errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
1308 error( RtError::WARNING );
1312 MUTEX_LOCK( &stream_.mutex );
1314 OSStatus result = noErr;
1315 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1316 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
1318 if ( handle->drainCounter == 0 ) {
1319 handle->drainCounter = 1;
1320 pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
1323 result = AudioDeviceStop( handle->id[0], callbackHandler );
1324 if ( result != noErr ) {
1325 errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
1326 errorText_ = errorStream_.str();
1331 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
1333 result = AudioDeviceStop( handle->id[1], callbackHandler );
1334 if ( result != noErr ) {
1335 errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
1336 errorText_ = errorStream_.str();
1342 MUTEX_UNLOCK( &stream_.mutex );
1344 stream_.state = STREAM_STOPPED;
1345 if ( result == noErr ) return;
1346 error( RtError::SYSTEM_ERROR );
1349 void RtApiCore :: abortStream( void )
1352 if ( stream_.state == STREAM_STOPPED ) {
1353 errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
1354 error( RtError::WARNING );
1358 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1359 handle->drainCounter = 1;
1364 bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
1365 const AudioBufferList *inBufferList,
1366 const AudioBufferList *outBufferList )
1368 if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
1369 if ( stream_.state == STREAM_CLOSED ) {
1370 errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
1371 error( RtError::WARNING );
1375 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
1376 CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
1378 // Check if we were draining the stream and signal is finished.
1379 if ( handle->drainCounter > 3 ) {
1380 if ( handle->internalDrain == false )
1381 pthread_cond_signal( &handle->condition );
1387 MUTEX_LOCK( &stream_.mutex );
1389 AudioDeviceID outputDevice = handle->id[0];
1391 // Invoke user callback to get fresh output data UNLESS we are
1392 // draining stream or duplex mode AND the input/output devices are
1393 // different AND this function is called for the input device.
1394 if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {
1395 RtAudioCallback callback = (RtAudioCallback) info->callback;
1396 double streamTime = getStreamTime();
1397 RtAudioStreamStatus status = 0;
1398 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
1399 status |= RTAUDIO_OUTPUT_UNDERFLOW;
1400 handle->xrun[0] = false;
1402 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
1403 status |= RTAUDIO_INPUT_OVERFLOW;
1404 handle->xrun[1] = false;
1406 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
1407 stream_.bufferSize, streamTime, status, info->userData );
1408 if ( handle->drainCounter == 2 ) {
1409 MUTEX_UNLOCK( &stream_.mutex );
1413 else if ( handle->drainCounter == 1 )
1414 handle->internalDrain = true;
1417 if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
1419 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
1421 if ( stream_.deviceInterleaved[0] ) {
1422 memset( outBufferList->mBuffers[handle->iStream[0]].mData,
1424 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
1427 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
1428 memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1430 outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
1434 else if ( stream_.doConvertBuffer[0] ) {
1436 if ( stream_.deviceInterleaved[0] )
1437 stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData;
1439 stream_.deviceBuffer = handle->deviceBuffer;
1441 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
1443 if ( !stream_.deviceInterleaved[0] ) {
1444 UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
1445 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
1446 memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1447 &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
1453 if ( stream_.deviceInterleaved[0] ) {
1454 memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
1455 stream_.userBuffer[0],
1456 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
1459 UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
1460 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
1461 memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
1462 &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
1467 if ( handle->drainCounter ) {
1468 handle->drainCounter++;
1473 AudioDeviceID inputDevice = handle->id[1];
1474 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
1476 if ( stream_.doConvertBuffer[1] ) {
1478 if ( stream_.deviceInterleaved[1] )
1479 stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData;
1481 stream_.deviceBuffer = (char *) handle->deviceBuffer;
1482 UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
1483 for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
1484 memcpy( &stream_.deviceBuffer[i*bufferBytes],
1485 inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
1489 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
1493 memcpy( stream_.userBuffer[1],
1494 inBufferList->mBuffers[handle->iStream[1]].mData,
1495 inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
1500 MUTEX_UNLOCK( &stream_.mutex );
1502 RtApi::tickStreamTime();
1506 const char* RtApiCore :: getErrorCode( OSStatus code )
1510 case kAudioHardwareNotRunningError:
1511 return "kAudioHardwareNotRunningError";
1513 case kAudioHardwareUnspecifiedError:
1514 return "kAudioHardwareUnspecifiedError";
1516 case kAudioHardwareUnknownPropertyError:
1517 return "kAudioHardwareUnknownPropertyError";
1519 case kAudioHardwareBadPropertySizeError:
1520 return "kAudioHardwareBadPropertySizeError";
1522 case kAudioHardwareIllegalOperationError:
1523 return "kAudioHardwareIllegalOperationError";
1525 case kAudioHardwareBadObjectError:
1526 return "kAudioHardwareBadObjectError";
1528 case kAudioHardwareBadDeviceError:
1529 return "kAudioHardwareBadDeviceError";
1531 case kAudioHardwareBadStreamError:
1532 return "kAudioHardwareBadStreamError";
1534 case kAudioHardwareUnsupportedOperationError:
1535 return "kAudioHardwareUnsupportedOperationError";
1537 case kAudioDeviceUnsupportedFormatError:
1538 return "kAudioDeviceUnsupportedFormatError";
1540 case kAudioDevicePermissionsError:
1541 return "kAudioDevicePermissionsError";
1544 return "CoreAudio unknown error";
1548 //******************** End of __MACOSX_CORE__ *********************//
1551 #if defined(__UNIX_JACK__)
1553 // JACK is a low-latency audio server, originally written for the
1554 // GNU/Linux operating system and now also ported to OS-X. It can
1555 // connect a number of different applications to an audio device, as
1556 // well as allowing them to share audio between themselves.
1558 // When using JACK with RtAudio, "devices" refer to JACK clients that
1559 // have ports connected to the server. The JACK server is typically
1560 // started in a terminal as follows:
1562 // .jackd -d alsa -d hw:0
1564 // or through an interface program such as qjackctl. Many of the
1565 // parameters normally set for a stream are fixed by the JACK server
1566 // and can be specified when the JACK server is started. In
1569 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
1571 // specifies a sample rate of 44100 Hz, a buffer size of 512 sample
1572 // frames, and number of buffers = 4. Once the server is running, it
1573 // is not possible to override these values. If the values are not
1574 // specified in the command-line, the JACK server uses default values.
1576 // The JACK server does not have to be running when an instance of
1577 // RtApiJack is created, though the function getDeviceCount() will
1578 // report 0 devices found until JACK has been started. When no
1579 // devices are available (i.e., the JACK server is not running), a
1580 // stream cannot be opened.
1582 #include <jack/jack.h>
1585 // A structure to hold various information related to the Jack API
1588 jack_client_t *client;
1589 jack_port_t **ports[2];
1590 std::string deviceName[2];
1592 pthread_cond_t condition;
1593 int drainCounter; // Tracks callback counts when draining
1594 bool internalDrain; // Indicates if stop is initiated from callback or not.
1597 :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
1600 RtApiJack :: RtApiJack()
1602 // Nothing to do here.
1605 RtApiJack :: ~RtApiJack()
1607 if ( stream_.state != STREAM_CLOSED ) closeStream();
1610 unsigned int RtApiJack :: getDeviceCount( void )
1612 // See if we can become a jack client.
1613 jack_client_t *client = jack_client_new( "RtApiJackCount" );
1614 if ( client == 0 ) return 0;
1617 std::string port, previousPort;
1618 unsigned int nChannels = 0, nDevices = 0;
1619 ports = jack_get_ports( client, NULL, NULL, 0 );
1621 // Parse the port names up to the first colon (:).
1622 unsigned int iColon = 0;
1624 port = (char *) ports[ nChannels ];
1625 iColon = port.find(":");
1626 if ( iColon != std::string::npos ) {
1627 port = port.substr( 0, iColon + 1 );
1628 if ( port != previousPort ) {
1630 previousPort = port;
1633 } while ( ports[++nChannels] );
1637 jack_client_close( client );
1641 RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
1643 RtAudio::DeviceInfo info;
1644 info.probed = false;
1646 jack_client_t *client = jack_client_new( "RtApiJackInfo" );
1647 if ( client == 0 ) {
1648 errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
1649 error( RtError::WARNING );
1654 std::string port, previousPort;
1655 unsigned int nPorts = 0, nDevices = 0;
1656 ports = jack_get_ports( client, NULL, NULL, 0 );
1658 // Parse the port names up to the first colon (:).
1659 unsigned int iColon = 0;
1661 port = (char *) ports[ nPorts ];
1662 iColon = port.find(":");
1663 if ( iColon != std::string::npos ) {
1664 port = port.substr( 0, iColon );
1665 if ( port != previousPort ) {
1666 if ( nDevices == device ) info.name = port;
1668 previousPort = port;
1671 } while ( ports[++nPorts] );
1675 if ( device >= nDevices ) {
1676 errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
1677 error( RtError::INVALID_USE );
1680 // Get the current jack server sample rate.
1681 info.sampleRates.clear();
1682 info.sampleRates.push_back( jack_get_sample_rate( client ) );
1684 // Count the available ports containing the client name as device
1685 // channels. Jack "input ports" equal RtAudio output channels.
1686 unsigned int nChannels = 0;
1687 ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );
1689 while ( ports[ nChannels ] ) nChannels++;
1691 info.outputChannels = nChannels;
1694 // Jack "output ports" equal RtAudio input channels.
1696 ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );
1698 while ( ports[ nChannels ] ) nChannels++;
1700 info.inputChannels = nChannels;
1703 if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
1704 jack_client_close(client);
1705 errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
1706 error( RtError::WARNING );
1710 // If device opens for both playback and capture, we determine the channels.
1711 if ( info.outputChannels > 0 && info.inputChannels > 0 )
1712 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
1714 // Jack always uses 32-bit floats.
1715 info.nativeFormats = RTAUDIO_FLOAT32;
1717 // Jack doesn't provide default devices so we'll use the first available one.
1718 if ( device == 0 && info.outputChannels > 0 )
1719 info.isDefaultOutput = true;
1720 if ( device == 0 && info.inputChannels > 0 )
1721 info.isDefaultInput = true;
1723 jack_client_close(client);
1728 int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
1730 CallbackInfo *info = (CallbackInfo *) infoPointer;
1732 RtApiJack *object = (RtApiJack *) info->object;
1733 if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
1738 void jackShutdown( void *infoPointer )
1740 CallbackInfo *info = (CallbackInfo *) infoPointer;
1741 RtApiJack *object = (RtApiJack *) info->object;
1743 // Check current stream state. If stopped, then we'll assume this
1744 // was called as a result of a call to RtApiJack::stopStream (the
1745 // deactivation of a client handle causes this function to be called).
1746 // If not, we'll assume the Jack server is shutting down or some
1747 // other problem occurred and we should close the stream.
1748 if ( object->isStreamRunning() == false ) return;
1750 object->closeStream();
1751 std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
1754 int jackXrun( void *infoPointer )
1756 JackHandle *handle = (JackHandle *) infoPointer;
1758 if ( handle->ports[0] ) handle->xrun[0] = true;
1759 if ( handle->ports[1] ) handle->xrun[1] = true;
1764 bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
1765 unsigned int firstChannel, unsigned int sampleRate,
1766 RtAudioFormat format, unsigned int *bufferSize,
1767 RtAudio::StreamOptions *options )
1769 JackHandle *handle = (JackHandle *) stream_.apiHandle;
1771 // Look for jack server and try to become a client (only do once per stream).
1772 jack_client_t *client = 0;
1773 if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
1774 if ( options && !options->streamName.empty() )
1775 client = jack_client_new( options->streamName.c_str() );
1777 client = jack_client_new( "RtApiJack" );
1778 if ( client == 0 ) {
1779 errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
1780 error( RtError::WARNING );
1785 // The handle must have been created on an earlier pass.
1786 client = handle->client;
1790 std::string port, previousPort, deviceName;
1791 unsigned int nPorts = 0, nDevices = 0;
1792 ports = jack_get_ports( client, NULL, NULL, 0 );
1794 // Parse the port names up to the first colon (:).
1795 unsigned int iColon = 0;
1797 port = (char *) ports[ nPorts ];
1798 iColon = port.find(":");
1799 if ( iColon != std::string::npos ) {
1800 port = port.substr( 0, iColon );
1801 if ( port != previousPort ) {
1802 if ( nDevices == device ) deviceName = port;
1804 previousPort = port;
1807 } while ( ports[++nPorts] );
1811 if ( device >= nDevices ) {
1812 errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
1816 // Count the available ports containing the client name as device
1817 // channels. Jack "input ports" equal RtAudio output channels.
1818 unsigned int nChannels = 0;
1819 unsigned long flag = JackPortIsOutput;
1820 if ( mode == INPUT ) flag = JackPortIsInput;
1821 ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
1823 while ( ports[ nChannels ] ) nChannels++;
1827 // Compare the jack ports for specified client to the requested number of channels.
1828 if ( nChannels < (channels + firstChannel) ) {
1829 errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
1830 errorText_ = errorStream_.str();
1834 // Check the jack server sample rate.
1835 unsigned int jackRate = jack_get_sample_rate( client );
1836 if ( sampleRate != jackRate ) {
1837 jack_client_close( client );
1838 errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
1839 errorText_ = errorStream_.str();
1842 stream_.sampleRate = jackRate;
1844 // Get the latency of the JACK port.
1845 ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
1846 if ( ports[ firstChannel ] )
1847 stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
1850 // The jack server always uses 32-bit floating-point data.
1851 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
1852 stream_.userFormat = format;
1854 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
1855 else stream_.userInterleaved = true;
1857 // Jack always uses non-interleaved buffers.
1858 stream_.deviceInterleaved[mode] = false;
1860 // Jack always provides host byte-ordered data.
1861 stream_.doByteSwap[mode] = false;
1863 // Get the buffer size. The buffer size and number of buffers
1864 // (periods) is set when the jack server is started.
1865 stream_.bufferSize = (int) jack_get_buffer_size( client );
1866 *bufferSize = stream_.bufferSize;
1868 stream_.nDeviceChannels[mode] = channels;
1869 stream_.nUserChannels[mode] = channels;
1871 // Set flags for buffer conversion.
1872 stream_.doConvertBuffer[mode] = false;
1873 if ( stream_.userFormat != stream_.deviceFormat[mode] )
1874 stream_.doConvertBuffer[mode] = true;
1875 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
1876 stream_.nUserChannels[mode] > 1 )
1877 stream_.doConvertBuffer[mode] = true;
1879 // Allocate our JackHandle structure for the stream.
1880 if ( handle == 0 ) {
1882 handle = new JackHandle;
1884 catch ( std::bad_alloc& ) {
1885 errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";
1889 if ( pthread_cond_init(&handle->condition, NULL) ) {
1890 errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
1893 stream_.apiHandle = (void *) handle;
1894 handle->client = client;
1896 handle->deviceName[mode] = deviceName;
1898 // Allocate necessary internal buffers.
1899 unsigned long bufferBytes;
1900 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
1901 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
1902 if ( stream_.userBuffer[mode] == NULL ) {
1903 errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";
1907 if ( stream_.doConvertBuffer[mode] ) {
1909 bool makeBuffer = true;
1910 if ( mode == OUTPUT )
1911 bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
1912 else { // mode == INPUT
1913 bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );
1914 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
1915 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
1916 if ( bufferBytes < bytesOut ) makeBuffer = false;
1921 bufferBytes *= *bufferSize;
1922 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
1923 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
1924 if ( stream_.deviceBuffer == NULL ) {
1925 errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";
1931 // Allocate memory for the Jack ports (channels) identifiers.
1932 handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
1933 if ( handle->ports[mode] == NULL ) {
1934 errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
1938 stream_.device[mode] = device;
1939 stream_.channelOffset[mode] = firstChannel;
1940 stream_.state = STREAM_STOPPED;
1941 stream_.callbackInfo.object = (void *) this;
1943 if ( stream_.mode == OUTPUT && mode == INPUT )
1944 // We had already set up the stream for output.
1945 stream_.mode = DUPLEX;
1947 stream_.mode = mode;
1948 jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
1949 jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );
1950 jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
1953 // Register our ports.
1955 if ( mode == OUTPUT ) {
1956 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
1957 snprintf( label, 64, "outport %d", i );
1958 handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
1959 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
1963 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
1964 snprintf( label, 64, "inport %d", i );
1965 handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
1966 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
1970 // Setup the buffer conversion information structure. We don't use
1971 // buffers to do channel offsets, so we override that parameter
1973 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
1979 pthread_cond_destroy( &handle->condition );
1980 jack_client_close( handle->client );
1982 if ( handle->ports[0] ) free( handle->ports[0] );
1983 if ( handle->ports[1] ) free( handle->ports[1] );
1986 stream_.apiHandle = 0;
1989 for ( int i=0; i<2; i++ ) {
1990 if ( stream_.userBuffer[i] ) {
1991 free( stream_.userBuffer[i] );
1992 stream_.userBuffer[i] = 0;
1996 if ( stream_.deviceBuffer ) {
1997 free( stream_.deviceBuffer );
1998 stream_.deviceBuffer = 0;
2004 void RtApiJack :: closeStream( void )
2006 if ( stream_.state == STREAM_CLOSED ) {
2007 errorText_ = "RtApiJack::closeStream(): no open stream to close!";
2008 error( RtError::WARNING );
2012 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2015 if ( stream_.state == STREAM_RUNNING )
2016 jack_deactivate( handle->client );
2018 jack_client_close( handle->client );
2022 if ( handle->ports[0] ) free( handle->ports[0] );
2023 if ( handle->ports[1] ) free( handle->ports[1] );
2024 pthread_cond_destroy( &handle->condition );
2026 stream_.apiHandle = 0;
2029 for ( int i=0; i<2; i++ ) {
2030 if ( stream_.userBuffer[i] ) {
2031 free( stream_.userBuffer[i] );
2032 stream_.userBuffer[i] = 0;
2036 if ( stream_.deviceBuffer ) {
2037 free( stream_.deviceBuffer );
2038 stream_.deviceBuffer = 0;
2041 stream_.mode = UNINITIALIZED;
2042 stream_.state = STREAM_CLOSED;
2045 void RtApiJack :: startStream( void )
2048 if ( stream_.state == STREAM_RUNNING ) {
2049 errorText_ = "RtApiJack::startStream(): the stream is already running!";
2050 error( RtError::WARNING );
2054 MUTEX_LOCK(&stream_.mutex);
2056 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2057 int result = jack_activate( handle->client );
2059 errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
2065 // Get the list of available ports.
2066 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2068 ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);
2069 if ( ports == NULL) {
2070 errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
2074 // Now make the port connections. Since RtAudio wasn't designed to
2075 // allow the user to select particular channels of a device, we'll
2076 // just open the first "nChannels" ports with offset.
2077 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2079 if ( ports[ stream_.channelOffset[0] + i ] )
2080 result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
2083 errorText_ = "RtApiJack::startStream(): error connecting output ports!";
2090 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
2092 ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );
2093 if ( ports == NULL) {
2094 errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
2098 // Now make the port connections. See note above.
2099 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2101 if ( ports[ stream_.channelOffset[1] + i ] )
2102 result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
2105 errorText_ = "RtApiJack::startStream(): error connecting input ports!";
2112 handle->drainCounter = 0;
2113 handle->internalDrain = false;
2114 stream_.state = STREAM_RUNNING;
2117 MUTEX_UNLOCK(&stream_.mutex);
2119 if ( result == 0 ) return;
2120 error( RtError::SYSTEM_ERROR );
2123 void RtApiJack :: stopStream( void )
2126 if ( stream_.state == STREAM_STOPPED ) {
2127 errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
2128 error( RtError::WARNING );
2132 MUTEX_LOCK( &stream_.mutex );
2134 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2135 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2137 if ( handle->drainCounter == 0 ) {
2138 handle->drainCounter = 1;
2139 pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
2143 jack_deactivate( handle->client );
2144 stream_.state = STREAM_STOPPED;
2146 MUTEX_UNLOCK( &stream_.mutex );
2149 void RtApiJack :: abortStream( void )
2152 if ( stream_.state == STREAM_STOPPED ) {
2153 errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
2154 error( RtError::WARNING );
2158 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2159 handle->drainCounter = 1;
2164 bool RtApiJack :: callbackEvent( unsigned long nframes )
2166 if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
2167 if ( stream_.state == STREAM_CLOSED ) {
2168 errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
2169 error( RtError::WARNING );
2172 if ( stream_.bufferSize != nframes ) {
2173 errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
2174 error( RtError::WARNING );
2178 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
2179 JackHandle *handle = (JackHandle *) stream_.apiHandle;
2181 // Check if we were draining the stream and signal is finished.
2182 if ( handle->drainCounter > 3 ) {
2183 if ( handle->internalDrain == false )
2184 pthread_cond_signal( &handle->condition );
2190 MUTEX_LOCK( &stream_.mutex );
2192 // Invoke user callback first, to get fresh output data.
2193 if ( handle->drainCounter == 0 ) {
2194 RtAudioCallback callback = (RtAudioCallback) info->callback;
2195 double streamTime = getStreamTime();
2196 RtAudioStreamStatus status = 0;
2197 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
2198 status |= RTAUDIO_OUTPUT_UNDERFLOW;
2199 handle->xrun[0] = false;
2201 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
2202 status |= RTAUDIO_INPUT_OVERFLOW;
2203 handle->xrun[1] = false;
2205 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
2206 stream_.bufferSize, streamTime, status, info->userData );
2207 if ( handle->drainCounter == 2 ) {
2208 MUTEX_UNLOCK( &stream_.mutex );
2212 else if ( handle->drainCounter == 1 )
2213 handle->internalDrain = true;
2216 jack_default_audio_sample_t *jackbuffer;
2217 unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
2218 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2220 if ( handle->drainCounter > 0 ) { // write zeros to the output stream
2222 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2223 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2224 memset( jackbuffer, 0, bufferBytes );
2228 else if ( stream_.doConvertBuffer[0] ) {
2230 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
2232 for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
2233 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2234 memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
2237 else { // no buffer conversion
2238 for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
2239 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
2240 memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
2244 if ( handle->drainCounter ) {
2245 handle->drainCounter++;
2250 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
2252 if ( stream_.doConvertBuffer[1] ) {
2253 for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
2254 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2255 memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
2257 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
2259 else { // no buffer conversion
2260 for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
2261 jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
2262 memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
2268 MUTEX_UNLOCK(&stream_.mutex);
2270 RtApi::tickStreamTime();
2273 //******************** End of __UNIX_JACK__ *********************//
2276 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows
2278 // The ASIO API is designed around a callback scheme, so this
2279 // implementation is similar to that used for OS-X CoreAudio and Linux
2280 // Jack. The primary constraint with ASIO is that it only allows
2281 // access to a single driver at a time. Thus, it is not possible to
2282 // have more than one simultaneous RtAudio stream.
2284 // This implementation also requires a number of external ASIO files
2285 // and a few global variables. The ASIO callback scheme does not
2286 // allow for the passing of user data, so we must create a global
2287 // pointer to our callbackInfo structure.
2289 // On unix systems, we make use of a pthread condition variable.
2290 // Since there is no equivalent in Windows, I hacked something based
2291 // on information found in
2292 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
2294 #include "asio/asiosys.h"
2295 #include "asio/asio.h"
2296 #include "asio/iasiothiscallresolver.h"
2297 #include "asio/asiodrivers.h"
2300 AsioDrivers drivers;
2301 ASIOCallbacks asioCallbacks;
2302 ASIODriverInfo driverInfo;
2303 CallbackInfo *asioCallbackInfo;
2307 int drainCounter; // Tracks callback counts when draining
2308 bool internalDrain; // Indicates if stop is initiated from callback or not.
2309 ASIOBufferInfo *bufferInfos;
2313 :drainCounter(0), internalDrain(false), bufferInfos(0) {}
2316 // Function declarations (definitions at end of section)
2317 static const char* getAsioErrorString( ASIOError result );
2318 void sampleRateChanged( ASIOSampleRate sRate );
2319 long asioMessages( long selector, long value, void* message, double* opt );
2321 RtApiAsio :: RtApiAsio()
2323 // ASIO cannot run on a multi-threaded appartment. You can call
2324 // CoInitialize beforehand, but it must be for appartment threading
2325 // (in which case, CoInitilialize will return S_FALSE here).
2326 coInitialized_ = false;
2327 HRESULT hr = CoInitialize( NULL );
2329 errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";
2330 error( RtError::WARNING );
2332 coInitialized_ = true;
2334 drivers.removeCurrentDriver();
2335 driverInfo.asioVersion = 2;
2337 // See note in DirectSound implementation about GetDesktopWindow().
2338 driverInfo.sysRef = GetForegroundWindow();
2341 RtApiAsio :: ~RtApiAsio()
2343 if ( stream_.state != STREAM_CLOSED ) closeStream();
2344 if ( coInitialized_ ) CoUninitialize();
2347 unsigned int RtApiAsio :: getDeviceCount( void )
2349 return (unsigned int) drivers.asioGetNumDev();
2352 RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
2354 RtAudio::DeviceInfo info;
2355 info.probed = false;
2358 unsigned int nDevices = getDeviceCount();
2359 if ( nDevices == 0 ) {
2360 errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
2361 error( RtError::INVALID_USE );
2364 if ( device >= nDevices ) {
2365 errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
2366 error( RtError::INVALID_USE );
2369 // Don't probe if a stream is already open.
2370 if ( stream_.state != STREAM_CLOSED ) {
2371 errorText_ = "RtApiAsio::getDeviceInfo: unable to probe driver while a stream is open.";
2372 error( RtError::WARNING );
2376 char driverName[32];
2377 ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
2378 if ( result != ASE_OK ) {
2379 errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";
2380 errorText_ = errorStream_.str();
2381 error( RtError::WARNING );
2385 info.name = driverName;
2387 if ( !drivers.loadDriver( driverName ) ) {
2388 errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
2389 errorText_ = errorStream_.str();
2390 error( RtError::WARNING );
2394 result = ASIOInit( &driverInfo );
2395 if ( result != ASE_OK ) {
2396 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
2397 errorText_ = errorStream_.str();
2398 error( RtError::WARNING );
2402 // Determine the device channel information.
2403 long inputChannels, outputChannels;
2404 result = ASIOGetChannels( &inputChannels, &outputChannels );
2405 if ( result != ASE_OK ) {
2406 drivers.removeCurrentDriver();
2407 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
2408 errorText_ = errorStream_.str();
2409 error( RtError::WARNING );
2413 info.outputChannels = outputChannels;
2414 info.inputChannels = inputChannels;
2415 if ( info.outputChannels > 0 && info.inputChannels > 0 )
2416 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
2418 // Determine the supported sample rates.
2419 info.sampleRates.clear();
2420 for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
2421 result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
2422 if ( result == ASE_OK )
2423 info.sampleRates.push_back( SAMPLE_RATES[i] );
2426 // Determine supported data types ... just check first channel and assume rest are the same.
2427 ASIOChannelInfo channelInfo;
2428 channelInfo.channel = 0;
2429 channelInfo.isInput = true;
2430 if ( info.inputChannels <= 0 ) channelInfo.isInput = false;
2431 result = ASIOGetChannelInfo( &channelInfo );
2432 if ( result != ASE_OK ) {
2433 drivers.removeCurrentDriver();
2434 errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";
2435 errorText_ = errorStream_.str();
2436 error( RtError::WARNING );
2440 info.nativeFormats = 0;
2441 if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
2442 info.nativeFormats |= RTAUDIO_SINT16;
2443 else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
2444 info.nativeFormats |= RTAUDIO_SINT32;
2445 else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
2446 info.nativeFormats |= RTAUDIO_FLOAT32;
2447 else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
2448 info.nativeFormats |= RTAUDIO_FLOAT64;
2450 if ( getDefaultOutputDevice() == device )
2451 info.isDefaultOutput = true;
2452 if ( getDefaultInputDevice() == device )
2453 info.isDefaultInput = true;
2456 drivers.removeCurrentDriver();
2460 void bufferSwitch( long index, ASIOBool processNow )
2462 RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
2463 object->callbackEvent( index );
2466 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
2467 unsigned int firstChannel, unsigned int sampleRate,
2468 RtAudioFormat format, unsigned int *bufferSize,
2469 RtAudio::StreamOptions *options )
2471 // For ASIO, a duplex stream MUST use the same driver.
2472 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {
2473 errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
2477 char driverName[32];
2478 ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
2479 if ( result != ASE_OK ) {
2480 errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";
2481 errorText_ = errorStream_.str();
2485 // Only load the driver once for duplex stream.
2486 if ( mode != INPUT || stream_.mode != OUTPUT ) {
2487 if ( !drivers.loadDriver( driverName ) ) {
2488 errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
2489 errorText_ = errorStream_.str();
2493 result = ASIOInit( &driverInfo );
2494 if ( result != ASE_OK ) {
2495 errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
2496 errorText_ = errorStream_.str();
2501 // Check the device channel count.
2502 long inputChannels, outputChannels;
2503 result = ASIOGetChannels( &inputChannels, &outputChannels );
2504 if ( result != ASE_OK ) {
2505 drivers.removeCurrentDriver();
2506 errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
2507 errorText_ = errorStream_.str();
2511 if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
2512 ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
2513 drivers.removeCurrentDriver();
2514 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
2515 errorText_ = errorStream_.str();
2518 stream_.nDeviceChannels[mode] = channels;
2519 stream_.nUserChannels[mode] = channels;
2520 stream_.channelOffset[mode] = firstChannel;
2522 // Verify the sample rate is supported.
2523 result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
2524 if ( result != ASE_OK ) {
2525 drivers.removeCurrentDriver();
2526 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
2527 errorText_ = errorStream_.str();
2531 // Set the sample rate.
2532 result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
2533 if ( result != ASE_OK ) {
2534 drivers.removeCurrentDriver();
2535 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
2536 errorText_ = errorStream_.str();
2540 // Determine the driver data type.
2541 ASIOChannelInfo channelInfo;
2542 channelInfo.channel = 0;
2543 if ( mode == OUTPUT ) channelInfo.isInput = false;
2544 else channelInfo.isInput = true;
2545 result = ASIOGetChannelInfo( &channelInfo );
2546 if ( result != ASE_OK ) {
2547 drivers.removeCurrentDriver();
2548 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
2549 errorText_ = errorStream_.str();
2553 // Assuming WINDOWS host is always little-endian.
2554 stream_.doByteSwap[mode] = false;
2555 stream_.userFormat = format;
2556 stream_.deviceFormat[mode] = 0;
2557 if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
2558 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
2559 if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
2561 else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
2562 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
2563 if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
2565 else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
2566 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
2567 if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
2569 else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
2570 stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
2571 if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
2574 if ( stream_.deviceFormat[mode] == 0 ) {
2575 drivers.removeCurrentDriver();
2576 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
2577 errorText_ = errorStream_.str();
2581 // Set the buffer size. For a duplex stream, this will end up
2582 // setting the buffer size based on the input constraints, which
2584 long minSize, maxSize, preferSize, granularity;
2585 result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
2586 if ( result != ASE_OK ) {
2587 drivers.removeCurrentDriver();
2588 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
2589 errorText_ = errorStream_.str();
2593 if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
2594 else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
2595 else if ( granularity == -1 ) {
2596 // Make sure bufferSize is a power of two.
2597 double power = std::log10( (double) *bufferSize ) / log10( 2.0 );
2598 *bufferSize = (int) pow( 2.0, floor(power+0.5) );
2599 if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
2600 else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
2601 else *bufferSize = preferSize;
2603 else if ( granularity != 0 ) {
2604 // Set to an even multiple of granularity, rounding up.
2605 *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
2608 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {
2609 drivers.removeCurrentDriver();
2610 errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
2614 stream_.bufferSize = *bufferSize;
2615 stream_.nBuffers = 2;
2617 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
2618 else stream_.userInterleaved = true;
2620 // ASIO always uses non-interleaved buffers.
2621 stream_.deviceInterleaved[mode] = false;
2623 // Allocate, if necessary, our AsioHandle structure for the stream.
2624 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2625 if ( handle == 0 ) {
2627 handle = new AsioHandle;
2629 catch ( std::bad_alloc& ) {
2630 //if ( handle == NULL ) {
2631 drivers.removeCurrentDriver();
2632 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
2635 handle->bufferInfos = 0;
2637 // Create a manual-reset event.
2638 handle->condition = CreateEvent( NULL, // no security
2639 TRUE, // manual-reset
2640 FALSE, // non-signaled initially
2642 stream_.apiHandle = (void *) handle;
2645 // Create the ASIO internal buffers. Since RtAudio sets up input
2646 // and output separately, we'll have to dispose of previously
2647 // created output buffers for a duplex stream.
2648 long inputLatency, outputLatency;
2649 if ( mode == INPUT && stream_.mode == OUTPUT ) {
2650 ASIODisposeBuffers();
2651 if ( handle->bufferInfos ) free( handle->bufferInfos );
2654 // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
2655 bool buffersAllocated = false;
2656 unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
2657 handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
2658 if ( handle->bufferInfos == NULL ) {
2659 errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
2660 errorText_ = errorStream_.str();
2664 ASIOBufferInfo *infos;
2665 infos = handle->bufferInfos;
2666 for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
2667 infos->isInput = ASIOFalse;
2668 infos->channelNum = i + stream_.channelOffset[0];
2669 infos->buffers[0] = infos->buffers[1] = 0;
2671 for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
2672 infos->isInput = ASIOTrue;
2673 infos->channelNum = i + stream_.channelOffset[1];
2674 infos->buffers[0] = infos->buffers[1] = 0;
2677 // Set up the ASIO callback structure and create the ASIO data buffers.
2678 asioCallbacks.bufferSwitch = &bufferSwitch;
2679 asioCallbacks.sampleRateDidChange = &sampleRateChanged;
2680 asioCallbacks.asioMessage = &asioMessages;
2681 asioCallbacks.bufferSwitchTimeInfo = NULL;
2682 result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
2683 if ( result != ASE_OK ) {
2684 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
2685 errorText_ = errorStream_.str();
2688 buffersAllocated = true;
2690 // Set flags for buffer conversion.
2691 stream_.doConvertBuffer[mode] = false;
2692 if ( stream_.userFormat != stream_.deviceFormat[mode] )
2693 stream_.doConvertBuffer[mode] = true;
2694 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
2695 stream_.nUserChannels[mode] > 1 )
2696 stream_.doConvertBuffer[mode] = true;
2698 // Allocate necessary internal buffers
2699 unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
2700 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
2701 if ( stream_.userBuffer[mode] == NULL ) {
2702 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
2706 if ( stream_.doConvertBuffer[mode] ) {
2708 bool makeBuffer = true;
2709 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
2710 if ( mode == INPUT ) {
2711 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
2712 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
2713 if ( bufferBytes <= bytesOut ) makeBuffer = false;
2718 bufferBytes *= *bufferSize;
2719 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
2720 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
2721 if ( stream_.deviceBuffer == NULL ) {
2722 errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
2728 stream_.sampleRate = sampleRate;
2729 stream_.device[mode] = device;
2730 stream_.state = STREAM_STOPPED;
2731 asioCallbackInfo = &stream_.callbackInfo;
2732 stream_.callbackInfo.object = (void *) this;
2733 if ( stream_.mode == OUTPUT && mode == INPUT )
2734 // We had already set up an output stream.
2735 stream_.mode = DUPLEX;
2737 stream_.mode = mode;
2739 // Determine device latencies
2740 result = ASIOGetLatencies( &inputLatency, &outputLatency );
2741 if ( result != ASE_OK ) {
2742 errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
2743 errorText_ = errorStream_.str();
2744 error( RtError::WARNING); // warn but don't fail
2747 stream_.latency[0] = outputLatency;
2748 stream_.latency[1] = inputLatency;
2751 // Setup the buffer conversion information structure. We don't use
2752 // buffers to do channel offsets, so we override that parameter
2754 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
2759 if ( buffersAllocated )
2760 ASIODisposeBuffers();
2761 drivers.removeCurrentDriver();
2764 CloseHandle( handle->condition );
2765 if ( handle->bufferInfos )
2766 free( handle->bufferInfos );
2768 stream_.apiHandle = 0;
2771 for ( int i=0; i<2; i++ ) {
2772 if ( stream_.userBuffer[i] ) {
2773 free( stream_.userBuffer[i] );
2774 stream_.userBuffer[i] = 0;
2778 if ( stream_.deviceBuffer ) {
2779 free( stream_.deviceBuffer );
2780 stream_.deviceBuffer = 0;
2786 void RtApiAsio :: closeStream()
2788 if ( stream_.state == STREAM_CLOSED ) {
2789 errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
2790 error( RtError::WARNING );
2794 if ( stream_.state == STREAM_RUNNING ) {
2795 stream_.state = STREAM_STOPPED;
2798 ASIODisposeBuffers();
2799 drivers.removeCurrentDriver();
2801 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2803 CloseHandle( handle->condition );
2804 if ( handle->bufferInfos )
2805 free( handle->bufferInfos );
2807 stream_.apiHandle = 0;
2810 for ( int i=0; i<2; i++ ) {
2811 if ( stream_.userBuffer[i] ) {
2812 free( stream_.userBuffer[i] );
2813 stream_.userBuffer[i] = 0;
2817 if ( stream_.deviceBuffer ) {
2818 free( stream_.deviceBuffer );
2819 stream_.deviceBuffer = 0;
2822 stream_.mode = UNINITIALIZED;
2823 stream_.state = STREAM_CLOSED;
2826 void RtApiAsio :: startStream()
2829 if ( stream_.state == STREAM_RUNNING ) {
2830 errorText_ = "RtApiAsio::startStream(): the stream is already running!";
2831 error( RtError::WARNING );
2835 MUTEX_LOCK( &stream_.mutex );
2837 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2838 ASIOError result = ASIOStart();
2839 if ( result != ASE_OK ) {
2840 errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";
2841 errorText_ = errorStream_.str();
2845 handle->drainCounter = 0;
2846 handle->internalDrain = false;
2847 stream_.state = STREAM_RUNNING;
2851 MUTEX_UNLOCK( &stream_.mutex );
2853 if ( result == ASE_OK ) return;
2854 error( RtError::SYSTEM_ERROR );
2857 void RtApiAsio :: stopStream()
2860 if ( stream_.state == STREAM_STOPPED ) {
2861 errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
2862 error( RtError::WARNING );
2866 MUTEX_LOCK( &stream_.mutex );
2868 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2869 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2870 if ( handle->drainCounter == 0 ) {
2871 handle->drainCounter = 1;
2872 MUTEX_UNLOCK( &stream_.mutex );
2873 WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE ); // block until signaled
2874 ResetEvent( handle->condition );
2875 MUTEX_LOCK( &stream_.mutex );
2879 ASIOError result = ASIOStop();
2880 if ( result != ASE_OK ) {
2881 errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
2882 errorText_ = errorStream_.str();
2885 stream_.state = STREAM_STOPPED;
2886 MUTEX_UNLOCK( &stream_.mutex );
2888 if ( result == ASE_OK ) return;
2889 error( RtError::SYSTEM_ERROR );
2892 void RtApiAsio :: abortStream()
2895 if ( stream_.state == STREAM_STOPPED ) {
2896 errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
2897 error( RtError::WARNING );
2901 // The following lines were commented-out because some behavior was
2902 // noted where the device buffers need to be zeroed to avoid
2903 // continuing sound, even when the device buffers are completed
2904 // disposed. So now, calling abort is the same as calling stop.
2905 //AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2906 //handle->drainCounter = 1;
2910 bool RtApiAsio :: callbackEvent( long bufferIndex )
2912 if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
2913 if ( stream_.state == STREAM_CLOSED ) {
2914 errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
2915 error( RtError::WARNING );
2919 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
2920 AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
2922 // Check if we were draining the stream and signal is finished.
2923 if ( handle->drainCounter > 3 ) {
2924 if ( handle->internalDrain == false )
2925 SetEvent( handle->condition );
2931 MUTEX_LOCK( &stream_.mutex );
2933 // The state might change while waiting on a mutex.
2934 if ( stream_.state == STREAM_STOPPED ) goto unlock;
2936 // Invoke user callback to get fresh output data UNLESS we are
2938 if ( handle->drainCounter == 0 ) {
2939 RtAudioCallback callback = (RtAudioCallback) info->callback;
2940 double streamTime = getStreamTime();
2941 RtAudioStreamStatus status = 0;
2942 if ( stream_.mode != INPUT && asioXRun == true ) {
2943 status |= RTAUDIO_OUTPUT_UNDERFLOW;
2946 if ( stream_.mode != OUTPUT && asioXRun == true ) {
2947 status |= RTAUDIO_INPUT_OVERFLOW;
2950 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
2951 stream_.bufferSize, streamTime, status, info->userData );
2952 if ( handle->drainCounter == 2 ) {
2953 MUTEX_UNLOCK( &stream_.mutex );
2957 else if ( handle->drainCounter == 1 )
2958 handle->internalDrain = true;
2961 unsigned int bufferBytes, i, j;
2962 unsigned int nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
2963 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
2965 bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
2967 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
2969 for ( i=0, j=0; i<nChannels; i++ ) {
2970 if ( handle->bufferInfos[i].isInput != ASIOTrue )
2971 memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
2975 else if ( stream_.doConvertBuffer[0] ) {
2977 convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
2978 if ( stream_.doByteSwap[0] )
2979 byteSwapBuffer( stream_.deviceBuffer,
2980 stream_.bufferSize * stream_.nDeviceChannels[0],
2981 stream_.deviceFormat[0] );
2983 for ( i=0, j=0; i<nChannels; i++ ) {
2984 if ( handle->bufferInfos[i].isInput != ASIOTrue )
2985 memcpy( handle->bufferInfos[i].buffers[bufferIndex],
2986 &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
2992 if ( stream_.doByteSwap[0] )
2993 byteSwapBuffer( stream_.userBuffer[0],
2994 stream_.bufferSize * stream_.nUserChannels[0],
2995 stream_.userFormat );
2997 for ( i=0, j=0; i<nChannels; i++ ) {
2998 if ( handle->bufferInfos[i].isInput != ASIOTrue )
2999 memcpy( handle->bufferInfos[i].buffers[bufferIndex],
3000 &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );
3005 if ( handle->drainCounter ) {
3006 handle->drainCounter++;
3011 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
3013 bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
3015 if (stream_.doConvertBuffer[1]) {
3017 // Always interleave ASIO input data.
3018 for ( i=0, j=0; i<nChannels; i++ ) {
3019 if ( handle->bufferInfos[i].isInput == ASIOTrue )
3020 memcpy( &stream_.deviceBuffer[j++*bufferBytes],
3021 handle->bufferInfos[i].buffers[bufferIndex],
3025 if ( stream_.doByteSwap[1] )
3026 byteSwapBuffer( stream_.deviceBuffer,
3027 stream_.bufferSize * stream_.nDeviceChannels[1],
3028 stream_.deviceFormat[1] );
3029 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
3033 for ( i=0, j=0; i<nChannels; i++ ) {
3034 if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
3035 memcpy( &stream_.userBuffer[1][bufferBytes*j++],
3036 handle->bufferInfos[i].buffers[bufferIndex],
3041 if ( stream_.doByteSwap[1] )
3042 byteSwapBuffer( stream_.userBuffer[1],
3043 stream_.bufferSize * stream_.nUserChannels[1],
3044 stream_.userFormat );
3049 // The following call was suggested by Malte Clasen. While the API
3050 // documentation indicates it should not be required, some device
3051 // drivers apparently do not function correctly without it.
3054 MUTEX_UNLOCK( &stream_.mutex );
3056 RtApi::tickStreamTime();
3060 void sampleRateChanged( ASIOSampleRate sRate )
3062 // The ASIO documentation says that this usually only happens during
3063 // external sync. Audio processing is not stopped by the driver,
3064 // actual sample rate might not have even changed, maybe only the
3065 // sample rate status of an AES/EBU or S/PDIF digital input at the
3068 RtApi *object = (RtApi *) asioCallbackInfo->object;
3070 object->stopStream();
3072 catch ( RtError &exception ) {
3073 std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
3077 std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
3080 long asioMessages( long selector, long value, void* message, double* opt )
3084 switch( selector ) {
3085 case kAsioSelectorSupported:
3086 if ( value == kAsioResetRequest
3087 || value == kAsioEngineVersion
3088 || value == kAsioResyncRequest
3089 || value == kAsioLatenciesChanged
3090 // The following three were added for ASIO 2.0, you don't
3091 // necessarily have to support them.
3092 || value == kAsioSupportsTimeInfo
3093 || value == kAsioSupportsTimeCode
3094 || value == kAsioSupportsInputMonitor)
3097 case kAsioResetRequest:
3098 // Defer the task and perform the reset of the driver during the
3099 // next "safe" situation. You cannot reset the driver right now,
3100 // as this code is called from the driver. Reset the driver is
3101 // done by completely destruct is. I.e. ASIOStop(),
3102 // ASIODisposeBuffers(), Destruction Afterwards you initialize the
3104 std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
3107 case kAsioResyncRequest:
3108 // This informs the application that the driver encountered some
3109 // non-fatal data loss. It is used for synchronization purposes
3110 // of different media. Added mainly to work around the Win16Mutex
3111 // problems in Windows 95/98 with the Windows Multimedia system,
3112 // which could lose data because the Mutex was held too long by
3113 // another thread. However a driver can issue it in other
3115 // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
3119 case kAsioLatenciesChanged:
3120 // This will inform the host application that the drivers were
3121 // latencies changed. Beware, it this does not mean that the
3122 // buffer sizes have changed! You might need to update internal
3124 std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
3127 case kAsioEngineVersion:
3128 // Return the supported ASIO version of the host application. If
3129 // a host application does not implement this selector, ASIO 1.0
3130 // is assumed by the driver.
3133 case kAsioSupportsTimeInfo:
3134 // Informs the driver whether the
3135 // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
3136 // For compatibility with ASIO 1.0 drivers the host application
3137 // should always support the "old" bufferSwitch method, too.
3140 case kAsioSupportsTimeCode:
3141 // Informs the driver whether application is interested in time
3142 // code info. If an application does not need to know about time
3143 // code, the driver has less work to do.
3150 static const char* getAsioErrorString( ASIOError result )
3158 static Messages m[] =
3160 { ASE_NotPresent, "Hardware input or output is not present or available." },
3161 { ASE_HWMalfunction, "Hardware is malfunctioning." },
3162 { ASE_InvalidParameter, "Invalid input parameter." },
3163 { ASE_InvalidMode, "Invalid mode." },
3164 { ASE_SPNotAdvancing, "Sample position not advancing." },
3165 { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },
3166 { ASE_NoMemory, "Not enough memory to complete the request." }
3169 for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
3170 if ( m[i].value == result ) return m[i].message;
3172 return "Unknown error.";
3174 //******************** End of __WINDOWS_ASIO__ *********************//
3178 #if defined(__WINDOWS_DS__) // Windows DirectSound API
3180 // Modified by Robin Davies, October 2005
3181 // - Improvements to DirectX pointer chasing.
3182 // - Backdoor RtDsStatistics hook provides DirectX performance information.
3183 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
3184 // - Auto-call CoInitialize for DSOUND and ASIO platforms.
3185 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
3190 #define MINIMUM_DEVICE_BUFFER_SIZE 32768
3192 #ifdef _MSC_VER // if Microsoft Visual C++
3193 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.
3196 static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
3198 if (laterPointer > earlierPointer)
3199 return laterPointer - earlierPointer;
3201 return laterPointer - earlierPointer + bufferSize;
3204 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
3206 if ( pointer > bufferSize ) pointer -= bufferSize;
3207 if ( laterPointer < earlierPointer ) laterPointer += bufferSize;
3208 if ( pointer < earlierPointer ) pointer += bufferSize;
3209 return pointer >= earlierPointer && pointer < laterPointer;
3212 // A structure to hold various information related to the DirectSound
3213 // API implementation.
3215 unsigned int drainCounter; // Tracks callback counts when draining
3216 bool internalDrain; // Indicates if stop is initiated from callback or not.
3220 UINT bufferPointer[2];
3221 DWORD dsBufferSize[2];
3222 DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
3226 :drainCounter(0), internalDrain(false) { id[0] = 0, id[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }
3230 RtApiDs::RtDsStatistics RtApiDs::statistics;
3232 // Provides a backdoor hook to monitor for DirectSound read overruns and write underruns.
3233 RtApiDs::RtDsStatistics RtApiDs::getDsStatistics()
3235 RtDsStatistics s = statistics;
3237 // update the calculated fields.
3238 if ( s.inputFrameSize != 0 )
3239 s.latency += s.readDeviceSafeLeadBytes * 1.0 / s.inputFrameSize / s.sampleRate;
3241 if ( s.outputFrameSize != 0 )
3242 s.latency += (s.writeDeviceSafeLeadBytes + s.writeDeviceBufferLeadBytes) * 1.0 / s.outputFrameSize / s.sampleRate;
3248 // Declarations for utility functions, callbacks, and structures
3249 // specific to the DirectSound implementation.
3250 static bool CALLBACK deviceCountCallback( LPGUID lpguid,
3251 LPCTSTR description,
3255 static char* getErrorString( int code );
3257 extern "C" unsigned __stdcall callbackHandler( void *ptr );
3263 unsigned int counter;
3269 : isInput(false), getDefault(false), findIndex(false), counter(0), index(0) {}
3272 RtApiDs :: RtApiDs()
3274 // Dsound will run both-threaded. If CoInitialize fails, then just
3275 // accept whatever the mainline chose for a threading model.
3276 coInitialized_ = false;
3277 HRESULT hr = CoInitialize( NULL );
3278 if ( !FAILED( hr ) ) coInitialized_ = true;
3281 RtApiDs :: ~RtApiDs()
3283 if ( coInitialized_ ) CoUninitialize(); // balanced call.
3284 if ( stream_.state != STREAM_CLOSED ) closeStream();
3287 unsigned int RtApiDs :: getDefaultInputDevice( void )
3289 // Count output devices.
3291 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
3292 if ( FAILED( result ) ) {
3293 errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") counting output devices!";
3294 errorText_ = errorStream_.str();
3295 error( RtError::WARNING );
3299 // Now enumerate input devices until we find the id = NULL.
3300 info.isInput = true;
3301 info.getDefault = true;
3302 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
3303 if ( FAILED( result ) ) {
3304 errorStream_ << "RtApiDs::getDefaultInputDevice: error (" << getErrorString( result ) << ") enumerating input devices!";
3305 errorText_ = errorStream_.str();
3306 error( RtError::WARNING );
3310 if ( info.counter > 0 ) return info.counter - 1;
3314 unsigned int RtApiDs :: getDefaultOutputDevice( void )
3316 // Enumerate output devices until we find the id = NULL.
3318 info.getDefault = true;
3319 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
3320 if ( FAILED( result ) ) {
3321 errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") enumerating output devices!";
3322 errorText_ = errorStream_.str();
3323 error( RtError::WARNING );
3327 if ( info.counter > 0 ) return info.counter - 1;
3331 unsigned int RtApiDs :: getDeviceCount( void )
3333 // Count DirectSound devices.
3335 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
3336 if ( FAILED( result ) ) {
3337 errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
3338 errorText_ = errorStream_.str();
3339 error( RtError::WARNING );
3342 // Count DirectSoundCapture devices.
3343 info.isInput = true;
3344 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
3345 if ( FAILED( result ) ) {
3346 errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
3347 errorText_ = errorStream_.str();
3348 error( RtError::WARNING );
3351 return info.counter;
3354 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
3356 // Because DirectSound always enumerates input and output devices
3357 // separately (and because we don't attempt to combine devices
3358 // internally), none of our "devices" will ever be duplex.
3360 RtAudio::DeviceInfo info;
3361 info.probed = false;
3363 // Enumerate through devices to find the id (if it exists). Note
3364 // that we have to do the output enumeration first, even if this is
3365 // an input device, in order for the device counter to be correct.
3367 dsinfo.findIndex = true;
3368 dsinfo.index = device;
3369 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
3370 if ( FAILED( result ) ) {
3371 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating output devices!";
3372 errorText_ = errorStream_.str();
3373 error( RtError::WARNING );
3376 if ( dsinfo.name.empty() ) goto probeInput;
3378 LPDIRECTSOUND output;
3380 result = DirectSoundCreate( dsinfo.id, &output, NULL );
3381 if ( FAILED( result ) ) {
3382 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
3383 errorText_ = errorStream_.str();
3384 error( RtError::WARNING );
3388 outCaps.dwSize = sizeof( outCaps );
3389 result = output->GetCaps( &outCaps );
3390 if ( FAILED( result ) ) {
3392 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
3393 errorText_ = errorStream_.str();
3394 error( RtError::WARNING );
3398 // Get output channel information.
3399 info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
3401 // Get sample rate information.
3402 info.sampleRates.clear();
3403 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
3404 if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
3405 SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )
3406 info.sampleRates.push_back( SAMPLE_RATES[k] );
3409 // Get format information.
3410 if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
3411 if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
3415 if ( getDefaultOutputDevice() == device )
3416 info.isDefaultOutput = true;
3418 // Copy name and return.
3419 info.name = dsinfo.name;
3426 dsinfo.isInput = true;
3427 result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
3428 if ( FAILED( result ) ) {
3429 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating input devices!";
3430 errorText_ = errorStream_.str();
3431 error( RtError::WARNING );
3434 if ( dsinfo.name.empty() ) return info;
3436 LPDIRECTSOUNDCAPTURE input;
3437 result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
3438 if ( FAILED( result ) ) {
3439 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
3440 errorText_ = errorStream_.str();
3441 error( RtError::WARNING );
3446 inCaps.dwSize = sizeof( inCaps );
3447 result = input->GetCaps( &inCaps );
3448 if ( FAILED( result ) ) {
3450 errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsinfo.name << ")!";
3451 errorText_ = errorStream_.str();
3452 error( RtError::WARNING );
3456 // Get input channel information.
3457 info.inputChannels = inCaps.dwChannels;
3459 // Get sample rate and format information.
3460 if ( inCaps.dwChannels == 2 ) {
3461 if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3462 if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3463 if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3464 if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
3465 if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3466 if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3467 if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3468 if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
3470 if ( info.nativeFormats & RTAUDIO_SINT16 ) {
3471 if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.sampleRates.push_back( 11025 );
3472 if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.sampleRates.push_back( 22050 );
3473 if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.sampleRates.push_back( 44100 );
3474 if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.sampleRates.push_back( 96000 );
3476 else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
3477 if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.sampleRates.push_back( 11025 );
3478 if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.sampleRates.push_back( 22050 );
3479 if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.sampleRates.push_back( 44100 );
3480 if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.sampleRates.push_back( 44100 );
3483 else if ( inCaps.dwChannels == 1 ) {
3484 if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3485 if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3486 if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3487 if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
3488 if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3489 if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3490 if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3491 if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
3493 if ( info.nativeFormats & RTAUDIO_SINT16 ) {
3494 if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.sampleRates.push_back( 11025 );
3495 if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.sampleRates.push_back( 22050 );
3496 if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.sampleRates.push_back( 44100 );
3497 if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.sampleRates.push_back( 96000 );
3499 else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
3500 if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.sampleRates.push_back( 11025 );
3501 if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.sampleRates.push_back( 22050 );
3502 if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.sampleRates.push_back( 44100 );
3503 if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.sampleRates.push_back( 96000 );
3506 else info.inputChannels = 0; // technically, this would be an error
3510 if ( info.inputChannels == 0 ) return info;
3512 if ( getDefaultInputDevice() == device )
3513 info.isDefaultInput = true;
3515 // Copy name and return.
3516 info.name = dsinfo.name;
3521 bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
3522 unsigned int firstChannel, unsigned int sampleRate,
3523 RtAudioFormat format, unsigned int *bufferSize,
3524 RtAudio::StreamOptions *options )
3526 if ( channels + firstChannel > 2 ) {
3527 errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
3531 // Enumerate through devices to find the id (if it exists). Note
3532 // that we have to do the output enumeration first, even if this is
3533 // an input device, in order for the device counter to be correct.
3535 dsinfo.findIndex = true;
3536 dsinfo.index = device;
3537 HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
3538 if ( FAILED( result ) ) {
3539 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating output devices!";
3540 errorText_ = errorStream_.str();
3544 if ( mode == OUTPUT ) {
3545 if ( dsinfo.name.empty() ) {
3546 errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
3547 errorText_ = errorStream_.str();
3551 else { // mode == INPUT
3552 dsinfo.isInput = true;
3553 HRESULT result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
3554 if ( FAILED( result ) ) {
3555 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating input devices!";
3556 errorText_ = errorStream_.str();
3559 if ( dsinfo.name.empty() ) {
3560 errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
3561 errorText_ = errorStream_.str();
3566 // According to a note in PortAudio, using GetDesktopWindow()
3567 // instead of GetForegroundWindow() is supposed to avoid problems
3568 // that occur when the application's window is not the foreground
3569 // window. Also, if the application window closes before the
3570 // DirectSound buffer, DirectSound can crash. However, for console
3571 // applications, no sound was produced when using GetDesktopWindow().
3572 HWND hWnd = GetForegroundWindow();
3574 // Check the numberOfBuffers parameter and limit the lowest value to
3575 // two. This is a judgement call and a value of two is probably too
3576 // low for capture, but it should work for playback.
3578 if ( options ) nBuffers = options->numberOfBuffers;
3579 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
3580 if ( nBuffers < 2 ) nBuffers = 3;
3582 // Create the wave format structure. The data format setting will
3583 // be determined later.
3584 WAVEFORMATEX waveFormat;
3585 ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
3586 waveFormat.wFormatTag = WAVE_FORMAT_PCM;
3587 waveFormat.nChannels = channels + firstChannel;
3588 waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
3590 // Determine the device buffer size. By default, 32k, but we will
3591 // grow it to make allowances for very large software buffer sizes.
3592 DWORD dsBufferSize = 0;
3593 DWORD dsPointerLeadTime = 0;
3594 long bufferBytes = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
3596 void *ohandle = 0, *bhandle = 0;
3597 if ( mode == OUTPUT ) {
3599 LPDIRECTSOUND output;
3600 result = DirectSoundCreate( dsinfo.id, &output, NULL );
3601 if ( FAILED( result ) ) {
3602 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
3603 errorText_ = errorStream_.str();
3608 outCaps.dwSize = sizeof( outCaps );
3609 result = output->GetCaps( &outCaps );
3610 if ( FAILED( result ) ) {
3612 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsinfo.name << ")!";
3613 errorText_ = errorStream_.str();
3617 // Check channel information.
3618 if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
3619 errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsinfo.name << ") does not support stereo playback.";
3620 errorText_ = errorStream_.str();
3624 // Check format information. Use 16-bit format unless not
3625 // supported or user requests 8-bit.
3626 if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
3627 !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
3628 waveFormat.wBitsPerSample = 16;
3629 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3632 waveFormat.wBitsPerSample = 8;
3633 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3635 stream_.userFormat = format;
3637 // Update wave format structure and buffer information.
3638 waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
3639 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
3640 dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
3642 // If the user wants an even bigger buffer, increase the device buffer size accordingly.
3643 while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes )
3646 // Set cooperative level to DSSCL_EXCLUSIVE
3647 result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
3648 if ( FAILED( result ) ) {
3650 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsinfo.name << ")!";
3651 errorText_ = errorStream_.str();
3655 // Even though we will write to the secondary buffer, we need to
3656 // access the primary buffer to set the correct output format
3657 // (since the default is 8-bit, 22 kHz!). Setup the DS primary
3658 // buffer description.
3659 DSBUFFERDESC bufferDescription;
3660 ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
3661 bufferDescription.dwSize = sizeof( DSBUFFERDESC );
3662 bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
3664 // Obtain the primary buffer
3665 LPDIRECTSOUNDBUFFER buffer;
3666 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3667 if ( FAILED( result ) ) {
3669 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsinfo.name << ")!";
3670 errorText_ = errorStream_.str();
3674 // Set the primary DS buffer sound format.
3675 result = buffer->SetFormat( &waveFormat );
3676 if ( FAILED( result ) ) {
3678 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsinfo.name << ")!";
3679 errorText_ = errorStream_.str();
3683 // Setup the secondary DS buffer description.
3684 dsBufferSize = (DWORD) bufferBytes;
3685 ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
3686 bufferDescription.dwSize = sizeof( DSBUFFERDESC );
3687 bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
3688 DSBCAPS_GETCURRENTPOSITION2 |
3689 DSBCAPS_LOCHARDWARE ); // Force hardware mixing
3690 bufferDescription.dwBufferBytes = bufferBytes;
3691 bufferDescription.lpwfxFormat = &waveFormat;
3693 // Try to create the secondary DS buffer. If that doesn't work,
3694 // try to use software mixing. Otherwise, there's a problem.
3695 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3696 if ( FAILED( result ) ) {
3697 bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
3698 DSBCAPS_GETCURRENTPOSITION2 |
3699 DSBCAPS_LOCSOFTWARE ); // Force software mixing
3700 result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
3701 if ( FAILED( result ) ) {
3703 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsinfo.name << ")!";
3704 errorText_ = errorStream_.str();
3709 // Get the buffer size ... might be different from what we specified.
3711 dsbcaps.dwSize = sizeof( DSBCAPS );
3712 result = buffer->GetCaps( &dsbcaps );
3713 if ( FAILED( result ) ) {
3716 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!";
3717 errorText_ = errorStream_.str();
3721 bufferBytes = dsbcaps.dwBufferBytes;
3723 // Lock the DS buffer
3726 result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
3727 if ( FAILED( result ) ) {
3730 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsinfo.name << ")!";
3731 errorText_ = errorStream_.str();
3735 // Zero the DS buffer
3736 ZeroMemory( audioPtr, dataLen );
3738 // Unlock the DS buffer
3739 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
3740 if ( FAILED( result ) ) {
3743 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsinfo.name << ")!";
3744 errorText_ = errorStream_.str();
3748 dsBufferSize = bufferBytes;
3749 ohandle = (void *) output;
3750 bhandle = (void *) buffer;
3753 if ( mode == INPUT ) {
3755 LPDIRECTSOUNDCAPTURE input;
3756 result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
3757 if ( FAILED( result ) ) {
3758 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
3759 errorText_ = errorStream_.str();
3764 inCaps.dwSize = sizeof( inCaps );
3765 result = input->GetCaps( &inCaps );
3766 if ( FAILED( result ) ) {
3768 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsinfo.name << ")!";
3769 errorText_ = errorStream_.str();
3773 // Check channel information.
3774 if ( inCaps.dwChannels < channels + firstChannel ) {
3775 errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
3779 // Check format information. Use 16-bit format unless user
3781 DWORD deviceFormats;
3782 if ( channels + firstChannel == 2 ) {
3783 deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
3784 if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
3785 waveFormat.wBitsPerSample = 8;
3786 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3788 else { // assume 16-bit is supported
3789 waveFormat.wBitsPerSample = 16;
3790 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3793 else { // channel == 1
3794 deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
3795 if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
3796 waveFormat.wBitsPerSample = 8;
3797 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
3799 else { // assume 16-bit is supported
3800 waveFormat.wBitsPerSample = 16;
3801 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
3804 stream_.userFormat = format;
3806 // Update wave format structure and buffer information.
3807 waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
3808 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
3810 // Setup the secondary DS buffer description.
3811 dsBufferSize = bufferBytes;
3812 DSCBUFFERDESC bufferDescription;
3813 ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
3814 bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
3815 bufferDescription.dwFlags = 0;
3816 bufferDescription.dwReserved = 0;
3817 bufferDescription.dwBufferBytes = bufferBytes;
3818 bufferDescription.lpwfxFormat = &waveFormat;
3820 // Create the capture buffer.
3821 LPDIRECTSOUNDCAPTUREBUFFER buffer;
3822 result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
3823 if ( FAILED( result ) ) {
3825 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsinfo.name << ")!";
3826 errorText_ = errorStream_.str();
3830 // Lock the capture buffer
3833 result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
3834 if ( FAILED( result ) ) {
3837 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsinfo.name << ")!";
3838 errorText_ = errorStream_.str();
3843 ZeroMemory( audioPtr, dataLen );
3845 // Unlock the buffer
3846 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
3847 if ( FAILED( result ) ) {
3850 errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsinfo.name << ")!";
3851 errorText_ = errorStream_.str();
3855 dsBufferSize = bufferBytes;
3856 ohandle = (void *) input;
3857 bhandle = (void *) buffer;
3860 // Set various stream parameters
3861 stream_.nDeviceChannels[mode] = channels + firstChannel;
3862 stream_.nUserChannels[mode] = channels;
3863 stream_.bufferSize = *bufferSize;
3864 stream_.channelOffset[mode] = firstChannel;
3865 stream_.deviceInterleaved[mode] = true;
3866 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
3867 else stream_.userInterleaved = true;
3869 // Set flag for buffer conversion
3870 stream_.doConvertBuffer[mode] = false;
3871 if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])
3872 stream_.doConvertBuffer[mode] = true;
3873 if (stream_.userFormat != stream_.deviceFormat[mode])
3874 stream_.doConvertBuffer[mode] = true;
3875 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
3876 stream_.nUserChannels[mode] > 1 )
3877 stream_.doConvertBuffer[mode] = true;
3879 // Allocate necessary internal buffers
3880 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
3881 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
3882 if ( stream_.userBuffer[mode] == NULL ) {
3883 errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
3887 if ( stream_.doConvertBuffer[mode] ) {
3889 bool makeBuffer = true;
3890 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
3891 if ( mode == INPUT ) {
3892 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
3893 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
3894 if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
3899 bufferBytes *= *bufferSize;
3900 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
3901 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
3902 if ( stream_.deviceBuffer == NULL ) {
3903 errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
3909 // Allocate our DsHandle structures for the stream.
3911 if ( stream_.apiHandle == 0 ) {
3913 handle = new DsHandle;
3915 catch ( std::bad_alloc& ) {
3916 errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
3920 // Create a manual-reset event.
3921 handle->condition = CreateEvent( NULL, // no security
3922 TRUE, // manual-reset
3923 FALSE, // non-signaled initially
3925 stream_.apiHandle = (void *) handle;
3928 handle = (DsHandle *) stream_.apiHandle;
3929 handle->id[mode] = ohandle;
3930 handle->buffer[mode] = bhandle;
3931 handle->dsBufferSize[mode] = dsBufferSize;
3932 handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
3934 stream_.device[mode] = device;
3935 stream_.state = STREAM_STOPPED;
3936 if ( stream_.mode == OUTPUT && mode == INPUT )
3937 // We had already set up an output stream.
3938 stream_.mode = DUPLEX;
3940 stream_.mode = mode;
3941 stream_.nBuffers = nBuffers;
3942 stream_.sampleRate = sampleRate;
3944 // Setup the buffer conversion information structure.
3945 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
3947 // Setup the callback thread.
3949 stream_.callbackInfo.object = (void *) this;
3950 stream_.callbackInfo.isRunning = true;
3951 stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,
3952 &stream_.callbackInfo, 0, &threadId );
3953 if ( stream_.callbackInfo.thread == 0 ) {
3954 errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";
3962 if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
3963 LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
3964 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
3965 if ( buffer ) buffer->Release();
3968 if ( handle->buffer[1] ) {
3969 LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
3970 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
3971 if ( buffer ) buffer->Release();
3974 CloseHandle( handle->condition );
3976 stream_.apiHandle = 0;
3979 for ( int i=0; i<2; i++ ) {
3980 if ( stream_.userBuffer[i] ) {
3981 free( stream_.userBuffer[i] );
3982 stream_.userBuffer[i] = 0;
3986 if ( stream_.deviceBuffer ) {
3987 free( stream_.deviceBuffer );
3988 stream_.deviceBuffer = 0;
3994 void RtApiDs :: closeStream()
3996 if ( stream_.state == STREAM_CLOSED ) {
3997 errorText_ = "RtApiDs::closeStream(): no open stream to close!";
3998 error( RtError::WARNING );
4002 // Stop the callback thread.
4003 stream_.callbackInfo.isRunning = false;
4004 WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
4005 CloseHandle( (HANDLE) stream_.callbackInfo.thread );
4007 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4009 if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
4010 LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
4011 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4018 if ( handle->buffer[1] ) {
4019 LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
4020 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4027 CloseHandle( handle->condition );
4029 stream_.apiHandle = 0;
4032 for ( int i=0; i<2; i++ ) {
4033 if ( stream_.userBuffer[i] ) {
4034 free( stream_.userBuffer[i] );
4035 stream_.userBuffer[i] = 0;
4039 if ( stream_.deviceBuffer ) {
4040 free( stream_.deviceBuffer );
4041 stream_.deviceBuffer = 0;
4044 stream_.mode = UNINITIALIZED;
4045 stream_.state = STREAM_CLOSED;
4048 void RtApiDs :: startStream()
4051 if ( stream_.state == STREAM_RUNNING ) {
4052 errorText_ = "RtApiDs::startStream(): the stream is already running!";
4053 error( RtError::WARNING );
4057 // Increase scheduler frequency on lesser windows (a side-effect of
4058 // increasing timer accuracy). On greater windows (Win2K or later),
4059 // this is already in effect.
4061 MUTEX_LOCK( &stream_.mutex );
4063 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4065 timeBeginPeriod( 1 );
4068 memset( &statistics, 0, sizeof( statistics ) );
4069 statistics.sampleRate = stream_.sampleRate;
4070 statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0];
4073 buffersRolling = false;
4074 duplexPrerollBytes = 0;
4076 if ( stream_.mode == DUPLEX ) {
4077 // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
4078 duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
4082 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4083 //statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0] ) * stream_.nDeviceChannels[0];
4085 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4086 result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
4087 if ( FAILED( result ) ) {
4088 errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
4089 errorText_ = errorStream_.str();
4094 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4095 //statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) * stream_.nDeviceChannels[1];
4097 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4098 result = buffer->Start( DSCBSTART_LOOPING );
4099 if ( FAILED( result ) ) {
4100 errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";
4101 errorText_ = errorStream_.str();
4106 handle->drainCounter = 0;
4107 handle->internalDrain = false;
4108 stream_.state = STREAM_RUNNING;
4111 MUTEX_UNLOCK( &stream_.mutex );
4113 if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
4116 void RtApiDs :: stopStream()
4119 if ( stream_.state == STREAM_STOPPED ) {
4120 errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
4121 error( RtError::WARNING );
4125 MUTEX_LOCK( &stream_.mutex );
4130 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4131 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4132 if ( handle->drainCounter == 0 ) {
4133 handle->drainCounter = 1;
4134 MUTEX_UNLOCK( &stream_.mutex );
4135 WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE ); // block until signaled
4136 ResetEvent( handle->condition );
4137 MUTEX_LOCK( &stream_.mutex );
4140 // Stop the buffer and clear memory
4141 LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4142 result = buffer->Stop();
4143 if ( FAILED( result ) ) {
4144 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!";
4145 errorText_ = errorStream_.str();
4149 // Lock the buffer and clear it so that if we start to play again,
4150 // we won't have old data playing.
4151 result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
4152 if ( FAILED( result ) ) {
4153 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking output buffer!";
4154 errorText_ = errorStream_.str();
4158 // Zero the DS buffer
4159 ZeroMemory( audioPtr, dataLen );
4161 // Unlock the DS buffer
4162 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4163 if ( FAILED( result ) ) {
4164 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
4165 errorText_ = errorStream_.str();
4169 // If we start playing again, we must begin at beginning of buffer.
4170 handle->bufferPointer[0] = 0;
4173 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4174 LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4178 result = buffer->Stop();
4179 if ( FAILED( result ) ) {
4180 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping input buffer!";
4181 errorText_ = errorStream_.str();
4185 // Lock the buffer and clear it so that if we start to play again,
4186 // we won't have old data playing.
4187 result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
4188 if ( FAILED( result ) ) {
4189 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking input buffer!";
4190 errorText_ = errorStream_.str();
4194 // Zero the DS buffer
4195 ZeroMemory( audioPtr, dataLen );
4197 // Unlock the DS buffer
4198 result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
4199 if ( FAILED( result ) ) {
4200 errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
4201 errorText_ = errorStream_.str();
4205 // If we start recording again, we must begin at beginning of buffer.
4206 handle->bufferPointer[1] = 0;
4210 timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
4211 stream_.state = STREAM_STOPPED;
4212 MUTEX_UNLOCK( &stream_.mutex );
4213 if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
4216 void RtApiDs :: abortStream()
4219 if ( stream_.state == STREAM_STOPPED ) {
4220 errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
4221 error( RtError::WARNING );
4225 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4226 handle->drainCounter = 1;
4231 void RtApiDs :: callbackEvent()
4233 if ( stream_.state == STREAM_STOPPED ) {
4234 Sleep(50); // sleep 50 milliseconds
4238 if ( stream_.state == STREAM_CLOSED ) {
4239 errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
4240 error( RtError::WARNING );
4244 CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
4245 DsHandle *handle = (DsHandle *) stream_.apiHandle;
4247 // Check if we were draining the stream and signal is finished.
4248 if ( handle->drainCounter > stream_.nBuffers + 2 ) {
4249 if ( handle->internalDrain == false )
4250 SetEvent( handle->condition );
4256 MUTEX_LOCK( &stream_.mutex );
4258 // Invoke user callback to get fresh output data UNLESS we are
4260 if ( handle->drainCounter == 0 ) {
4261 RtAudioCallback callback = (RtAudioCallback) info->callback;
4262 double streamTime = getStreamTime();
4263 RtAudioStreamStatus status = 0;
4264 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
4265 status |= RTAUDIO_OUTPUT_UNDERFLOW;
4266 handle->xrun[0] = false;
4268 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
4269 status |= RTAUDIO_INPUT_OVERFLOW;
4270 handle->xrun[1] = false;
4272 handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
4273 stream_.bufferSize, streamTime, status, info->userData );
4274 if ( handle->drainCounter == 2 ) {
4275 MUTEX_UNLOCK( &stream_.mutex );
4279 else if ( handle->drainCounter == 1 )
4280 handle->internalDrain = true;
4284 DWORD currentWritePos, safeWritePos;
4285 DWORD currentReadPos, safeReadPos;
4289 #ifdef GENERATE_DEBUG_LOG
4290 DWORD writeTime, readTime;
4293 LPVOID buffer1 = NULL;
4294 LPVOID buffer2 = NULL;
4295 DWORD bufferSize1 = 0;
4296 DWORD bufferSize2 = 0;
4301 if ( stream_.mode == DUPLEX && !buffersRolling ) {
4302 assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
4304 // It takes a while for the devices to get rolling. As a result,
4305 // there's no guarantee that the capture and write device pointers
4306 // will move in lockstep. Wait here for both devices to start
4307 // rolling, and then set our buffer pointers accordingly.
4308 // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600
4309 // bytes later than the write buffer.
4311 // Stub: a serious risk of having a pre-emptive scheduling round
4312 // take place between the two GetCurrentPosition calls... but I'm
4313 // really not sure how to solve the problem. Temporarily boost to
4314 // Realtime priority, maybe; but I'm not sure what priority the
4315 // DirectSound service threads run at. We *should* be roughly
4316 // within a ms or so of correct.
4318 LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4319 LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4321 DWORD initialWritePos, initialSafeWritePos;
4322 DWORD initialReadPos, initialSafeReadPos;
4324 result = dsWriteBuffer->GetCurrentPosition( &initialWritePos, &initialSafeWritePos );
4325 if ( FAILED( result ) ) {
4326 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4327 errorText_ = errorStream_.str();
4328 error( RtError::SYSTEM_ERROR );
4330 result = dsCaptureBuffer->GetCurrentPosition( &initialReadPos, &initialSafeReadPos );
4331 if ( FAILED( result ) ) {
4332 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4333 errorText_ = errorStream_.str();
4334 error( RtError::SYSTEM_ERROR );
4337 result = dsWriteBuffer->GetCurrentPosition( ¤tWritePos, &safeWritePos );
4338 if ( FAILED( result ) ) {
4339 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4340 errorText_ = errorStream_.str();
4341 error( RtError::SYSTEM_ERROR );
4343 result = dsCaptureBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos );
4344 if ( FAILED( result ) ) {
4345 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4346 errorText_ = errorStream_.str();
4347 error( RtError::SYSTEM_ERROR );
4349 if ( safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos ) break;
4353 assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
4355 buffersRolling = true;
4356 handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] );
4357 handle->bufferPointer[1] = safeReadPos;
4360 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
4362 LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
4364 if ( handle->drainCounter > 1 ) { // write zeros to the output stream
4365 bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
4366 bufferBytes *= formatBytes( stream_.userFormat );
4367 memset( stream_.userBuffer[0], 0, bufferBytes );
4370 // Setup parameters and do buffer conversion if necessary.
4371 if ( stream_.doConvertBuffer[0] ) {
4372 buffer = stream_.deviceBuffer;
4373 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
4374 bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
4375 bufferBytes *= formatBytes( stream_.deviceFormat[0] );
4378 buffer = stream_.userBuffer[0];
4379 bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
4380 bufferBytes *= formatBytes( stream_.userFormat );
4383 // No byte swapping necessary in DirectSound implementation.
4385 // Ahhh ... windoze. 16-bit data is signed but 8-bit data is
4386 // unsigned. So, we need to convert our signed 8-bit data here to
4388 if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
4389 for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
4391 DWORD dsBufferSize = handle->dsBufferSize[0];
4392 nextWritePos = handle->bufferPointer[0];
4396 // Find out where the read and "safe write" pointers are.
4397 result = dsBuffer->GetCurrentPosition( ¤tWritePos, &safeWritePos );
4398 if ( FAILED( result ) ) {
4399 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
4400 errorText_ = errorStream_.str();
4401 error( RtError::SYSTEM_ERROR );
4404 leadPos = safeWritePos + handle->dsPointerLeadTime[0];
4405 if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize;
4406 if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
4407 endWrite = nextWritePos + bufferBytes;
4409 // Check whether the entire write region is behind the play pointer.
4410 if ( leadPos >= endWrite ) break;
4412 // If we are here, then we must wait until the play pointer gets
4413 // beyond the write region. The approach here is to use the
4414 // Sleep() function to suspend operation until safePos catches
4415 // up. Calculate number of milliseconds to wait as:
4416 // time = distance * (milliseconds/second) * fudgefactor /
4417 // ((bytes/sample) * (samples/second))
4418 // A "fudgefactor" less than 1 is used because it was found
4419 // that sleeping too long was MUCH worse than sleeping for
4420 // several shorter periods.
4421 double millis = ( endWrite - leadPos ) * 900.0;
4422 millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
4423 if ( millis < 1.0 ) millis = 1.0;
4424 if ( millis > 50.0 ) {
4425 static int nOverruns = 0;
4428 Sleep( (DWORD) millis );
4431 //if ( statistics.writeDeviceSafeLeadBytes < dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ) ) {
4432 // statistics.writeDeviceSafeLeadBytes = dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] );
4435 if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize )
4436 || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) {
4437 // We've strayed into the forbidden zone ... resync the read pointer.
4438 //++statistics.numberOfWriteUnderruns;
4439 handle->xrun[0] = true;
4440 nextWritePos = safeWritePos + handle->dsPointerLeadTime[0] - bufferBytes + dsBufferSize;
4441 while ( nextWritePos >= dsBufferSize ) nextWritePos -= dsBufferSize;
4442 handle->bufferPointer[0] = nextWritePos;
4443 endWrite = nextWritePos + bufferBytes;
4446 // Lock free space in the buffer
4447 result = dsBuffer->Lock( nextWritePos, bufferBytes, &buffer1,
4448 &bufferSize1, &buffer2, &bufferSize2, 0 );
4449 if ( FAILED( result ) ) {
4450 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
4451 errorText_ = errorStream_.str();
4452 error( RtError::SYSTEM_ERROR );
4455 // Copy our buffer into the DS buffer
4456 CopyMemory( buffer1, buffer, bufferSize1 );
4457 if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );
4459 // Update our buffer offset and unlock sound buffer
4460 dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
4461 if ( FAILED( result ) ) {
4462 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
4463 errorText_ = errorStream_.str();
4464 error( RtError::SYSTEM_ERROR );
4466 nextWritePos = ( nextWritePos + bufferSize1 + bufferSize2 ) % dsBufferSize;
4467 handle->bufferPointer[0] = nextWritePos;
4469 if ( handle->drainCounter ) {
4470 handle->drainCounter++;
4475 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
4477 // Setup parameters.
4478 if ( stream_.doConvertBuffer[1] ) {
4479 buffer = stream_.deviceBuffer;
4480 bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];
4481 bufferBytes *= formatBytes( stream_.deviceFormat[1] );
4484 buffer = stream_.userBuffer[1];
4485 bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];
4486 bufferBytes *= formatBytes( stream_.userFormat );
4489 LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
4490 long nextReadPos = handle->bufferPointer[1];
4491 DWORD dsBufferSize = handle->dsBufferSize[1];
4493 // Find out where the write and "safe read" pointers are.
4494 result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos );
4495 if ( FAILED( result ) ) {
4496 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4497 errorText_ = errorStream_.str();
4498 error( RtError::SYSTEM_ERROR );
4501 if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
4502 DWORD endRead = nextReadPos + bufferBytes;
4504 // Handling depends on whether we are INPUT or DUPLEX.
4505 // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
4506 // then a wait here will drag the write pointers into the forbidden zone.
4508 // In DUPLEX mode, rather than wait, we will back off the read pointer until
4509 // it's in a safe position. This causes dropouts, but it seems to be the only
4510 // practical way to sync up the read and write pointers reliably, given the
4511 // the very complex relationship between phase and increment of the read and write
4514 // In order to minimize audible dropouts in DUPLEX mode, we will
4515 // provide a pre-roll period of 0.5 seconds in which we return
4516 // zeros from the read buffer while the pointers sync up.
4518 if ( stream_.mode == DUPLEX ) {
4519 if ( safeReadPos < endRead ) {
4520 if ( duplexPrerollBytes <= 0 ) {
4521 // Pre-roll time over. Be more agressive.
4522 int adjustment = endRead-safeReadPos;
4524 handle->xrun[1] = true;
4525 //++statistics.numberOfReadOverruns;
4527 // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
4528 // and perform fine adjustments later.
4529 // - small adjustments: back off by twice as much.
4530 if ( adjustment >= 2*bufferBytes )
4531 nextReadPos = safeReadPos-2*bufferBytes;
4533 nextReadPos = safeReadPos-bufferBytes-adjustment;
4535 //statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos;
4536 //if ( statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize;
4537 if ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
4541 // In pre=roll time. Just do it.
4542 nextReadPos = safeReadPos-bufferBytes;
4543 while ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
4545 endRead = nextReadPos + bufferBytes;
4548 else { // mode == INPUT
4549 while ( safeReadPos < endRead ) {
4550 // See comments for playback.
4551 double millis = (endRead - safeReadPos) * 900.0;
4552 millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
4553 if ( millis < 1.0 ) millis = 1.0;
4554 Sleep( (DWORD) millis );
4556 // Wake up, find out where we are now
4557 result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos );
4558 if ( FAILED( result ) ) {
4559 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
4560 errorText_ = errorStream_.str();
4561 error( RtError::SYSTEM_ERROR );
4564 if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
4568 //if (statistics.readDeviceSafeLeadBytes < dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ) )
4569 // statistics.readDeviceSafeLeadBytes = dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize );
4571 // Lock free space in the buffer
4572 result = dsBuffer->Lock( nextReadPos, bufferBytes, &buffer1,
4573 &bufferSize1, &buffer2, &bufferSize2, 0 );
4574 if ( FAILED( result ) ) {
4575 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
4576 errorText_ = errorStream_.str();
4577 error( RtError::SYSTEM_ERROR );
4580 if ( duplexPrerollBytes <= 0 ) {
4581 // Copy our buffer into the DS buffer
4582 CopyMemory( buffer, buffer1, bufferSize1 );
4583 if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );
4586 memset( buffer, 0, bufferSize1 );
4587 if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );
4588 duplexPrerollBytes -= bufferSize1 + bufferSize2;
4591 // Update our buffer offset and unlock sound buffer
4592 nextReadPos = ( nextReadPos + bufferSize1 + bufferSize2 ) % dsBufferSize;
4593 dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
4594 if ( FAILED( result ) ) {
4595 errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
4596 errorText_ = errorStream_.str();
4597 error( RtError::SYSTEM_ERROR );
4599 handle->bufferPointer[1] = nextReadPos;
4601 // No byte swapping necessary in DirectSound implementation.
4603 // If necessary, convert 8-bit data from unsigned to signed.
4604 if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
4605 for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
4607 // Do buffer conversion if necessary.
4608 if ( stream_.doConvertBuffer[1] )
4609 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
4611 #ifdef GENERATE_DEBUG_LOG
4612 if ( currentDebugLogEntry < debugLog.size() )
4614 TTickRecord &r = debugLog[currentDebugLogEntry++];
4615 r.currentReadPointer = currentReadPos;
4616 r.safeReadPointer = safeReadPos;
4617 r.currentWritePointer = currentWritePos;
4618 r.safeWritePointer = safeWritePos;
4619 r.readTime = readTime;
4620 r.writeTime = writeTime;
4621 r.nextReadPointer = handles[1].bufferPointer;
4622 r.nextWritePointer = handles[0].bufferPointer;
4627 MUTEX_UNLOCK( &stream_.mutex );
4629 RtApi::tickStreamTime();
4632 // Definitions for utility functions and callbacks
4633 // specific to the DirectSound implementation.
4635 extern "C" unsigned __stdcall callbackHandler( void *ptr )
4637 CallbackInfo *info = (CallbackInfo *) ptr;
4638 RtApiDs *object = (RtApiDs *) info->object;
4639 bool* isRunning = &info->isRunning;
4641 while ( *isRunning == true ) {
4642 object->callbackEvent();
4651 std::string convertTChar( LPCTSTR name )
4655 #if defined( UNICODE ) || defined( _UNICODE )
4656 // Yes, this conversion doesn't make sense for two-byte characters
4657 // but RtAudio is currently written to return an std::string of
4658 // one-byte chars for the device name.
4659 for ( unsigned int i=0; i<wcslen( name ); i++ )
4660 s.push_back( name[i] );
4662 s.append( std::string( name ) );
4668 static bool CALLBACK deviceCountCallback( LPGUID lpguid,
4669 LPCTSTR description,
4673 EnumInfo *info = (EnumInfo *) lpContext;
4676 if ( info->isInput == true ) {
4678 LPDIRECTSOUNDCAPTURE object;
4680 hr = DirectSoundCaptureCreate( lpguid, &object, NULL );
4681 if ( hr != DS_OK ) return true;
4683 caps.dwSize = sizeof(caps);
4684 hr = object->GetCaps( &caps );
4685 if ( hr == DS_OK ) {
4686 if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
4693 LPDIRECTSOUND object;
4694 hr = DirectSoundCreate( lpguid, &object, NULL );
4695 if ( hr != DS_OK ) return true;
4697 caps.dwSize = sizeof(caps);
4698 hr = object->GetCaps( &caps );
4699 if ( hr == DS_OK ) {
4700 if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
4706 if ( info->getDefault && lpguid == NULL ) return false;
4708 if ( info->findIndex && info->counter > info->index ) {
4710 info->name = convertTChar( description );
4717 static char* getErrorString( int code )
4721 case DSERR_ALLOCATED:
4722 return "Already allocated";
4724 case DSERR_CONTROLUNAVAIL:
4725 return "Control unavailable";
4727 case DSERR_INVALIDPARAM:
4728 return "Invalid parameter";
4730 case DSERR_INVALIDCALL:
4731 return "Invalid call";
4734 return "Generic error";
4736 case DSERR_PRIOLEVELNEEDED:
4737 return "Priority level needed";
4739 case DSERR_OUTOFMEMORY:
4740 return "Out of memory";
4742 case DSERR_BADFORMAT:
4743 return "The sample rate or the channel format is not supported";
4745 case DSERR_UNSUPPORTED:
4746 return "Not supported";
4748 case DSERR_NODRIVER:
4751 case DSERR_ALREADYINITIALIZED:
4752 return "Already initialized";
4754 case DSERR_NOAGGREGATION:
4755 return "No aggregation";
4757 case DSERR_BUFFERLOST:
4758 return "Buffer lost";
4760 case DSERR_OTHERAPPHASPRIO:
4761 return "Another application already has priority";
4763 case DSERR_UNINITIALIZED:
4764 return "Uninitialized";
4767 return "DirectSound unknown error";
4770 //******************** End of __WINDOWS_DS__ *********************//
4774 #if defined(__LINUX_ALSA__)
4776 #include <alsa/asoundlib.h>
4779 // A structure to hold various information related to the ALSA API
4782 snd_pcm_t *handles[2];
4787 :synchronized(false) { xrun[0] = false; xrun[1] = false; }
4790 extern "C" void *alsaCallbackHandler( void * ptr );
4792 RtApiAlsa :: RtApiAlsa()
4794 // Nothing to do here.
4797 RtApiAlsa :: ~RtApiAlsa()
4799 if ( stream_.state != STREAM_CLOSED ) closeStream();
4802 unsigned int RtApiAlsa :: getDeviceCount( void )
4804 unsigned nDevices = 0;
4805 int result, subdevice, card;
4809 // Count cards and devices
4811 snd_card_next( &card );
4812 while ( card >= 0 ) {
4813 sprintf( name, "hw:%d", card );
4814 result = snd_ctl_open( &handle, name, 0 );
4816 errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
4817 errorText_ = errorStream_.str();
4818 error( RtError::WARNING );
4823 result = snd_ctl_pcm_next_device( handle, &subdevice );
4825 errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
4826 errorText_ = errorStream_.str();
4827 error( RtError::WARNING );
4830 if ( subdevice < 0 )
4835 snd_ctl_close( handle );
4836 snd_card_next( &card );
4842 RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
4844 RtAudio::DeviceInfo info;
4845 info.probed = false;
4847 unsigned nDevices = 0;
4848 int result, subdevice, card;
4852 // Count cards and devices
4854 snd_card_next( &card );
4855 while ( card >= 0 ) {
4856 sprintf( name, "hw:%d", card );
4857 result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
4859 errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
4860 errorText_ = errorStream_.str();
4861 error( RtError::WARNING );
4866 result = snd_ctl_pcm_next_device( chandle, &subdevice );
4868 errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
4869 errorText_ = errorStream_.str();
4870 error( RtError::WARNING );
4873 if ( subdevice < 0 ) break;
4874 if ( nDevices == device ) {
4875 sprintf( name, "hw:%d,%d", card, subdevice );
4881 snd_ctl_close( chandle );
4882 snd_card_next( &card );
4885 if ( nDevices == 0 ) {
4886 errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
4887 error( RtError::INVALID_USE );
4890 if ( device >= nDevices ) {
4891 errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
4892 error( RtError::INVALID_USE );
4897 int openMode = SND_PCM_ASYNC;
4898 snd_pcm_stream_t stream;
4899 snd_pcm_info_t *pcminfo;
4900 snd_pcm_info_alloca( &pcminfo );
4902 snd_pcm_hw_params_t *params;
4903 snd_pcm_hw_params_alloca( ¶ms );
4905 // First try for playback
4906 stream = SND_PCM_STREAM_PLAYBACK;
4907 snd_pcm_info_set_device( pcminfo, subdevice );
4908 snd_pcm_info_set_subdevice( pcminfo, 0 );
4909 snd_pcm_info_set_stream( pcminfo, stream );
4911 result = snd_ctl_pcm_info( chandle, pcminfo );
4913 // Device probably doesn't support playback.
4917 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );
4919 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
4920 errorText_ = errorStream_.str();
4921 error( RtError::WARNING );
4925 // The device is open ... fill the parameter structure.
4926 result = snd_pcm_hw_params_any( phandle, params );
4928 snd_pcm_close( phandle );
4929 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
4930 errorText_ = errorStream_.str();
4931 error( RtError::WARNING );
4935 // Get output channel information.
4937 result = snd_pcm_hw_params_get_channels_max( params, &value );
4939 snd_pcm_close( phandle );
4940 errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";
4941 errorText_ = errorStream_.str();
4942 error( RtError::WARNING );
4945 info.outputChannels = value;
4946 snd_pcm_close( phandle );
4949 // Now try for capture
4950 stream = SND_PCM_STREAM_CAPTURE;
4951 snd_pcm_info_set_stream( pcminfo, stream );
4953 result = snd_ctl_pcm_info( chandle, pcminfo );
4954 snd_ctl_close( chandle );
4956 // Device probably doesn't support capture.
4957 if ( info.outputChannels == 0 ) return info;
4958 goto probeParameters;
4961 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
4963 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
4964 errorText_ = errorStream_.str();
4965 error( RtError::WARNING );
4966 if ( info.outputChannels == 0 ) return info;
4967 goto probeParameters;
4970 // The device is open ... fill the parameter structure.
4971 result = snd_pcm_hw_params_any( phandle, params );
4973 snd_pcm_close( phandle );
4974 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
4975 errorText_ = errorStream_.str();
4976 error( RtError::WARNING );
4977 if ( info.outputChannels == 0 ) return info;
4978 goto probeParameters;
4981 result = snd_pcm_hw_params_get_channels_max( params, &value );
4983 snd_pcm_close( phandle );
4984 errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";
4985 errorText_ = errorStream_.str();
4986 error( RtError::WARNING );
4987 if ( info.outputChannels == 0 ) return info;
4988 goto probeParameters;
4990 info.inputChannels = value;
4991 snd_pcm_close( phandle );
4993 // If device opens for both playback and capture, we determine the channels.
4994 if ( info.outputChannels > 0 && info.inputChannels > 0 )
4995 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
4997 // ALSA doesn't provide default devices so we'll use the first available one.
4998 if ( device == 0 && info.outputChannels > 0 )
4999 info.isDefaultOutput = true;
5000 if ( device == 0 && info.inputChannels > 0 )
5001 info.isDefaultInput = true;
5004 // At this point, we just need to figure out the supported data
5005 // formats and sample rates. We'll proceed by opening the device in
5006 // the direction with the maximum number of channels, or playback if
5007 // they are equal. This might limit our sample rate options, but so
5010 if ( info.outputChannels >= info.inputChannels )
5011 stream = SND_PCM_STREAM_PLAYBACK;
5013 stream = SND_PCM_STREAM_CAPTURE;
5014 snd_pcm_info_set_stream( pcminfo, stream );
5016 result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
5018 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
5019 errorText_ = errorStream_.str();
5020 error( RtError::WARNING );
5024 // The device is open ... fill the parameter structure.
5025 result = snd_pcm_hw_params_any( phandle, params );
5027 snd_pcm_close( phandle );
5028 errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
5029 errorText_ = errorStream_.str();
5030 error( RtError::WARNING );
5034 // Test our discrete set of sample rate values.
5035 info.sampleRates.clear();
5036 for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
5037 if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )
5038 info.sampleRates.push_back( SAMPLE_RATES[i] );
5040 if ( info.sampleRates.size() == 0 ) {
5041 snd_pcm_close( phandle );
5042 errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
5043 errorText_ = errorStream_.str();
5044 error( RtError::WARNING );
5048 // Probe the supported data formats ... we don't care about endian-ness just yet
5049 snd_pcm_format_t format;
5050 info.nativeFormats = 0;
5051 format = SND_PCM_FORMAT_S8;
5052 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5053 info.nativeFormats |= RTAUDIO_SINT8;
5054 format = SND_PCM_FORMAT_S16;
5055 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5056 info.nativeFormats |= RTAUDIO_SINT16;
5057 format = SND_PCM_FORMAT_S24;
5058 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5059 info.nativeFormats |= RTAUDIO_SINT24;
5060 format = SND_PCM_FORMAT_S32;
5061 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5062 info.nativeFormats |= RTAUDIO_SINT32;
5063 format = SND_PCM_FORMAT_FLOAT;
5064 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5065 info.nativeFormats |= RTAUDIO_FLOAT32;
5066 format = SND_PCM_FORMAT_FLOAT64;
5067 if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
5068 info.nativeFormats |= RTAUDIO_FLOAT64;
5070 // Check that we have at least one supported format
5071 if ( info.nativeFormats == 0 ) {
5072 errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
5073 errorText_ = errorStream_.str();
5074 error( RtError::WARNING );
5078 // Get the device name
5080 result = snd_card_get_name( card, &cardname );
5082 sprintf( name, "hw:%s,%d", cardname, subdevice );
5085 // That's all ... close the device and return
5086 snd_pcm_close( phandle );
5091 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
5092 unsigned int firstChannel, unsigned int sampleRate,
5093 RtAudioFormat format, unsigned int *bufferSize,
5094 RtAudio::StreamOptions *options )
5097 #if defined(__RTAUDIO_DEBUG__)
5099 snd_output_stdio_attach(&out, stderr, 0);
5102 // I'm not using the "plug" interface ... too much inconsistent behavior.
5104 unsigned nDevices = 0;
5105 int result, subdevice, card;
5109 // Count cards and devices
5111 snd_card_next( &card );
5112 while ( card >= 0 ) {
5113 sprintf( name, "hw:%d", card );
5114 result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
5116 errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
5117 errorText_ = errorStream_.str();
5122 result = snd_ctl_pcm_next_device( chandle, &subdevice );
5123 if ( result < 0 ) break;
5124 if ( subdevice < 0 ) break;
5125 if ( nDevices == device ) {
5126 sprintf( name, "hw:%d,%d", card, subdevice );
5131 snd_ctl_close( chandle );
5132 snd_card_next( &card );
5135 if ( nDevices == 0 ) {
5136 // This should not happen because a check is made before this function is called.
5137 errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";
5141 if ( device >= nDevices ) {
5142 // This should not happen because a check is made before this function is called.
5143 errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";
5149 snd_pcm_stream_t stream;
5150 if ( mode == OUTPUT )
5151 stream = SND_PCM_STREAM_PLAYBACK;
5153 stream = SND_PCM_STREAM_CAPTURE;
5156 int openMode = SND_PCM_ASYNC;
5157 result = snd_pcm_open( &phandle, name, stream, openMode );
5159 if ( mode == OUTPUT )
5160 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";
5162 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";
5163 errorText_ = errorStream_.str();
5167 // Fill the parameter structure.
5168 snd_pcm_hw_params_t *hw_params;
5169 snd_pcm_hw_params_alloca( &hw_params );
5170 result = snd_pcm_hw_params_any( phandle, hw_params );
5172 snd_pcm_close( phandle );
5173 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";
5174 errorText_ = errorStream_.str();
5178 #if defined(__RTAUDIO_DEBUG__)
5179 fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );
5180 snd_pcm_hw_params_dump( hw_params, out );
5183 // Set access ... check user preference.
5184 if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {
5185 stream_.userInterleaved = false;
5186 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
5188 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
5189 stream_.deviceInterleaved[mode] = true;
5192 stream_.deviceInterleaved[mode] = false;
5195 stream_.userInterleaved = true;
5196 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
5198 result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
5199 stream_.deviceInterleaved[mode] = false;
5202 stream_.deviceInterleaved[mode] = true;
5206 snd_pcm_close( phandle );
5207 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
5208 errorText_ = errorStream_.str();
5212 // Determine how to set the device format.
5213 stream_.userFormat = format;
5214 snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
5216 if ( format == RTAUDIO_SINT8 )
5217 deviceFormat = SND_PCM_FORMAT_S8;
5218 else if ( format == RTAUDIO_SINT16 )
5219 deviceFormat = SND_PCM_FORMAT_S16;
5220 else if ( format == RTAUDIO_SINT24 )
5221 deviceFormat = SND_PCM_FORMAT_S24;
5222 else if ( format == RTAUDIO_SINT32 )
5223 deviceFormat = SND_PCM_FORMAT_S32;
5224 else if ( format == RTAUDIO_FLOAT32 )
5225 deviceFormat = SND_PCM_FORMAT_FLOAT;
5226 else if ( format == RTAUDIO_FLOAT64 )
5227 deviceFormat = SND_PCM_FORMAT_FLOAT64;
5229 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
5230 stream_.deviceFormat[mode] = format;
5234 // The user requested format is not natively supported by the device.
5235 deviceFormat = SND_PCM_FORMAT_FLOAT64;
5236 if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {
5237 stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
5241 deviceFormat = SND_PCM_FORMAT_FLOAT;
5242 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5243 stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
5247 deviceFormat = SND_PCM_FORMAT_S32;
5248 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5249 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
5253 deviceFormat = SND_PCM_FORMAT_S24;
5254 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5255 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
5259 deviceFormat = SND_PCM_FORMAT_S16;
5260 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5261 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
5265 deviceFormat = SND_PCM_FORMAT_S8;
5266 if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
5267 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
5271 // If we get here, no supported format was found.
5272 errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";
5273 errorText_ = errorStream_.str();
5277 result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );
5279 snd_pcm_close( phandle );
5280 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";
5281 errorText_ = errorStream_.str();
5285 // Determine whether byte-swaping is necessary.
5286 stream_.doByteSwap[mode] = false;
5287 if ( deviceFormat != SND_PCM_FORMAT_S8 ) {
5288 result = snd_pcm_format_cpu_endian( deviceFormat );
5290 stream_.doByteSwap[mode] = true;
5291 else if (result < 0) {
5292 snd_pcm_close( phandle );
5293 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";
5294 errorText_ = errorStream_.str();
5299 // Set the sample rate.
5300 result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );
5302 snd_pcm_close( phandle );
5303 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";
5304 errorText_ = errorStream_.str();
5308 // Determine the number of channels for this device. We support a possible
5309 // minimum device channel number > than the value requested by the user.
5310 stream_.nUserChannels[mode] = channels;
5312 result = snd_pcm_hw_params_get_channels_max( hw_params, &value );
5313 unsigned int deviceChannels = value;
5314 if ( result < 0 || deviceChannels < channels + firstChannel ) {
5315 snd_pcm_close( phandle );
5316 errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";
5317 errorText_ = errorStream_.str();
5321 result = snd_pcm_hw_params_get_channels_min( hw_params, &value );
5323 snd_pcm_close( phandle );
5324 errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";
5325 errorText_ = errorStream_.str();
5328 deviceChannels = value;
5329 if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;
5330 stream_.nDeviceChannels[mode] = deviceChannels;
5332 // Set the device channels.
5333 result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );
5335 snd_pcm_close( phandle );
5336 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";
5337 errorText_ = errorStream_.str();
5341 // Set the buffer number, which in ALSA is referred to as the "period".
5343 unsigned int periods = 0;
5344 if ( options ) periods = options->numberOfBuffers;
5345 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
5346 // Even though the hardware might allow 1 buffer, it won't work reliably.
5347 if ( periods < 2 ) periods = 2;
5348 result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
5350 snd_pcm_close( phandle );
5351 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
5352 errorText_ = errorStream_.str();
5356 // Set the buffer (or period) size.
5357 snd_pcm_uframes_t periodSize = *bufferSize;
5358 result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );
5360 snd_pcm_close( phandle );
5361 errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";
5362 errorText_ = errorStream_.str();
5365 *bufferSize = periodSize;
5367 // If attempting to setup a duplex stream, the bufferSize parameter
5368 // MUST be the same in both directions!
5369 if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
5370 errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
5371 errorText_ = errorStream_.str();
5375 stream_.bufferSize = *bufferSize;
5377 // Install the hardware configuration
5378 result = snd_pcm_hw_params( phandle, hw_params );
5380 snd_pcm_close( phandle );
5381 errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";
5382 errorText_ = errorStream_.str();
5386 #if defined(__RTAUDIO_DEBUG__)
5387 fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
5388 snd_pcm_hw_params_dump( hw_params, out );
5391 // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
5392 snd_pcm_sw_params_t *sw_params = NULL;
5393 snd_pcm_sw_params_alloca( &sw_params );
5394 snd_pcm_sw_params_current( phandle, sw_params );
5395 snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
5396 snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, 0x7fffffff );
5397 snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
5398 snd_pcm_sw_params_set_silence_size( phandle, sw_params, INT_MAX );
5399 result = snd_pcm_sw_params( phandle, sw_params );
5401 snd_pcm_close( phandle );
5402 errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
5403 errorText_ = errorStream_.str();
5407 #if defined(__RTAUDIO_DEBUG__)
5408 fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
5409 snd_pcm_sw_params_dump( sw_params, out );
5412 // Set flags for buffer conversion
5413 stream_.doConvertBuffer[mode] = false;
5414 if ( stream_.userFormat != stream_.deviceFormat[mode] )
5415 stream_.doConvertBuffer[mode] = true;
5416 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
5417 stream_.doConvertBuffer[mode] = true;
5418 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
5419 stream_.nUserChannels[mode] > 1 )
5420 stream_.doConvertBuffer[mode] = true;
5422 // Allocate the ApiHandle if necessary and then save.
5423 AlsaHandle *apiInfo = 0;
5424 if ( stream_.apiHandle == 0 ) {
5426 apiInfo = (AlsaHandle *) new AlsaHandle;
5428 catch ( std::bad_alloc& ) {
5429 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
5432 stream_.apiHandle = (void *) apiInfo;
5433 apiInfo->handles[0] = 0;
5434 apiInfo->handles[1] = 0;
5437 apiInfo = (AlsaHandle *) stream_.apiHandle;
5439 apiInfo->handles[mode] = phandle;
5441 // Allocate necessary internal buffers.
5442 unsigned long bufferBytes;
5443 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
5444 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
5445 if ( stream_.userBuffer[mode] == NULL ) {
5446 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";
5450 if ( stream_.doConvertBuffer[mode] ) {
5452 bool makeBuffer = true;
5453 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
5454 if ( mode == INPUT ) {
5455 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
5456 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
5457 if ( bufferBytes <= bytesOut ) makeBuffer = false;
5462 bufferBytes *= *bufferSize;
5463 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
5464 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
5465 if ( stream_.deviceBuffer == NULL ) {
5466 errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";
5472 stream_.sampleRate = sampleRate;
5473 stream_.nBuffers = periods;
5474 stream_.device[mode] = device;
5475 stream_.state = STREAM_STOPPED;
5477 // Setup the buffer conversion information structure.
5478 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
5480 // Setup thread if necessary.
5481 if ( stream_.mode == OUTPUT && mode == INPUT ) {
5482 // We had already set up an output stream.
5483 stream_.mode = DUPLEX;
5484 // Link the streams if possible.
5485 apiInfo->synchronized = false;
5486 if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )
5487 apiInfo->synchronized = true;
5489 errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";
5490 error( RtError::WARNING );
5494 stream_.mode = mode;
5496 // Setup callback thread.
5497 stream_.callbackInfo.object = (void *) this;
5499 // Set the thread attributes for joinable and realtime scheduling
5500 // priority. The higher priority will only take affect if the
5501 // program is run as root or suid.
5502 pthread_attr_t attr;
5503 pthread_attr_init( &attr );
5504 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
5505 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
5506 pthread_attr_setschedpolicy( &attr, SCHED_RR );
5508 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
5511 stream_.callbackInfo.isRunning = true;
5512 result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
5513 pthread_attr_destroy( &attr );
5515 stream_.callbackInfo.isRunning = false;
5516 errorText_ = "RtApiAlsa::error creating callback thread!";
5525 if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
5526 if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
5528 stream_.apiHandle = 0;
5531 for ( int i=0; i<2; i++ ) {
5532 if ( stream_.userBuffer[i] ) {
5533 free( stream_.userBuffer[i] );
5534 stream_.userBuffer[i] = 0;
5538 if ( stream_.deviceBuffer ) {
5539 free( stream_.deviceBuffer );
5540 stream_.deviceBuffer = 0;
5546 void RtApiAlsa :: closeStream()
5548 if ( stream_.state == STREAM_CLOSED ) {
5549 errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
5550 error( RtError::WARNING );
5554 stream_.callbackInfo.isRunning = false;
5555 pthread_join( stream_.callbackInfo.thread, NULL );
5557 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5558 if ( stream_.state == STREAM_RUNNING ) {
5559 stream_.state = STREAM_STOPPED;
5560 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
5561 snd_pcm_drop( apiInfo->handles[0] );
5562 if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
5563 snd_pcm_drop( apiInfo->handles[1] );
5567 if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
5568 if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
5570 stream_.apiHandle = 0;
5573 for ( int i=0; i<2; i++ ) {
5574 if ( stream_.userBuffer[i] ) {
5575 free( stream_.userBuffer[i] );
5576 stream_.userBuffer[i] = 0;
5580 if ( stream_.deviceBuffer ) {
5581 free( stream_.deviceBuffer );
5582 stream_.deviceBuffer = 0;
5585 stream_.mode = UNINITIALIZED;
5586 stream_.state = STREAM_CLOSED;
5589 void RtApiAlsa :: startStream()
5591 // This method calls snd_pcm_prepare if the device isn't already in that state.
5594 if ( stream_.state == STREAM_RUNNING ) {
5595 errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
5596 error( RtError::WARNING );
5600 MUTEX_LOCK( &stream_.mutex );
5603 snd_pcm_state_t state;
5604 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5605 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5606 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5607 state = snd_pcm_state( handle[0] );
5608 if ( state != SND_PCM_STATE_PREPARED ) {
5609 result = snd_pcm_prepare( handle[0] );
5611 errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";
5612 errorText_ = errorStream_.str();
5618 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
5619 state = snd_pcm_state( handle[1] );
5620 if ( state != SND_PCM_STATE_PREPARED ) {
5621 result = snd_pcm_prepare( handle[1] );
5623 errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";
5624 errorText_ = errorStream_.str();
5630 stream_.state = STREAM_RUNNING;
5633 MUTEX_UNLOCK( &stream_.mutex );
5635 if ( result >= 0 ) return;
5636 error( RtError::SYSTEM_ERROR );
5639 void RtApiAlsa :: stopStream()
5642 if ( stream_.state == STREAM_STOPPED ) {
5643 errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
5644 error( RtError::WARNING );
5648 // Change the state before the lock to improve shutdown response
5649 // when using a callback.
5650 stream_.state = STREAM_STOPPED;
5651 MUTEX_LOCK( &stream_.mutex );
5654 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5655 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5656 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5657 if ( apiInfo->synchronized )
5658 result = snd_pcm_drop( handle[0] );
5660 result = snd_pcm_drain( handle[0] );
5662 errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";
5663 errorText_ = errorStream_.str();
5668 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
5669 result = snd_pcm_drop( handle[1] );
5671 errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";
5672 errorText_ = errorStream_.str();
5678 MUTEX_UNLOCK( &stream_.mutex );
5680 if ( result >= 0 ) return;
5681 error( RtError::SYSTEM_ERROR );
5684 void RtApiAlsa :: abortStream()
5687 if ( stream_.state == STREAM_STOPPED ) {
5688 errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
5689 error( RtError::WARNING );
5693 // Change the state before the lock to improve shutdown response
5694 // when using a callback.
5695 stream_.state = STREAM_STOPPED;
5696 MUTEX_LOCK( &stream_.mutex );
5699 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5700 snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
5701 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5702 result = snd_pcm_drop( handle[0] );
5704 errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";
5705 errorText_ = errorStream_.str();
5710 if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
5711 result = snd_pcm_drop( handle[1] );
5713 errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";
5714 errorText_ = errorStream_.str();
5720 MUTEX_UNLOCK( &stream_.mutex );
5722 stream_.state = STREAM_STOPPED;
5723 if ( result >= 0 ) return;
5724 error( RtError::SYSTEM_ERROR );
5727 void RtApiAlsa :: callbackEvent()
5729 if ( stream_.state == STREAM_STOPPED ) {
5730 if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
5734 if ( stream_.state == STREAM_CLOSED ) {
5735 errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
5736 error( RtError::WARNING );
5740 int doStopStream = 0;
5741 AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
5742 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
5743 double streamTime = getStreamTime();
5744 RtAudioStreamStatus status = 0;
5745 if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
5746 status |= RTAUDIO_OUTPUT_UNDERFLOW;
5747 apiInfo->xrun[0] = false;
5749 if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
5750 status |= RTAUDIO_INPUT_OVERFLOW;
5751 apiInfo->xrun[1] = false;
5753 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
5754 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
5756 MUTEX_LOCK( &stream_.mutex );
5758 // The state might change while waiting on a mutex.
5759 if ( stream_.state == STREAM_STOPPED ) goto unlock;
5765 snd_pcm_sframes_t frames;
5766 RtAudioFormat format;
5767 handle = (snd_pcm_t **) apiInfo->handles;
5769 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
5771 // Setup parameters.
5772 if ( stream_.doConvertBuffer[1] ) {
5773 buffer = stream_.deviceBuffer;
5774 channels = stream_.nDeviceChannels[1];
5775 format = stream_.deviceFormat[1];
5778 buffer = stream_.userBuffer[1];
5779 channels = stream_.nUserChannels[1];
5780 format = stream_.userFormat;
5783 // Read samples from device in interleaved/non-interleaved format.
5784 if ( stream_.deviceInterleaved[1] )
5785 result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
5787 void *bufs[channels];
5788 size_t offset = stream_.bufferSize * formatBytes( format );
5789 for ( int i=0; i<channels; i++ )
5790 bufs[i] = (void *) (buffer + (i * offset));
5791 result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
5794 if ( result < (int) stream_.bufferSize ) {
5795 // Either an error or underrun occured.
5796 if ( result == -EPIPE ) {
5797 snd_pcm_state_t state = snd_pcm_state( handle[1] );
5798 if ( state == SND_PCM_STATE_XRUN ) {
5799 apiInfo->xrun[1] = true;
5800 result = snd_pcm_prepare( handle[1] );
5802 errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
5803 errorText_ = errorStream_.str();
5807 errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
5808 errorText_ = errorStream_.str();
5812 errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
5813 errorText_ = errorStream_.str();
5815 error( RtError::WARNING );
5819 // Do byte swapping if necessary.
5820 if ( stream_.doByteSwap[1] )
5821 byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
5823 // Do buffer conversion if necessary.
5824 if ( stream_.doConvertBuffer[1] )
5825 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
5827 // Check stream latency
5828 result = snd_pcm_delay( handle[1], &frames );
5829 if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
5832 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
5834 // Setup parameters and do buffer conversion if necessary.
5835 if ( stream_.doConvertBuffer[0] ) {
5836 buffer = stream_.deviceBuffer;
5837 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
5838 channels = stream_.nDeviceChannels[0];
5839 format = stream_.deviceFormat[0];
5842 buffer = stream_.userBuffer[0];
5843 channels = stream_.nUserChannels[0];
5844 format = stream_.userFormat;
5847 // Do byte swapping if necessary.
5848 if ( stream_.doByteSwap[0] )
5849 byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
5851 // Write samples to device in interleaved/non-interleaved format.
5852 if ( stream_.deviceInterleaved[0] )
5853 result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );
5855 void *bufs[channels];
5856 size_t offset = stream_.bufferSize * formatBytes( format );
5857 for ( int i=0; i<channels; i++ )
5858 bufs[i] = (void *) (buffer + (i * offset));
5859 result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );
5862 if ( result < (int) stream_.bufferSize ) {
5863 // Either an error or underrun occured.
5864 if ( result == -EPIPE ) {
5865 snd_pcm_state_t state = snd_pcm_state( handle[0] );
5866 if ( state == SND_PCM_STATE_XRUN ) {
5867 apiInfo->xrun[0] = true;
5868 result = snd_pcm_prepare( handle[0] );
5870 errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
5871 errorText_ = errorStream_.str();
5875 errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
5876 errorText_ = errorStream_.str();
5880 errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
5881 errorText_ = errorStream_.str();
5883 error( RtError::WARNING );
5887 // Check stream latency
5888 result = snd_pcm_delay( handle[0], &frames );
5889 if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
5893 MUTEX_UNLOCK( &stream_.mutex );
5895 RtApi::tickStreamTime();
5896 if ( doStopStream == 1 ) this->stopStream();
5897 else if ( doStopStream == 2 ) this->abortStream();
5900 extern "C" void *alsaCallbackHandler( void *ptr )
5902 CallbackInfo *info = (CallbackInfo *) ptr;
5903 RtApiAlsa *object = (RtApiAlsa *) info->object;
5904 bool *isRunning = &info->isRunning;
5907 // Set a higher scheduler priority (P.J. Leonard)
5908 struct sched_param param;
5909 param.sched_priority = 39; // Is this the best number?
5910 sched_setscheduler( 0, SCHED_RR, ¶m );
5913 while ( *isRunning == true ) {
5914 pthread_testcancel();
5915 object->callbackEvent();
5918 pthread_exit( NULL );
5921 //******************** End of __LINUX_ALSA__ *********************//
5925 #if defined(__LINUX_OSS__)
5928 #include <sys/ioctl.h>
5931 #include "oss/soundcard.h"
5935 extern "C" void *ossCallbackHandler(void * ptr);
5937 // A structure to hold various information related to the OSS API
5940 int id[2]; // device ids
5945 :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
5948 RtApiOss :: RtApiOss()
5950 // Nothing to do here.
5953 RtApiOss :: ~RtApiOss()
5955 if ( stream_.state != STREAM_CLOSED ) closeStream();
5958 unsigned int RtApiOss :: getDeviceCount( void )
5960 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
5961 if ( mixerfd == -1 ) {
5962 errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
5963 error( RtError::WARNING );
5967 oss_sysinfo sysinfo;
5968 if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
5970 errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
5971 error( RtError::WARNING );
5975 return sysinfo.numaudios;
5978 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
5980 RtAudio::DeviceInfo info;
5981 info.probed = false;
5983 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
5984 if ( mixerfd == -1 ) {
5985 errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
5986 error( RtError::WARNING );
5990 oss_sysinfo sysinfo;
5991 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
5992 if ( result == -1 ) {
5994 errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
5995 error( RtError::WARNING );
5999 unsigned nDevices = sysinfo.numaudios;
6000 if ( nDevices == 0 ) {
6002 errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
6003 error( RtError::INVALID_USE );
6006 if ( device >= nDevices ) {
6008 errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
6009 error( RtError::INVALID_USE );
6012 oss_audioinfo ainfo;
6014 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6016 if ( result == -1 ) {
6017 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6018 errorText_ = errorStream_.str();
6019 error( RtError::WARNING );
6024 if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
6025 if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
6026 if ( ainfo.caps & PCM_CAP_DUPLEX ) {
6027 if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
6028 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
6031 // Probe data formats ... do for input
6032 unsigned long mask = ainfo.iformats;
6033 if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
6034 info.nativeFormats |= RTAUDIO_SINT16;
6035 if ( mask & AFMT_S8 )
6036 info.nativeFormats |= RTAUDIO_SINT8;
6037 if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
6038 info.nativeFormats |= RTAUDIO_SINT32;
6039 if ( mask & AFMT_FLOAT )
6040 info.nativeFormats |= RTAUDIO_FLOAT32;
6041 if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
6042 info.nativeFormats |= RTAUDIO_SINT24;
6044 // Check that we have at least one supported format
6045 if ( info.nativeFormats == 0 ) {
6046 errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
6047 errorText_ = errorStream_.str();
6048 error( RtError::WARNING );
6052 // Probe the supported sample rates.
6053 info.sampleRates.clear();
6054 if ( ainfo.nrates ) {
6055 for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
6056 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6057 if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
6058 info.sampleRates.push_back( SAMPLE_RATES[k] );
6065 // Check min and max rate values;
6066 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6067 if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )
6068 info.sampleRates.push_back( SAMPLE_RATES[k] );
6072 if ( info.sampleRates.size() == 0 ) {
6073 errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
6074 errorText_ = errorStream_.str();
6075 error( RtError::WARNING );
6079 info.name = ainfo.name;
6086 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
6087 unsigned int firstChannel, unsigned int sampleRate,
6088 RtAudioFormat format, unsigned int *bufferSize,
6089 RtAudio::StreamOptions *options )
6091 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6092 if ( mixerfd == -1 ) {
6093 errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
6097 oss_sysinfo sysinfo;
6098 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
6099 if ( result == -1 ) {
6101 errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
6105 unsigned nDevices = sysinfo.numaudios;
6106 if ( nDevices == 0 ) {
6107 // This should not happen because a check is made before this function is called.
6109 errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
6113 if ( device >= nDevices ) {
6114 // This should not happen because a check is made before this function is called.
6116 errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
6120 oss_audioinfo ainfo;
6122 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6124 if ( result == -1 ) {
6125 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6126 errorText_ = errorStream_.str();
6130 // Check if device supports input or output
6131 if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
6132 ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
6133 if ( mode == OUTPUT )
6134 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
6136 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
6137 errorText_ = errorStream_.str();
6142 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6143 if ( mode == OUTPUT )
6145 else { // mode == INPUT
6146 if (stream_.mode == OUTPUT && stream_.device[0] == device) {
6147 // We just set the same device for playback ... close and reopen for duplex (OSS only).
6148 close( handle->id[0] );
6150 if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
6151 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
6152 errorText_ = errorStream_.str();
6155 // Check that the number previously set channels is the same.
6156 if ( stream_.nUserChannels[0] != channels ) {
6157 errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
6158 errorText_ = errorStream_.str();
6167 // Set exclusive access if specified.
6168 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
6170 // Try to open the device.
6172 fd = open( ainfo.devnode, flags, 0 );
6174 if ( errno == EBUSY )
6175 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
6177 errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
6178 errorText_ = errorStream_.str();
6182 // For duplex operation, specifically set this mode (this doesn't seem to work).
6184 if ( flags | O_RDWR ) {
6185 result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
6186 if ( result == -1) {
6187 errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
6188 errorText_ = errorStream_.str();
6194 // Check the device channel support.
6195 stream_.nUserChannels[mode] = channels;
6196 if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
6198 errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
6199 errorText_ = errorStream_.str();
6203 // Set the number of channels.
6204 int deviceChannels = channels + firstChannel;
6205 result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
6206 if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
6208 errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
6209 errorText_ = errorStream_.str();
6212 stream_.nDeviceChannels[mode] = deviceChannels;
6214 // Get the data format mask
6216 result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
6217 if ( result == -1 ) {
6219 errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
6220 errorText_ = errorStream_.str();
6224 // Determine how to set the device format.
6225 stream_.userFormat = format;
6226 int deviceFormat = -1;
6227 stream_.doByteSwap[mode] = false;
6228 if ( format == RTAUDIO_SINT8 ) {
6229 if ( mask & AFMT_S8 ) {
6230 deviceFormat = AFMT_S8;
6231 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
6234 else if ( format == RTAUDIO_SINT16 ) {
6235 if ( mask & AFMT_S16_NE ) {
6236 deviceFormat = AFMT_S16_NE;
6237 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6239 else if ( mask & AFMT_S16_OE ) {
6240 deviceFormat = AFMT_S16_OE;
6241 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6242 stream_.doByteSwap[mode] = true;
6245 else if ( format == RTAUDIO_SINT24 ) {
6246 if ( mask & AFMT_S24_NE ) {
6247 deviceFormat = AFMT_S24_NE;
6248 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6250 else if ( mask & AFMT_S24_OE ) {
6251 deviceFormat = AFMT_S24_OE;
6252 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6253 stream_.doByteSwap[mode] = true;
6256 else if ( format == RTAUDIO_SINT32 ) {
6257 if ( mask & AFMT_S32_NE ) {
6258 deviceFormat = AFMT_S32_NE;
6259 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6261 else if ( mask & AFMT_S32_OE ) {
6262 deviceFormat = AFMT_S32_OE;
6263 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6264 stream_.doByteSwap[mode] = true;
6268 if ( deviceFormat == -1 ) {
6269 // The user requested format is not natively supported by the device.
6270 if ( mask & AFMT_S16_NE ) {
6271 deviceFormat = AFMT_S16_NE;
6272 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6274 else if ( mask & AFMT_S32_NE ) {
6275 deviceFormat = AFMT_S32_NE;
6276 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6278 else if ( mask & AFMT_S24_NE ) {
6279 deviceFormat = AFMT_S24_NE;
6280 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6282 else if ( mask & AFMT_S16_OE ) {
6283 deviceFormat = AFMT_S16_OE;
6284 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6285 stream_.doByteSwap[mode] = true;
6287 else if ( mask & AFMT_S32_OE ) {
6288 deviceFormat = AFMT_S32_OE;
6289 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6290 stream_.doByteSwap[mode] = true;
6292 else if ( mask & AFMT_S24_OE ) {
6293 deviceFormat = AFMT_S24_OE;
6294 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6295 stream_.doByteSwap[mode] = true;
6297 else if ( mask & AFMT_S8) {
6298 deviceFormat = AFMT_S8;
6299 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
6303 if ( stream_.deviceFormat[mode] == 0 ) {
6304 // This really shouldn't happen ...
6306 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
6307 errorText_ = errorStream_.str();
6311 // Set the data format.
6312 int temp = deviceFormat;
6313 result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
6314 if ( result == -1 || deviceFormat != temp ) {
6316 errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
6317 errorText_ = errorStream_.str();
6321 // Attempt to set the buffer size. According to OSS, the minimum
6322 // number of buffers is two. The supposed minimum buffer size is 16
6323 // bytes, so that will be our lower bound. The argument to this
6324 // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
6325 // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
6326 // We'll check the actual value used near the end of the setup
6328 int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
6329 if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
6331 if ( options ) buffers = options->numberOfBuffers;
6332 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
6333 if ( buffers < 2 ) buffers = 3;
6334 temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
6335 result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
6336 if ( result == -1 ) {
6338 errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
6339 errorText_ = errorStream_.str();
6342 stream_.nBuffers = buffers;
6344 // Save buffer size (in sample frames).
6345 *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
6346 stream_.bufferSize = *bufferSize;
6348 // Set the sample rate.
6349 int srate = sampleRate;
6350 result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
6351 if ( result == -1 ) {
6353 errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
6354 errorText_ = errorStream_.str();
6358 // Verify the sample rate setup worked.
6359 if ( abs( srate - sampleRate ) > 100 ) {
6361 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
6362 errorText_ = errorStream_.str();
6365 stream_.sampleRate = sampleRate;
6367 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
6368 // We're doing duplex setup here.
6369 stream_.deviceFormat[0] = stream_.deviceFormat[1];
6370 stream_.nDeviceChannels[0] = deviceChannels;
6373 // Set interleaving parameters.
6374 stream_.userInterleaved = true;
6375 stream_.deviceInterleaved[mode] = true;
6376 if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
6377 stream_.userInterleaved = false;
6379 // Set flags for buffer conversion
6380 stream_.doConvertBuffer[mode] = false;
6381 if ( stream_.userFormat != stream_.deviceFormat[mode] )
6382 stream_.doConvertBuffer[mode] = true;
6383 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
6384 stream_.doConvertBuffer[mode] = true;
6385 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
6386 stream_.nUserChannels[mode] > 1 )
6387 stream_.doConvertBuffer[mode] = true;
6389 // Allocate the stream handles if necessary and then save.
6390 if ( stream_.apiHandle == 0 ) {
6392 handle = new OssHandle;
6394 catch ( std::bad_alloc& ) {
6395 errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
6399 stream_.apiHandle = (void *) handle;
6402 handle = (OssHandle *) stream_.apiHandle;
6404 handle->id[mode] = fd;
6406 // Allocate necessary internal buffers.
6407 unsigned long bufferBytes;
6408 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
6409 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
6410 if ( stream_.userBuffer[mode] == NULL ) {
6411 errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
6415 if ( stream_.doConvertBuffer[mode] ) {
6417 bool makeBuffer = true;
6418 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
6419 if ( mode == INPUT ) {
6420 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
6421 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
6422 if ( bufferBytes <= bytesOut ) makeBuffer = false;
6427 bufferBytes *= *bufferSize;
6428 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
6429 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
6430 if ( stream_.deviceBuffer == NULL ) {
6431 errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
6437 stream_.device[mode] = device;
6438 stream_.state = STREAM_STOPPED;
6440 // Setup the buffer conversion information structure.
6441 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
6443 // Setup thread if necessary.
6444 if ( stream_.mode == OUTPUT && mode == INPUT ) {
6445 // We had already set up an output stream.
6446 stream_.mode = DUPLEX;
6447 if ( stream_.device[0] == device ) handle->id[0] = fd;
6450 stream_.mode = mode;
6452 // Setup callback thread.
6453 stream_.callbackInfo.object = (void *) this;
6455 // Set the thread attributes for joinable and realtime scheduling
6456 // priority. The higher priority will only take affect if the
6457 // program is run as root or suid.
6458 pthread_attr_t attr;
6459 pthread_attr_init( &attr );
6460 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
6461 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
6462 pthread_attr_setschedpolicy( &attr, SCHED_RR );
6464 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
6467 stream_.callbackInfo.isRunning = true;
6468 result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
6469 pthread_attr_destroy( &attr );
6471 stream_.callbackInfo.isRunning = false;
6472 errorText_ = "RtApiOss::error creating callback thread!";
6481 if ( handle->id[0] ) close( handle->id[0] );
6482 if ( handle->id[1] ) close( handle->id[1] );
6484 stream_.apiHandle = 0;
6487 for ( int i=0; i<2; i++ ) {
6488 if ( stream_.userBuffer[i] ) {
6489 free( stream_.userBuffer[i] );
6490 stream_.userBuffer[i] = 0;
6494 if ( stream_.deviceBuffer ) {
6495 free( stream_.deviceBuffer );
6496 stream_.deviceBuffer = 0;
6502 void RtApiOss :: closeStream()
6504 if ( stream_.state == STREAM_CLOSED ) {
6505 errorText_ = "RtApiOss::closeStream(): no open stream to close!";
6506 error( RtError::WARNING );
6510 stream_.callbackInfo.isRunning = false;
6511 pthread_join( stream_.callbackInfo.thread, NULL );
6513 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6514 if ( stream_.state == STREAM_RUNNING ) {
6515 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
6516 ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6518 ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6519 stream_.state = STREAM_STOPPED;
6523 if ( handle->id[0] ) close( handle->id[0] );
6524 if ( handle->id[1] ) close( handle->id[1] );
6526 stream_.apiHandle = 0;
6529 for ( int i=0; i<2; i++ ) {
6530 if ( stream_.userBuffer[i] ) {
6531 free( stream_.userBuffer[i] );
6532 stream_.userBuffer[i] = 0;
6536 if ( stream_.deviceBuffer ) {
6537 free( stream_.deviceBuffer );
6538 stream_.deviceBuffer = 0;
6541 stream_.mode = UNINITIALIZED;
6542 stream_.state = STREAM_CLOSED;
6545 void RtApiOss :: startStream()
6548 if ( stream_.state == STREAM_RUNNING ) {
6549 errorText_ = "RtApiOss::startStream(): the stream is already running!";
6550 error( RtError::WARNING );
6554 MUTEX_LOCK( &stream_.mutex );
6556 stream_.state = STREAM_RUNNING;
6558 // No need to do anything else here ... OSS automatically starts
6559 // when fed samples.
6561 MUTEX_UNLOCK( &stream_.mutex );
6564 void RtApiOss :: stopStream()
6567 if ( stream_.state == STREAM_STOPPED ) {
6568 errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
6569 error( RtError::WARNING );
6573 // Change the state before the lock to improve shutdown response
6574 // when using a callback.
6575 stream_.state = STREAM_STOPPED;
6576 MUTEX_LOCK( &stream_.mutex );
6579 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6580 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6582 // Flush the output with zeros a few times.
6585 RtAudioFormat format;
6587 if ( stream_.doConvertBuffer[0] ) {
6588 buffer = stream_.deviceBuffer;
6589 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
6590 format = stream_.deviceFormat[0];
6593 buffer = stream_.userBuffer[0];
6594 samples = stream_.bufferSize * stream_.nUserChannels[0];
6595 format = stream_.userFormat;
6598 memset( buffer, 0, samples * formatBytes(format) );
6599 for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
6600 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6601 if ( result == -1 ) {
6602 errorText_ = "RtApiOss::stopStream: audio write error.";
6603 error( RtError::WARNING );
6607 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6608 if ( result == -1 ) {
6609 errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
6610 errorText_ = errorStream_.str();
6613 handle->triggered = false;
6616 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
6617 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6618 if ( result == -1 ) {
6619 errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
6620 errorText_ = errorStream_.str();
6626 MUTEX_UNLOCK( &stream_.mutex );
6628 stream_.state = STREAM_STOPPED;
6629 if ( result != -1 ) return;
6630 error( RtError::SYSTEM_ERROR );
6633 void RtApiOss :: abortStream()
6636 if ( stream_.state == STREAM_STOPPED ) {
6637 errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
6638 error( RtError::WARNING );
6642 // Change the state before the lock to improve shutdown response
6643 // when using a callback.
6644 stream_.state = STREAM_STOPPED;
6645 MUTEX_LOCK( &stream_.mutex );
6648 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6649 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6650 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6651 if ( result == -1 ) {
6652 errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
6653 errorText_ = errorStream_.str();
6656 handle->triggered = false;
6659 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
6660 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6661 if ( result == -1 ) {
6662 errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
6663 errorText_ = errorStream_.str();
6669 MUTEX_UNLOCK( &stream_.mutex );
6671 stream_.state = STREAM_STOPPED;
6672 if ( result != -1 ) return;
6673 error( RtError::SYSTEM_ERROR );
6676 void RtApiOss :: callbackEvent()
6678 if ( stream_.state == STREAM_STOPPED ) {
6679 if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
6683 if ( stream_.state == STREAM_CLOSED ) {
6684 errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
6685 error( RtError::WARNING );
6689 // Invoke user callback to get fresh output data.
6690 int doStopStream = 0;
6691 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
6692 double streamTime = getStreamTime();
6693 RtAudioStreamStatus status = 0;
6694 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6695 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
6696 status |= RTAUDIO_OUTPUT_UNDERFLOW;
6697 handle->xrun[0] = false;
6699 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
6700 status |= RTAUDIO_INPUT_OVERFLOW;
6701 handle->xrun[1] = false;
6703 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
6704 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
6706 MUTEX_LOCK( &stream_.mutex );
6708 // The state might change while waiting on a mutex.
6709 if ( stream_.state == STREAM_STOPPED ) goto unlock;
6714 RtAudioFormat format;
6716 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6718 // Setup parameters and do buffer conversion if necessary.
6719 if ( stream_.doConvertBuffer[0] ) {
6720 buffer = stream_.deviceBuffer;
6721 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
6722 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
6723 format = stream_.deviceFormat[0];
6726 buffer = stream_.userBuffer[0];
6727 samples = stream_.bufferSize * stream_.nUserChannels[0];
6728 format = stream_.userFormat;
6731 // Do byte swapping if necessary.
6732 if ( stream_.doByteSwap[0] )
6733 byteSwapBuffer( buffer, samples, format );
6735 if ( stream_.mode == DUPLEX && handle->triggered == false ) {
6737 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
6738 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6739 trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
6740 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
6741 handle->triggered = true;
6744 // Write samples to device.
6745 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6747 if ( result == -1 ) {
6748 // We'll assume this is an underrun, though there isn't a
6749 // specific means for determining that.
6750 handle->xrun[0] = true;
6751 errorText_ = "RtApiOss::callbackEvent: audio write error.";
6752 error( RtError::WARNING );
6757 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
6759 // Setup parameters.
6760 if ( stream_.doConvertBuffer[1] ) {
6761 buffer = stream_.deviceBuffer;
6762 samples = stream_.bufferSize * stream_.nDeviceChannels[1];
6763 format = stream_.deviceFormat[1];
6766 buffer = stream_.userBuffer[1];
6767 samples = stream_.bufferSize * stream_.nUserChannels[1];
6768 format = stream_.userFormat;
6771 // Read samples from device.
6772 result = read( handle->id[1], buffer, samples * formatBytes(format) );
6774 if ( result == -1 ) {
6775 // We'll assume this is an overrun, though there isn't a
6776 // specific means for determining that.
6777 handle->xrun[1] = true;
6778 errorText_ = "RtApiOss::callbackEvent: audio read error.";
6779 error( RtError::WARNING );
6783 // Do byte swapping if necessary.
6784 if ( stream_.doByteSwap[1] )
6785 byteSwapBuffer( buffer, samples, format );
6787 // Do buffer conversion if necessary.
6788 if ( stream_.doConvertBuffer[1] )
6789 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
6793 MUTEX_UNLOCK( &stream_.mutex );
6795 RtApi::tickStreamTime();
6796 if ( doStopStream == 1 ) this->stopStream();
6797 else if ( doStopStream == 2 ) this->abortStream();
6800 extern "C" void *ossCallbackHandler( void *ptr )
6802 CallbackInfo *info = (CallbackInfo *) ptr;
6803 RtApiOss *object = (RtApiOss *) info->object;
6804 bool *isRunning = &info->isRunning;
6807 // Set a higher scheduler priority (P.J. Leonard)
6808 struct sched_param param;
6809 param.sched_priority = 39; // Is this the best number?
6810 sched_setscheduler( 0, SCHED_RR, ¶m );
6813 while ( *isRunning == true ) {
6814 pthread_testcancel();
6815 object->callbackEvent();
6818 pthread_exit( NULL );
6821 //******************** End of __LINUX_OSS__ *********************//
6825 // *************************************************** //
6827 // Protected common (OS-independent) RtAudio methods.
6829 // *************************************************** //
6831 // This method can be modified to control the behavior of error
6832 // message printing.
6833 void RtApi :: error( RtError::Type type )
6835 if ( type == RtError::RtError::WARNING && showWarnings_ == true )
6836 std::cerr << '\n' << errorText_ << "\n\n";
6838 throw( RtError( errorText_, type ) );
6839 errorStream_.str(""); // clear the ostringstream
6842 void RtApi :: verifyStream()
6844 if ( stream_.state == STREAM_CLOSED ) {
6845 errorText_ = "RtApi:: a stream is not open!";
6846 error( RtError::INVALID_USE );
6850 void RtApi :: clearStreamInfo()
6852 stream_.mode = UNINITIALIZED;
6853 stream_.state = STREAM_CLOSED;
6854 stream_.sampleRate = 0;
6855 stream_.bufferSize = 0;
6856 stream_.nBuffers = 0;
6857 stream_.userFormat = 0;
6858 stream_.userInterleaved = true;
6859 stream_.streamTime = 0.0;
6860 stream_.apiHandle = 0;
6861 stream_.deviceBuffer = 0;
6862 stream_.callbackInfo.callback = 0;
6863 stream_.callbackInfo.userData = 0;
6864 stream_.callbackInfo.isRunning = false;
6865 for ( int i=0; i<2; i++ ) {
6866 stream_.device[i] = 0;
6867 stream_.doConvertBuffer[i] = false;
6868 stream_.deviceInterleaved[i] = true;
6869 stream_.doByteSwap[i] = false;
6870 stream_.nUserChannels[i] = 0;
6871 stream_.nDeviceChannels[i] = 0;
6872 stream_.channelOffset[i] = 0;
6873 stream_.deviceFormat[i] = 0;
6874 stream_.latency[i] = 0;
6875 stream_.userBuffer[i] = 0;
6876 stream_.convertInfo[i].channels = 0;
6877 stream_.convertInfo[i].inJump = 0;
6878 stream_.convertInfo[i].outJump = 0;
6879 stream_.convertInfo[i].inFormat = 0;
6880 stream_.convertInfo[i].outFormat = 0;
6881 stream_.convertInfo[i].inOffset.clear();
6882 stream_.convertInfo[i].outOffset.clear();
6886 unsigned int RtApi :: formatBytes( RtAudioFormat format )
6888 if ( format == RTAUDIO_SINT16 )
6890 else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
6891 format == RTAUDIO_FLOAT32 )
6893 else if ( format == RTAUDIO_FLOAT64 )
6895 else if ( format == RTAUDIO_SINT8 )
6898 errorText_ = "RtApi::formatBytes: undefined format.";
6899 error( RtError::WARNING );
6904 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
6906 if ( mode == INPUT ) { // convert device to user buffer
6907 stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
6908 stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
6909 stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
6910 stream_.convertInfo[mode].outFormat = stream_.userFormat;
6912 else { // convert user to device buffer
6913 stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
6914 stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
6915 stream_.convertInfo[mode].inFormat = stream_.userFormat;
6916 stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
6919 if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
6920 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
6922 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
6924 // Set up the interleave/deinterleave offsets.
6925 if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
6926 if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
6927 ( mode == INPUT && stream_.userInterleaved ) ) {
6928 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6929 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
6930 stream_.convertInfo[mode].outOffset.push_back( k );
6931 stream_.convertInfo[mode].inJump = 1;
6935 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6936 stream_.convertInfo[mode].inOffset.push_back( k );
6937 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
6938 stream_.convertInfo[mode].outJump = 1;
6942 else { // no (de)interleaving
6943 if ( stream_.userInterleaved ) {
6944 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6945 stream_.convertInfo[mode].inOffset.push_back( k );
6946 stream_.convertInfo[mode].outOffset.push_back( k );
6950 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6951 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
6952 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
6953 stream_.convertInfo[mode].inJump = 1;
6954 stream_.convertInfo[mode].outJump = 1;
6959 // Add channel offset.
6960 if ( firstChannel > 0 ) {
6961 if ( stream_.deviceInterleaved[mode] ) {
6962 if ( mode == OUTPUT ) {
6963 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6964 stream_.convertInfo[mode].outOffset[k] += firstChannel;
6967 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6968 stream_.convertInfo[mode].inOffset[k] += firstChannel;
6972 if ( mode == OUTPUT ) {
6973 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6974 stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
6977 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6978 stream_.convertInfo[mode].inOffset[k] += ( firstChannel * stream_.bufferSize );
6984 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
6986 // This function does format conversion, input/output channel compensation, and
6987 // data interleaving/deinterleaving. 24-bit integers are assumed to occupy
6988 // the upper three bytes of a 32-bit integer.
6990 // Clear our device buffer when in/out duplex device channels are different
6991 if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
6992 ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
6993 memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
6996 if (info.outFormat == RTAUDIO_FLOAT64) {
6998 Float64 *out = (Float64 *)outBuffer;
7000 if (info.inFormat == RTAUDIO_SINT8) {
7001 signed char *in = (signed char *)inBuffer;
7002 scale = 1.0 / 128.0;
7003 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7004 for (j=0; j<info.channels; j++) {
7005 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7006 out[info.outOffset[j]] *= scale;
7009 out += info.outJump;
7012 else if (info.inFormat == RTAUDIO_SINT16) {
7013 Int16 *in = (Int16 *)inBuffer;
7014 scale = 1.0 / 32768.0;
7015 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7016 for (j=0; j<info.channels; j++) {
7017 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7018 out[info.outOffset[j]] *= scale;
7021 out += info.outJump;
7024 else if (info.inFormat == RTAUDIO_SINT24) {
7025 Int32 *in = (Int32 *)inBuffer;
7026 scale = 1.0 / 8388608.0;
7027 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7028 for (j=0; j<info.channels; j++) {
7029 out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
7030 out[info.outOffset[j]] *= scale;
7033 out += info.outJump;
7036 else if (info.inFormat == RTAUDIO_SINT32) {
7037 Int32 *in = (Int32 *)inBuffer;
7038 scale = 1.0 / 2147483648.0;
7039 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7040 for (j=0; j<info.channels; j++) {
7041 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7042 out[info.outOffset[j]] *= scale;
7045 out += info.outJump;
7048 else if (info.inFormat == RTAUDIO_FLOAT32) {
7049 Float32 *in = (Float32 *)inBuffer;
7050 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7051 for (j=0; j<info.channels; j++) {
7052 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7055 out += info.outJump;
7058 else if (info.inFormat == RTAUDIO_FLOAT64) {
7059 // Channel compensation and/or (de)interleaving only.
7060 Float64 *in = (Float64 *)inBuffer;
7061 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7062 for (j=0; j<info.channels; j++) {
7063 out[info.outOffset[j]] = in[info.inOffset[j]];
7066 out += info.outJump;
7070 else if (info.outFormat == RTAUDIO_FLOAT32) {
7072 Float32 *out = (Float32 *)outBuffer;
7074 if (info.inFormat == RTAUDIO_SINT8) {
7075 signed char *in = (signed char *)inBuffer;
7076 scale = 1.0 / 128.0;
7077 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7078 for (j=0; j<info.channels; j++) {
7079 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7080 out[info.outOffset[j]] *= scale;
7083 out += info.outJump;
7086 else if (info.inFormat == RTAUDIO_SINT16) {
7087 Int16 *in = (Int16 *)inBuffer;
7088 scale = 1.0 / 32768.0;
7089 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7090 for (j=0; j<info.channels; j++) {
7091 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7092 out[info.outOffset[j]] *= scale;
7095 out += info.outJump;
7098 else if (info.inFormat == RTAUDIO_SINT24) {
7099 Int32 *in = (Int32 *)inBuffer;
7100 scale = 1.0 / 8388608.0;
7101 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7102 for (j=0; j<info.channels; j++) {
7103 out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
7104 out[info.outOffset[j]] *= scale;
7107 out += info.outJump;
7110 else if (info.inFormat == RTAUDIO_SINT32) {
7111 Int32 *in = (Int32 *)inBuffer;
7112 scale = 1.0 / 2147483648.0;
7113 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7114 for (j=0; j<info.channels; j++) {
7115 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7116 out[info.outOffset[j]] *= scale;
7119 out += info.outJump;
7122 else if (info.inFormat == RTAUDIO_FLOAT32) {
7123 // Channel compensation and/or (de)interleaving only.
7124 Float32 *in = (Float32 *)inBuffer;
7125 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7126 for (j=0; j<info.channels; j++) {
7127 out[info.outOffset[j]] = in[info.inOffset[j]];
7130 out += info.outJump;
7133 else if (info.inFormat == RTAUDIO_FLOAT64) {
7134 Float64 *in = (Float64 *)inBuffer;
7135 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7136 for (j=0; j<info.channels; j++) {
7137 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7140 out += info.outJump;
7144 else if (info.outFormat == RTAUDIO_SINT32) {
7145 Int32 *out = (Int32 *)outBuffer;
7146 if (info.inFormat == RTAUDIO_SINT8) {
7147 signed char *in = (signed char *)inBuffer;
7148 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7149 for (j=0; j<info.channels; j++) {
7150 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7151 out[info.outOffset[j]] <<= 24;
7154 out += info.outJump;
7157 else if (info.inFormat == RTAUDIO_SINT16) {
7158 Int16 *in = (Int16 *)inBuffer;
7159 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7160 for (j=0; j<info.channels; j++) {
7161 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7162 out[info.outOffset[j]] <<= 16;
7165 out += info.outJump;
7168 else if (info.inFormat == RTAUDIO_SINT24) {
7169 Int32 *in = (Int32 *)inBuffer;
7170 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7171 for (j=0; j<info.channels; j++) {
7172 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7173 out[info.outOffset[j]] <<= 8;
7176 out += info.outJump;
7179 else if (info.inFormat == RTAUDIO_SINT32) {
7180 // Channel compensation and/or (de)interleaving only.
7181 Int32 *in = (Int32 *)inBuffer;
7182 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7183 for (j=0; j<info.channels; j++) {
7184 out[info.outOffset[j]] = in[info.inOffset[j]];
7187 out += info.outJump;
7190 else if (info.inFormat == RTAUDIO_FLOAT32) {
7191 Float32 *in = (Float32 *)inBuffer;
7192 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7193 for (j=0; j<info.channels; j++) {
7194 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
7197 out += info.outJump;
7200 else if (info.inFormat == RTAUDIO_FLOAT64) {
7201 Float64 *in = (Float64 *)inBuffer;
7202 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7203 for (j=0; j<info.channels; j++) {
7204 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
7207 out += info.outJump;
7211 else if (info.outFormat == RTAUDIO_SINT24) {
7212 Int32 *out = (Int32 *)outBuffer;
7213 if (info.inFormat == RTAUDIO_SINT8) {
7214 signed char *in = (signed char *)inBuffer;
7215 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7216 for (j=0; j<info.channels; j++) {
7217 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7218 out[info.outOffset[j]] <<= 16;
7221 out += info.outJump;
7224 else if (info.inFormat == RTAUDIO_SINT16) {
7225 Int16 *in = (Int16 *)inBuffer;
7226 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7227 for (j=0; j<info.channels; j++) {
7228 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7229 out[info.outOffset[j]] <<= 8;
7232 out += info.outJump;
7235 else if (info.inFormat == RTAUDIO_SINT24) {
7236 // Channel compensation and/or (de)interleaving only.
7237 Int32 *in = (Int32 *)inBuffer;
7238 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7239 for (j=0; j<info.channels; j++) {
7240 out[info.outOffset[j]] = in[info.inOffset[j]];
7243 out += info.outJump;
7246 else if (info.inFormat == RTAUDIO_SINT32) {
7247 Int32 *in = (Int32 *)inBuffer;
7248 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7249 for (j=0; j<info.channels; j++) {
7250 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7251 out[info.outOffset[j]] >>= 8;
7254 out += info.outJump;
7257 else if (info.inFormat == RTAUDIO_FLOAT32) {
7258 Float32 *in = (Float32 *)inBuffer;
7259 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7260 for (j=0; j<info.channels; j++) {
7261 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388608.0);
7264 out += info.outJump;
7267 else if (info.inFormat == RTAUDIO_FLOAT64) {
7268 Float64 *in = (Float64 *)inBuffer;
7269 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7270 for (j=0; j<info.channels; j++) {
7271 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
7274 out += info.outJump;
7278 else if (info.outFormat == RTAUDIO_SINT16) {
7279 Int16 *out = (Int16 *)outBuffer;
7280 if (info.inFormat == RTAUDIO_SINT8) {
7281 signed char *in = (signed char *)inBuffer;
7282 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7283 for (j=0; j<info.channels; j++) {
7284 out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
7285 out[info.outOffset[j]] <<= 8;
7288 out += info.outJump;
7291 else if (info.inFormat == RTAUDIO_SINT16) {
7292 // Channel compensation and/or (de)interleaving only.
7293 Int16 *in = (Int16 *)inBuffer;
7294 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7295 for (j=0; j<info.channels; j++) {
7296 out[info.outOffset[j]] = in[info.inOffset[j]];
7299 out += info.outJump;
7302 else if (info.inFormat == RTAUDIO_SINT24) {
7303 Int32 *in = (Int32 *)inBuffer;
7304 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7305 for (j=0; j<info.channels; j++) {
7306 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 8) & 0x0000ffff);
7309 out += info.outJump;
7312 else if (info.inFormat == RTAUDIO_SINT32) {
7313 Int32 *in = (Int32 *)inBuffer;
7314 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7315 for (j=0; j<info.channels; j++) {
7316 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
7319 out += info.outJump;
7322 else if (info.inFormat == RTAUDIO_FLOAT32) {
7323 Float32 *in = (Float32 *)inBuffer;
7324 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7325 for (j=0; j<info.channels; j++) {
7326 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
7329 out += info.outJump;
7332 else if (info.inFormat == RTAUDIO_FLOAT64) {
7333 Float64 *in = (Float64 *)inBuffer;
7334 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7335 for (j=0; j<info.channels; j++) {
7336 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
7339 out += info.outJump;
7343 else if (info.outFormat == RTAUDIO_SINT8) {
7344 signed char *out = (signed char *)outBuffer;
7345 if (info.inFormat == RTAUDIO_SINT8) {
7346 // Channel compensation and/or (de)interleaving only.
7347 signed char *in = (signed char *)inBuffer;
7348 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7349 for (j=0; j<info.channels; j++) {
7350 out[info.outOffset[j]] = in[info.inOffset[j]];
7353 out += info.outJump;
7356 if (info.inFormat == RTAUDIO_SINT16) {
7357 Int16 *in = (Int16 *)inBuffer;
7358 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7359 for (j=0; j<info.channels; j++) {
7360 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
7363 out += info.outJump;
7366 else if (info.inFormat == RTAUDIO_SINT24) {
7367 Int32 *in = (Int32 *)inBuffer;
7368 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7369 for (j=0; j<info.channels; j++) {
7370 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 16) & 0x000000ff);
7373 out += info.outJump;
7376 else if (info.inFormat == RTAUDIO_SINT32) {
7377 Int32 *in = (Int32 *)inBuffer;
7378 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7379 for (j=0; j<info.channels; j++) {
7380 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
7383 out += info.outJump;
7386 else if (info.inFormat == RTAUDIO_FLOAT32) {
7387 Float32 *in = (Float32 *)inBuffer;
7388 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7389 for (j=0; j<info.channels; j++) {
7390 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
7393 out += info.outJump;
7396 else if (info.inFormat == RTAUDIO_FLOAT64) {
7397 Float64 *in = (Float64 *)inBuffer;
7398 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7399 for (j=0; j<info.channels; j++) {
7400 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
7403 out += info.outJump;
7409 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
7415 if ( format == RTAUDIO_SINT16 ) {
7416 for ( unsigned int i=0; i<samples; i++ ) {
7417 // Swap 1st and 2nd bytes.
7422 // Increment 2 bytes.
7426 else if ( format == RTAUDIO_SINT24 ||
7427 format == RTAUDIO_SINT32 ||
7428 format == RTAUDIO_FLOAT32 ) {
7429 for ( unsigned int i=0; i<samples; i++ ) {
7430 // Swap 1st and 4th bytes.
7435 // Swap 2nd and 3rd bytes.
7441 // Increment 4 bytes.
7445 else if ( format == RTAUDIO_FLOAT64 ) {
7446 for ( unsigned int i=0; i<samples; i++ ) {
7447 // Swap 1st and 8th bytes
7452 // Swap 2nd and 7th bytes
7458 // Swap 3rd and 6th bytes
7464 // Swap 4th and 5th bytes
7470 // Increment 8 bytes.
7476 // Indentation settings for Vim and Emacs
7479 // c-basic-offset: 2
7480 // indent-tabs-mode: nil
7483 // vim: et sts=2 sw=2