Sophie

Sophie

distrib > Mandriva > 2008.1 > x86_64 > by-pkgid > 0a90919b61859a90da060685ee09fe54 > files > 4

lastfm-player-1.3.2.13-1mdv2008.1.src.rpm

--- a/src/output/alsa-playback/alsaaudio.cpp
+++ b/src/output/alsa-playback/alsaaudio.cpp
@@ -36,10 +36,13 @@
 #define snd_pcm_dump( x, y )
 
 
-QMutex AlsaAudio::mutex;
 pthread_t AlsaAudio::audio_thread;
 
-QByteArray AlsaAudio::audioData;
+char* AlsaAudio::thread_buffer = NULL;
+int AlsaAudio::thread_buffer_size = 0;
+int AlsaAudio::rd_index = 0;
+int AlsaAudio::wr_index = 0;
+
 snd_output_t* AlsaAudio::logs = NULL;
 bool AlsaAudio::going = false;
 snd_pcm_t *AlsaAudio::alsa_pcm = NULL;
@@ -53,13 +56,9 @@
 convert_channel_func_t AlsaAudio::alsa_stereo_convert_func = NULL;
 convert_freq_func_t AlsaAudio::alsa_frequency_convert_func = NULL;
 xmms_convert_buffers* AlsaAudio::convertb = NULL;
-bool AlsaAudio::use_mmap = false;
-
 
 AlsaAudio::AlsaAudio()
 {
-    m_zero_pad = false;
-    m_max_buffer_size = 0;
 }
 
 
@@ -83,6 +82,12 @@
     int err = 0;
     m_devices.clear();
 
+    // First add the default PCM device
+    AlsaDeviceInfo dev;
+    dev.name = "Default PCM device (default)";
+    dev.device = "default";
+    m_devices.push_back( dev );
+
     if ((err = snd_card_next( &card )) != 0)
         goto getCardsFailed;
 
@@ -91,9 +96,9 @@
         if ((err = snd_card_next( &card )) != 0)
             goto getCardsFailed;
     }
-    
+
     return m_devices.size();
-    
+
 getCardsFailed:
     qDebug() << __PRETTY_FUNCTION__ << "failed: " << snd_strerror( -err );
     return -1;
@@ -123,18 +128,6 @@
     else
         cardName = alsa_name;
 
-    // Each card has its own default device
-    // But test, just to be sure it's there
-    AlsaDeviceInfo dev;
-    dev.name = QString("%1: Default Device (default:%2)").arg( cardName ).arg( card );
-    dev.device = "default:" + QString::number(card);
-    snd_pcm_t *test_pcm;
-    err = snd_pcm_open( &test_pcm, dev.device.toAscii(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
-    if (err >= 0)
-        snd_pcm_close( test_pcm );
-    if (err == 0 || err == -EBUSY)
-        m_devices.push_back( dev );
-
     snd_pcm_info_alloca( &pcm_info );
 
     for (;;)
@@ -155,11 +148,12 @@
         {
             if ( err != -ENOENT )
                 qDebug() << "Failed: snd_ctl_pcm_info() failed"
-                         "(" << card << ":" << pcm_device << "): " 
+                         "(" << card << ":" << pcm_device << "): "
                          << snd_strerror( -err );
             continue;
         }
 
+        AlsaDeviceInfo dev;
         dev.device = QString( "hw:%1,%2" )
                 .arg( card )
                 .arg( pcm_device );
@@ -167,7 +161,7 @@
                 .arg( cardName )
                 .arg( snd_pcm_info_get_name( pcm_info ) )
                 .arg( dev.device );
-        
+
         m_devices.push_back( dev );
     }
 
@@ -188,22 +182,18 @@
 bool
 AlsaAudio::alsaOpen( QString device, AFormat format, unsigned int rate,
                       unsigned int channels, snd_pcm_uframes_t periodSize,
-                      unsigned int periodCount )
+                      unsigned int periodCount, int minBufferCapacity )
 {
-    int err;
+    int err, hw_buffer_size;
     ssize_t hw_period_size;
     snd_pcm_hw_params_t *hwparams;
     snd_pcm_sw_params_t *swparams;
     snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
-    snd_pcm_access_mask_t *mask;
 
     Q_DEBUG_BLOCK << "Setting up Device:" << device;
 
     inputf = snd_format_from_xmms( format, rate, channels );
 
-    // We'll be using this in alsaWrite
-    m_max_buffer_size = inputf->bps;
-
     convertb = xmms_convert_buffers_new();
 
     snd_output_stdio_attach( &logs, stderr, 0 );
@@ -219,9 +209,9 @@
     qDebug() << "Opening device:" << device;
 
     // FIXME: Can snd_pcm_open() return EAGAIN?
-    if ((err = snd_pcm_open( &alsa_pcm, 
-                             device.toAscii(), 
-                             SND_PCM_STREAM_PLAYBACK, 
+    if ((err = snd_pcm_open( &alsa_pcm,
+                             device.toAscii(),
+                             SND_PCM_STREAM_PLAYBACK,
                              SND_PCM_NONBLOCK )) < 0)
     {
         qDebug() << "Failed to open pcm device (" << device << "): " << snd_strerror( -err );
@@ -240,7 +230,7 @@
     alsa_card = snd_pcm_info_get_card( info );
     alsa_device = snd_pcm_info_get_device( info );
     alsa_subdevice = snd_pcm_info_get_subdevice( info );
-    
+
     qDebug() << "Card:" << alsa_card;
     qDebug() << "Device:" << alsa_device;
     qDebug() << "Subdevice:" << alsa_subdevice;
@@ -249,38 +239,19 @@
 
     if ((err = snd_pcm_hw_params_any( alsa_pcm, hwparams )) < 0)
     {
-        qDebug() << "No configuration available for playback: " 
+        qDebug() << "No configuration available for playback: "
                  << snd_strerror( -err );
         alsaClose();
         return false;
     }
 
-    // First try to set up mmapped access
-    mask = (snd_pcm_access_mask_t*)alloca(snd_pcm_access_mask_sizeof());
-    snd_pcm_access_mask_none( mask );
-    snd_pcm_access_mask_set( mask, SND_PCM_ACCESS_MMAP_INTERLEAVED );
-    snd_pcm_access_mask_set( mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED );
-    snd_pcm_access_mask_set( mask, SND_PCM_ACCESS_MMAP_COMPLEX );
-
-
-    qDebug() << "Trying to set mmapped write mode";
-
-    if ( ( err = snd_pcm_hw_params_set_access_mask( alsa_pcm, hwparams, mask ) ) < 0 )
+    if ( ( err = snd_pcm_hw_params_set_access( alsa_pcm, hwparams,
+           SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
     {
-        use_mmap = false;
-
-        qDebug() << "Setting mmapped write mode failed: " << snd_strerror( -err ) << "\n Trying normal write mode";
-
-        if ( ( err = snd_pcm_hw_params_set_access( alsa_pcm, hwparams,
-               SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
-        {
-            qDebug() << "Cannot set normal write mode: " << snd_strerror( -err );
-            alsaClose();
-            return false;
-        }
+        qDebug() << "Cannot set normal write mode: " << snd_strerror( -err );
+        alsaClose();
+        return false;
     }
-    else
-        use_mmap = true;
 
     if ( ( err = snd_pcm_hw_params_set_format( alsa_pcm, hwparams, outputf->format ) ) < 0 )
     {
@@ -444,7 +415,21 @@
         hw_period_size_in = hw_period_size;
     }
 
+    hw_buffer_size = snd_pcm_frames_to_bytes( alsa_pcm, alsa_buffer_size );
+    thread_buffer_size = minBufferCapacity * 2;
+    if (thread_buffer_size < hw_buffer_size)
+        thread_buffer_size = hw_buffer_size * 2;
+    if (thread_buffer_size < 8192)
+        thread_buffer_size = 8192;
+    thread_buffer_size += hw_buffer_size;
+    thread_buffer_size -= thread_buffer_size % hw_period_size;
+
+    thread_buffer = (char*)calloc(thread_buffer_size, sizeof(char));
+
     qDebug() << "Device setup: period size:" << hw_period_size;
+    qDebug() << "Device setup: hw_period_size_in:" << hw_period_size_in;
+    qDebug() << "Device setup: hw_buffer_size:" << hw_buffer_size;
+    qDebug() << "Device setup: thread_buffer_size:" << thread_buffer_size;
     qDebug() << "bits per sample:" <<  snd_pcm_format_physical_width( outputf->format )
              << "frame size:" <<  snd_pcm_frames_to_bytes( alsa_pcm, 1 )
              << "Bps:" << outputf->bps;
@@ -464,12 +449,6 @@
     if ( !alsa_pcm )
         return 1;
 
-    //HACK because we should zero pad only after we have started
-    m_zero_pad = false;
-
-    // And clear the buffer, just in case
-    clearBuffer();
-
     going = true;
 
     AlsaAudio* aaThread = new AlsaAudio();
@@ -483,42 +462,67 @@
 
 void AlsaAudio::clearBuffer( void )
 {
-    QMutexLocker locker( &mutex );
-    audioData.clear();
+    wr_index = rd_index = 0;
+    if ( thread_buffer )
+        memset( thread_buffer, 0, thread_buffer_size );
 }
 
 /******************************************************************************
     Play Interface
 ******************************************************************************/
 
-void AlsaAudio::alsaWrite( const QByteArray* input )
+void AlsaAudio::alsaWrite( const QByteArray& input )
 {
-    #if 0
-    qDebug() << "max buffer size:" << m_max_buffer_size << ';'
-             << "buffer data:" << audioData.size() << ';'
-             << "input data:" << input->size();
-    #endif
-
-    QMutexLocker locker( &mutex );
-    // why would we want to lose frames?
-    //int const n = m_max_buffer_size * 2 - audioData.size();    
-    //audioData.append( input->left( n ) );
-    audioData += *input;
+    int cnt;
+    const char *src = input.data();
+    int length = input.size();
+    //qDebug() << "alsaWrite length:" << length;
+
+    while (length > 0)
+    {
+        int wr;
+        cnt = qMin(length, thread_buffer_size - wr_index);
+        memcpy(thread_buffer + wr_index, src, cnt);
+        wr = (wr_index + cnt) % thread_buffer_size;
+        wr_index = wr;
+        length -= cnt;
+        src += cnt;
+    }
 }
 
 
 int
-AlsaAudio::bufferSize() const
+AlsaAudio::get_thread_buffer_filled() const
 {
-    QMutexLocker locker( &mutex );
-    return audioData.size();
+    if ( wr_index >= rd_index )
+    {
+        return wr_index - rd_index;
+    }
+    return ( thread_buffer_size - ( rd_index - wr_index ) );
 }
 
-bool
-AlsaAudio::needsData() const
+
+// HACK: the buffer may have data, but not enough to send to the card.  In that
+// case we tell alsaplayback that we don't have any.  This may chop off some
+// data, but only at the natural end of a track.  On my machine, this is at
+// most 3759 bytes.  That's less than 0.022 sec.  It beats padding the buffer
+// with 0's if the stream fails mid track.  No stutter this way.
+int
+AlsaAudio::hasData()
+{
+    int tempSize = get_thread_buffer_filled();
+    if ( tempSize < hw_period_size_in )
+        return 0;
+    else
+        return tempSize;
+}
+
+
+int
+AlsaAudio::alsa_free() const
 {
-    QMutexLocker locker( &mutex );
-    return audioData.size() < m_max_buffer_size;
+    //qDebug() << "alsa_free:" << thread_buffer_size - get_thread_buffer_filled() - 1;
+    return thread_buffer_size - get_thread_buffer_filled() - 1;
 }
 
 
@@ -554,6 +558,11 @@
     xmms_convert_buffers_destroy( convertb );
     convertb = NULL;
 
+    if ( thread_buffer )
+    {
+        free(thread_buffer);
+        thread_buffer = NULL;
+    }
     if ( inputf )
     {
         free( inputf );
@@ -588,74 +597,46 @@
 AlsaAudio::run()
 {
     int npfds = snd_pcm_poll_descriptors_count( alsa_pcm );
+    int wr = 0;
     int err;
-    struct pollfd *pfds;
-    unsigned short *revents;
 
     if ( npfds <= 0 )
         goto _error;
-    pfds = (struct pollfd*)malloc( sizeof( *pfds ) * npfds );
-    revents = (unsigned short*)malloc( sizeof( *revents ) * npfds );
+
     err = snd_pcm_prepare( alsa_pcm );
     if ( err < 0 )
         qDebug() << "snd_pcm_prepare error:" << snd_strerror( err );
 
     while ( going && alsa_pcm )
     {
-        if (audioData.size() < hw_period_size_in)
-            if (m_zero_pad && audioData.size())
+        if ( get_thread_buffer_filled() >= hw_period_size_in )
+        {
+            wr = snd_pcm_wait( alsa_pcm, 10 );
+
+            if ( wr > 0 )
             {
-//                 qDebug() << "zeroPadding" << audioData.size();
-            
-                QMutexLocker locker( &mutex );
-                audioData = audioData.rightJustified( hw_period_size_in, '\0' );
+                alsa_write_out_thread_data();
             }
-            else
+            else if ( wr < 0 )
             {
-//                 qDebug() << "Waiting for more data";
-            
-                struct timespec req;
-                req.tv_sec = 0;
-                req.tv_nsec = 10000000; //0.1 seconds
-                nanosleep( &req, NULL );
+                alsa_handle_error( wr );
             }
-        
-        if (audioData.size() >= hw_period_size_in)
+        }
+        else
         {
-        
-            // zero pad next time, as we have now started this track
-            // NOTE this is a HACK and will cause crap behaviour when the streamer 
-            // fails mid track, and you'll get a stuttered restart after buffering
-            m_zero_pad = true;
-        
-            snd_pcm_poll_descriptors( alsa_pcm, pfds, npfds );
-            
-            if ( poll( pfds, npfds, 10 ) > 0 )
-            {
-                // need to check revents.  poll() with
-                // dmix returns a postive value even
-                // if no data is available
-                int i;
-                snd_pcm_poll_descriptors_revents( alsa_pcm, pfds, npfds, revents );
-                for (i = 0; i < npfds; i++)
-                    if (revents[i] & POLLOUT)
-                    {
-                        pumpThreadData();
-                        break;
-                    }
-            }
+            struct timespec req;
+            req.tv_sec = 0;
+            req.tv_nsec = 10000000; //0.1 seconds
+            nanosleep( &req, NULL );
         }
     }
-    free( pfds );
-    free( revents );
 
  _error:
     err = snd_pcm_drop( alsa_pcm );
     if ( err < 0 )
         qDebug() << "snd_pcm_drop error:" << snd_strerror( err );
-    QMutexLocker locker( &mutex );
-    audioData.clear();
-    locker.unlock();
+    wr_index = rd_index = 0;
+    memset( thread_buffer, 0, thread_buffer_size );
 
     qDebug() << "Exiting thread";
 
@@ -664,25 +645,27 @@
 
 
 /* transfer audio data from thread buffer to h/w */
-void AlsaAudio::pumpThreadData( void )
+void AlsaAudio::alsa_write_out_thread_data( void )
 {
     ssize_t length;
-    length = qMin( hw_period_size_in, ssize_t(audioData.size()) );
-    length = qMin( length, snd_pcm_frames_to_bytes( alsa_pcm, getAvailableFrames() ) );
-    
-    for (ssize_t n = 0; length > 0; length -= n)
-    {
-        n = qMin( length, ssize_t(audioData.size()) );
-        convertData( audioData.left( n ).data(), n );
-        
-        QMutexLocker locker( &mutex );
-        audioData.remove( 0, n );
+    int cnt;
+    length = qMin( hw_period_size_in, ssize_t(get_thread_buffer_filled()) );
+    length = qMin( length, snd_pcm_frames_to_bytes( alsa_pcm, alsa_get_avail() ) );
+
+    while (length > 0)
+    {
+        int rd;
+        cnt = qMin( length, ssize_t(thread_buffer_size - rd_index) );
+        alsa_do_write( thread_buffer + rd_index, cnt);
+        rd = (rd_index + cnt) % thread_buffer_size;
+        rd_index = rd;
+        length -= cnt;
     }
 }
 
 
 /* update and get the available space on h/w buffer (in frames) */
-snd_pcm_sframes_t AlsaAudio::getAvailableFrames( void )
+snd_pcm_sframes_t AlsaAudio::alsa_get_avail( void )
 {
     snd_pcm_sframes_t ret;
 
@@ -697,7 +680,6 @@
             qDebug() << "alsa_get_avail(): snd_pcm_avail_update() failed: " << snd_strerror( -ret );
             return 0;
         }
-        return 0;
     }
     return ret;
 }
@@ -708,7 +690,7 @@
  * data can be modified via rate conversion or
  * software volume before passed to audio h/w
  */
-void AlsaAudio::convertData( void* data, ssize_t length )
+void AlsaAudio::alsa_do_write( void* data, ssize_t length )
 {
     if ( alsa_convert_func != NULL )
         length = alsa_convert_func( convertb, &data, length );
@@ -721,9 +703,9 @@
                                               outputf->rate );
     }
 
-    adjustVolume( data, length, outputf->xmms_format );
+    volume_adjust( data, length, outputf->xmms_format );
 
-    writeToCard( (char*)data, length );
+    alsa_write_audio( (char*)data, length );
 }
 
 
@@ -747,7 +729,7 @@
     }                                    \
 } while ( 0 )
 
-void AlsaAudio::adjustVolume( void* data, ssize_t length, AFormat fmt )
+void AlsaAudio::volume_adjust( void* data, ssize_t length, AFormat fmt )
 {
     ssize_t i;
     if ( volume == 1.0 )
@@ -781,20 +763,14 @@
 
 
 /* transfer data to audio h/w via normal write */
-void AlsaAudio::writeToCard( char *data, ssize_t length )
+void AlsaAudio::alsa_write_audio( char *data, ssize_t length )
 {
     snd_pcm_sframes_t written_frames;
 
     while ( length > 0 )
     {
         snd_pcm_sframes_t frames = snd_pcm_bytes_to_frames( alsa_pcm, length );
-
-        if ( use_mmap )
-        {
-            written_frames = snd_pcm_mmap_writei( alsa_pcm, data, frames );
-        }
-        else
-            written_frames = snd_pcm_writei( alsa_pcm, data, frames );
+        written_frames = snd_pcm_writei( alsa_pcm, data, frames );
 
         if ( written_frames > 0 )
         {
@@ -893,6 +869,7 @@
 
 int AlsaAudio::xrun_recover( void )
 {
+#ifndef QT_NO_DEBUG
     snd_pcm_status_t *alsa_status;
     snd_pcm_status_alloca( &alsa_status );
     if ( snd_pcm_status( alsa_pcm, alsa_status ) < 0 )
@@ -904,7 +881,7 @@
         snd_pcm_status_dump( alsa_status, logs );
         qDebug() << "Status:\n" << logs;
     }
-
+#endif
 
     return snd_pcm_prepare( alsa_pcm );
 }
--- a/LastFM.pro
+++ b/LastFM.pro
@@ -35,8 +35,7 @@
     SUBDIRS -= src/LastFMHelper \
                src/mediadevices/itunes
 
-    SUBDIRS += src/output/alsa-playback \
-               src/output/portAudio
+    SUBDIRS += src/output/alsa-playback
 }
 
 
--- a/src/output/alsa-playback/alsaaudio.h
+++ b/src/output/alsa-playback/alsaaudio.h
@@ -25,7 +25,6 @@
 #include <QByteArray>
 #include <QList>
 #include <QString>
-#include <QMutex>
 #include "xconvert.h"
 
 
@@ -72,26 +71,22 @@
 
     bool alsaOpen( QString device, AFormat format, unsigned int rate, 
                    unsigned int channels, snd_pcm_uframes_t periodSize,
-                   unsigned int periodCount );
+                   unsigned int periodCount, int minBufferCapacity );
     int startPlayback();
-    void alsaWrite( const QByteArray* inputData );
+    void alsaWrite( const QByteArray& inputData );
     void stopPlayback();
     void alsaClose();
 
     void setVolume ( float vol );
-    int bufferSize() const;
-    bool needsData() const;
+    int hasData();
+    int get_thread_buffer_filled() const;
+    //bool needsData() const;
+    int alsa_free() const;
     void clearBuffer();
-    
-    void setMaxBufferCapacity( int ) { /*m_max_buffer_size = m;*/ }
-
-    static QByteArray audioData;
 
 private:
     QList<AlsaDeviceInfo> m_devices;
 
-    int m_max_buffer_size;
-
     // The following static variables are configured in either
     // alsaOpen or alsaSetup and used later in the audio thread
     static ssize_t hw_period_size_in;
@@ -106,20 +101,22 @@
     static convert_freq_func_t alsa_frequency_convert_func;
     static xmms_convert_buffers *convertb;
     static pthread_t audio_thread;
-    static QMutex mutex;
-    static bool use_mmap;
 
     void getDevicesForCard( int card );
-    bool alsaSetup( QString device, snd_pcm_uframes_t periodSize, uint periodCount, snd_format *f );
 
     static void* alsa_loop( void* );
     void run();
-    void pumpThreadData();
-    void convertData( void* data, ssize_t length );
-    void adjustVolume( void* data, ssize_t length, AFormat fmt );
-    void writeToCard( char *data, ssize_t length );
+    void alsa_write_out_thread_data();
+    void alsa_do_write( void* data, ssize_t length );
+    void volume_adjust( void* data, ssize_t length, AFormat fmt );
+    void alsa_write_audio( char *data, ssize_t length );
+    //int get_thread_buffer_filled() const;
+
+    static char* thread_buffer;
+    static int thread_buffer_size;
+    static int rd_index, wr_index;
 
-    snd_pcm_sframes_t getAvailableFrames( void );
+    snd_pcm_sframes_t alsa_get_avail( void );
     int alsa_handle_error( int err );
     int xrun_recover();
     int suspend_recover();
@@ -127,8 +124,6 @@
     snd_format* snd_format_from_xmms( AFormat fmt, unsigned int rate, unsigned int channels );
 
     void alsa_close_pcm( void );
-    
-    bool m_zero_pad;
 };
 
 #endif
--- a/src/output/alsa-playback/alsaplayback.cpp
+++ b/src/output/alsa-playback/alsaplayback.cpp
@@ -32,6 +32,7 @@
 
 AlsaPlayback::AlsaPlayback()
 		: m_audio( 0 )
+        , m_bufferCapacity( 0 )
 {
     initAudio( 44100, 2 );
 }
@@ -46,28 +47,29 @@
 bool
 AlsaPlayback::hasData()
 {
-    return m_audio->bufferSize();
+    bool has = m_audio->hasData() > 0;
+    return has;
 }
 
 
 bool
 AlsaPlayback::needsData()
 {
-    return m_audio->needsData();
+    return ( m_audio->get_thread_buffer_filled() < m_bufferCapacity );
 }
 
 
 void
 AlsaPlayback::setBufferCapacity( int size )
 {
-    m_audio->setMaxBufferCapacity( size );
+    m_bufferCapacity = size;
 }
 
 
 int
 AlsaPlayback::bufferSize()
 {
-    return m_audio->bufferSize();
+    return m_audio->get_thread_buffer_filled();
 }
 
 
@@ -150,9 +152,9 @@
 
     // We assume host byte order
 #ifdef WORDS_BIGENDIAN
-    if (!m_audio->alsaOpen( cardDevice, FMT_S16_BE, sampleRate, channels, periodSize, periodCount ))
+    if (!m_audio->alsaOpen( cardDevice, FMT_S16_BE, sampleRate, channels, periodSize, periodCount, m_bufferCapacity ))
 #else
-    if (!m_audio->alsaOpen( cardDevice, FMT_S16_LE, sampleRate, channels, periodSize, periodCount ))
+    if (!m_audio->alsaOpen( cardDevice, FMT_S16_LE, sampleRate, channels, periodSize, periodCount, m_bufferCapacity ))
 #endif
         goto _error;
 
@@ -170,7 +172,7 @@
 void
 AlsaPlayback::processData( const QByteArray &buffer )
 {
-    m_audio->alsaWrite( &buffer );
+    m_audio->alsaWrite( buffer );
 }
 
 
--- a/src/output/alsa-playback/alsaplayback.h
+++ b/src/output/alsa-playback/alsaplayback.h
@@ -62,6 +62,7 @@
 
     private:
         class AlsaAudio *m_audio;
+        int m_bufferCapacity;
 
         static float m_volume;