annotate audioio/AudioPulseAudioTarget.cpp @ 366:0876ea394902 warnfix_no_size_t

Remove size_t's, fix compiler warnings
author Chris Cannam
date Tue, 17 Jun 2014 16:23:06 +0100
parents 068235cf5bf7
children 53fee450891e
rev   line source
Chris@117 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@117 2
Chris@117 3 /*
Chris@117 4 Sonic Visualiser
Chris@117 5 An audio file viewer and annotation editor.
Chris@117 6 Centre for Digital Music, Queen Mary, University of London.
Chris@117 7 This file copyright 2008 QMUL.
Chris@117 8
Chris@117 9 This program is free software; you can redistribute it and/or
Chris@117 10 modify it under the terms of the GNU General Public License as
Chris@117 11 published by the Free Software Foundation; either version 2 of the
Chris@117 12 License, or (at your option) any later version. See the file
Chris@117 13 COPYING included with this distribution for more information.
Chris@117 14 */
Chris@117 15
Chris@117 16 #ifdef HAVE_LIBPULSE
Chris@117 17
Chris@117 18 #include "AudioPulseAudioTarget.h"
Chris@117 19 #include "AudioCallbackPlaySource.h"
Chris@117 20
Chris@117 21 #include <QMutexLocker>
Chris@117 22
Chris@117 23 #include <iostream>
Chris@117 24 #include <cassert>
Chris@117 25 #include <cmath>
Chris@117 26
Chris@195 27 #define DEBUG_AUDIO_PULSE_AUDIO_TARGET 1
Chris@195 28 //#define DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY 1
Chris@117 29
Chris@117 30 AudioPulseAudioTarget::AudioPulseAudioTarget(AudioCallbackPlaySource *source) :
Chris@117 31 AudioCallbackPlayTarget(source),
Chris@117 32 m_mutex(QMutex::Recursive),
Chris@117 33 m_loop(0),
Chris@117 34 m_api(0),
Chris@117 35 m_context(0),
Chris@117 36 m_stream(0),
Chris@117 37 m_loopThread(0),
Chris@117 38 m_bufferSize(0),
Chris@117 39 m_sampleRate(0),
Chris@117 40 m_latency(0),
Chris@117 41 m_done(false)
Chris@117 42 {
Chris@117 43 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
Chris@293 44 cerr << "AudioPulseAudioTarget: Initialising for PulseAudio" << endl;
Chris@117 45 #endif
Chris@117 46
Chris@117 47 m_loop = pa_mainloop_new();
Chris@117 48 if (!m_loop) {
Chris@293 49 cerr << "ERROR: AudioPulseAudioTarget: Failed to create main loop" << endl;
Chris@117 50 return;
Chris@117 51 }
Chris@117 52
Chris@117 53 m_api = pa_mainloop_get_api(m_loop);
Chris@117 54
Chris@117 55 //!!! handle signals how?
Chris@117 56
Chris@118 57 m_bufferSize = 20480;
Chris@117 58 m_sampleRate = 44100;
Chris@117 59 if (m_source && (m_source->getSourceSampleRate() != 0)) {
Chris@117 60 m_sampleRate = m_source->getSourceSampleRate();
Chris@117 61 }
Chris@117 62 m_spec.rate = m_sampleRate;
Chris@117 63 m_spec.channels = 2;
Chris@117 64 m_spec.format = PA_SAMPLE_FLOAT32NE;
Chris@117 65
Chris@195 66 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
Chris@293 67 cerr << "AudioPulseAudioTarget: Creating context" << endl;
Chris@195 68 #endif
Chris@195 69
Chris@117 70 m_context = pa_context_new(m_api, source->getClientName().toLocal8Bit().data());
Chris@117 71 if (!m_context) {
Chris@293 72 cerr << "ERROR: AudioPulseAudioTarget: Failed to create context object" << endl;
Chris@117 73 return;
Chris@117 74 }
Chris@117 75
Chris@117 76 pa_context_set_state_callback(m_context, contextStateChangedStatic, this);
Chris@117 77
Chris@195 78 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
Chris@293 79 cerr << "AudioPulseAudioTarget: Connecting to default server..." << endl;
Chris@195 80 #endif
Chris@195 81
Chris@195 82 pa_context_connect(m_context, 0, // default server
Chris@195 83 (pa_context_flags_t)PA_CONTEXT_NOAUTOSPAWN, 0);
Chris@195 84
Chris@195 85 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
Chris@293 86 cerr << "AudioPulseAudioTarget: Starting main loop" << endl;
Chris@195 87 #endif
Chris@117 88
Chris@117 89 m_loopThread = new MainLoopThread(m_loop);
Chris@117 90 m_loopThread->start();
Chris@117 91
Chris@117 92 #ifdef DEBUG_PULSE_AUDIO_TARGET
Chris@293 93 cerr << "AudioPulseAudioTarget: initialised OK" << endl;
Chris@117 94 #endif
Chris@117 95 }
Chris@117 96
Chris@117 97 AudioPulseAudioTarget::~AudioPulseAudioTarget()
Chris@117 98 {
Chris@233 99 SVDEBUG << "AudioPulseAudioTarget::~AudioPulseAudioTarget()" << endl;
Chris@117 100
Chris@117 101 if (m_source) {
Chris@117 102 m_source->setTarget(0, m_bufferSize);
Chris@117 103 }
Chris@117 104
Chris@117 105 shutdown();
Chris@117 106
Chris@117 107 QMutexLocker locker(&m_mutex);
Chris@117 108
Chris@117 109 if (m_stream) pa_stream_unref(m_stream);
Chris@117 110
Chris@117 111 if (m_context) pa_context_unref(m_context);
Chris@117 112
Chris@117 113 if (m_loop) {
Chris@117 114 pa_signal_done();
Chris@117 115 pa_mainloop_free(m_loop);
Chris@117 116 }
Chris@117 117
Chris@117 118 m_stream = 0;
Chris@117 119 m_context = 0;
Chris@117 120 m_loop = 0;
Chris@117 121
Chris@233 122 SVDEBUG << "AudioPulseAudioTarget::~AudioPulseAudioTarget() done" << endl;
Chris@117 123 }
Chris@117 124
Chris@117 125 void
Chris@117 126 AudioPulseAudioTarget::shutdown()
Chris@117 127 {
Chris@117 128 m_done = true;
Chris@117 129 }
Chris@117 130
Chris@117 131 bool
Chris@117 132 AudioPulseAudioTarget::isOK() const
Chris@117 133 {
Chris@117 134 return (m_context != 0);
Chris@117 135 }
Chris@117 136
Chris@117 137 double
Chris@117 138 AudioPulseAudioTarget::getCurrentTime() const
Chris@117 139 {
Chris@117 140 if (!m_stream) return 0.0;
Chris@118 141
Chris@118 142 pa_usec_t usec = 0;
Chris@118 143 pa_stream_get_time(m_stream, &usec);
Chris@118 144 return usec / 1000000.f;
Chris@117 145 }
Chris@117 146
Chris@117 147 void
Chris@117 148 AudioPulseAudioTarget::sourceModelReplaced()
Chris@117 149 {
Chris@117 150 m_source->setTargetSampleRate(m_sampleRate);
Chris@117 151 }
Chris@117 152
Chris@117 153 void
Chris@117 154 AudioPulseAudioTarget::streamWriteStatic(pa_stream *stream,
Chris@117 155 size_t length,
Chris@117 156 void *data)
Chris@117 157 {
Chris@117 158 AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
Chris@117 159
Chris@117 160 assert(stream == target->m_stream);
Chris@117 161
Chris@117 162 target->streamWrite(length);
Chris@117 163 }
Chris@117 164
Chris@117 165 void
Chris@366 166 AudioPulseAudioTarget::streamWrite(int requested)
Chris@117 167 {
Chris@195 168 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
Chris@293 169 cout << "AudioPulseAudioTarget::streamWrite(" << requested << ")" << endl;
Chris@117 170 #endif
Chris@117 171 if (m_done) return;
Chris@117 172
Chris@117 173 QMutexLocker locker(&m_mutex);
Chris@117 174
Chris@193 175 pa_usec_t latency = 0;
Chris@193 176 int negative = 0;
Chris@193 177 if (!pa_stream_get_latency(m_stream, &latency, &negative)) {
Chris@193 178 int latframes = (latency / 1000000.f) * float(m_sampleRate);
Chris@193 179 if (latframes > 0) m_source->setTargetPlayLatency(latframes);
Chris@118 180 }
Chris@118 181
Chris@117 182 static float *output = 0;
Chris@117 183 static float **tmpbuf = 0;
Chris@366 184 static int tmpbufch = 0;
Chris@366 185 static int tmpbufsz = 0;
Chris@117 186
Chris@366 187 int sourceChannels = m_source->getSourceChannelCount();
Chris@117 188
Chris@117 189 // Because we offer pan, we always want at least 2 channels
Chris@117 190 if (sourceChannels < 2) sourceChannels = 2;
Chris@117 191
Chris@366 192 int nframes = requested / (sourceChannels * sizeof(float));
Chris@120 193
Chris@120 194 if (nframes > m_bufferSize) {
Chris@293 195 cerr << "WARNING: AudioPulseAudioTarget::streamWrite: nframes " << nframes << " > m_bufferSize " << m_bufferSize << endl;
Chris@120 196 }
Chris@120 197
Chris@195 198 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
Chris@293 199 cout << "AudioPulseAudioTarget::streamWrite: nframes = " << nframes << endl;
Chris@120 200 #endif
Chris@120 201
Chris@117 202 if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < nframes) {
Chris@117 203
Chris@117 204 if (tmpbuf) {
Chris@366 205 for (int i = 0; i < tmpbufch; ++i) {
Chris@117 206 delete[] tmpbuf[i];
Chris@117 207 }
Chris@117 208 delete[] tmpbuf;
Chris@117 209 }
Chris@117 210
Chris@117 211 if (output) {
Chris@117 212 delete[] output;
Chris@117 213 }
Chris@117 214
Chris@117 215 tmpbufch = sourceChannels;
Chris@117 216 tmpbufsz = nframes;
Chris@117 217 tmpbuf = new float *[tmpbufch];
Chris@117 218
Chris@366 219 for (int i = 0; i < tmpbufch; ++i) {
Chris@117 220 tmpbuf[i] = new float[tmpbufsz];
Chris@117 221 }
Chris@117 222
Chris@117 223 output = new float[tmpbufsz * tmpbufch];
Chris@117 224 }
Chris@117 225
Chris@366 226 int received = m_source->getSourceSamples(nframes, tmpbuf);
Chris@117 227
Chris@195 228 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
Chris@293 229 cerr << "requested " << nframes << ", received " << received << endl;
Chris@120 230
Chris@119 231 if (received < nframes) {
Chris@293 232 cerr << "*** WARNING: Wrong number of frames received" << endl;
Chris@119 233 }
Chris@120 234 #endif
Chris@119 235
Chris@117 236 float peakLeft = 0.0, peakRight = 0.0;
Chris@117 237
Chris@366 238 for (int ch = 0; ch < 2; ++ch) {
Chris@117 239
Chris@117 240 float peak = 0.0;
Chris@117 241
Chris@117 242 if (ch < sourceChannels) {
Chris@117 243
Chris@117 244 // PulseAudio samples are interleaved
Chris@366 245 for (int i = 0; i < nframes; ++i) {
Chris@117 246 if (i < received) {
Chris@117 247 output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
Chris@117 248 float sample = fabsf(output[i * 2 + ch]);
Chris@117 249 if (sample > peak) peak = sample;
Chris@117 250 } else {
Chris@117 251 output[i * 2 + ch] = 0;
Chris@117 252 }
Chris@117 253 }
Chris@117 254
Chris@117 255 } else if (ch == 1 && sourceChannels == 1) {
Chris@117 256
Chris@366 257 for (int i = 0; i < nframes; ++i) {
Chris@117 258 if (i < received) {
Chris@117 259 output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
Chris@117 260 float sample = fabsf(output[i * 2 + ch]);
Chris@117 261 if (sample > peak) peak = sample;
Chris@117 262 } else {
Chris@117 263 output[i * 2 + ch] = 0;
Chris@117 264 }
Chris@117 265 }
Chris@117 266
Chris@117 267 } else {
Chris@366 268 for (int i = 0; i < nframes; ++i) {
Chris@117 269 output[i * 2 + ch] = 0;
Chris@117 270 }
Chris@117 271 }
Chris@117 272
Chris@117 273 if (ch == 0) peakLeft = peak;
Chris@117 274 if (ch > 0 || sourceChannels == 1) peakRight = peak;
Chris@117 275 }
Chris@117 276
Chris@195 277 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
Chris@233 278 SVDEBUG << "calling pa_stream_write with "
Chris@229 279 << nframes * tmpbufch * sizeof(float) << " bytes" << endl;
Chris@120 280 #endif
Chris@120 281
Chris@117 282 pa_stream_write(m_stream, output, nframes * tmpbufch * sizeof(float),
Chris@117 283 0, 0, PA_SEEK_RELATIVE);
Chris@117 284
Chris@117 285 m_source->setOutputLevels(peakLeft, peakRight);
Chris@117 286
Chris@117 287 return;
Chris@117 288 }
Chris@117 289
Chris@117 290 void
Chris@117 291 AudioPulseAudioTarget::streamStateChangedStatic(pa_stream *stream,
Chris@117 292 void *data)
Chris@117 293 {
Chris@117 294 AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
Chris@117 295
Chris@117 296 assert(stream == target->m_stream);
Chris@117 297
Chris@117 298 target->streamStateChanged();
Chris@117 299 }
Chris@117 300
Chris@117 301 void
Chris@117 302 AudioPulseAudioTarget::streamStateChanged()
Chris@117 303 {
Chris@117 304 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
Chris@233 305 SVDEBUG << "AudioPulseAudioTarget::streamStateChanged" << endl;
Chris@117 306 #endif
Chris@117 307 QMutexLocker locker(&m_mutex);
Chris@117 308
Chris@117 309 switch (pa_stream_get_state(m_stream)) {
Chris@117 310
Chris@366 311 case PA_STREAM_UNCONNECTED:
Chris@366 312 case PA_STREAM_CREATING:
Chris@366 313 case PA_STREAM_TERMINATED:
Chris@366 314 break;
Chris@117 315
Chris@366 316 case PA_STREAM_READY:
Chris@366 317 {
Chris@366 318 SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: Ready" << endl;
Chris@366 319
Chris@366 320 pa_usec_t latency = 0;
Chris@366 321 int negative = 0;
Chris@366 322 if (pa_stream_get_latency(m_stream, &latency, &negative)) {
Chris@366 323 cerr << "AudioPulseAudioTarget::streamStateChanged: Failed to query latency" << endl;
Chris@366 324 }
Chris@366 325 cerr << "Latency = " << latency << " usec" << endl;
Chris@366 326 int latframes = (latency / 1000000.f) * float(m_sampleRate);
Chris@366 327 cerr << "that's " << latframes << " frames" << endl;
Chris@191 328
Chris@366 329 const pa_buffer_attr *attr;
Chris@366 330 if (!(attr = pa_stream_get_buffer_attr(m_stream))) {
Chris@366 331 SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: Cannot query stream buffer attributes" << endl;
Chris@366 332 m_source->setTarget(this, m_bufferSize);
Chris@366 333 m_source->setTargetSampleRate(m_sampleRate);
Chris@366 334 if (latframes != 0) m_source->setTargetPlayLatency(latframes);
Chris@366 335 } else {
Chris@366 336 int targetLength = attr->tlength;
Chris@366 337 SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: stream target length = " << targetLength << endl;
Chris@366 338 m_source->setTarget(this, targetLength);
Chris@366 339 m_source->setTargetSampleRate(m_sampleRate);
Chris@366 340 if (latframes == 0) latframes = targetLength;
Chris@366 341 cerr << "latency = " << latframes << endl;
Chris@366 342 m_source->setTargetPlayLatency(latframes);
Chris@191 343 }
Chris@366 344 }
Chris@366 345 break;
Chris@366 346
Chris@366 347 case PA_STREAM_FAILED:
Chris@366 348 default:
Chris@366 349 cerr << "AudioPulseAudioTarget::streamStateChanged: Error: "
Chris@366 350 << pa_strerror(pa_context_errno(m_context)) << endl;
Chris@366 351 //!!! do something...
Chris@366 352 break;
Chris@117 353 }
Chris@117 354 }
Chris@117 355
Chris@117 356 void
Chris@117 357 AudioPulseAudioTarget::contextStateChangedStatic(pa_context *context,
Chris@117 358 void *data)
Chris@117 359 {
Chris@117 360 AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
Chris@117 361
Chris@117 362 assert(context == target->m_context);
Chris@117 363
Chris@117 364 target->contextStateChanged();
Chris@117 365 }
Chris@117 366
Chris@117 367 void
Chris@117 368 AudioPulseAudioTarget::contextStateChanged()
Chris@117 369 {
Chris@117 370 #ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
Chris@233 371 SVDEBUG << "AudioPulseAudioTarget::contextStateChanged" << endl;
Chris@117 372 #endif
Chris@117 373 QMutexLocker locker(&m_mutex);
Chris@117 374
Chris@117 375 switch (pa_context_get_state(m_context)) {
Chris@117 376
Chris@366 377 case PA_CONTEXT_UNCONNECTED:
Chris@117 378 case PA_CONTEXT_CONNECTING:
Chris@117 379 case PA_CONTEXT_AUTHORIZING:
Chris@117 380 case PA_CONTEXT_SETTING_NAME:
Chris@117 381 break;
Chris@117 382
Chris@117 383 case PA_CONTEXT_READY:
Chris@233 384 SVDEBUG << "AudioPulseAudioTarget::contextStateChanged: Ready"
Chris@229 385 << endl;
Chris@117 386
Chris@117 387 m_stream = pa_stream_new(m_context, "stream", &m_spec, 0);
Chris@117 388 assert(m_stream); //!!!
Chris@117 389
Chris@117 390 pa_stream_set_state_callback(m_stream, streamStateChangedStatic, this);
Chris@117 391 pa_stream_set_write_callback(m_stream, streamWriteStatic, this);
Chris@120 392 pa_stream_set_overflow_callback(m_stream, streamOverflowStatic, this);
Chris@120 393 pa_stream_set_underflow_callback(m_stream, streamUnderflowStatic, this);
Chris@118 394 if (pa_stream_connect_playback
Chris@118 395 (m_stream, 0, 0,
Chris@118 396 pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING |
Chris@118 397 PA_STREAM_AUTO_TIMING_UPDATE),
Chris@118 398 0, 0)) { //??? return value
Chris@293 399 cerr << "AudioPulseAudioTarget: Failed to connect playback stream" << endl;
Chris@117 400 }
Chris@117 401
Chris@117 402 break;
Chris@117 403
Chris@117 404 case PA_CONTEXT_TERMINATED:
Chris@233 405 SVDEBUG << "AudioPulseAudioTarget::contextStateChanged: Terminated" << endl;
Chris@117 406 //!!! do something...
Chris@117 407 break;
Chris@117 408
Chris@117 409 case PA_CONTEXT_FAILED:
Chris@117 410 default:
Chris@293 411 cerr << "AudioPulseAudioTarget::contextStateChanged: Error: "
Chris@293 412 << pa_strerror(pa_context_errno(m_context)) << endl;
Chris@117 413 //!!! do something...
Chris@117 414 break;
Chris@117 415 }
Chris@117 416 }
Chris@117 417
Chris@120 418 void
Chris@120 419 AudioPulseAudioTarget::streamOverflowStatic(pa_stream *, void *)
Chris@120 420 {
Chris@233 421 SVDEBUG << "AudioPulseAudioTarget::streamOverflowStatic: Overflow!" << endl;
Chris@120 422 }
Chris@120 423
Chris@120 424 void
Chris@130 425 AudioPulseAudioTarget::streamUnderflowStatic(pa_stream *, void *data)
Chris@120 426 {
Chris@233 427 SVDEBUG << "AudioPulseAudioTarget::streamUnderflowStatic: Underflow!" << endl;
Chris@130 428 AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
Chris@130 429 if (target && target->m_source) {
Chris@130 430 target->m_source->audioProcessingOverload();
Chris@130 431 }
Chris@120 432 }
Chris@120 433
Chris@117 434 #endif /* HAVE_PULSEAUDIO */
Chris@117 435