annotate audioio/AudioCallbackPlaySource.h @ 32:e3b32dc5180b

* Make resampler quality configurable * Fall back to linear resampling when playing very fast * Switch off transient detection in time stretcher when playing very very fast
author Chris Cannam
date Thu, 21 Sep 2006 11:17:19 +0000
parents d88d117e0c34
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