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