svapp
1.9
|
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