annotate audio/AudioCallbackPlaySource.h @ 570:6f54789f3127 3.0-integration

Fix race condition in first-time recording, where adding the recording wave model would prompt the audio play source to note that its channel count had increased (from 0 to, say, 2) and thus to cause the audio device to be reopened, stopping recording. Fix is to make this only happen if channel count increases beyond that of the device, which shouldn't happen in the recording case
author Chris Cannam
date Wed, 04 Jan 2017 11:48:03 +0000
parents fc70aa31d8c5
children b3c35447ef31
rev   line source
Chris@43 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@43 2
Chris@43 3 /*
Chris@43 4 Sonic Visualiser
Chris@43 5 An audio file viewer and annotation editor.
Chris@43 6 Centre for Digital Music, Queen Mary, University of London.
Chris@43 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@43 8
Chris@43 9 This program is free software; you can redistribute it and/or
Chris@43 10 modify it under the terms of the GNU General Public License as
Chris@43 11 published by the Free Software Foundation; either version 2 of the
Chris@43 12 License, or (at your option) any later version. See the file
Chris@43 13 COPYING included with this distribution for more information.
Chris@43 14 */
Chris@43 15
Chris@475 16 #ifndef AUDIO_CALLBACK_PLAY_SOURCE_H
Chris@475 17 #define AUDIO_CALLBACK_PLAY_SOURCE_H
Chris@43 18
Chris@43 19 #include "base/RingBuffer.h"
Chris@43 20 #include "base/AudioPlaySource.h"
Chris@43 21 #include "base/PropertyContainer.h"
Chris@43 22 #include "base/Scavenger.h"
Chris@43 23
Chris@468 24 #include <bqaudioio/ApplicationPlaybackSource.h>
Chris@468 25
Chris@43 26 #include <QObject>
Chris@43 27 #include <QMutex>
Chris@43 28 #include <QWaitCondition>
Chris@43 29
Chris@43 30 #include "base/Thread.h"
Chris@93 31 #include "base/RealTime.h"
Chris@43 32
Chris@43 33 #include <samplerate.h>
Chris@43 34
Chris@43 35 #include <set>
Chris@43 36 #include <map>
Chris@43 37
Chris@91 38 namespace RubberBand {
Chris@91 39 class RubberBandStretcher;
Chris@91 40 }
Chris@62 41
Chris@544 42 namespace breakfastquay {
Chris@551 43 class ResamplerWrapper;
Chris@544 44 }
Chris@544 45
Chris@43 46 class Model;
Chris@105 47 class ViewManagerBase;
Chris@43 48 class AudioGenerator;
Chris@43 49 class PlayParameters;
Chris@43 50 class RealTimePluginInstance;
Chris@91 51 class AudioCallbackPlayTarget;
Chris@43 52
Chris@43 53 /**
Chris@43 54 * AudioCallbackPlaySource manages audio data supply to callback-based
Chris@43 55 * audio APIs such as JACK or CoreAudio. It maintains one ring buffer
Chris@43 56 * per channel, filled during playback by a non-realtime thread, and
Chris@43 57 * provides a method for a realtime thread to pick up the latest
Chris@43 58 * available sample data from these buffers.
Chris@43 59 */
Chris@238 60 class AudioCallbackPlaySource : public QObject,
Chris@468 61 public AudioPlaySource,
Chris@468 62 public breakfastquay::ApplicationPlaybackSource
Chris@43 63 {
Chris@43 64 Q_OBJECT
Chris@43 65
Chris@43 66 public:
Chris@105 67 AudioCallbackPlaySource(ViewManagerBase *, QString clientName);
Chris@43 68 virtual ~AudioCallbackPlaySource();
Chris@43 69
Chris@43 70 /**
Chris@43 71 * Add a data model to be played from. The source can mix
Chris@43 72 * playback from a number of sources including dense and sparse
Chris@43 73 * models. The models must match in sample rate, but they don't
Chris@43 74 * have to have identical numbers of channels.
Chris@43 75 */
Chris@43 76 virtual void addModel(Model *model);
Chris@43 77
Chris@43 78 /**
Chris@43 79 * Remove a model.
Chris@43 80 */
Chris@43 81 virtual void removeModel(Model *model);
Chris@43 82
Chris@43 83 /**
Chris@43 84 * Remove all models. (Silence will ensue.)
Chris@43 85 */
Chris@43 86 virtual void clearModels();
Chris@43 87
Chris@43 88 /**
Chris@43 89 * Start making data available in the ring buffers for playback,
Chris@43 90 * from the given frame. If playback is already under way, reseek
Chris@43 91 * to the given frame and continue.
Chris@43 92 */
Chris@559 93 virtual void play(sv_frame_t startFrame) override;
Chris@43 94
Chris@43 95 /**
Chris@43 96 * Stop playback and ensure that no more data is returned.
Chris@43 97 */
Chris@559 98 virtual void stop() override;
Chris@43 99
Chris@43 100 /**
Chris@43 101 * Return whether playback is currently supposed to be happening.
Chris@43 102 */
Chris@559 103 virtual bool isPlaying() const override { return m_playing; }
Chris@43 104
Chris@43 105 /**
Chris@43 106 * Return the frame number that is currently expected to be coming
Chris@43 107 * out of the speakers. (i.e. compensating for playback latency.)
Chris@43 108 */
Chris@559 109 virtual sv_frame_t getCurrentPlayingFrame() override;
Chris@93 110
Chris@93 111 /**
Chris@93 112 * Return the last frame that would come out of the speakers if we
Chris@93 113 * stopped playback right now.
Chris@93 114 */
Chris@434 115 virtual sv_frame_t getCurrentBufferedFrame();
Chris@43 116
Chris@43 117 /**
Chris@43 118 * Return the frame at which playback is expected to end (if not looping).
Chris@43 119 */
Chris@434 120 virtual sv_frame_t getPlayEndFrame() { return m_lastModelEndFrame; }
Chris@43 121
Chris@43 122 /**
Chris@498 123 * Set the playback target.
Chris@43 124 */
Chris@468 125 virtual void setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *);
Chris@468 126
Chris@468 127 /**
Chris@551 128 * Set the resampler wrapper, if one is in use.
Chris@551 129 */
Chris@551 130 virtual void setResamplerWrapper(breakfastquay::ResamplerWrapper *);
Chris@551 131
Chris@551 132 /**
Chris@468 133 * Set the block size of the target audio device. This should be
Chris@468 134 * called by the target class.
Chris@468 135 */
Chris@559 136 virtual void setSystemPlaybackBlockSize(int blockSize) override;
Chris@43 137
Chris@43 138 /**
Chris@91 139 * Get the block size of the target audio device. This may be an
Chris@91 140 * estimate or upper bound, if the target has a variable block
Chris@91 141 * size; the source should behave itself even if this value turns
Chris@91 142 * out to be inaccurate.
Chris@43 143 */
cannam@561 144 virtual int getTargetBlockSize() const override;
Chris@43 145
Chris@43 146 /**
Chris@43 147 * Set the playback latency of the target audio device, in frames
Chris@553 148 * at the device sample rate. This is the difference between the
Chris@43 149 * frame currently "leaving the speakers" and the last frame (or
Chris@43 150 * highest last frame across all channels) requested via
Chris@43 151 * getSamples(). The default is zero.
Chris@43 152 */
Chris@559 153 virtual void setSystemPlaybackLatency(int) override;
Chris@43 154
Chris@43 155 /**
Chris@43 156 * Get the playback latency of the target audio device.
Chris@43 157 */
Chris@434 158 sv_frame_t getTargetPlayLatency() const;
Chris@43 159
Chris@43 160 /**
Chris@43 161 * Specify that the target audio device has a fixed sample rate
Chris@43 162 * (i.e. cannot accommodate arbitrary sample rates based on the
Chris@43 163 * source). If the target sets this to something other than the
Chris@43 164 * source sample rate, this class will resample automatically to
Chris@43 165 * fit.
Chris@43 166 */
Chris@559 167 virtual void setSystemPlaybackSampleRate(int) override;
Chris@43 168
Chris@43 169 /**
Chris@43 170 * Return the sample rate set by the target audio device (or the
Chris@43 171 * source sample rate if the target hasn't set one).
Chris@43 172 */
cannam@561 173 virtual sv_samplerate_t getDeviceSampleRate() const override;
Chris@43 174
Chris@43 175 /**
Chris@546 176 * Indicate how many channels the target audio device was opened
Chris@546 177 * with. Note that the target device does channel mixing in the
Chris@559 178 * case where our requested channel count does not match its, so
Chris@559 179 * long as we provide the number of channels we specified when the
Chris@559 180 * target was started in getApplicationChannelCount().
Chris@546 181 */
Chris@559 182 virtual void setSystemPlaybackChannelCount(int) override;
Chris@546 183
Chris@546 184 /**
Chris@43 185 * Set the current output levels for metering (for call from the
Chris@43 186 * target)
Chris@43 187 */
cannam@561 188 virtual void setOutputLevels(float left, float right) override;
Chris@43 189
Chris@43 190 /**
Chris@43 191 * Return the current (or thereabouts) output levels in the range
Chris@43 192 * 0.0 -> 1.0, for metering purposes.
Chris@43 193 */
cannam@561 194 virtual bool getOutputLevels(float &left, float &right) override;
Chris@43 195
Chris@43 196 /**
Chris@43 197 * Get the number of channels of audio that in the source models.
Chris@43 198 * This may safely be called from a realtime thread. Returns 0 if
Chris@43 199 * there is no source yet available.
Chris@43 200 */
Chris@366 201 int getSourceChannelCount() const;
Chris@43 202
Chris@43 203 /**
Chris@43 204 * Get the number of channels of audio that will be provided
Chris@43 205 * to the play target. This may be more than the source channel
Chris@43 206 * count: for example, a mono source will provide 2 channels
Chris@43 207 * after pan.
Chris@552 208 *
Chris@43 209 * This may safely be called from a realtime thread. Returns 0 if
Chris@43 210 * there is no source yet available.
Chris@552 211 *
Chris@552 212 * override from AudioPlaySource
Chris@43 213 */
Chris@552 214 virtual int getTargetChannelCount() const override;
Chris@43 215
Chris@43 216 /**
Chris@559 217 * Get the number of channels of audio the device is
Chris@559 218 * expecting. Equal to whatever getTargetChannelCount() was
Chris@559 219 * returning at the time the device was initialised.
Chris@559 220 */
Chris@559 221 int getDeviceChannelCount() const;
Chris@559 222
Chris@559 223 /**
Chris@468 224 * ApplicationPlaybackSource equivalent of the above.
Chris@552 225 *
Chris@552 226 * override from breakfastquay::ApplicationPlaybackSource
Chris@468 227 */
Chris@552 228 virtual int getApplicationChannelCount() const override {
Chris@468 229 return getTargetChannelCount();
Chris@468 230 }
Chris@468 231
Chris@468 232 /**
Chris@552 233 * Get the actual sample rate of the source material (the main
Chris@552 234 * model). This may safely be called from a realtime thread.
Chris@552 235 * Returns 0 if there is no source yet available.
Chris@552 236 *
Chris@552 237 * When this changes, the AudioCallbackPlaySource notifies its
Chris@552 238 * ResamplerWrapper of the new sample rate so that it can resample
Chris@552 239 * correctly on the way to the device (which is opened at a fixed
Chris@552 240 * rate, see getApplicationSampleRate).
Chris@43 241 */
Chris@552 242 virtual sv_samplerate_t getSourceSampleRate() const override;
Chris@43 243
Chris@43 244 /**
Chris@552 245 * ApplicationPlaybackSource interface method: get the sample rate
Chris@552 246 * at which the application wants the device to be opened. We
Chris@552 247 * always allow the device to open at its default rate, and then
Chris@552 248 * we resample if the audio is at a different rate. This avoids
Chris@552 249 * having to close and re-open the device to obtain consistent
Chris@552 250 * behaviour for consecutive sessions with different source rates.
Chris@468 251 */
Chris@552 252 virtual int getApplicationSampleRate() const override {
Chris@552 253 return 0;
Chris@468 254 }
Chris@468 255
Chris@468 256 /**
Chris@43 257 * Get "count" samples (at the target sample rate) of the mixed
Chris@43 258 * audio data, in all channels. This may safely be called from a
Chris@43 259 * realtime thread.
Chris@43 260 */
Chris@559 261 virtual int getSourceSamples(float *const *buffer, int nchannels, int count) override;
Chris@43 262
Chris@43 263 /**
Chris@91 264 * Set the time stretcher factor (i.e. playback speed).
Chris@43 265 */
Chris@436 266 void setTimeStretch(double factor);
Chris@43 267
Chris@43 268 /**
Chris@43 269 * Set a single real-time plugin as a processing effect for
Chris@43 270 * auditioning during playback.
Chris@43 271 *
Chris@43 272 * The plugin must have been initialised with
Chris@43 273 * getTargetChannelCount() channels and a getTargetBlockSize()
Chris@43 274 * sample frame processing block size.
Chris@43 275 *
Chris@43 276 * This playback source takes ownership of the plugin, which will
Chris@43 277 * be deleted at some point after the following call to
Chris@107 278 * setAuditioningEffect (depending on real-time constraints).
Chris@43 279 *
Chris@43 280 * Pass a null pointer to remove the current auditioning plugin,
Chris@43 281 * if any.
Chris@43 282 */
cannam@561 283 virtual void setAuditioningEffect(Auditionable *plugin) override;
Chris@43 284
Chris@43 285 /**
Chris@43 286 * Specify that only the given set of models should be played.
Chris@43 287 */
Chris@43 288 void setSoloModelSet(std::set<Model *>s);
Chris@43 289
Chris@43 290 /**
Chris@43 291 * Specify that all models should be played as normal (if not
Chris@43 292 * muted).
Chris@43 293 */
Chris@43 294 void clearSoloModelSet();
Chris@43 295
cannam@561 296 virtual std::string getClientName() const override {
cannam@561 297 return m_clientName;
cannam@561 298 }
Chris@57 299
Chris@43 300 signals:
Chris@43 301 void playStatusChanged(bool isPlaying);
Chris@43 302
Chris@436 303 void sampleRateMismatch(sv_samplerate_t requested,
Chris@436 304 sv_samplerate_t available,
Chris@436 305 bool willResample);
Chris@43 306
Chris@570 307 void channelCountIncreased(int count); // target channel count (see getTargetChannelCount())
Chris@559 308
Chris@43 309 void audioOverloadPluginDisabled();
Chris@130 310 void audioTimeStretchMultiChannelDisabled();
Chris@43 311
Chris@158 312 void activity(QString);
Chris@158 313
Chris@43 314 public slots:
cannam@561 315 void audioProcessingOverload() override;
Chris@43 316
Chris@43 317 protected slots:
Chris@43 318 void selectionChanged();
Chris@43 319 void playLoopModeChanged();
Chris@43 320 void playSelectionModeChanged();
Chris@43 321 void playParametersChanged(PlayParameters *);
Chris@43 322 void preferenceChanged(PropertyContainer::PropertyName);
Chris@435 323 void modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame);
Chris@43 324
Chris@43 325 protected:
Chris@105 326 ViewManagerBase *m_viewManager;
Chris@57 327 AudioGenerator *m_audioGenerator;
Chris@468 328 std::string m_clientName;
Chris@43 329
Chris@43 330 class RingBufferVector : public std::vector<RingBuffer<float> *> {
Chris@43 331 public:
Chris@43 332 virtual ~RingBufferVector() {
Chris@43 333 while (!empty()) {
Chris@43 334 delete *begin();
Chris@43 335 erase(begin());
Chris@43 336 }
Chris@43 337 }
Chris@43 338 };
Chris@43 339
Chris@43 340 std::set<Model *> m_models;
Chris@43 341 RingBufferVector *m_readBuffers;
Chris@43 342 RingBufferVector *m_writeBuffers;
Chris@436 343 sv_frame_t m_readBufferFill;
Chris@436 344 sv_frame_t m_writeBufferFill;
Chris@43 345 Scavenger<RingBufferVector> m_bufferScavenger;
Chris@366 346 int m_sourceChannelCount;
Chris@436 347 sv_frame_t m_blockSize;
Chris@434 348 sv_samplerate_t m_sourceSampleRate;
Chris@553 349 sv_samplerate_t m_deviceSampleRate;
Chris@559 350 int m_deviceChannelCount;
Chris@436 351 sv_frame_t m_playLatency;
Chris@468 352 breakfastquay::SystemPlaybackTarget *m_target;
Chris@91 353 double m_lastRetrievalTimestamp;
Chris@436 354 sv_frame_t m_lastRetrievedBlockSize;
Chris@102 355 bool m_trustworthyTimestamps;
Chris@434 356 sv_frame_t m_lastCurrentFrame;
Chris@43 357 bool m_playing;
Chris@43 358 bool m_exiting;
Chris@434 359 sv_frame_t m_lastModelEndFrame;
Chris@366 360 int m_ringBufferSize;
Chris@43 361 float m_outputLeft;
Chris@43 362 float m_outputRight;
Chris@43 363 RealTimePluginInstance *m_auditioningPlugin;
Chris@43 364 bool m_auditioningPluginBypassed;
Chris@43 365 Scavenger<RealTimePluginInstance> m_pluginScavenger;
Chris@434 366 sv_frame_t m_playStartFrame;
Chris@94 367 bool m_playStartFramePassed;
Chris@94 368 RealTime m_playStartedAt;
Chris@43 369
Chris@366 370 RingBuffer<float> *getWriteRingBuffer(int c) {
Chris@366 371 if (m_writeBuffers && c < (int)m_writeBuffers->size()) {
Chris@43 372 return (*m_writeBuffers)[c];
Chris@43 373 } else {
Chris@43 374 return 0;
Chris@43 375 }
Chris@43 376 }
Chris@43 377
Chris@366 378 RingBuffer<float> *getReadRingBuffer(int c) {
Chris@43 379 RingBufferVector *rb = m_readBuffers;
Chris@366 380 if (rb && c < (int)rb->size()) {
Chris@43 381 return (*rb)[c];
Chris@43 382 } else {
Chris@43 383 return 0;
Chris@43 384 }
Chris@43 385 }
Chris@43 386
Chris@366 387 void clearRingBuffers(bool haveLock = false, int count = 0);
Chris@43 388 void unifyRingBuffers();
Chris@43 389
Chris@62 390 RubberBand::RubberBandStretcher *m_timeStretcher;
Chris@130 391 RubberBand::RubberBandStretcher *m_monoStretcher;
Chris@436 392 double m_stretchRatio;
Chris@130 393 bool m_stretchMono;
Chris@91 394
Chris@436 395 int m_stretcherInputCount;
Chris@91 396 float **m_stretcherInputs;
Chris@436 397 sv_frame_t *m_stretcherInputSizes;
Chris@43 398
Chris@43 399 // Called from fill thread, m_playing true, mutex held
Chris@43 400 // Return true if work done
Chris@43 401 bool fillBuffers();
Chris@43 402
Chris@43 403 // Called from fillBuffers. Return the number of frames written,
Chris@43 404 // which will be count or fewer. Return in the frame argument the
Chris@43 405 // new buffered frame position (which may be earlier than the
Chris@43 406 // frame argument passed in, in the case of looping).
Chris@434 407 sv_frame_t mixModels(sv_frame_t &frame, sv_frame_t count, float **buffers);
Chris@43 408
Chris@43 409 // Called from getSourceSamples.
Chris@559 410 void applyAuditioningEffect(sv_frame_t count, float *const *buffers);
Chris@43 411
Chris@93 412 // Ranges of current selections, if play selection is active
Chris@93 413 std::vector<RealTime> m_rangeStarts;
Chris@93 414 std::vector<RealTime> m_rangeDurations;
Chris@93 415 void rebuildRangeLists();
Chris@93 416
Chris@434 417 sv_frame_t getCurrentFrame(RealTime outputLatency);
Chris@93 418
Chris@43 419 class FillThread : public Thread
Chris@43 420 {
Chris@43 421 public:
Chris@43 422 FillThread(AudioCallbackPlaySource &source) :
Chris@43 423 Thread(Thread::NonRTThread),
Chris@43 424 m_source(source) { }
Chris@43 425
Chris@43 426 virtual void run();
Chris@43 427
Chris@43 428 protected:
Chris@43 429 AudioCallbackPlaySource &m_source;
Chris@43 430 };
Chris@43 431
Chris@43 432 QMutex m_mutex;
Chris@43 433 QWaitCondition m_condition;
Chris@43 434 FillThread *m_fillThread;
Chris@551 435 breakfastquay::ResamplerWrapper *m_resamplerWrapper; // I don't own this
Chris@43 436 };
Chris@43 437
Chris@43 438 #endif
Chris@43 439
Chris@43 440