From bee81ff356bbec279facb50dab5e6069c6475fcc Mon Sep 17 00:00:00 2001 From: Gary Scavone Date: Mon, 10 Dec 2012 20:30:46 +0000 Subject: [PATCH] Various updates for pulseaudio API and default ALSA device enumeration (gs). --- Makefile.in | 7 +- RtAudio.cpp | 158 +++++++++++++++++++++++++++++-------------- configure.ac | 23 ++++++- librtaudio.pc.in | 12 ++++ tests/audioprobe.cpp | 1 + 5 files changed, 143 insertions(+), 58 deletions(-) create mode 100644 librtaudio.pc.in diff --git a/Makefile.in b/Makefile.in index c2cdcb1..af5c957 100644 --- a/Makefile.in +++ b/Makefile.in @@ -28,9 +28,8 @@ $(LIBRARIES): $(OBJECTS) $(AR) ruv $(STATIC) $(OBJECTS) ranlib $(STATIC) $(CC) -fPIC @libflags@ $(OBJECTS) @LIBS@ - $(LN) -s @sharedname@ $(SHARED) - -# $(CC) -shared $(OBJECTS) -o $(SHARED) @LIBS@ + $(LN) -sf @sharedname@ $(SHARED) + $(LN) -sf @sharedname@ $(SHARED).$(MAJOR) %.o : %.cpp $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@ @@ -48,7 +47,7 @@ distclean: $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)* $(RM) -f $(OBJECTS) $(RM) -f *~ - $(RM) -rf config.log config.status autom4te.cache Makefile rtaudio-config + $(RM) -rf config.log config.status autom4te.cache Makefile rtaudio-config librtaudio.pc cd tests && $(MAKE) distclean strip : diff --git a/RtAudio.cpp b/RtAudio.cpp index 68dabff..70414a6 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -5160,6 +5160,12 @@ unsigned int RtApiAlsa :: getDeviceCount( void ) snd_card_next( &card ); } + result = snd_ctl_open( &handle, "default", 0 ); + if (result == 0) { + nDevices++; + snd_ctl_close( handle ); + } + return nDevices; } @@ -5206,6 +5212,15 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_card_next( &card ); } + result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); + if ( result == 0 ) { + if ( nDevices == device ) { + strcpy( name, "default" ); + goto foundDevice; + } + nDevices++; + } + if ( nDevices == 0 ) { errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!"; error( RtError::INVALID_USE ); @@ -5239,16 +5254,18 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca( ¶ms ); - // First try for playback + // First try for playback unless default device (which has subdev -1) stream = SND_PCM_STREAM_PLAYBACK; - snd_pcm_info_set_device( pcminfo, subdevice ); - snd_pcm_info_set_subdevice( pcminfo, 0 ); snd_pcm_info_set_stream( pcminfo, stream ); + if ( subdevice != -1 ) { + snd_pcm_info_set_device( pcminfo, subdevice ); + snd_pcm_info_set_subdevice( pcminfo, 0 ); - result = snd_ctl_pcm_info( chandle, pcminfo ); - if ( result < 0 ) { - // Device probably doesn't support playback. - goto captureProbe; + result = snd_ctl_pcm_info( chandle, pcminfo ); + if ( result < 0 ) { + // Device probably doesn't support playback. + goto captureProbe; + } } result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK ); @@ -5283,16 +5300,18 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); captureProbe: - // Now try for capture stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream( pcminfo, stream ); - result = snd_ctl_pcm_info( chandle, pcminfo ); - snd_ctl_close( chandle ); - if ( result < 0 ) { - // Device probably doesn't support capture. - if ( info.outputChannels == 0 ) return info; - goto probeParameters; + // Now try for capture unless default device (with subdev = -1) + if ( subdevice != -1 ) { + result = snd_ctl_pcm_info( chandle, pcminfo ); + snd_ctl_close( chandle ); + if ( result < 0 ) { + // Device probably doesn't support capture. + if ( info.outputChannels == 0 ) return info; + goto probeParameters; + } } result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); @@ -5483,6 +5502,15 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne snd_card_next( &card ); } + result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); + if ( result == 0 ) { + if ( nDevices == device ) { + strcpy( name, "default" ); + goto foundDevice; + } + nDevices++; + } + if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!"; @@ -6474,7 +6502,7 @@ void RtApiPulse::callbackEvent( void ) RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; - int doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], + int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); @@ -6484,50 +6512,52 @@ void RtApiPulse::callbackEvent( void ) } MUTEX_LOCK( &stream_.mutex ); + void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT]; + void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT]; if ( stream_.state != STREAM_RUNNING ) goto unlock; int pa_error; size_t bytes; - switch ( stream_.mode ) { - case INPUT: - bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) { - errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); - } - break; - case OUTPUT: - bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0 ) { + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + if ( stream_.doConvertBuffer[OUTPUT] ) { + convertBuffer( stream_.deviceBuffer, + stream_.userBuffer[OUTPUT], + stream_.convertInfo[OUTPUT] ); + bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize * + formatBytes( stream_.deviceFormat[OUTPUT] ); + } else + bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize * + formatBytes( stream_.userFormat ); + + if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); error( RtError::WARNING ); } - break; - case DUPLEX: - bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) { + } + + if ( stream_.mode == INPUT || stream_.mode == DUPLEX) { + if ( stream_.doConvertBuffer[INPUT] ) + bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize * + formatBytes( stream_.deviceFormat[INPUT] ); + else + bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize * + formatBytes( stream_.userFormat ); + + if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); error( RtError::WARNING ); } - bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0) { - errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); + if ( stream_.doConvertBuffer[INPUT] ) { + convertBuffer( stream_.userBuffer[INPUT], + stream_.deviceBuffer, + stream_.convertInfo[INPUT] ); } - break; - default: - // ERROR - break; } unlock: @@ -6676,20 +6706,16 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, return false; } - if ( options && ( options->flags & RTAUDIO_NONINTERLEAVED ) ) { - errorText_ = "RtApiPulse::probeDeviceOpen: only interleaved audio data supported."; - return false; - } - - stream_.userInterleaved = true; - stream_.nBuffers = 1; - + // Set interleaving parameters. + if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; + else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; + stream_.nBuffers = 1; stream_.doByteSwap[mode] = false; - stream_.doConvertBuffer[mode] = false; + stream_.doConvertBuffer[mode] = channels > 1 && !stream_.userInterleaved; stream_.deviceFormat[mode] = stream_.userFormat; stream_.nUserChannels[mode] = channels; - stream_.nDeviceChannels[mode] = channels; + stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.channelOffset[mode] = 0; // Allocate necessary internal buffers. @@ -6701,6 +6727,34 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, } stream_.bufferSize = *bufferSize; + if ( stream_.doConvertBuffer[mode] ) { + + bool makeBuffer = true; + bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); + if ( mode == INPUT ) { + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); + if ( bufferBytes <= bytesOut ) makeBuffer = false; + } + } + + if ( makeBuffer ) { + bufferBytes *= *bufferSize; + if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); + stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); + if ( stream_.deviceBuffer == NULL ) { + errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory."; + goto error; + } + } + } + + stream_.device[mode] = device; + stream_.state = STREAM_STOPPED; + + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); + if ( !stream_.apiHandle ) { PulseAudioHandle *pah = new PulseAudioHandle; if ( !pah ) { diff --git a/configure.ac b/configure.ac index 69250e8..6586116 100644 --- a/configure.ac +++ b/configure.ac @@ -2,11 +2,25 @@ AC_INIT(RtAudio, 4.0, gary@music.mcgill.ca, rtaudio) AC_CONFIG_AUX_DIR(config) AC_CONFIG_SRCDIR(RtAudio.cpp) -AC_CONFIG_FILES([rtaudio-config Makefile tests/Makefile]) +AC_CONFIG_FILES([rtaudio-config librtaudio.pc Makefile tests/Makefile]) # Fill GXX with something before test. AC_SUBST( GXX, ["no"] ) +dnl Check for pkg-config program, used for configuring some libraries. +m4_define_default([PKG_PROG_PKG_CONFIG], +[AC_MSG_CHECKING([pkg-config]) +AC_MSG_RESULT([no])]) + +PKG_PROG_PKG_CONFIG + +dnl If the pkg-config autoconf support isn't installed, define its +dnl autoconf macro to disable any packages depending on it. +m4_define_default([PKG_CHECK_MODULES], +[AC_MSG_CHECKING([$1]) +AC_MSG_RESULT([no]) +$4]) + # Checks for programs. AC_PROG_CXX(g++ CC c++ cxx) AC_PROG_RANLIB @@ -58,6 +72,7 @@ esac # Checks for package options and external software AC_SUBST( api, [""] ) +AC_SUBST( req, [""] ) AC_MSG_CHECKING(for audio API) case $host in *-*-netbsd*) @@ -77,14 +92,17 @@ case $host in # Look for ALSA flag AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [ api="$api -D__LINUX_ALSA__" + req="$req alsa" AC_MSG_RESULT(using ALSA) AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))], ) # Look for PULSE flag AC_ARG_WITH(pulse, [ --with-pulse = choose PulseAudio API support (linux only)], [ api="$api -D__LINUX_PULSE__" + req="$req libpulse-simple" AC_MSG_RESULT(using PulseAudio) - AC_CHECK_LIB(pulse-simple, pa_simple_new, , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!))], ) + PKG_CHECK_MODULES([PULSE], [libpulse-simple], , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!)) + LIBS="$LIBS `pkg-config --libs libpulse-simple`" ], ) # Look for OSS flag AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [ @@ -95,6 +113,7 @@ case $host in if [test "$api" == "";] then AC_MSG_RESULT(using ALSA) AC_SUBST( api, [-D__LINUX_ALSA__] ) + req="$req alsa" AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!)) fi diff --git a/librtaudio.pc.in b/librtaudio.pc.in new file mode 100644 index 0000000..e80da3c --- /dev/null +++ b/librtaudio.pc.in @@ -0,0 +1,12 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: librtaudio +Description: RtAudio - a set of C++ classes that provide a common API for realtime audio input/output +Version: 4.0.11 +Requires: @req@ +Libs: -L${libdir} -lrtaudio +Libs.private: -lpthread +Cflags: -pthread -I${includedir} @CPPFLAGS@ \ No newline at end of file diff --git a/tests/audioprobe.cpp b/tests/audioprobe.cpp index f7246fb..7ec0ec6 100644 --- a/tests/audioprobe.cpp +++ b/tests/audioprobe.cpp @@ -20,6 +20,7 @@ int main() apiMap[RtAudio::WINDOWS_DS] = "Windows Direct Sound"; apiMap[RtAudio::UNIX_JACK] = "Jack Client"; apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; -- 2.30.2