Chris@0
|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 A waveform viewer and audio annotation editor.
|
Chris@2
|
5 Chris Cannam, Queen Mary University of London, 2005-2006
|
Chris@0
|
6
|
Chris@0
|
7 This is experimental software. Not for distribution.
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 #ifndef _AUDIO_CALLBACK_PLAY_SOURCE_H_
|
Chris@0
|
11 #define _AUDIO_CALLBACK_PLAY_SOURCE_H_
|
Chris@0
|
12
|
Chris@0
|
13 #include "base/RingBuffer.h"
|
Chris@0
|
14 #include "base/AudioPlaySource.h"
|
Chris@0
|
15 #include "base/Scavenger.h"
|
Chris@0
|
16
|
Chris@0
|
17 #include <QObject>
|
Chris@0
|
18 #include <QMutex>
|
Chris@0
|
19 #include <QWaitCondition>
|
Chris@0
|
20 #include <QThread>
|
Chris@0
|
21
|
Chris@0
|
22 #include <samplerate.h>
|
Chris@0
|
23
|
Chris@0
|
24 #include <set>
|
Chris@0
|
25 #include <map>
|
Chris@0
|
26
|
Chris@0
|
27 class Model;
|
Chris@0
|
28 class ViewManager;
|
Chris@0
|
29 class AudioGenerator;
|
Chris@0
|
30 class IntegerTimeStretcher;
|
Chris@0
|
31
|
Chris@0
|
32 /**
|
Chris@0
|
33 * AudioCallbackPlaySource manages audio data supply to callback-based
|
Chris@0
|
34 * audio APIs such as JACK or CoreAudio. It maintains one ring buffer
|
Chris@0
|
35 * per channel, filled during playback by a non-realtime thread, and
|
Chris@0
|
36 * provides a method for a realtime thread to pick up the latest
|
Chris@0
|
37 * available sample data from these buffers.
|
Chris@0
|
38 */
|
Chris@0
|
39 class AudioCallbackPlaySource : public virtual QObject,
|
Chris@0
|
40 public AudioPlaySource
|
Chris@0
|
41 {
|
Chris@0
|
42 Q_OBJECT
|
Chris@0
|
43
|
Chris@0
|
44 public:
|
Chris@0
|
45 AudioCallbackPlaySource(ViewManager *);
|
Chris@0
|
46 virtual ~AudioCallbackPlaySource();
|
Chris@0
|
47
|
Chris@0
|
48 /**
|
Chris@0
|
49 * Add a data model to be played from. The source can mix
|
Chris@0
|
50 * playback from a number of sources including dense and sparse
|
Chris@0
|
51 * models. The models must match in sample rate, but they don't
|
Chris@0
|
52 * have to have identical numbers of channels.
|
Chris@0
|
53 */
|
Chris@0
|
54 virtual void addModel(Model *model);
|
Chris@0
|
55
|
Chris@0
|
56 /**
|
Chris@0
|
57 * Remove a model.
|
Chris@0
|
58 */
|
Chris@0
|
59 virtual void removeModel(Model *model);
|
Chris@0
|
60
|
Chris@0
|
61 /**
|
Chris@0
|
62 * Remove all models. (Silence will ensue.)
|
Chris@0
|
63 */
|
Chris@0
|
64 virtual void clearModels();
|
Chris@0
|
65
|
Chris@0
|
66 /**
|
Chris@0
|
67 * Start making data available in the ring buffers for playback,
|
Chris@0
|
68 * from the given frame. If playback is already under way, reseek
|
Chris@0
|
69 * to the given frame and continue.
|
Chris@0
|
70 */
|
Chris@0
|
71 virtual void play(size_t startFrame);
|
Chris@0
|
72
|
Chris@0
|
73 /**
|
Chris@0
|
74 * Stop playback and ensure that no more data is returned.
|
Chris@0
|
75 */
|
Chris@0
|
76 virtual void stop();
|
Chris@0
|
77
|
Chris@0
|
78 /**
|
Chris@0
|
79 * Return whether playback is currently supposed to be happening.
|
Chris@0
|
80 */
|
Chris@0
|
81 virtual bool isPlaying() const { return m_playing; }
|
Chris@0
|
82
|
Chris@0
|
83 /**
|
Chris@0
|
84 * Return the frame number that is currently expected to be coming
|
Chris@0
|
85 * out of the speakers. (i.e. compensating for playback latency.)
|
Chris@0
|
86 */
|
Chris@0
|
87 virtual size_t getCurrentPlayingFrame();
|
Chris@0
|
88
|
Chris@0
|
89 /**
|
Chris@0
|
90 * Set the block size of the target audio device. This should
|
Chris@0
|
91 * be called by the target class.
|
Chris@0
|
92 */
|
Chris@0
|
93 void setTargetBlockSize(size_t);
|
Chris@0
|
94
|
Chris@0
|
95 /**
|
Chris@0
|
96 * Get the block size of the target audio device.
|
Chris@0
|
97 */
|
Chris@0
|
98 size_t getTargetBlockSize() const;
|
Chris@0
|
99
|
Chris@0
|
100 /**
|
Chris@0
|
101 * Set the playback latency of the target audio device, in frames
|
Chris@0
|
102 * at the target sample rate. This is the difference between the
|
Chris@0
|
103 * frame currently "leaving the speakers" and the last frame (or
|
Chris@0
|
104 * highest last frame across all channels) requested via
|
Chris@0
|
105 * getSamples(). The default is zero.
|
Chris@0
|
106 */
|
Chris@0
|
107 void setTargetPlayLatency(size_t);
|
Chris@0
|
108
|
Chris@0
|
109 /**
|
Chris@0
|
110 * Get the playback latency of the target audio device.
|
Chris@0
|
111 */
|
Chris@0
|
112 size_t getTargetPlayLatency() const;
|
Chris@0
|
113
|
Chris@0
|
114 /**
|
Chris@0
|
115 * Specify that the target audio device has a fixed sample rate
|
Chris@0
|
116 * (i.e. cannot accommodate arbitrary sample rates based on the
|
Chris@0
|
117 * source). If the target sets this to something other than the
|
Chris@0
|
118 * source sample rate, this class will resample automatically to
|
Chris@0
|
119 * fit.
|
Chris@0
|
120 */
|
Chris@0
|
121 void setTargetSampleRate(size_t);
|
Chris@0
|
122
|
Chris@0
|
123 /**
|
Chris@0
|
124 * Return the sample rate set by the target audio device (or the
|
Chris@0
|
125 * source sample rate if the target hasn't set one).
|
Chris@0
|
126 */
|
Chris@0
|
127 size_t getTargetSampleRate() const;
|
Chris@0
|
128
|
Chris@0
|
129 /**
|
Chris@0
|
130 * Set the current output levels for metering (for call from the
|
Chris@0
|
131 * target)
|
Chris@0
|
132 */
|
Chris@0
|
133 void setOutputLevels(float left, float right);
|
Chris@0
|
134
|
Chris@0
|
135 /**
|
Chris@0
|
136 * Return the current (or thereabouts) output levels in the range
|
Chris@0
|
137 * 0.0 -> 1.0, for metering purposes.
|
Chris@0
|
138 */
|
Chris@0
|
139 virtual bool getOutputLevels(float &left, float &right);
|
Chris@0
|
140
|
Chris@0
|
141 /**
|
Chris@0
|
142 * Get the number of channels of audio that will be available.
|
Chris@0
|
143 * This may safely be called from a realtime thread. Returns 0 if
|
Chris@0
|
144 * there is no source yet available.
|
Chris@0
|
145 */
|
Chris@0
|
146 size_t getSourceChannelCount() const;
|
Chris@0
|
147
|
Chris@0
|
148 /**
|
Chris@0
|
149 * Get the actual sample rate of the source material. This may
|
Chris@0
|
150 * safely be called from a realtime thread. Returns 0 if there is
|
Chris@0
|
151 * no source yet available.
|
Chris@0
|
152 */
|
Chris@0
|
153 size_t getSourceSampleRate() const;
|
Chris@0
|
154
|
Chris@0
|
155 /**
|
Chris@0
|
156 * Get "count" samples (at the target sample rate) of the mixed
|
Chris@0
|
157 * audio data, in all channels. This may safely be called from a
|
Chris@0
|
158 * realtime thread.
|
Chris@0
|
159 */
|
Chris@0
|
160 size_t getSourceSamples(size_t count, float **buffer);
|
Chris@0
|
161
|
Chris@0
|
162 void setSlowdownFactor(size_t factor);
|
Chris@0
|
163
|
Chris@0
|
164 signals:
|
Chris@0
|
165 void modelReplaced();
|
Chris@0
|
166
|
Chris@4
|
167 void playStatusChanged(bool isPlaying);
|
Chris@4
|
168
|
Chris@0
|
169 /// Just a warning
|
Chris@0
|
170 void sampleRateMismatch(size_t requested, size_t available);
|
Chris@0
|
171
|
Chris@3
|
172 protected slots:
|
Chris@3
|
173 void selectionChanged();
|
Chris@3
|
174 void playLoopModeChanged();
|
Chris@3
|
175 void playSelectionModeChanged();
|
Chris@3
|
176
|
Chris@0
|
177 protected:
|
Chris@0
|
178 ViewManager *m_viewManager;
|
Chris@0
|
179 AudioGenerator *m_audioGenerator;
|
Chris@0
|
180
|
Chris@0
|
181 std::set<Model *> m_models;
|
Chris@0
|
182 std::vector<RingBuffer<float> *> m_buffers;
|
Chris@0
|
183 size_t m_bufferCount;
|
Chris@0
|
184 size_t m_blockSize;
|
Chris@0
|
185 size_t m_sourceSampleRate;
|
Chris@0
|
186 size_t m_targetSampleRate;
|
Chris@0
|
187 size_t m_playLatency;
|
Chris@0
|
188 bool m_playing;
|
Chris@0
|
189 bool m_exiting;
|
Chris@0
|
190 size_t m_bufferedToFrame;
|
Chris@4
|
191 size_t m_lastModelEndFrame;
|
Chris@0
|
192 static const size_t m_ringBufferSize;
|
Chris@0
|
193 float m_outputLeft;
|
Chris@0
|
194 float m_outputRight;
|
Chris@0
|
195
|
Chris@0
|
196 RingBuffer<float> &getRingBuffer(size_t c) {
|
Chris@0
|
197 return *m_buffers[c];
|
Chris@0
|
198 }
|
Chris@0
|
199
|
Chris@0
|
200 class TimeStretcherData
|
Chris@0
|
201 {
|
Chris@0
|
202 public:
|
Chris@0
|
203 TimeStretcherData(size_t channels, size_t factor, size_t blockSize);
|
Chris@0
|
204 ~TimeStretcherData();
|
Chris@0
|
205
|
Chris@0
|
206 size_t getFactor() const { return m_factor; }
|
Chris@0
|
207 IntegerTimeStretcher *getStretcher(size_t channel);
|
Chris@0
|
208 double *getOutputBuffer(size_t channel);
|
Chris@0
|
209 double *getInputBuffer();
|
Chris@0
|
210
|
Chris@0
|
211 void run(size_t channel);
|
Chris@0
|
212
|
Chris@0
|
213 protected:
|
Chris@0
|
214 TimeStretcherData(const TimeStretcherData &); // not provided
|
Chris@0
|
215 TimeStretcherData &operator=(const TimeStretcherData &); // not provided
|
Chris@0
|
216
|
Chris@0
|
217 typedef std::pair<IntegerTimeStretcher *, double *> StretcherBuffer;
|
Chris@0
|
218 std::map<size_t, StretcherBuffer> m_stretcher;
|
Chris@0
|
219 double *m_stretchInputBuffer;
|
Chris@0
|
220 size_t m_factor;
|
Chris@0
|
221 size_t m_blockSize;
|
Chris@0
|
222 };
|
Chris@0
|
223
|
Chris@0
|
224 size_t m_slowdownCounter;
|
Chris@0
|
225 TimeStretcherData *m_timeStretcher;
|
Chris@0
|
226 Scavenger<TimeStretcherData> m_timeStretcherScavenger;
|
Chris@0
|
227
|
Chris@4
|
228 // Called from fill thread, m_playing true, mutex held
|
Chris@4
|
229 void fillBuffers();
|
Chris@4
|
230
|
Chris@4
|
231 // Called from fillBuffers
|
Chris@3
|
232 bool mixModels(size_t &frame, size_t count, float **buffers);
|
Chris@0
|
233
|
Chris@0
|
234 class AudioCallbackPlaySourceFillThread : public QThread
|
Chris@0
|
235 {
|
Chris@0
|
236 public:
|
Chris@0
|
237 AudioCallbackPlaySourceFillThread(AudioCallbackPlaySource &source) :
|
Chris@0
|
238 m_source(source) { }
|
Chris@0
|
239
|
Chris@0
|
240 virtual void run();
|
Chris@0
|
241
|
Chris@0
|
242 protected:
|
Chris@0
|
243 AudioCallbackPlaySource &m_source;
|
Chris@0
|
244 };
|
Chris@0
|
245
|
Chris@0
|
246 QMutex m_mutex;
|
Chris@0
|
247 QWaitCondition m_condition;
|
Chris@0
|
248 AudioCallbackPlaySourceFillThread *m_fillThread;
|
Chris@0
|
249 SRC_STATE *m_converter;
|
Chris@0
|
250 };
|
Chris@0
|
251
|
Chris@0
|
252 #endif
|
Chris@0
|
253
|
Chris@0
|
254
|