svapp  1.9
AudioPulseAudioTarget.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2008 QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #ifdef HAVE_LIBPULSE
00017 
00018 #include "AudioPulseAudioTarget.h"
00019 #include "AudioCallbackPlaySource.h"
00020 
00021 #include <QMutexLocker>
00022 
00023 #include <iostream>
00024 #include <cassert>
00025 #include <cmath>
00026 
00027 #define DEBUG_AUDIO_PULSE_AUDIO_TARGET 1
00028 //#define DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY 1
00029 
00030 AudioPulseAudioTarget::AudioPulseAudioTarget(AudioCallbackPlaySource *source) :
00031     AudioCallbackPlayTarget(source),
00032     m_mutex(QMutex::Recursive),
00033     m_loop(0),
00034     m_api(0),
00035     m_context(0),
00036     m_stream(0),
00037     m_loopThread(0),
00038     m_bufferSize(0),
00039     m_sampleRate(0),
00040     m_latency(0),
00041     m_done(false)
00042 {
00043 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
00044     cerr << "AudioPulseAudioTarget: Initialising for PulseAudio" << endl;
00045 #endif
00046 
00047     m_loop = pa_mainloop_new();
00048     if (!m_loop) {
00049         cerr << "ERROR: AudioPulseAudioTarget: Failed to create main loop" << endl;
00050         return;
00051     }
00052 
00053     m_api = pa_mainloop_get_api(m_loop);
00054 
00056 
00057     m_bufferSize = 20480;
00058     m_sampleRate = 44100;
00059     if (m_source && (m_source->getSourceSampleRate() != 0)) {
00060         m_sampleRate = m_source->getSourceSampleRate();
00061     }
00062     m_spec.rate = m_sampleRate;
00063     m_spec.channels = 2;
00064     m_spec.format = PA_SAMPLE_FLOAT32NE;
00065 
00066 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
00067     cerr << "AudioPulseAudioTarget: Creating context" << endl;
00068 #endif
00069 
00070     m_context = pa_context_new(m_api, source->getClientName().toLocal8Bit().data());
00071     if (!m_context) {
00072         cerr << "ERROR: AudioPulseAudioTarget: Failed to create context object" << endl;
00073         return;
00074     }
00075 
00076     pa_context_set_state_callback(m_context, contextStateChangedStatic, this);
00077 
00078 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
00079     cerr << "AudioPulseAudioTarget: Connecting to default server..." << endl;
00080 #endif
00081 
00082     pa_context_connect(m_context, 0, // default server
00083                        (pa_context_flags_t)PA_CONTEXT_NOAUTOSPAWN, 0);
00084 
00085 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
00086     cerr << "AudioPulseAudioTarget: Starting main loop" << endl;
00087 #endif
00088 
00089     m_loopThread = new MainLoopThread(m_loop);
00090     m_loopThread->start();
00091 
00092 #ifdef DEBUG_PULSE_AUDIO_TARGET
00093     cerr << "AudioPulseAudioTarget: initialised OK" << endl;
00094 #endif
00095 }
00096 
00097 AudioPulseAudioTarget::~AudioPulseAudioTarget()
00098 {
00099     SVDEBUG << "AudioPulseAudioTarget::~AudioPulseAudioTarget()" << endl;
00100 
00101     if (m_source) {
00102         m_source->setTarget(0, m_bufferSize);
00103     }
00104 
00105     shutdown();
00106 
00107     QMutexLocker locker(&m_mutex);
00108 
00109     if (m_stream) pa_stream_unref(m_stream);
00110 
00111     if (m_context) pa_context_unref(m_context);
00112 
00113     if (m_loop) {
00114         pa_signal_done();
00115         pa_mainloop_free(m_loop);
00116     }
00117 
00118     m_stream = 0;
00119     m_context = 0;
00120     m_loop = 0;
00121 
00122     SVDEBUG << "AudioPulseAudioTarget::~AudioPulseAudioTarget() done" << endl;
00123 }
00124 
00125 void 
00126 AudioPulseAudioTarget::shutdown()
00127 {
00128     m_done = true;
00129 }
00130 
00131 bool
00132 AudioPulseAudioTarget::isOK() const
00133 {
00134     return (m_context != 0);
00135 }
00136 
00137 double
00138 AudioPulseAudioTarget::getCurrentTime() const
00139 {
00140     if (!m_stream) return 0.0;
00141     
00142     pa_usec_t usec = 0;
00143     pa_stream_get_time(m_stream, &usec);
00144     return usec / 1000000.f;
00145 }
00146 
00147 void
00148 AudioPulseAudioTarget::sourceModelReplaced()
00149 {
00150     m_source->setTargetSampleRate(m_sampleRate);
00151 }
00152 
00153 void
00154 AudioPulseAudioTarget::streamWriteStatic(pa_stream *stream,
00155                                          size_t length,
00156                                          void *data)
00157 {
00158     AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
00159     
00160     assert(stream == target->m_stream);
00161 
00162     target->streamWrite(length);
00163 }
00164 
00165 void
00166 AudioPulseAudioTarget::streamWrite(int requested)
00167 {
00168 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
00169     cout << "AudioPulseAudioTarget::streamWrite(" << requested << ")" << endl;
00170 #endif
00171     if (m_done) return;
00172 
00173     QMutexLocker locker(&m_mutex);
00174 
00175     pa_usec_t latency = 0;
00176     int negative = 0;
00177     if (!pa_stream_get_latency(m_stream, &latency, &negative)) {
00178         int latframes = (latency / 1000000.f) * float(m_sampleRate);
00179         if (latframes > 0) m_source->setTargetPlayLatency(latframes);
00180     }
00181 
00182     static float *output = 0;
00183     static float **tmpbuf = 0;
00184     static int tmpbufch = 0;
00185     static int tmpbufsz = 0;
00186 
00187     int sourceChannels = m_source->getSourceChannelCount();
00188 
00189     // Because we offer pan, we always want at least 2 channels
00190     if (sourceChannels < 2) sourceChannels = 2;
00191 
00192     int nframes = requested / (sourceChannels * sizeof(float));
00193 
00194     if (nframes > m_bufferSize) {
00195         cerr << "WARNING: AudioPulseAudioTarget::streamWrite: nframes " << nframes << " > m_bufferSize " << m_bufferSize << endl;
00196     }
00197 
00198 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
00199     cout << "AudioPulseAudioTarget::streamWrite: nframes = " << nframes << endl;
00200 #endif
00201 
00202     if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < nframes) {
00203 
00204         if (tmpbuf) {
00205             for (int i = 0; i < tmpbufch; ++i) {
00206                 delete[] tmpbuf[i];
00207             }
00208             delete[] tmpbuf;
00209         }
00210 
00211         if (output) {
00212             delete[] output;
00213         }
00214 
00215         tmpbufch = sourceChannels;
00216         tmpbufsz = nframes;
00217         tmpbuf = new float *[tmpbufch];
00218 
00219         for (int i = 0; i < tmpbufch; ++i) {
00220             tmpbuf[i] = new float[tmpbufsz];
00221         }
00222 
00223         output = new float[tmpbufsz * tmpbufch];
00224     }
00225         
00226     int received = m_source->getSourceSamples(nframes, tmpbuf);
00227 
00228 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
00229     cerr << "requested " << nframes << ", received " << received << endl;
00230 
00231     if (received < nframes) {
00232         cerr << "*** WARNING: Wrong number of frames received" << endl;
00233     }
00234 #endif
00235 
00236     float peakLeft = 0.0, peakRight = 0.0;
00237 
00238     for (int ch = 0; ch < 2; ++ch) {
00239         
00240         float peak = 0.0;
00241 
00242         // PulseAudio samples are interleaved
00243         for (int i = 0; i < nframes; ++i) {
00244             if (i < received) {
00245                 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
00246                 float sample = fabsf(output[i * 2 + ch]);
00247                 if (sample > peak) peak = sample;
00248             } else {
00249                 output[i * 2 + ch] = 0;
00250             }
00251         }
00252 
00253         if (ch == 0) peakLeft = peak;
00254         if (ch == 1) peakRight = peak;
00255     }
00256 
00257 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
00258     SVDEBUG << "calling pa_stream_write with "
00259               << nframes * tmpbufch * sizeof(float) << " bytes" << endl;
00260 #endif
00261 
00262     pa_stream_write(m_stream, output, nframes * tmpbufch * sizeof(float),
00263                     0, 0, PA_SEEK_RELATIVE);
00264 
00265     m_source->setOutputLevels(peakLeft, peakRight);
00266 
00267     return;
00268 }
00269 
00270 void
00271 AudioPulseAudioTarget::streamStateChangedStatic(pa_stream *stream,
00272                                                 void *data)
00273 {
00274     AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
00275     
00276     assert(stream == target->m_stream);
00277 
00278     target->streamStateChanged();
00279 }
00280 
00281 void
00282 AudioPulseAudioTarget::streamStateChanged()
00283 {
00284 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
00285     SVDEBUG << "AudioPulseAudioTarget::streamStateChanged" << endl;
00286 #endif
00287     QMutexLocker locker(&m_mutex);
00288 
00289     switch (pa_stream_get_state(m_stream)) {
00290 
00291     case PA_STREAM_UNCONNECTED:
00292     case PA_STREAM_CREATING:
00293     case PA_STREAM_TERMINATED:
00294         break;
00295 
00296     case PA_STREAM_READY:
00297     {
00298         SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: Ready" << endl;
00299         
00300         pa_usec_t latency = 0;
00301         int negative = 0;
00302         if (pa_stream_get_latency(m_stream, &latency, &negative)) {
00303             cerr << "AudioPulseAudioTarget::streamStateChanged: Failed to query latency" << endl;
00304         }
00305         cerr << "Latency = " << latency << " usec" << endl;
00306         int latframes = (latency / 1000000.f) * float(m_sampleRate);
00307         cerr << "that's " << latframes << " frames" << endl;
00308 
00309         const pa_buffer_attr *attr;
00310         if (!(attr = pa_stream_get_buffer_attr(m_stream))) {
00311             SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: Cannot query stream buffer attributes" << endl;
00312             m_source->setTarget(this, m_bufferSize);
00313             m_source->setTargetSampleRate(m_sampleRate);
00314             if (latframes != 0) m_source->setTargetPlayLatency(latframes);
00315         } else {
00316             int targetLength = attr->tlength;
00317             SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: stream target length = " << targetLength << endl;
00318             m_source->setTarget(this, targetLength);
00319             m_source->setTargetSampleRate(m_sampleRate);
00320             if (latframes == 0) latframes = targetLength;
00321             cerr << "latency = " << latframes << endl;
00322             m_source->setTargetPlayLatency(latframes);
00323         }
00324     }
00325     break;
00326     
00327     case PA_STREAM_FAILED:
00328     default:
00329         cerr << "AudioPulseAudioTarget::streamStateChanged: Error: "
00330              << pa_strerror(pa_context_errno(m_context)) << endl;
00332         break;
00333     }
00334 }
00335 
00336 void
00337 AudioPulseAudioTarget::contextStateChangedStatic(pa_context *context,
00338                                                  void *data)
00339 {
00340     AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
00341     
00342     assert(context == target->m_context);
00343 
00344     target->contextStateChanged();
00345 }
00346 
00347 void
00348 AudioPulseAudioTarget::contextStateChanged()
00349 {
00350 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
00351     SVDEBUG << "AudioPulseAudioTarget::contextStateChanged" << endl;
00352 #endif
00353     QMutexLocker locker(&m_mutex);
00354 
00355     switch (pa_context_get_state(m_context)) {
00356 
00357         case PA_CONTEXT_UNCONNECTED:
00358         case PA_CONTEXT_CONNECTING:
00359         case PA_CONTEXT_AUTHORIZING:
00360         case PA_CONTEXT_SETTING_NAME:
00361             break;
00362 
00363         case PA_CONTEXT_READY:
00364             SVDEBUG << "AudioPulseAudioTarget::contextStateChanged: Ready"
00365                       << endl;
00366 
00367             m_stream = pa_stream_new(m_context, "stream", &m_spec, 0);
00368             assert(m_stream); 
00369             
00370             pa_stream_set_state_callback(m_stream, streamStateChangedStatic, this);
00371             pa_stream_set_write_callback(m_stream, streamWriteStatic, this);
00372             pa_stream_set_overflow_callback(m_stream, streamOverflowStatic, this);
00373             pa_stream_set_underflow_callback(m_stream, streamUnderflowStatic, this);
00374             if (pa_stream_connect_playback
00375                 (m_stream, 0, 0,
00376                  pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING |
00377                                    PA_STREAM_AUTO_TIMING_UPDATE),
00378                  0, 0)) { //??? return value
00379                 cerr << "AudioPulseAudioTarget: Failed to connect playback stream" << endl;
00380             }
00381 
00382             break;
00383 
00384         case PA_CONTEXT_TERMINATED:
00385             SVDEBUG << "AudioPulseAudioTarget::contextStateChanged: Terminated" << endl;
00387             break;
00388 
00389         case PA_CONTEXT_FAILED:
00390         default:
00391             cerr << "AudioPulseAudioTarget::contextStateChanged: Error: "
00392                       << pa_strerror(pa_context_errno(m_context)) << endl;
00394             break;
00395     }
00396 }
00397 
00398 void
00399 AudioPulseAudioTarget::streamOverflowStatic(pa_stream *, void *)
00400 {
00401     SVDEBUG << "AudioPulseAudioTarget::streamOverflowStatic: Overflow!" << endl;
00402 }
00403 
00404 void
00405 AudioPulseAudioTarget::streamUnderflowStatic(pa_stream *, void *data)
00406 {
00407     SVDEBUG << "AudioPulseAudioTarget::streamUnderflowStatic: Underflow!" << endl;
00408     AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
00409     if (target && target->m_source) {
00410         target->m_source->audioProcessingOverload();
00411     }
00412 }
00413 
00414 #endif /* HAVE_PULSEAUDIO */
00415