src/multimedia/audio/qaudiooutput_alsa_p.cpp
branchRCL_3
changeset 8 3f74d0d4af4c
parent 4 3b1da2848fc7
child 14 c0432d11811c
--- a/src/multimedia/audio/qaudiooutput_alsa_p.cpp	Mon Mar 15 12:43:09 2010 +0200
+++ b/src/multimedia/audio/qaudiooutput_alsa_p.cpp	Thu Apr 08 14:19:33 2010 +0300
@@ -259,6 +259,7 @@
 {
     if(deviceState == QAudio::StoppedState)
         return;
+    errorState = QAudio::NoError;
     deviceState = QAudio::StoppedState;
     close();
     emit stateChanged(deviceState);
@@ -368,10 +369,52 @@
         }
     }
     if ( !fatal ) {
+        unsigned int maxBufferTime = 0;
+        unsigned int minBufferTime = 0;
+        unsigned int maxPeriodTime = 0;
+        unsigned int minPeriodTime = 0;
+
+        err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir);
+        if ( err >= 0)
+            err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir);
+        if ( err >= 0)
+            err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir);
+        if ( err >= 0)
+            err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir);
+
+        if ( err < 0 ) {
+            fatal = true;
+            errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err);
+        } else {
+            if (maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time) {
+#ifdef DEBUG_AUDIO
+                qDebug()<<"defaults out of range";
+                qDebug()<<"pmin="<<minPeriodTime<<", pmax="<<maxPeriodTime<<", bmin="<<minBufferTime<<", bmax="<<maxBufferTime;
+#endif
+                period_time = minPeriodTime;
+                if (period_time*4 <= maxBufferTime) {
+                    // Use 4 periods if possible
+                    buffer_time = period_time*4;
+                    chunks = 4;
+                } else if (period_time*2 <= maxBufferTime) {
+                    // Use 2 periods if possible
+                    buffer_time = period_time*2;
+                    chunks = 2;
+                } else {
+                    qWarning()<<"QAudioOutput: alsa only supports single period!";
+                    fatal = true;
+                }
+#ifdef DEBUG_AUDIO
+                qDebug()<<"used: buffer_time="<<buffer_time<<", period_time="<<period_time;
+#endif
+            }
+        }
+    }
+    if ( !fatal ) {
         err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
         if ( err < 0 ) {
             fatal = true;
-                errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
+            errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
         }
     }
     if ( !fatal ) {
@@ -494,10 +537,13 @@
         err = snd_pcm_writei( handle, data, frames );
     }
     if(err > 0) {
-        totalTimeValue += err*1000000/settings.frequency();
+        totalTimeValue += err;
         resuming = false;
         errorState = QAudio::NoError;
-        deviceState = QAudio::ActiveState;
+        if (deviceState != QAudio::ActiveState) {
+            deviceState = QAudio::ActiveState;
+            emit stateChanged(deviceState);
+        }
         return snd_pcm_frames_to_bytes( handle, err );
     } else
         err = xrun_recovery(err);
@@ -542,7 +588,7 @@
 
 qint64 QAudioOutputPrivate::processedUSecs() const
 {
-    return totalTimeValue;
+    return qint64(1000000) * totalTimeValue / settings.frequency();
 }
 
 void QAudioOutputPrivate::resume()
@@ -562,10 +608,8 @@
             bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames);
         }
         resuming = true;
-        if(pullMode)
-            deviceState = QAudio::ActiveState;
-        else
-            deviceState = QAudio::IdleState;
+
+        deviceState = QAudio::ActiveState;
 
         errorState = QAudio::NoError;
         timer->start(period_time/1000);
@@ -604,9 +648,10 @@
 
 void QAudioOutputPrivate::feedback()
 {
-    QMetaObject::invokeMethod(this, SLOT(updateAvailable()), Qt::QueuedConnection);
+    updateAvailable();
 }
 
+
 void QAudioOutputPrivate::updateAvailable()
 {
 #ifdef DEBUG_AUDIO
@@ -637,7 +682,9 @@
             // Got some data to output
             if(deviceState != QAudio::ActiveState)
                 return true;
-            write(audioBuffer,l);
+            qint64 bytesWritten = write(audioBuffer,l);
+            if (bytesWritten != l)
+                audioSource->seek(audioSource->pos()-(l-bytesWritten));
             bytesAvailable = bytesFree();
 
         } else if(l == 0) {
@@ -645,9 +692,11 @@
             bytesAvailable = bytesFree();
             if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
                 // Underrun
-                errorState = QAudio::UnderrunError;
-                deviceState = QAudio::IdleState;
-                emit stateChanged(deviceState);
+                if (deviceState != QAudio::IdleState) {
+                    errorState = QAudio::UnderrunError;
+                    deviceState = QAudio::IdleState;
+                    emit stateChanged(deviceState);
+                }
             }
 
         } else if(l < 0) {
@@ -655,8 +704,17 @@
             errorState = QAudio::IOError;
             emit stateChanged(deviceState);
         }
-    } else
+    } else {
         bytesAvailable = bytesFree();
+        if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
+            // Underrun
+            if (deviceState != QAudio::IdleState) {
+                errorState = QAudio::UnderrunError;
+                deviceState = QAudio::IdleState;
+                emit stateChanged(deviceState);
+            }
+        }
+    }
 
     if(deviceState != QAudio::ActiveState)
         return true;
@@ -671,35 +729,10 @@
 
 qint64 QAudioOutputPrivate::elapsedUSecs() const
 {
-    if(!handle)
-        return 0;
-
     if (deviceState == QAudio::StoppedState)
         return 0;
 
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
-    snd_pcm_status_t* status;
-    snd_pcm_status_alloca(&status);
-
-    snd_timestamp_t t1,t2;
-    if( snd_pcm_status(handle, status) >= 0) {
-        snd_pcm_status_get_tstamp(status,&t1);
-        snd_pcm_status_get_trigger_tstamp(status,&t2);
-        t1.tv_sec-=t2.tv_sec;
-
-        signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec;
-        if(l < 0) {
-            t1.tv_sec--;
-            l = -l;
-            l %= 1000000;
-        }
-        return ((t1.tv_sec * 1000000)+l);
-    } else
-        return 0;
-#else
     return clockStamp.elapsed()*1000;
-#endif
-    return 0;
 }
 
 void QAudioOutputPrivate::reset()