Hi guys,

Good news! Kevin Kolfer has written a patch for PortAudio that makes it
work with ALSA when mmap isn't supported. This is good because the
PulseAudio ALSA plugin (a plugin for ALSA which makes it use PulseAudio
for mixing) doesn't support mmap, and I _think_ this will make Mixxx
work nicer out-of-the-box on Ubuntu.

If anyone wants to test this patch, we can pass some feedback along to
Kevin.

Thanks,
Albert
--- Begin Message ---
Hi,

The attached experimental patch makes PortAudio v19 work for ALSA devices 
without mmap support, such as the PulseAudio ALSA plugin.

I tested this with espeak and with some of the examples which come with 
PortAudio:
* Espeak seems to work.
* There's a problem with the PortAudio pa_devs example and the PulseAudio ALSA 
plugin:
pcm_pulse.c:479: pulse_prepare: Assertion `pcm->stream' failed.
The PulseAudio ALSA plugin does not like the call to snd_pcm_hw_params in 
PortAudio's TestParameters function:
#7  0x077eaa2d in snd_pcm_hw_params () from /lib/libasound.so.2
#8  0x0011fd0c in TestParameters (hostApi=0x8e701c0, parameters=0xbfed273c,
    sampleRate=8000, streamDir=StreamDirection_In)
    at src/hostapi/alsa/pa_linux_alsa.c:1081
#9  0x0011ff40 in IsFormatSupported (hostApi=0x8e701c0, 
    inputParameters=0xbfed273c, outputParameters=0x0, sampleRate=8000)
    at src/hostapi/alsa/pa_linux_alsa.c:1141
It appeary to only expect snd_pcm_hw_params to get called when something is 
actually being played or recorded. Do you think this is a PulseAudio bug 
(i.e. should I report it to them)? Or should PortAudio not be calling that 
function at that point?
* To test some of the PortAudio examples (e.g. patest_sine), I had to change 
the paDefaultHostApiIndex in pa_unix_hostapis.c. Why is this still defaulting 
to the obsolete OSS now that we have ALSA?
* There is some stuttering (especially audible in simple tests, such as 
patest_sine). Increasing the minimum buffer size in this code I added:
    /* non-mmap mode needs a reasonably-sized buffer or it'll stutter */
    if( !self->canMmap && framesPerHostBuffer < 2048 )
        framesPerHostBuffer = 2048;
to something even higher reduces it (but doesn't completely fix it and it 
increases the latency). I'm not sure what the proper fix is.
* I haven't really tested recording yet (it's supposed to work though).
* My main goal with this is ultimately to get Audacity to work with 
PulseAudio, but the Fedora packages of Audacity are linked against a private 
PortAudio, so I haven't tested my patch with Audacity yet.

(Note: Please don't use the abbreviation "PA" when replying to this thread, 
there are 2 PAs here (PortAudio and PulseAudio), so it can get confusing.)

        Kevin Kofler
diff -ur portaudio/src/hostapi/alsa/pa_linux_alsa.c portaudio-pulseaudio/src/hostapi/alsa/pa_linux_alsa.c
--- portaudio/src/hostapi/alsa/pa_linux_alsa.c	2007-09-12 19:39:48.000000000 +0200
+++ portaudio-pulseaudio/src/hostapi/alsa/pa_linux_alsa.c	2008-11-09 04:13:07.000000000 +0100
@@ -6,6 +6,7 @@
  *
  * Copyright (c) 2002 Joshua Haberman <[EMAIL PROTECTED]>
  * Copyright (c) 2005-2007 Arve Knudsen <[EMAIL PROTECTED]>
+ * Copyright (c) 2008 Kevin Kofler <[EMAIL PROTECTED]>
  *
  * Based on the Open Source API proposed by Ross Bencina
  * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -118,6 +119,8 @@
     unsigned long framesPerBuffer;
     int numUserChannels, numHostChannels;
     int userInterleaved, hostInterleaved;
+    int canMmap;
+    void *nonMmapBuffer;
     PaDeviceIndex device;     /* Keep the device index */
 
     snd_pcm_t *pcm;
@@ -321,7 +324,7 @@
  * and a suitable result returned. The device is closed before returning.
  */
 static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, int openBlocking,
-        PaAlsaDeviceInfo* devInfo, int* canMmap )
+        PaAlsaDeviceInfo* devInfo )
 {
     PaError result = paNoError;
     snd_pcm_hw_params_t *hwParams;
@@ -354,9 +357,6 @@
     snd_pcm_hw_params_alloca( &hwParams );
     snd_pcm_hw_params_any( pcm, hwParams );
 
-    *canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED ) >= 0 ||
-            snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) >= 0;
-
     if( defaultSr >= 0 )
     {
         /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
@@ -566,7 +566,6 @@
     PaError result = 0;
     PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo;
     snd_pcm_t *pcm;
-    int canMmap = -1;
     PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep;
 
     /* Zero fields */
@@ -580,8 +579,7 @@
             OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 )
             >= 0 )
     {
-        if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo,
-                    &canMmap ) != paNoError )
+        if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo ) != paNoError )
         {
             /* Error */
             PA_DEBUG(("%s: Failed groping %s for capture\n", __FUNCTION__, deviceName->alsaName));
@@ -594,8 +592,7 @@
             OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 )
             >= 0 )
     {
-        if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo,
-                    &canMmap ) != paNoError )
+        if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo ) != paNoError )
         {
             /* Error */
             PA_DEBUG(("%s: Failed groping %s for playback\n", __FUNCTION__, deviceName->alsaName));
@@ -603,12 +600,6 @@
         }
     }
 
-    if( 0 == canMmap )
-    {
-        PA_DEBUG(("%s: Device %s doesn't support mmap\n", __FUNCTION__, deviceName->alsaName));
-        goto end;
-    }
-
     baseDeviceInfo->structVersion = 2;
     baseDeviceInfo->hostApi = alsaApi->hostApiIndex;
     baseDeviceInfo->name = deviceName->name;
@@ -1197,6 +1188,8 @@
     self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
     self->numUserChannels = params->channelCount;
     self->streamDir = streamDir;
+    self->canMmap = 0;
+    self->nonMmapBuffer = NULL;
 
     if( !callbackMode && !self->userInterleaved )
     {
@@ -1239,6 +1232,7 @@
 
     PaError result = paNoError;
     snd_pcm_access_t accessMode, alternateAccessMode;
+    snd_pcm_access_t rwAccessMode, alternateRwAccessMode;
     int dir = 0;
     snd_pcm_t *pcm = self->pcm;
     double sr = *sampleRate;
@@ -1258,32 +1252,40 @@
     if( self->userInterleaved )
     {
         accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+        rwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
         alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+        alternateRwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
     }
     else
     {
         accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+        rwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
         alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+        alternateRwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
     }
     /* If requested access mode fails, try alternate mode */
+    self->canMmap = 1;
     if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
     {
-        int err = 0;
-        if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
+        if( snd_pcm_hw_params_set_access( pcm, hwParams, rwAccessMode ) >= 0 )
+            self->canMmap = 0;
+        else
         {
-            result = paUnanticipatedHostError;
-            if( -EINVAL == err )
-            {
-                PaUtil_SetLastHostErrorInfo( paALSA, err, "PA ALSA requires that a device supports mmap access" );
-            }
-            else
+            if( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ) < 0 )
             {
-                PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+                int err = 0;
+                if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateRwAccessMode )) >= 0)
+                    self->canMmap = 0;
+                else
+                {
+                    result = paUnanticipatedHostError;
+                    PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+                    goto error;
+                }
             }
-            goto error;
+            /* Flip mode */
+            self->hostInterleaved = !self->userInterleaved;
         }
-        /* Flip mode */
-        self->hostInterleaved = !self->userInterleaved;
     }
 
     ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
@@ -1361,7 +1363,7 @@
         
     ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
     ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError );
-    ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError );
 
     /* Set the parameters! */
     ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError );
@@ -1589,6 +1591,10 @@
         }
     }
 
+    /* non-mmap mode needs a reasonably-sized buffer or it'll stutter */
+    if( !self->canMmap && framesPerHostBuffer < 2048 )
+        framesPerHostBuffer = 2048;
+
     assert( framesPerHostBuffer > 0 );
     {
         snd_pcm_uframes_t min = 0, max = 0;
@@ -2059,7 +2065,8 @@
             {
                 /* Buffer isn't primed, so prepare and silence */
                 ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
-                SilenceBuffer( stream );
+                if( stream->playback.canMmap )
+                    SilenceBuffer( stream );
             }
             ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
         }
@@ -2563,7 +2570,7 @@
 static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
 {
     PaError result = paNoError;
-    int res;
+    int res = 0;
 
     /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
      * afterwards
@@ -2571,7 +2578,34 @@
     if( !self->ready )
         goto end;
 
-    res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+    if( !self->canMmap && StreamDirection_Out == self->streamDir )
+    {
+        /* Play sound */
+        if( self->hostInterleaved )
+            res = snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames );
+        else
+        {
+            void *bufs[self->numHostChannels];
+            int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer );
+            unsigned char *buffer = self->nonMmapBuffer;
+            int i;
+            for( i = 0; i < self->numHostChannels; ++i )
+            {
+                bufs[i] = buffer;
+                buffer += bufsize;
+            }
+            res = snd_pcm_writen( self->pcm, bufs, numFrames );
+        }
+    }
+
+    if( self->canMmap )
+        res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+    else
+    {
+        free( self->nonMmapBuffer );
+        self->nonMmapBuffer = NULL;
+    }
+
     if( res == -EPIPE || res == -ESTRPIPE )
     {
         *xrun = 1;
@@ -2611,7 +2645,7 @@
     if( self->hostInterleaved )
     {
         int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
-        unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+        unsigned char *buffer = self->canMmap ? ExtractAddress( self->channelAreas, self->offset ) : self->nonMmapBuffer;
 
         /* Start after the last user channel */
         p = buffer + self->numUserChannels * swidth;
@@ -2991,13 +3025,23 @@
         goto end;
     }
 
-    ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+    if( self->canMmap )
+    {
+        ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+        /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+        self->channelAreas = (snd_pcm_channel_area_t *)areas;
+    }
+    else
+    {
+        free( self->nonMmapBuffer );
+        self->nonMmapBuffer = calloc( self->numHostChannels, snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer ) );
+    }
 
     if( self->hostInterleaved )
     {
         int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
 
-        p = buffer = ExtractAddress( areas, self->offset );
+        p = buffer = self->canMmap ? ExtractAddress( areas, self->offset ) : self->nonMmapBuffer;
         for( i = 0; i < self->numUserChannels; ++i )
         {
             /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
@@ -3007,16 +3051,52 @@
     }
     else
     {
-        for( i = 0; i < self->numUserChannels; ++i )
+        if( self->canMmap )
+            for( i = 0; i < self->numUserChannels; ++i )
+            {
+                area = areas + i;
+                buffer = ExtractAddress( area, self->offset );
+                setChannel( bp, i, buffer, 1 );
+            }
+        else
         {
-            area = areas + i;
-            buffer = ExtractAddress( area, self->offset );
-            setChannel( bp, i, buffer, 1 );
+            int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer );
+            buffer = self->nonMmapBuffer;
+            for( i = 0; i < self->numUserChannels; ++i )
+            {
+                setChannel( bp, i, buffer, 1 );
+                buffer += bufsize;
+            }
         }
     }
 
-    /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
-    self->channelAreas = (snd_pcm_channel_area_t *)areas;
+    if( !self->canMmap && StreamDirection_In == self->streamDir )
+    {
+        /* Read sound */
+        int res;
+        if( self->hostInterleaved )
+            res = snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames );
+        else
+        {
+            void *bufs[self->numHostChannels];
+            int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer );
+            unsigned char *buffer = self->nonMmapBuffer;
+            int i;
+            for( i = 0; i < self->numHostChannels; ++i )
+            {
+                bufs[i] = buffer;
+                buffer += bufsize;
+            }
+            res = snd_pcm_readn( self->pcm, bufs, *numFrames );
+        }
+        if( res == -EPIPE || res == -ESTRPIPE )
+        {
+            *xrun = 1;
+            *numFrames = 0;
+            free( self->nonMmapBuffer );
+            self->nonMmapBuffer = NULL;
+        }
+    }
 
 end:
 error:
_______________________________________________
Portaudio mailing list
[EMAIL PROTECTED]
http://techweb.rfa.org/mailman/listinfo/portaudio

--- End Message ---
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Mixxx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mixxx-devel

Reply via email to