Various updates for pulseaudio API and default ALSA device enumeration (gs).
authorGary Scavone <gary@music.mcgill.ca>
Mon, 10 Dec 2012 20:30:46 +0000 (20:30 +0000)
committerStephen Sinclair <sinclair@music.mcgill.ca>
Thu, 10 Oct 2013 23:38:32 +0000 (01:38 +0200)
Makefile.in
RtAudio.cpp
configure.ac
librtaudio.pc.in [new file with mode: 0644]
tests/audioprobe.cpp

index c2cdcb1ac458a7a0ea1be5446f53aae77287f6aa..af5c9573f8657c75e35e77e64045322c40383a56 100644 (file)
@@ -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 : 
index 68dabff367ccb3c1f8ae1469ca7b96943f580009..70414a68297577db58426f2ab46b9c3c5d9860e0 100644 (file)
@@ -5160,6 +5160,12 @@ unsigned int RtApiAlsa :: getDeviceCount( void )
     snd_card_next( &card );\r
   }\r
 \r
+  result = snd_ctl_open( &handle, "default", 0 );\r
+  if (result == 0) {\r
+    nDevices++;\r
+    snd_ctl_close( handle );\r
+  }\r
+\r
   return nDevices;\r
 }\r
 \r
@@ -5206,6 +5212,15 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
     snd_card_next( &card );\r
   }\r
 \r
+  result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+  if ( result == 0 ) {\r
+    if ( nDevices == device ) {\r
+      strcpy( name, "default" );\r
+      goto foundDevice;\r
+    }\r
+    nDevices++;\r
+  }\r
+\r
   if ( nDevices == 0 ) {\r
     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
     error( RtError::INVALID_USE );\r
@@ -5239,16 +5254,18 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   snd_pcm_hw_params_t *params;\r
   snd_pcm_hw_params_alloca( &params );\r
 \r
-  // First try for playback\r
+  // First try for playback unless default device (which has subdev -1)\r
   stream = SND_PCM_STREAM_PLAYBACK;\r
-  snd_pcm_info_set_device( pcminfo, subdevice );\r
-  snd_pcm_info_set_subdevice( pcminfo, 0 );\r
   snd_pcm_info_set_stream( pcminfo, stream );\r
+  if ( subdevice != -1 ) {\r
+    snd_pcm_info_set_device( pcminfo, subdevice );\r
+    snd_pcm_info_set_subdevice( pcminfo, 0 );\r
 \r
-  result = snd_ctl_pcm_info( chandle, pcminfo );\r
-  if ( result < 0 ) {\r
-    // Device probably doesn't support playback.\r
-    goto captureProbe;\r
+    result = snd_ctl_pcm_info( chandle, pcminfo );\r
+    if ( result < 0 ) {\r
+      // Device probably doesn't support playback.\r
+      goto captureProbe;\r
+    }\r
   }\r
 \r
   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
@@ -5283,16 +5300,18 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   snd_pcm_close( phandle );\r
 \r
  captureProbe:\r
-  // Now try for capture\r
   stream = SND_PCM_STREAM_CAPTURE;\r
   snd_pcm_info_set_stream( pcminfo, stream );\r
 \r
-  result = snd_ctl_pcm_info( chandle, pcminfo );\r
-  snd_ctl_close( chandle );\r
-  if ( result < 0 ) {\r
-    // Device probably doesn't support capture.\r
-    if ( info.outputChannels == 0 ) return info;\r
-    goto probeParameters;\r
+  // Now try for capture unless default device (with subdev = -1)\r
+  if ( subdevice != -1 ) {\r
+    result = snd_ctl_pcm_info( chandle, pcminfo );\r
+    snd_ctl_close( chandle );\r
+    if ( result < 0 ) {\r
+      // Device probably doesn't support capture.\r
+      if ( info.outputChannels == 0 ) return info;\r
+      goto probeParameters;\r
+    }\r
   }\r
 \r
   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
@@ -5483,6 +5502,15 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       snd_card_next( &card );\r
     }\r
 \r
+    result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+    if ( result == 0 ) {\r
+      if ( nDevices == device ) {\r
+        strcpy( name, "default" );\r
+        goto foundDevice;\r
+      }\r
+      nDevices++;\r
+    }\r
+\r
     if ( nDevices == 0 ) {\r
       // This should not happen because a check is made before this function is called.\r
       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
@@ -6474,7 +6502,7 @@ void RtApiPulse::callbackEvent( void )
   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
   double streamTime = getStreamTime();\r
   RtAudioStreamStatus status = 0;\r
-  int doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+  int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
                                stream_.bufferSize, streamTime, status,\r
                                stream_.callbackInfo.userData );\r
 \r
@@ -6484,50 +6512,52 @@ void RtApiPulse::callbackEvent( void )
   }\r
 \r
   MUTEX_LOCK( &stream_.mutex );\r
+  void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
+  void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
 \r
   if ( stream_.state != STREAM_RUNNING )\r
     goto unlock;\r
 \r
   int pa_error;\r
   size_t bytes;\r
-  switch ( stream_.mode ) {\r
-  case INPUT:\r
-    bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
-    if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) {\r
-      errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
-        pa_strerror( pa_error ) << ".";\r
-      errorText_ = errorStream_.str();\r
-      error( RtError::WARNING );\r
-    }\r
-    break;\r
-  case OUTPUT:\r
-    bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
-    if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0 ) {\r
+  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+    if ( stream_.doConvertBuffer[OUTPUT] ) {\r
+        convertBuffer( stream_.deviceBuffer,\r
+                       stream_.userBuffer[OUTPUT],\r
+                       stream_.convertInfo[OUTPUT] );\r
+        bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
+                formatBytes( stream_.deviceFormat[OUTPUT] );\r
+    } else\r
+        bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
+                formatBytes( stream_.userFormat );\r
+\r
+    if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
       errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
         pa_strerror( pa_error ) << ".";\r
       errorText_ = errorStream_.str();\r
       error( RtError::WARNING );\r
     }\r
-    break;\r
-  case DUPLEX:\r
-    bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
-    if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) {\r
+  }\r
+\r
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
+    if ( stream_.doConvertBuffer[INPUT] )\r
+      bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
+        formatBytes( stream_.deviceFormat[INPUT] );\r
+    else\r
+      bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
+        formatBytes( stream_.userFormat );\r
+            \r
+    if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
       errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
         pa_strerror( pa_error ) << ".";\r
       errorText_ = errorStream_.str();\r
       error( RtError::WARNING );\r
     }\r
-    bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
-    if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0) {\r
-      errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
-        pa_strerror( pa_error ) << ".";\r
-      errorText_ = errorStream_.str();\r
-      error( RtError::WARNING );\r
+    if ( stream_.doConvertBuffer[INPUT] ) {\r
+      convertBuffer( stream_.userBuffer[INPUT],\r
+                     stream_.deviceBuffer,\r
+                     stream_.convertInfo[INPUT] );\r
     }\r
-    break;\r
-  default:\r
-    // ERROR\r
-    break;\r
   }\r
 \r
  unlock:\r
@@ -6676,20 +6706,16 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     return false;\r
   }\r
 \r
-  if ( options && ( options->flags & RTAUDIO_NONINTERLEAVED ) ) {\r
-    errorText_ = "RtApiPulse::probeDeviceOpen: only interleaved audio data supported.";\r
-    return false;\r
-  }\r
-\r
-  stream_.userInterleaved = true;\r
-  stream_.nBuffers = 1;\r
-\r
+  // Set interleaving parameters.\r
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+  else stream_.userInterleaved = true;\r
   stream_.deviceInterleaved[mode] = true;\r
+  stream_.nBuffers = 1;\r
   stream_.doByteSwap[mode] = false;\r
-  stream_.doConvertBuffer[mode] = false;\r
+  stream_.doConvertBuffer[mode] = channels > 1 && !stream_.userInterleaved;\r
   stream_.deviceFormat[mode] = stream_.userFormat;\r
   stream_.nUserChannels[mode] = channels;\r
-  stream_.nDeviceChannels[mode] = channels;\r
+  stream_.nDeviceChannels[mode] = channels + firstChannel;\r
   stream_.channelOffset[mode] = 0;\r
 \r
   // Allocate necessary internal buffers.\r
@@ -6701,6 +6727,34 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
   }\r
   stream_.bufferSize = *bufferSize;\r
 \r
+  if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+    bool makeBuffer = true;\r
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+    if ( mode == INPUT ) {\r
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+      }\r
+    }\r
+\r
+    if ( makeBuffer ) {\r
+      bufferBytes *= *bufferSize;\r
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+      if ( stream_.deviceBuffer == NULL ) {\r
+        errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
+        goto error;\r
+      }\r
+    }\r
+  }\r
+  \r
+  stream_.device[mode] = device;\r
+  stream_.state = STREAM_STOPPED;\r
+\r
+  // Setup the buffer conversion information structure.\r
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
   if ( !stream_.apiHandle ) {\r
     PulseAudioHandle *pah = new PulseAudioHandle;\r
     if ( !pah ) {\r
index 69250e887fd6375c7631cd3848af04f7658db125..6586116d1aa51fa2190fbbd7af710d0607b5bbef 100644 (file)
@@ -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 (file)
index 0000000..e80da3c
--- /dev/null
@@ -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
index f7246fb58311447525adfc673e407958ace8e9c5..7ec0ec6555e7eaaee0bbfcb75fc1d234ae365751 100644 (file)
@@ -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";