annotate audioio/AudioCallbackPlaySource.h @ 35:06787742542a

* Add a bit of resistance to pane dragging so as to make it harder to inadvertently drag in the other axis from the one you intended
author Chris Cannam
date Fri, 22 Sep 2006 16:46:10 +0000
parents e3b32dc5180b
children fbd7a497fd89
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Visualiser
Chris@0 5 An audio file viewer and annotation editor.
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #ifndef _AUDIO_CALLBACK_PLAY_SOURCE_H_
Chris@0 17 #define _AUDIO_CALLBACK_PLAY_SOURCE_H_
Chris@0 18
Chris@0 19 #include "base/RingBuffer.h"
Chris@0 20 #include "base/AudioPlaySource.h"
Chris@32 21 #include "base/PropertyContainer.h"
Chris@0 22 #include "base/Scavenger.h"
Chris@0 23
Chris@0 24 #include <QObject>
Chris@0 25 #include <QMutex>
Chris@0 26 #include <QWaitCondition>
Chris@0 27
Chris@0 28 #include "base/Thread.h"
Chris@0 29
Chris@0 30 #include <samplerate.h>
Chris@0 31
Chris@0 32 #include <set>
Chris@0 33 #include <map>
Chris@0 34
Chris@0 35 class Model;
Chris@0 36 class ViewManager;
Chris@0 37 class AudioGenerator;
Chris@0 38 class PlayParameters;
Chris@14 39 class PhaseVocoderTimeStretcher;
Chris@0 40
Chris@0 41 /**
Chris@0 42 * AudioCallbackPlaySource manages audio data supply to callback-based
Chris@0 43 * audio APIs such as JACK or CoreAudio. It maintains one ring buffer
Chris@0 44 * per channel, filled during playback by a non-realtime thread, and
Chris@0 45 * provides a method for a realtime thread to pick up the latest
Chris@0 46 * available sample data from these buffers.
Chris@0 47 */
Chris@0 48 class AudioCallbackPlaySource : public virtual QObject,
Chris@0 49 public AudioPlaySource
Chris@0 50 {
Chris@0 51 Q_OBJECT
Chris@0 52
Chris@0 53 public:
Chris@0 54 AudioCallbackPlaySource(ViewManager *);
Chris@0 55 virtual ~AudioCallbackPlaySource();
Chris@0 56
Chris@0 57 /**
Chris@0 58 * Add a data model to be played from. The source can mix
Chris@0 59 * playback from a number of sources including dense and sparse
Chris@0 60 * models. The models must match in sample rate, but they don't
Chris@0 61 * have to have identical numbers of channels.
Chris@0 62 */
Chris@0 63 virtual void addModel(Model *model);
Chris@0 64
Chris@0 65 /**
Chris@0 66 * Remove a model.
Chris@0 67 */
Chris@0 68 virtual void removeModel(Model *model);
Chris@0 69
Chris@0 70 /**
Chris@0 71 * Remove all models. (Silence will ensue.)
Chris@0 72 */
Chris@0 73 virtual void clearModels();
Chris@0 74
Chris@0 75 /**
Chris@0 76 * Start making data available in the ring buffers for playback,
Chris@0 77 * from the given frame. If playback is already under way, reseek
Chris@0 78 * to the given frame and continue.
Chris@0 79 */
Chris@0 80 virtual void play(size_t startFrame);
Chris@0 81
Chris@0 82 /**
Chris@0 83 * Stop playback and ensure that no more data is returned.
Chris@0 84 */
Chris@0 85 virtual void stop();
Chris@0 86
Chris@0 87 /**
Chris@0 88 * Return whether playback is currently supposed to be happening.
Chris@0 89 */
Chris@0 90 virtual bool isPlaying() const { return m_playing; }
Chris@0 91
Chris@0 92 /**
Chris@0 93 * Return the frame number that is currently expected to be coming
Chris@0 94 * out of the speakers. (i.e. compensating for playback latency.)
Chris@0 95 */
Chris@0 96 virtual size_t getCurrentPlayingFrame();
Chris@0 97
Chris@0 98 /**
Chris@0 99 * Set the block size of the target audio device. This should
Chris@0 100 * be called by the target class.
Chris@0 101 */
Chris@0 102 void setTargetBlockSize(size_t);
Chris@0 103
Chris@0 104 /**
Chris@0 105 * Get the block size of the target audio device.
Chris@0 106 */
Chris@0 107 size_t getTargetBlockSize() const;
Chris@0 108
Chris@0 109 /**
Chris@0 110 * Set the playback latency of the target audio device, in frames
Chris@0 111 * at the target sample rate. This is the difference between the
Chris@0 112 * frame currently "leaving the speakers" and the last frame (or
Chris@0 113 * highest last frame across all channels) requested via
Chris@0 114 * getSamples(). The default is zero.
Chris@0 115 */
Chris@0 116 void setTargetPlayLatency(size_t);
Chris@0 117
Chris@0 118 /**
Chris@0 119 * Get the playback latency of the target audio device.
Chris@0 120 */
Chris@0 121 size_t getTargetPlayLatency() const;
Chris@0 122
Chris@0 123 /**
Chris@0 124 * Specify that the target audio device has a fixed sample rate
Chris@0 125 * (i.e. cannot accommodate arbitrary sample rates based on the
Chris@0 126 * source). If the target sets this to something other than the
Chris@0 127 * source sample rate, this class will resample automatically to
Chris@0 128 * fit.
Chris@0 129 */
Chris@0 130 void setTargetSampleRate(size_t);
Chris@0 131
Chris@0 132 /**
Chris@0 133 * Return the sample rate set by the target audio device (or the
Chris@0 134 * source sample rate if the target hasn't set one).
Chris@0 135 */
Chris@0 136 virtual size_t getTargetSampleRate() const;
Chris@0 137
Chris@0 138 /**
Chris@0 139 * Set the current output levels for metering (for call from the
Chris@0 140 * target)
Chris@0 141 */
Chris@0 142 void setOutputLevels(float left, float right);
Chris@0 143
Chris@0 144 /**
Chris@0 145 * Return the current (or thereabouts) output levels in the range
Chris@0 146 * 0.0 -> 1.0, for metering purposes.
Chris@0 147 */
Chris@0 148 virtual bool getOutputLevels(float &left, float &right);
Chris@0 149
Chris@0 150 /**
Chris@0 151 * Get the number of channels of audio that in the source models.
Chris@0 152 * This may safely be called from a realtime thread. Returns 0 if
Chris@0 153 * there is no source yet available.
Chris@0 154 */
Chris@0 155 size_t getSourceChannelCount() const;
Chris@0 156
Chris@0 157 /**
Chris@0 158 * Get the number of channels of audio that will be provided
Chris@0 159 * to the play target. This may be more than the source channel
Chris@0 160 * count: for example, a mono source will provide 2 channels
Chris@0 161 * after pan.
Chris@0 162 * This may safely be called from a realtime thread. Returns 0 if
Chris@0 163 * there is no source yet available.
Chris@0 164 */
Chris@0 165 size_t getTargetChannelCount() const;
Chris@0 166
Chris@0 167 /**
Chris@0 168 * Get the actual sample rate of the source material. This may
Chris@0 169 * safely be called from a realtime thread. Returns 0 if there is
Chris@0 170 * no source yet available.
Chris@0 171 */
Chris@0 172 size_t getSourceSampleRate() const;
Chris@0 173
Chris@0 174 /**
Chris@0 175 * Get "count" samples (at the target sample rate) of the mixed
Chris@0 176 * audio data, in all channels. This may safely be called from a
Chris@0 177 * realtime thread.
Chris@0 178 */
Chris@0 179 size_t getSourceSamples(size_t count, float **buffer);
Chris@0 180
Chris@32 181 /**
Chris@32 182 * Set the time stretcher factor (i.e. playback speed). Also
Chris@32 183 * specify whether the time stretcher will be variable rate
Chris@32 184 * (sharpening transients), and whether time stretching will be
Chris@32 185 * carried out on data mixed down to mono for speed.
Chris@32 186 */
Chris@26 187 void setTimeStretch(float factor, bool sharpen, bool mono);
Chris@0 188
Chris@32 189 /**
Chris@32 190 * Set the resampler quality, 0 - 2 where 0 is fastest and 2 is
Chris@32 191 * highest quality.
Chris@32 192 */
Chris@32 193 void setResampleQuality(int q);
Chris@32 194
Chris@0 195 signals:
Chris@0 196 void modelReplaced();
Chris@0 197
Chris@0 198 void playStatusChanged(bool isPlaying);
Chris@0 199
Chris@0 200 void sampleRateMismatch(size_t requested, size_t available, bool willResample);
Chris@0 201
Chris@0 202 protected slots:
Chris@0 203 void selectionChanged();
Chris@0 204 void playLoopModeChanged();
Chris@0 205 void playSelectionModeChanged();
Chris@0 206 void playParametersChanged(PlayParameters *);
Chris@32 207 void preferenceChanged(PropertyContainer::PropertyName);
Chris@0 208
Chris@0 209 protected:
Chris@0 210 ViewManager *m_viewManager;
Chris@0 211 AudioGenerator *m_audioGenerator;
Chris@0 212
Chris@0 213 class RingBufferVector : public std::vector<RingBuffer<float> *> {
Chris@0 214 public:
Chris@0 215 virtual ~RingBufferVector() {
Chris@0 216 while (!empty()) {
Chris@0 217 delete *begin();
Chris@0 218 erase(begin());
Chris@0 219 }
Chris@0 220 }
Chris@0 221 };
Chris@0 222
Chris@0 223 std::set<Model *> m_models;
Chris@0 224 RingBufferVector *m_readBuffers;
Chris@0 225 RingBufferVector *m_writeBuffers;
Chris@0 226 size_t m_readBufferFill;
Chris@0 227 size_t m_writeBufferFill;
Chris@0 228 Scavenger<RingBufferVector> m_bufferScavenger;
Chris@0 229 size_t m_sourceChannelCount;
Chris@0 230 size_t m_blockSize;
Chris@0 231 size_t m_sourceSampleRate;
Chris@0 232 size_t m_targetSampleRate;
Chris@0 233 size_t m_playLatency;
Chris@0 234 bool m_playing;
Chris@0 235 bool m_exiting;
Chris@0 236 size_t m_lastModelEndFrame;
Chris@0 237 static const size_t m_ringBufferSize;
Chris@0 238 float m_outputLeft;
Chris@0 239 float m_outputRight;
Chris@0 240
Chris@0 241 RingBuffer<float> *getWriteRingBuffer(size_t c) {
Chris@0 242 if (m_writeBuffers && c < m_writeBuffers->size()) {
Chris@0 243 return (*m_writeBuffers)[c];
Chris@0 244 } else {
Chris@0 245 return 0;
Chris@0 246 }
Chris@0 247 }
Chris@0 248
Chris@0 249 RingBuffer<float> *getReadRingBuffer(size_t c) {
Chris@0 250 RingBufferVector *rb = m_readBuffers;
Chris@0 251 if (rb && c < rb->size()) {
Chris@0 252 return (*rb)[c];
Chris@0 253 } else {
Chris@0 254 return 0;
Chris@0 255 }
Chris@0 256 }
Chris@0 257
Chris@0 258 void clearRingBuffers(bool haveLock = false, size_t count = 0);
Chris@0 259 void unifyRingBuffers();
Chris@0 260
Chris@16 261 PhaseVocoderTimeStretcher *m_timeStretcher;
Chris@16 262 Scavenger<PhaseVocoderTimeStretcher> m_timeStretcherScavenger;
Chris@0 263
Chris@0 264 // Called from fill thread, m_playing true, mutex held
Chris@0 265 // Return true if work done
Chris@0 266 bool fillBuffers();
Chris@0 267
Chris@0 268 // Called from fillBuffers. Return the number of frames written,
Chris@0 269 // which will be count or fewer. Return in the frame argument the
Chris@0 270 // new buffered frame position (which may be earlier than the
Chris@0 271 // frame argument passed in, in the case of looping).
Chris@0 272 size_t mixModels(size_t &frame, size_t count, float **buffers);
Chris@0 273
Chris@0 274 class AudioCallbackPlaySourceFillThread : public Thread
Chris@0 275 {
Chris@0 276 public:
Chris@0 277 AudioCallbackPlaySourceFillThread(AudioCallbackPlaySource &source) :
Chris@0 278 Thread(Thread::NonRTThread),
Chris@0 279 m_source(source) { }
Chris@0 280
Chris@0 281 virtual void run();
Chris@0 282
Chris@0 283 protected:
Chris@0 284 AudioCallbackPlaySource &m_source;
Chris@0 285 };
Chris@0 286
Chris@0 287 QMutex m_mutex;
Chris@0 288 QWaitCondition m_condition;
Chris@0 289 AudioCallbackPlaySourceFillThread *m_fillThread;
Chris@0 290 SRC_STATE *m_converter;
Chris@32 291 SRC_STATE *m_crapConverter; // for use when playing very fast
Chris@32 292 int m_resampleQuality;
Chris@32 293 void initialiseConverter();
Chris@0 294 };
Chris@0 295
Chris@0 296 #endif
Chris@0 297
Chris@0 298