annotate audio/AudioCallbackPlaySource.h @ 738:48001ed9143b audio-source-refactor

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