Chris@43: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@43: Chris@43: /* Chris@43: Sonic Visualiser Chris@43: An audio file viewer and annotation editor. Chris@43: Centre for Digital Music, Queen Mary, University of London. Chris@43: This file copyright 2006 Chris Cannam and QMUL. Chris@43: Chris@43: This program is free software; you can redistribute it and/or Chris@43: modify it under the terms of the GNU General Public License as Chris@43: published by the Free Software Foundation; either version 2 of the Chris@43: License, or (at your option) any later version. See the file Chris@43: COPYING included with this distribution for more information. Chris@43: */ Chris@43: Chris@574: #ifndef SV_AUDIO_CALLBACK_PLAY_SOURCE_H Chris@574: #define SV_AUDIO_CALLBACK_PLAY_SOURCE_H Chris@43: Chris@43: #include "base/RingBuffer.h" Chris@43: #include "base/AudioPlaySource.h" Chris@43: #include "base/PropertyContainer.h" Chris@43: #include "base/Scavenger.h" Chris@43: Chris@468: #include Chris@468: Chris@43: #include Chris@43: #include Chris@43: #include Chris@43: Chris@43: #include "base/Thread.h" Chris@93: #include "base/RealTime.h" Chris@682: #include "data/model/Model.h" Chris@43: Chris@43: #include Chris@43: Chris@43: #include Chris@43: #include Chris@43: Chris@544: namespace breakfastquay { Chris@551: class ResamplerWrapper; Chris@544: } Chris@544: Chris@43: class Model; Chris@105: class ViewManagerBase; Chris@43: class AudioGenerator; Chris@43: class PlayParameters; Chris@43: class RealTimePluginInstance; Chris@91: class AudioCallbackPlayTarget; Chris@738: class TimeStretchWrapper; Chris@739: class EffectWrapper; Chris@43: Chris@43: /** Chris@43: * AudioCallbackPlaySource manages audio data supply to callback-based Chris@43: * audio APIs such as JACK or CoreAudio. It maintains one ring buffer Chris@43: * per channel, filled during playback by a non-realtime thread, and Chris@43: * provides a method for a realtime thread to pick up the latest Chris@43: * available sample data from these buffers. Chris@43: */ Chris@238: class AudioCallbackPlaySource : public QObject, Chris@595: public AudioPlaySource, Chris@468: public breakfastquay::ApplicationPlaybackSource Chris@43: { Chris@43: Q_OBJECT Chris@43: Chris@43: public: Chris@105: AudioCallbackPlaySource(ViewManagerBase *, QString clientName); Chris@43: virtual ~AudioCallbackPlaySource(); Chris@738: Chris@738: /** Chris@741: * Return an ApplicationPlaybackSource interface to this Chris@741: * class. Although this class implements ApplicationPlaybackSource Chris@741: * itself, the object returned here may be a wrapper which Chris@741: * provides facilities not implemented in this class, such as Chris@741: * time-stretching, resampling, and an auditioning effect. The Chris@741: * returned pointer points to an object which is owned by this Chris@741: * object. Caller must ensure the lifetime of this object exceeds Chris@741: * the scope which the returned pointer is retained. Chris@738: */ Chris@738: breakfastquay::ApplicationPlaybackSource *getApplicationPlaybackSource(); Chris@43: Chris@43: /** Chris@43: * Add a data model to be played from. The source can mix Chris@43: * playback from a number of sources including dense and sparse Chris@43: * models. The models must match in sample rate, but they don't Chris@43: * have to have identical numbers of channels. Chris@43: */ Chris@682: virtual void addModel(ModelId model); Chris@43: Chris@43: /** Chris@43: * Remove a model. Chris@43: */ Chris@682: virtual void removeModel(ModelId model); Chris@43: Chris@43: /** Chris@43: * Remove all models. (Silence will ensue.) Chris@43: */ Chris@43: virtual void clearModels(); Chris@43: Chris@43: /** Chris@43: * Start making data available in the ring buffers for playback, Chris@43: * from the given frame. If playback is already under way, reseek Chris@43: * to the given frame and continue. Chris@43: */ Chris@559: virtual void play(sv_frame_t startFrame) override; Chris@43: Chris@43: /** Chris@43: * Stop playback and ensure that no more data is returned. Chris@43: */ Chris@559: virtual void stop() override; Chris@43: Chris@43: /** Chris@43: * Return whether playback is currently supposed to be happening. Chris@43: */ Chris@559: virtual bool isPlaying() const override { return m_playing; } Chris@43: Chris@43: /** Chris@43: * Return the frame number that is currently expected to be coming Chris@43: * out of the speakers. (i.e. compensating for playback latency.) Chris@43: */ Chris@559: virtual sv_frame_t getCurrentPlayingFrame() override; Chris@93: Chris@93: /** Chris@93: * Return the last frame that would come out of the speakers if we Chris@93: * stopped playback right now. Chris@93: */ Chris@434: virtual sv_frame_t getCurrentBufferedFrame(); Chris@43: Chris@43: /** Chris@43: * Return the frame at which playback is expected to end (if not looping). Chris@43: */ Chris@434: virtual sv_frame_t getPlayEndFrame() { return m_lastModelEndFrame; } Chris@43: Chris@43: /** Chris@498: * Set the playback target. Chris@43: */ Chris@468: virtual void setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *); Chris@551: Chris@551: /** Chris@468: * Set the block size of the target audio device. This should be Chris@468: * called by the target class. Chris@468: */ Chris@559: virtual void setSystemPlaybackBlockSize(int blockSize) override; Chris@43: Chris@43: /** Chris@91: * Get the block size of the target audio device. This may be an Chris@91: * estimate or upper bound, if the target has a variable block Chris@91: * size; the source should behave itself even if this value turns Chris@91: * out to be inaccurate. Chris@43: */ cannam@561: virtual int getTargetBlockSize() const override; Chris@43: Chris@43: /** Chris@43: * Set the playback latency of the target audio device, in frames Chris@553: * at the device sample rate. This is the difference between the Chris@43: * frame currently "leaving the speakers" and the last frame (or Chris@43: * highest last frame across all channels) requested via Chris@43: * getSamples(). The default is zero. Chris@43: */ Chris@559: virtual void setSystemPlaybackLatency(int) override; Chris@43: Chris@43: /** Chris@43: * Get the playback latency of the target audio device. Chris@43: */ Chris@434: sv_frame_t getTargetPlayLatency() const; Chris@43: Chris@43: /** Chris@43: * Specify that the target audio device has a fixed sample rate Chris@43: * (i.e. cannot accommodate arbitrary sample rates based on the Chris@43: * source). If the target sets this to something other than the Chris@43: * source sample rate, this class will resample automatically to Chris@43: * fit. Chris@43: */ Chris@559: virtual void setSystemPlaybackSampleRate(int) override; Chris@43: Chris@43: /** Chris@43: * Return the sample rate set by the target audio device (or the Chris@43: * source sample rate if the target hasn't set one). Chris@43: */ cannam@561: virtual sv_samplerate_t getDeviceSampleRate() const override; Chris@43: Chris@43: /** Chris@546: * Indicate how many channels the target audio device was opened Chris@546: * with. Note that the target device does channel mixing in the Chris@559: * case where our requested channel count does not match its, so Chris@559: * long as we provide the number of channels we specified when the Chris@559: * target was started in getApplicationChannelCount(). Chris@546: */ Chris@559: virtual void setSystemPlaybackChannelCount(int) override; Chris@546: Chris@546: /** Chris@43: * Set the current output levels for metering (for call from the Chris@43: * target) Chris@43: */ cannam@561: virtual void setOutputLevels(float left, float right) override; Chris@43: Chris@43: /** Chris@580: * Return the current output levels in the range 0.0 -> 1.0, for Chris@580: * metering purposes. The values returned are the peak values Chris@580: * since the last time this function was called (after which they Chris@580: * are reset to zero until setOutputLevels is called again by the Chris@580: * driver). Chris@580: * Chris@580: * Return true if the values have been set since this function was Chris@580: * last called (i.e. if they are meaningful). Return false if they Chris@580: * have not been set (in which case both will be zero). Chris@43: */ cannam@561: virtual bool getOutputLevels(float &left, float &right) override; Chris@43: Chris@43: /** Chris@43: * Get the number of channels of audio that in the source models. Chris@43: * This may safely be called from a realtime thread. Returns 0 if Chris@43: * there is no source yet available. Chris@43: */ Chris@366: int getSourceChannelCount() const; Chris@43: Chris@43: /** Chris@43: * Get the number of channels of audio that will be provided Chris@43: * to the play target. This may be more than the source channel Chris@43: * count: for example, a mono source will provide 2 channels Chris@43: * after pan. Chris@552: * Chris@43: * This may safely be called from a realtime thread. Returns 0 if Chris@43: * there is no source yet available. Chris@552: * Chris@552: * override from AudioPlaySource Chris@43: */ Chris@552: virtual int getTargetChannelCount() const override; Chris@43: Chris@43: /** Chris@559: * Get the number of channels of audio the device is Chris@559: * expecting. Equal to whatever getTargetChannelCount() was Chris@559: * returning at the time the device was initialised. Chris@559: */ Chris@559: int getDeviceChannelCount() const; Chris@559: Chris@559: /** Chris@468: * ApplicationPlaybackSource equivalent of the above. Chris@552: * Chris@552: * override from breakfastquay::ApplicationPlaybackSource Chris@468: */ Chris@552: virtual int getApplicationChannelCount() const override { Chris@468: return getTargetChannelCount(); Chris@468: } Chris@468: Chris@468: /** Chris@552: * Get the actual sample rate of the source material (the main Chris@552: * model). This may safely be called from a realtime thread. Chris@552: * Returns 0 if there is no source yet available. Chris@552: * Chris@552: * When this changes, the AudioCallbackPlaySource notifies its Chris@552: * ResamplerWrapper of the new sample rate so that it can resample Chris@552: * correctly on the way to the device (which is opened at a fixed Chris@552: * rate, see getApplicationSampleRate). Chris@43: */ Chris@552: virtual sv_samplerate_t getSourceSampleRate() const override; Chris@43: Chris@43: /** Chris@552: * ApplicationPlaybackSource interface method: get the sample rate Chris@552: * at which the application wants the device to be opened. We Chris@552: * always allow the device to open at its default rate, and then Chris@552: * we resample if the audio is at a different rate. This avoids Chris@552: * having to close and re-open the device to obtain consistent Chris@552: * behaviour for consecutive sessions with different source rates. Chris@468: */ Chris@552: virtual int getApplicationSampleRate() const override { Chris@552: return 0; Chris@468: } Chris@468: Chris@468: /** Chris@43: * Get "count" samples (at the target sample rate) of the mixed Chris@43: * audio data, in all channels. This may safely be called from a Chris@43: * realtime thread. Chris@43: */ Chris@559: virtual int getSourceSamples(float *const *buffer, int nchannels, int count) override; Chris@43: Chris@43: /** Chris@91: * Set the time stretcher factor (i.e. playback speed). Chris@43: */ Chris@436: void setTimeStretch(double factor); Chris@43: Chris@43: /** Chris@43: * Set a single real-time plugin as a processing effect for Chris@43: * auditioning during playback. Chris@43: * Chris@43: * The plugin must have been initialised with Chris@43: * getTargetChannelCount() channels and a getTargetBlockSize() Chris@43: * sample frame processing block size. Chris@43: * Chris@43: * This playback source takes ownership of the plugin, which will Chris@43: * be deleted at some point after the following call to Chris@107: * setAuditioningEffect (depending on real-time constraints). Chris@43: * Chris@43: * Pass a null pointer to remove the current auditioning plugin, Chris@43: * if any. Chris@43: */ Chris@739: virtual void setAuditioningEffect(std::shared_ptr plugin) Chris@739: override; Chris@43: Chris@43: /** Chris@43: * Specify that only the given set of models should be played. Chris@43: */ Chris@682: void setSoloModelSet(std::sets); Chris@43: Chris@43: /** Chris@43: * Specify that all models should be played as normal (if not Chris@43: * muted). Chris@43: */ Chris@43: void clearSoloModelSet(); Chris@43: cannam@561: virtual std::string getClientName() const override { cannam@561: return m_clientName; cannam@561: } Chris@57: Chris@43: signals: Chris@43: void playStatusChanged(bool isPlaying); Chris@43: Chris@436: void sampleRateMismatch(sv_samplerate_t requested, Chris@436: sv_samplerate_t available, Chris@436: bool willResample); Chris@43: Chris@570: void channelCountIncreased(int count); // target channel count (see getTargetChannelCount()) Chris@559: Chris@43: void audioOverloadPluginDisabled(); Chris@43: Chris@158: void activity(QString); Chris@158: Chris@43: public slots: cannam@561: void audioProcessingOverload() override; Chris@43: Chris@43: protected slots: Chris@43: void selectionChanged(); Chris@43: void playLoopModeChanged(); Chris@43: void playSelectionModeChanged(); Chris@687: void playParametersChanged(int); Chris@43: void preferenceChanged(PropertyContainer::PropertyName); Chris@687: void modelChangedWithin(ModelId, sv_frame_t startFrame, sv_frame_t endFrame); Chris@43: Chris@43: protected: Chris@105: ViewManagerBase *m_viewManager; Chris@57: AudioGenerator *m_audioGenerator; Chris@468: std::string m_clientName; Chris@43: Chris@43: class RingBufferVector : public std::vector *> { Chris@43: public: Chris@595: virtual ~RingBufferVector() { Chris@595: while (!empty()) { Chris@595: delete *begin(); Chris@595: erase(begin()); Chris@595: } Chris@595: } Chris@43: }; Chris@43: Chris@682: std::set m_models; Chris@43: RingBufferVector *m_readBuffers; Chris@43: RingBufferVector *m_writeBuffers; Chris@436: sv_frame_t m_readBufferFill; Chris@436: sv_frame_t m_writeBufferFill; Chris@43: Scavenger m_bufferScavenger; Chris@366: int m_sourceChannelCount; Chris@436: sv_frame_t m_blockSize; Chris@434: sv_samplerate_t m_sourceSampleRate; Chris@553: sv_samplerate_t m_deviceSampleRate; Chris@559: int m_deviceChannelCount; Chris@436: sv_frame_t m_playLatency; Chris@468: breakfastquay::SystemPlaybackTarget *m_target; Chris@91: double m_lastRetrievalTimestamp; Chris@436: sv_frame_t m_lastRetrievedBlockSize; Chris@102: bool m_trustworthyTimestamps; Chris@434: sv_frame_t m_lastCurrentFrame; Chris@43: bool m_playing; Chris@43: bool m_exiting; Chris@434: sv_frame_t m_lastModelEndFrame; Chris@366: int m_ringBufferSize; Chris@43: float m_outputLeft; Chris@43: float m_outputRight; Chris@580: bool m_levelsSet; Chris@43: Scavenger m_pluginScavenger; Chris@434: sv_frame_t m_playStartFrame; Chris@94: bool m_playStartFramePassed; Chris@94: RealTime m_playStartedAt; Chris@43: Chris@366: RingBuffer *getWriteRingBuffer(int c) { Chris@595: if (m_writeBuffers && c < (int)m_writeBuffers->size()) { Chris@595: return (*m_writeBuffers)[c]; Chris@595: } else { Chris@595: return 0; Chris@595: } Chris@43: } Chris@43: Chris@366: RingBuffer *getReadRingBuffer(int c) { Chris@595: RingBufferVector *rb = m_readBuffers; Chris@595: if (rb && c < (int)rb->size()) { Chris@595: return (*rb)[c]; Chris@595: } else { Chris@595: return 0; Chris@595: } Chris@43: } Chris@43: Chris@366: void clearRingBuffers(bool haveLock = false, int count = 0); Chris@43: void unifyRingBuffers(); Chris@43: Chris@765: // Called from fill thread, mutex held. Return true if work done Chris@43: bool fillBuffers(); Chris@43: Chris@43: // Called from fillBuffers. Return the number of frames written, Chris@43: // which will be count or fewer. Return in the frame argument the Chris@43: // new buffered frame position (which may be earlier than the Chris@43: // frame argument passed in, in the case of looping). Chris@434: sv_frame_t mixModels(sv_frame_t &frame, sv_frame_t count, float **buffers); Chris@43: Chris@93: // Ranges of current selections, if play selection is active Chris@93: std::vector m_rangeStarts; Chris@93: std::vector m_rangeDurations; Chris@93: void rebuildRangeLists(); Chris@93: Chris@434: sv_frame_t getCurrentFrame(RealTime outputLatency); Chris@93: Chris@43: class FillThread : public Thread Chris@43: { Chris@43: public: Chris@595: FillThread(AudioCallbackPlaySource &source) : Chris@43: Thread(Thread::NonRTThread), Chris@595: m_source(source) { } Chris@43: Chris@634: void run() override; Chris@43: Chris@43: protected: Chris@595: AudioCallbackPlaySource &m_source; Chris@43: }; Chris@43: Chris@43: QMutex m_mutex; Chris@43: QWaitCondition m_condition; Chris@43: FillThread *m_fillThread; Chris@738: breakfastquay::ResamplerWrapper *m_resamplerWrapper; Chris@738: TimeStretchWrapper *m_timeStretchWrapper; Chris@739: EffectWrapper *m_auditioningEffectWrapper; Chris@738: void checkWrappers(); Chris@43: }; Chris@43: Chris@43: #endif Chris@43: