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 int min = sched_get_priority_min( SCHED_RR );
5910 int max = sched_get_priority_max( SCHED_RR );
5911 param.sched_priority = min + ( max - min ) / 2; // Is this the best number?
5912 sched_setscheduler( 0, SCHED_RR, ¶m );
5915 while ( *isRunning == true ) {
5916 pthread_testcancel();
5917 object->callbackEvent();
5920 pthread_exit( NULL );
5923 //******************** End of __LINUX_ALSA__ *********************//
5927 #if defined(__LINUX_OSS__)
5930 #include <sys/ioctl.h>
5933 #include "oss/soundcard.h"
5937 extern "C" void *ossCallbackHandler(void * ptr);
5939 // A structure to hold various information related to the OSS API
5942 int id[2]; // device ids
5947 :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
5950 RtApiOss :: RtApiOss()
5952 // Nothing to do here.
5955 RtApiOss :: ~RtApiOss()
5957 if ( stream_.state != STREAM_CLOSED ) closeStream();
5960 unsigned int RtApiOss :: getDeviceCount( void )
5962 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
5963 if ( mixerfd == -1 ) {
5964 errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
5965 error( RtError::WARNING );
5969 oss_sysinfo sysinfo;
5970 if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
5972 errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
5973 error( RtError::WARNING );
5977 return sysinfo.numaudios;
5980 RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
5982 RtAudio::DeviceInfo info;
5983 info.probed = false;
5985 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
5986 if ( mixerfd == -1 ) {
5987 errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
5988 error( RtError::WARNING );
5992 oss_sysinfo sysinfo;
5993 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
5994 if ( result == -1 ) {
5996 errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
5997 error( RtError::WARNING );
6001 unsigned nDevices = sysinfo.numaudios;
6002 if ( nDevices == 0 ) {
6004 errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
6005 error( RtError::INVALID_USE );
6008 if ( device >= nDevices ) {
6010 errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
6011 error( RtError::INVALID_USE );
6014 oss_audioinfo ainfo;
6016 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6018 if ( result == -1 ) {
6019 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6020 errorText_ = errorStream_.str();
6021 error( RtError::WARNING );
6026 if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
6027 if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
6028 if ( ainfo.caps & PCM_CAP_DUPLEX ) {
6029 if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
6030 info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
6033 // Probe data formats ... do for input
6034 unsigned long mask = ainfo.iformats;
6035 if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
6036 info.nativeFormats |= RTAUDIO_SINT16;
6037 if ( mask & AFMT_S8 )
6038 info.nativeFormats |= RTAUDIO_SINT8;
6039 if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
6040 info.nativeFormats |= RTAUDIO_SINT32;
6041 if ( mask & AFMT_FLOAT )
6042 info.nativeFormats |= RTAUDIO_FLOAT32;
6043 if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
6044 info.nativeFormats |= RTAUDIO_SINT24;
6046 // Check that we have at least one supported format
6047 if ( info.nativeFormats == 0 ) {
6048 errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
6049 errorText_ = errorStream_.str();
6050 error( RtError::WARNING );
6054 // Probe the supported sample rates.
6055 info.sampleRates.clear();
6056 if ( ainfo.nrates ) {
6057 for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
6058 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6059 if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
6060 info.sampleRates.push_back( SAMPLE_RATES[k] );
6067 // Check min and max rate values;
6068 for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
6069 if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )
6070 info.sampleRates.push_back( SAMPLE_RATES[k] );
6074 if ( info.sampleRates.size() == 0 ) {
6075 errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
6076 errorText_ = errorStream_.str();
6077 error( RtError::WARNING );
6081 info.name = ainfo.name;
6088 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
6089 unsigned int firstChannel, unsigned int sampleRate,
6090 RtAudioFormat format, unsigned int *bufferSize,
6091 RtAudio::StreamOptions *options )
6093 int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
6094 if ( mixerfd == -1 ) {
6095 errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
6099 oss_sysinfo sysinfo;
6100 int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
6101 if ( result == -1 ) {
6103 errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
6107 unsigned nDevices = sysinfo.numaudios;
6108 if ( nDevices == 0 ) {
6109 // This should not happen because a check is made before this function is called.
6111 errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
6115 if ( device >= nDevices ) {
6116 // This should not happen because a check is made before this function is called.
6118 errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
6122 oss_audioinfo ainfo;
6124 result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
6126 if ( result == -1 ) {
6127 errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
6128 errorText_ = errorStream_.str();
6132 // Check if device supports input or output
6133 if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
6134 ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
6135 if ( mode == OUTPUT )
6136 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
6138 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
6139 errorText_ = errorStream_.str();
6144 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6145 if ( mode == OUTPUT )
6147 else { // mode == INPUT
6148 if (stream_.mode == OUTPUT && stream_.device[0] == device) {
6149 // We just set the same device for playback ... close and reopen for duplex (OSS only).
6150 close( handle->id[0] );
6152 if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
6153 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
6154 errorText_ = errorStream_.str();
6157 // Check that the number previously set channels is the same.
6158 if ( stream_.nUserChannels[0] != channels ) {
6159 errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
6160 errorText_ = errorStream_.str();
6169 // Set exclusive access if specified.
6170 if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
6172 // Try to open the device.
6174 fd = open( ainfo.devnode, flags, 0 );
6176 if ( errno == EBUSY )
6177 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
6179 errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
6180 errorText_ = errorStream_.str();
6184 // For duplex operation, specifically set this mode (this doesn't seem to work).
6186 if ( flags | O_RDWR ) {
6187 result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
6188 if ( result == -1) {
6189 errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
6190 errorText_ = errorStream_.str();
6196 // Check the device channel support.
6197 stream_.nUserChannels[mode] = channels;
6198 if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
6200 errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
6201 errorText_ = errorStream_.str();
6205 // Set the number of channels.
6206 int deviceChannels = channels + firstChannel;
6207 result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
6208 if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
6210 errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
6211 errorText_ = errorStream_.str();
6214 stream_.nDeviceChannels[mode] = deviceChannels;
6216 // Get the data format mask
6218 result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
6219 if ( result == -1 ) {
6221 errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
6222 errorText_ = errorStream_.str();
6226 // Determine how to set the device format.
6227 stream_.userFormat = format;
6228 int deviceFormat = -1;
6229 stream_.doByteSwap[mode] = false;
6230 if ( format == RTAUDIO_SINT8 ) {
6231 if ( mask & AFMT_S8 ) {
6232 deviceFormat = AFMT_S8;
6233 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
6236 else if ( format == RTAUDIO_SINT16 ) {
6237 if ( mask & AFMT_S16_NE ) {
6238 deviceFormat = AFMT_S16_NE;
6239 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6241 else if ( mask & AFMT_S16_OE ) {
6242 deviceFormat = AFMT_S16_OE;
6243 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6244 stream_.doByteSwap[mode] = true;
6247 else if ( format == RTAUDIO_SINT24 ) {
6248 if ( mask & AFMT_S24_NE ) {
6249 deviceFormat = AFMT_S24_NE;
6250 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6252 else if ( mask & AFMT_S24_OE ) {
6253 deviceFormat = AFMT_S24_OE;
6254 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6255 stream_.doByteSwap[mode] = true;
6258 else if ( format == RTAUDIO_SINT32 ) {
6259 if ( mask & AFMT_S32_NE ) {
6260 deviceFormat = AFMT_S32_NE;
6261 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6263 else if ( mask & AFMT_S32_OE ) {
6264 deviceFormat = AFMT_S32_OE;
6265 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6266 stream_.doByteSwap[mode] = true;
6270 if ( deviceFormat == -1 ) {
6271 // The user requested format is not natively supported by the device.
6272 if ( mask & AFMT_S16_NE ) {
6273 deviceFormat = AFMT_S16_NE;
6274 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6276 else if ( mask & AFMT_S32_NE ) {
6277 deviceFormat = AFMT_S32_NE;
6278 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6280 else if ( mask & AFMT_S24_NE ) {
6281 deviceFormat = AFMT_S24_NE;
6282 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6284 else if ( mask & AFMT_S16_OE ) {
6285 deviceFormat = AFMT_S16_OE;
6286 stream_.deviceFormat[mode] = RTAUDIO_SINT16;
6287 stream_.doByteSwap[mode] = true;
6289 else if ( mask & AFMT_S32_OE ) {
6290 deviceFormat = AFMT_S32_OE;
6291 stream_.deviceFormat[mode] = RTAUDIO_SINT32;
6292 stream_.doByteSwap[mode] = true;
6294 else if ( mask & AFMT_S24_OE ) {
6295 deviceFormat = AFMT_S24_OE;
6296 stream_.deviceFormat[mode] = RTAUDIO_SINT24;
6297 stream_.doByteSwap[mode] = true;
6299 else if ( mask & AFMT_S8) {
6300 deviceFormat = AFMT_S8;
6301 stream_.deviceFormat[mode] = RTAUDIO_SINT8;
6305 if ( stream_.deviceFormat[mode] == 0 ) {
6306 // This really shouldn't happen ...
6308 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
6309 errorText_ = errorStream_.str();
6313 // Set the data format.
6314 int temp = deviceFormat;
6315 result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
6316 if ( result == -1 || deviceFormat != temp ) {
6318 errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
6319 errorText_ = errorStream_.str();
6323 // Attempt to set the buffer size. According to OSS, the minimum
6324 // number of buffers is two. The supposed minimum buffer size is 16
6325 // bytes, so that will be our lower bound. The argument to this
6326 // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
6327 // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
6328 // We'll check the actual value used near the end of the setup
6330 int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
6331 if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
6333 if ( options ) buffers = options->numberOfBuffers;
6334 if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
6335 if ( buffers < 2 ) buffers = 3;
6336 temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
6337 result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
6338 if ( result == -1 ) {
6340 errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
6341 errorText_ = errorStream_.str();
6344 stream_.nBuffers = buffers;
6346 // Save buffer size (in sample frames).
6347 *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
6348 stream_.bufferSize = *bufferSize;
6350 // Set the sample rate.
6351 int srate = sampleRate;
6352 result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
6353 if ( result == -1 ) {
6355 errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
6356 errorText_ = errorStream_.str();
6360 // Verify the sample rate setup worked.
6361 if ( abs( srate - sampleRate ) > 100 ) {
6363 errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
6364 errorText_ = errorStream_.str();
6367 stream_.sampleRate = sampleRate;
6369 if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
6370 // We're doing duplex setup here.
6371 stream_.deviceFormat[0] = stream_.deviceFormat[1];
6372 stream_.nDeviceChannels[0] = deviceChannels;
6375 // Set interleaving parameters.
6376 stream_.userInterleaved = true;
6377 stream_.deviceInterleaved[mode] = true;
6378 if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
6379 stream_.userInterleaved = false;
6381 // Set flags for buffer conversion
6382 stream_.doConvertBuffer[mode] = false;
6383 if ( stream_.userFormat != stream_.deviceFormat[mode] )
6384 stream_.doConvertBuffer[mode] = true;
6385 if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
6386 stream_.doConvertBuffer[mode] = true;
6387 if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
6388 stream_.nUserChannels[mode] > 1 )
6389 stream_.doConvertBuffer[mode] = true;
6391 // Allocate the stream handles if necessary and then save.
6392 if ( stream_.apiHandle == 0 ) {
6394 handle = new OssHandle;
6396 catch ( std::bad_alloc& ) {
6397 errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
6401 stream_.apiHandle = (void *) handle;
6404 handle = (OssHandle *) stream_.apiHandle;
6406 handle->id[mode] = fd;
6408 // Allocate necessary internal buffers.
6409 unsigned long bufferBytes;
6410 bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
6411 stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
6412 if ( stream_.userBuffer[mode] == NULL ) {
6413 errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
6417 if ( stream_.doConvertBuffer[mode] ) {
6419 bool makeBuffer = true;
6420 bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
6421 if ( mode == INPUT ) {
6422 if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
6423 unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
6424 if ( bufferBytes <= bytesOut ) makeBuffer = false;
6429 bufferBytes *= *bufferSize;
6430 if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
6431 stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
6432 if ( stream_.deviceBuffer == NULL ) {
6433 errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
6439 stream_.device[mode] = device;
6440 stream_.state = STREAM_STOPPED;
6442 // Setup the buffer conversion information structure.
6443 if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
6445 // Setup thread if necessary.
6446 if ( stream_.mode == OUTPUT && mode == INPUT ) {
6447 // We had already set up an output stream.
6448 stream_.mode = DUPLEX;
6449 if ( stream_.device[0] == device ) handle->id[0] = fd;
6452 stream_.mode = mode;
6454 // Setup callback thread.
6455 stream_.callbackInfo.object = (void *) this;
6457 // Set the thread attributes for joinable and realtime scheduling
6458 // priority. The higher priority will only take affect if the
6459 // program is run as root or suid.
6460 pthread_attr_t attr;
6461 pthread_attr_init( &attr );
6462 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
6463 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
6464 pthread_attr_setschedpolicy( &attr, SCHED_RR );
6466 pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
6469 stream_.callbackInfo.isRunning = true;
6470 result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
6471 pthread_attr_destroy( &attr );
6473 stream_.callbackInfo.isRunning = false;
6474 errorText_ = "RtApiOss::error creating callback thread!";
6483 if ( handle->id[0] ) close( handle->id[0] );
6484 if ( handle->id[1] ) close( handle->id[1] );
6486 stream_.apiHandle = 0;
6489 for ( int i=0; i<2; i++ ) {
6490 if ( stream_.userBuffer[i] ) {
6491 free( stream_.userBuffer[i] );
6492 stream_.userBuffer[i] = 0;
6496 if ( stream_.deviceBuffer ) {
6497 free( stream_.deviceBuffer );
6498 stream_.deviceBuffer = 0;
6504 void RtApiOss :: closeStream()
6506 if ( stream_.state == STREAM_CLOSED ) {
6507 errorText_ = "RtApiOss::closeStream(): no open stream to close!";
6508 error( RtError::WARNING );
6512 stream_.callbackInfo.isRunning = false;
6513 pthread_join( stream_.callbackInfo.thread, NULL );
6515 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6516 if ( stream_.state == STREAM_RUNNING ) {
6517 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
6518 ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6520 ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6521 stream_.state = STREAM_STOPPED;
6525 if ( handle->id[0] ) close( handle->id[0] );
6526 if ( handle->id[1] ) close( handle->id[1] );
6528 stream_.apiHandle = 0;
6531 for ( int i=0; i<2; i++ ) {
6532 if ( stream_.userBuffer[i] ) {
6533 free( stream_.userBuffer[i] );
6534 stream_.userBuffer[i] = 0;
6538 if ( stream_.deviceBuffer ) {
6539 free( stream_.deviceBuffer );
6540 stream_.deviceBuffer = 0;
6543 stream_.mode = UNINITIALIZED;
6544 stream_.state = STREAM_CLOSED;
6547 void RtApiOss :: startStream()
6550 if ( stream_.state == STREAM_RUNNING ) {
6551 errorText_ = "RtApiOss::startStream(): the stream is already running!";
6552 error( RtError::WARNING );
6556 MUTEX_LOCK( &stream_.mutex );
6558 stream_.state = STREAM_RUNNING;
6560 // No need to do anything else here ... OSS automatically starts
6561 // when fed samples.
6563 MUTEX_UNLOCK( &stream_.mutex );
6566 void RtApiOss :: stopStream()
6569 if ( stream_.state == STREAM_STOPPED ) {
6570 errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
6571 error( RtError::WARNING );
6575 // Change the state before the lock to improve shutdown response
6576 // when using a callback.
6577 stream_.state = STREAM_STOPPED;
6578 MUTEX_LOCK( &stream_.mutex );
6581 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6582 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6584 // Flush the output with zeros a few times.
6587 RtAudioFormat format;
6589 if ( stream_.doConvertBuffer[0] ) {
6590 buffer = stream_.deviceBuffer;
6591 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
6592 format = stream_.deviceFormat[0];
6595 buffer = stream_.userBuffer[0];
6596 samples = stream_.bufferSize * stream_.nUserChannels[0];
6597 format = stream_.userFormat;
6600 memset( buffer, 0, samples * formatBytes(format) );
6601 for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
6602 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6603 if ( result == -1 ) {
6604 errorText_ = "RtApiOss::stopStream: audio write error.";
6605 error( RtError::WARNING );
6609 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6610 if ( result == -1 ) {
6611 errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
6612 errorText_ = errorStream_.str();
6615 handle->triggered = false;
6618 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
6619 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6620 if ( result == -1 ) {
6621 errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
6622 errorText_ = errorStream_.str();
6628 MUTEX_UNLOCK( &stream_.mutex );
6630 stream_.state = STREAM_STOPPED;
6631 if ( result != -1 ) return;
6632 error( RtError::SYSTEM_ERROR );
6635 void RtApiOss :: abortStream()
6638 if ( stream_.state == STREAM_STOPPED ) {
6639 errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
6640 error( RtError::WARNING );
6644 // Change the state before the lock to improve shutdown response
6645 // when using a callback.
6646 stream_.state = STREAM_STOPPED;
6647 MUTEX_LOCK( &stream_.mutex );
6650 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6651 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6652 result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
6653 if ( result == -1 ) {
6654 errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
6655 errorText_ = errorStream_.str();
6658 handle->triggered = false;
6661 if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
6662 result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
6663 if ( result == -1 ) {
6664 errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
6665 errorText_ = errorStream_.str();
6671 MUTEX_UNLOCK( &stream_.mutex );
6673 stream_.state = STREAM_STOPPED;
6674 if ( result != -1 ) return;
6675 error( RtError::SYSTEM_ERROR );
6678 void RtApiOss :: callbackEvent()
6680 if ( stream_.state == STREAM_STOPPED ) {
6681 if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
6685 if ( stream_.state == STREAM_CLOSED ) {
6686 errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
6687 error( RtError::WARNING );
6691 // Invoke user callback to get fresh output data.
6692 int doStopStream = 0;
6693 RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
6694 double streamTime = getStreamTime();
6695 RtAudioStreamStatus status = 0;
6696 OssHandle *handle = (OssHandle *) stream_.apiHandle;
6697 if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
6698 status |= RTAUDIO_OUTPUT_UNDERFLOW;
6699 handle->xrun[0] = false;
6701 if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
6702 status |= RTAUDIO_INPUT_OVERFLOW;
6703 handle->xrun[1] = false;
6705 doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
6706 stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
6708 MUTEX_LOCK( &stream_.mutex );
6710 // The state might change while waiting on a mutex.
6711 if ( stream_.state == STREAM_STOPPED ) goto unlock;
6716 RtAudioFormat format;
6718 if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
6720 // Setup parameters and do buffer conversion if necessary.
6721 if ( stream_.doConvertBuffer[0] ) {
6722 buffer = stream_.deviceBuffer;
6723 convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
6724 samples = stream_.bufferSize * stream_.nDeviceChannels[0];
6725 format = stream_.deviceFormat[0];
6728 buffer = stream_.userBuffer[0];
6729 samples = stream_.bufferSize * stream_.nUserChannels[0];
6730 format = stream_.userFormat;
6733 // Do byte swapping if necessary.
6734 if ( stream_.doByteSwap[0] )
6735 byteSwapBuffer( buffer, samples, format );
6737 if ( stream_.mode == DUPLEX && handle->triggered == false ) {
6739 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
6740 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6741 trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
6742 ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
6743 handle->triggered = true;
6746 // Write samples to device.
6747 result = write( handle->id[0], buffer, samples * formatBytes(format) );
6749 if ( result == -1 ) {
6750 // We'll assume this is an underrun, though there isn't a
6751 // specific means for determining that.
6752 handle->xrun[0] = true;
6753 errorText_ = "RtApiOss::callbackEvent: audio write error.";
6754 error( RtError::WARNING );
6759 if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
6761 // Setup parameters.
6762 if ( stream_.doConvertBuffer[1] ) {
6763 buffer = stream_.deviceBuffer;
6764 samples = stream_.bufferSize * stream_.nDeviceChannels[1];
6765 format = stream_.deviceFormat[1];
6768 buffer = stream_.userBuffer[1];
6769 samples = stream_.bufferSize * stream_.nUserChannels[1];
6770 format = stream_.userFormat;
6773 // Read samples from device.
6774 result = read( handle->id[1], buffer, samples * formatBytes(format) );
6776 if ( result == -1 ) {
6777 // We'll assume this is an overrun, though there isn't a
6778 // specific means for determining that.
6779 handle->xrun[1] = true;
6780 errorText_ = "RtApiOss::callbackEvent: audio read error.";
6781 error( RtError::WARNING );
6785 // Do byte swapping if necessary.
6786 if ( stream_.doByteSwap[1] )
6787 byteSwapBuffer( buffer, samples, format );
6789 // Do buffer conversion if necessary.
6790 if ( stream_.doConvertBuffer[1] )
6791 convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
6795 MUTEX_UNLOCK( &stream_.mutex );
6797 RtApi::tickStreamTime();
6798 if ( doStopStream == 1 ) this->stopStream();
6799 else if ( doStopStream == 2 ) this->abortStream();
6802 extern "C" void *ossCallbackHandler( void *ptr )
6804 CallbackInfo *info = (CallbackInfo *) ptr;
6805 RtApiOss *object = (RtApiOss *) info->object;
6806 bool *isRunning = &info->isRunning;
6809 // Set a higher scheduler priority (P.J. Leonard)
6810 struct sched_param param;
6811 param.sched_priority = 39; // Is this the best number?
6812 sched_setscheduler( 0, SCHED_RR, ¶m );
6815 while ( *isRunning == true ) {
6816 pthread_testcancel();
6817 object->callbackEvent();
6820 pthread_exit( NULL );
6823 //******************** End of __LINUX_OSS__ *********************//
6827 // *************************************************** //
6829 // Protected common (OS-independent) RtAudio methods.
6831 // *************************************************** //
6833 // This method can be modified to control the behavior of error
6834 // message printing.
6835 void RtApi :: error( RtError::Type type )
6837 if ( type == RtError::WARNING && showWarnings_ == true )
6838 std::cerr << '\n' << errorText_ << "\n\n";
6840 throw( RtError( errorText_, type ) );
6841 errorStream_.str(""); // clear the ostringstream
6844 void RtApi :: verifyStream()
6846 if ( stream_.state == STREAM_CLOSED ) {
6847 errorText_ = "RtApi:: a stream is not open!";
6848 error( RtError::INVALID_USE );
6852 void RtApi :: clearStreamInfo()
6854 stream_.mode = UNINITIALIZED;
6855 stream_.state = STREAM_CLOSED;
6856 stream_.sampleRate = 0;
6857 stream_.bufferSize = 0;
6858 stream_.nBuffers = 0;
6859 stream_.userFormat = 0;
6860 stream_.userInterleaved = true;
6861 stream_.streamTime = 0.0;
6862 stream_.apiHandle = 0;
6863 stream_.deviceBuffer = 0;
6864 stream_.callbackInfo.callback = 0;
6865 stream_.callbackInfo.userData = 0;
6866 stream_.callbackInfo.isRunning = false;
6867 for ( int i=0; i<2; i++ ) {
6868 stream_.device[i] = 0;
6869 stream_.doConvertBuffer[i] = false;
6870 stream_.deviceInterleaved[i] = true;
6871 stream_.doByteSwap[i] = false;
6872 stream_.nUserChannels[i] = 0;
6873 stream_.nDeviceChannels[i] = 0;
6874 stream_.channelOffset[i] = 0;
6875 stream_.deviceFormat[i] = 0;
6876 stream_.latency[i] = 0;
6877 stream_.userBuffer[i] = 0;
6878 stream_.convertInfo[i].channels = 0;
6879 stream_.convertInfo[i].inJump = 0;
6880 stream_.convertInfo[i].outJump = 0;
6881 stream_.convertInfo[i].inFormat = 0;
6882 stream_.convertInfo[i].outFormat = 0;
6883 stream_.convertInfo[i].inOffset.clear();
6884 stream_.convertInfo[i].outOffset.clear();
6888 unsigned int RtApi :: formatBytes( RtAudioFormat format )
6890 if ( format == RTAUDIO_SINT16 )
6892 else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
6893 format == RTAUDIO_FLOAT32 )
6895 else if ( format == RTAUDIO_FLOAT64 )
6897 else if ( format == RTAUDIO_SINT8 )
6900 errorText_ = "RtApi::formatBytes: undefined format.";
6901 error( RtError::WARNING );
6906 void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
6908 if ( mode == INPUT ) { // convert device to user buffer
6909 stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
6910 stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
6911 stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
6912 stream_.convertInfo[mode].outFormat = stream_.userFormat;
6914 else { // convert user to device buffer
6915 stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
6916 stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
6917 stream_.convertInfo[mode].inFormat = stream_.userFormat;
6918 stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
6921 if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
6922 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
6924 stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
6926 // Set up the interleave/deinterleave offsets.
6927 if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
6928 if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
6929 ( mode == INPUT && stream_.userInterleaved ) ) {
6930 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6931 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
6932 stream_.convertInfo[mode].outOffset.push_back( k );
6933 stream_.convertInfo[mode].inJump = 1;
6937 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6938 stream_.convertInfo[mode].inOffset.push_back( k );
6939 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
6940 stream_.convertInfo[mode].outJump = 1;
6944 else { // no (de)interleaving
6945 if ( stream_.userInterleaved ) {
6946 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6947 stream_.convertInfo[mode].inOffset.push_back( k );
6948 stream_.convertInfo[mode].outOffset.push_back( k );
6952 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
6953 stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
6954 stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
6955 stream_.convertInfo[mode].inJump = 1;
6956 stream_.convertInfo[mode].outJump = 1;
6961 // Add channel offset.
6962 if ( firstChannel > 0 ) {
6963 if ( stream_.deviceInterleaved[mode] ) {
6964 if ( mode == OUTPUT ) {
6965 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6966 stream_.convertInfo[mode].outOffset[k] += firstChannel;
6969 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6970 stream_.convertInfo[mode].inOffset[k] += firstChannel;
6974 if ( mode == OUTPUT ) {
6975 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6976 stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
6979 for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
6980 stream_.convertInfo[mode].inOffset[k] += ( firstChannel * stream_.bufferSize );
6986 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
6988 // This function does format conversion, input/output channel compensation, and
6989 // data interleaving/deinterleaving. 24-bit integers are assumed to occupy
6990 // the upper three bytes of a 32-bit integer.
6992 // Clear our device buffer when in/out duplex device channels are different
6993 if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
6994 ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
6995 memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
6998 if (info.outFormat == RTAUDIO_FLOAT64) {
7000 Float64 *out = (Float64 *)outBuffer;
7002 if (info.inFormat == RTAUDIO_SINT8) {
7003 signed char *in = (signed char *)inBuffer;
7004 scale = 1.0 / 128.0;
7005 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7006 for (j=0; j<info.channels; j++) {
7007 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7008 out[info.outOffset[j]] *= scale;
7011 out += info.outJump;
7014 else if (info.inFormat == RTAUDIO_SINT16) {
7015 Int16 *in = (Int16 *)inBuffer;
7016 scale = 1.0 / 32768.0;
7017 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7018 for (j=0; j<info.channels; j++) {
7019 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7020 out[info.outOffset[j]] *= scale;
7023 out += info.outJump;
7026 else if (info.inFormat == RTAUDIO_SINT24) {
7027 Int32 *in = (Int32 *)inBuffer;
7028 scale = 1.0 / 8388608.0;
7029 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7030 for (j=0; j<info.channels; j++) {
7031 out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
7032 out[info.outOffset[j]] *= scale;
7035 out += info.outJump;
7038 else if (info.inFormat == RTAUDIO_SINT32) {
7039 Int32 *in = (Int32 *)inBuffer;
7040 scale = 1.0 / 2147483648.0;
7041 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7042 for (j=0; j<info.channels; j++) {
7043 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7044 out[info.outOffset[j]] *= scale;
7047 out += info.outJump;
7050 else if (info.inFormat == RTAUDIO_FLOAT32) {
7051 Float32 *in = (Float32 *)inBuffer;
7052 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7053 for (j=0; j<info.channels; j++) {
7054 out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
7057 out += info.outJump;
7060 else if (info.inFormat == RTAUDIO_FLOAT64) {
7061 // Channel compensation and/or (de)interleaving only.
7062 Float64 *in = (Float64 *)inBuffer;
7063 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7064 for (j=0; j<info.channels; j++) {
7065 out[info.outOffset[j]] = in[info.inOffset[j]];
7068 out += info.outJump;
7072 else if (info.outFormat == RTAUDIO_FLOAT32) {
7074 Float32 *out = (Float32 *)outBuffer;
7076 if (info.inFormat == RTAUDIO_SINT8) {
7077 signed char *in = (signed char *)inBuffer;
7078 scale = 1.0 / 128.0;
7079 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7080 for (j=0; j<info.channels; j++) {
7081 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7082 out[info.outOffset[j]] *= scale;
7085 out += info.outJump;
7088 else if (info.inFormat == RTAUDIO_SINT16) {
7089 Int16 *in = (Int16 *)inBuffer;
7090 scale = 1.0 / 32768.0;
7091 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7092 for (j=0; j<info.channels; j++) {
7093 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7094 out[info.outOffset[j]] *= scale;
7097 out += info.outJump;
7100 else if (info.inFormat == RTAUDIO_SINT24) {
7101 Int32 *in = (Int32 *)inBuffer;
7102 scale = 1.0 / 8388608.0;
7103 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7104 for (j=0; j<info.channels; j++) {
7105 out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
7106 out[info.outOffset[j]] *= scale;
7109 out += info.outJump;
7112 else if (info.inFormat == RTAUDIO_SINT32) {
7113 Int32 *in = (Int32 *)inBuffer;
7114 scale = 1.0 / 2147483648.0;
7115 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7116 for (j=0; j<info.channels; j++) {
7117 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7118 out[info.outOffset[j]] *= scale;
7121 out += info.outJump;
7124 else if (info.inFormat == RTAUDIO_FLOAT32) {
7125 // Channel compensation and/or (de)interleaving only.
7126 Float32 *in = (Float32 *)inBuffer;
7127 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7128 for (j=0; j<info.channels; j++) {
7129 out[info.outOffset[j]] = in[info.inOffset[j]];
7132 out += info.outJump;
7135 else if (info.inFormat == RTAUDIO_FLOAT64) {
7136 Float64 *in = (Float64 *)inBuffer;
7137 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7138 for (j=0; j<info.channels; j++) {
7139 out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
7142 out += info.outJump;
7146 else if (info.outFormat == RTAUDIO_SINT32) {
7147 Int32 *out = (Int32 *)outBuffer;
7148 if (info.inFormat == RTAUDIO_SINT8) {
7149 signed char *in = (signed char *)inBuffer;
7150 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7151 for (j=0; j<info.channels; j++) {
7152 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7153 out[info.outOffset[j]] <<= 24;
7156 out += info.outJump;
7159 else if (info.inFormat == RTAUDIO_SINT16) {
7160 Int16 *in = (Int16 *)inBuffer;
7161 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7162 for (j=0; j<info.channels; j++) {
7163 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7164 out[info.outOffset[j]] <<= 16;
7167 out += info.outJump;
7170 else if (info.inFormat == RTAUDIO_SINT24) {
7171 Int32 *in = (Int32 *)inBuffer;
7172 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7173 for (j=0; j<info.channels; j++) {
7174 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7175 out[info.outOffset[j]] <<= 8;
7178 out += info.outJump;
7181 else if (info.inFormat == RTAUDIO_SINT32) {
7182 // Channel compensation and/or (de)interleaving only.
7183 Int32 *in = (Int32 *)inBuffer;
7184 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7185 for (j=0; j<info.channels; j++) {
7186 out[info.outOffset[j]] = in[info.inOffset[j]];
7189 out += info.outJump;
7192 else if (info.inFormat == RTAUDIO_FLOAT32) {
7193 Float32 *in = (Float32 *)inBuffer;
7194 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7195 for (j=0; j<info.channels; j++) {
7196 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
7199 out += info.outJump;
7202 else if (info.inFormat == RTAUDIO_FLOAT64) {
7203 Float64 *in = (Float64 *)inBuffer;
7204 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7205 for (j=0; j<info.channels; j++) {
7206 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
7209 out += info.outJump;
7213 else if (info.outFormat == RTAUDIO_SINT24) {
7214 Int32 *out = (Int32 *)outBuffer;
7215 if (info.inFormat == RTAUDIO_SINT8) {
7216 signed char *in = (signed char *)inBuffer;
7217 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7218 for (j=0; j<info.channels; j++) {
7219 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7220 out[info.outOffset[j]] <<= 16;
7223 out += info.outJump;
7226 else if (info.inFormat == RTAUDIO_SINT16) {
7227 Int16 *in = (Int16 *)inBuffer;
7228 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7229 for (j=0; j<info.channels; j++) {
7230 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7231 out[info.outOffset[j]] <<= 8;
7234 out += info.outJump;
7237 else if (info.inFormat == RTAUDIO_SINT24) {
7238 // Channel compensation and/or (de)interleaving only.
7239 Int32 *in = (Int32 *)inBuffer;
7240 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7241 for (j=0; j<info.channels; j++) {
7242 out[info.outOffset[j]] = in[info.inOffset[j]];
7245 out += info.outJump;
7248 else if (info.inFormat == RTAUDIO_SINT32) {
7249 Int32 *in = (Int32 *)inBuffer;
7250 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7251 for (j=0; j<info.channels; j++) {
7252 out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
7253 out[info.outOffset[j]] >>= 8;
7256 out += info.outJump;
7259 else if (info.inFormat == RTAUDIO_FLOAT32) {
7260 Float32 *in = (Float32 *)inBuffer;
7261 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7262 for (j=0; j<info.channels; j++) {
7263 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388608.0);
7266 out += info.outJump;
7269 else if (info.inFormat == RTAUDIO_FLOAT64) {
7270 Float64 *in = (Float64 *)inBuffer;
7271 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7272 for (j=0; j<info.channels; j++) {
7273 out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
7276 out += info.outJump;
7280 else if (info.outFormat == RTAUDIO_SINT16) {
7281 Int16 *out = (Int16 *)outBuffer;
7282 if (info.inFormat == RTAUDIO_SINT8) {
7283 signed char *in = (signed char *)inBuffer;
7284 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7285 for (j=0; j<info.channels; j++) {
7286 out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
7287 out[info.outOffset[j]] <<= 8;
7290 out += info.outJump;
7293 else if (info.inFormat == RTAUDIO_SINT16) {
7294 // Channel compensation and/or (de)interleaving only.
7295 Int16 *in = (Int16 *)inBuffer;
7296 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7297 for (j=0; j<info.channels; j++) {
7298 out[info.outOffset[j]] = in[info.inOffset[j]];
7301 out += info.outJump;
7304 else if (info.inFormat == RTAUDIO_SINT24) {
7305 Int32 *in = (Int32 *)inBuffer;
7306 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7307 for (j=0; j<info.channels; j++) {
7308 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 8) & 0x0000ffff);
7311 out += info.outJump;
7314 else if (info.inFormat == RTAUDIO_SINT32) {
7315 Int32 *in = (Int32 *)inBuffer;
7316 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7317 for (j=0; j<info.channels; j++) {
7318 out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
7321 out += info.outJump;
7324 else if (info.inFormat == RTAUDIO_FLOAT32) {
7325 Float32 *in = (Float32 *)inBuffer;
7326 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7327 for (j=0; j<info.channels; j++) {
7328 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
7331 out += info.outJump;
7334 else if (info.inFormat == RTAUDIO_FLOAT64) {
7335 Float64 *in = (Float64 *)inBuffer;
7336 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7337 for (j=0; j<info.channels; j++) {
7338 out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
7341 out += info.outJump;
7345 else if (info.outFormat == RTAUDIO_SINT8) {
7346 signed char *out = (signed char *)outBuffer;
7347 if (info.inFormat == RTAUDIO_SINT8) {
7348 // Channel compensation and/or (de)interleaving only.
7349 signed char *in = (signed char *)inBuffer;
7350 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7351 for (j=0; j<info.channels; j++) {
7352 out[info.outOffset[j]] = in[info.inOffset[j]];
7355 out += info.outJump;
7358 if (info.inFormat == RTAUDIO_SINT16) {
7359 Int16 *in = (Int16 *)inBuffer;
7360 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7361 for (j=0; j<info.channels; j++) {
7362 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
7365 out += info.outJump;
7368 else if (info.inFormat == RTAUDIO_SINT24) {
7369 Int32 *in = (Int32 *)inBuffer;
7370 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7371 for (j=0; j<info.channels; j++) {
7372 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 16) & 0x000000ff);
7375 out += info.outJump;
7378 else if (info.inFormat == RTAUDIO_SINT32) {
7379 Int32 *in = (Int32 *)inBuffer;
7380 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7381 for (j=0; j<info.channels; j++) {
7382 out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
7385 out += info.outJump;
7388 else if (info.inFormat == RTAUDIO_FLOAT32) {
7389 Float32 *in = (Float32 *)inBuffer;
7390 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7391 for (j=0; j<info.channels; j++) {
7392 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
7395 out += info.outJump;
7398 else if (info.inFormat == RTAUDIO_FLOAT64) {
7399 Float64 *in = (Float64 *)inBuffer;
7400 for (unsigned int i=0; i<stream_.bufferSize; i++) {
7401 for (j=0; j<info.channels; j++) {
7402 out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
7405 out += info.outJump;
7411 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
7417 if ( format == RTAUDIO_SINT16 ) {
7418 for ( unsigned int i=0; i<samples; i++ ) {
7419 // Swap 1st and 2nd bytes.
7424 // Increment 2 bytes.
7428 else if ( format == RTAUDIO_SINT24 ||
7429 format == RTAUDIO_SINT32 ||
7430 format == RTAUDIO_FLOAT32 ) {
7431 for ( unsigned int i=0; i<samples; i++ ) {
7432 // Swap 1st and 4th bytes.
7437 // Swap 2nd and 3rd bytes.
7443 // Increment 4 bytes.
7447 else if ( format == RTAUDIO_FLOAT64 ) {
7448 for ( unsigned int i=0; i<samples; i++ ) {
7449 // Swap 1st and 8th bytes
7454 // Swap 2nd and 7th bytes
7460 // Swap 3rd and 6th bytes
7466 // Swap 4th and 5th bytes
7472 // Increment 8 bytes.
7478 // Indentation settings for Vim and Emacs
7481 // c-basic-offset: 2
7482 // indent-tabs-mode: nil
7485 // vim: et sts=2 sw=2