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@475: #ifndef AUDIO_CALLBACK_PLAY_SOURCE_H Chris@475: #define 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@43: Chris@43: #include Chris@43: Chris@43: #include Chris@43: #include Chris@43: Chris@91: namespace RubberBand { Chris@91: class RubberBandStretcher; Chris@91: } Chris@62: Chris@544: namespace breakfastquay { Chris@544: class Resampler; 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@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@468: 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@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@43: virtual void addModel(Model *model); Chris@43: Chris@43: /** Chris@43: * Remove a model. Chris@43: */ Chris@43: virtual void removeModel(Model *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@434: virtual void play(sv_frame_t startFrame); Chris@43: Chris@43: /** Chris@43: * Stop playback and ensure that no more data is returned. Chris@43: */ Chris@43: virtual void stop(); Chris@43: Chris@43: /** Chris@43: * Return whether playback is currently supposed to be happening. Chris@43: */ Chris@43: virtual bool isPlaying() const { 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@434: virtual sv_frame_t getCurrentPlayingFrame(); 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@468: Chris@468: /** Chris@468: * Set the block size of the target audio device. This should be Chris@468: * called by the target class. Chris@468: */ Chris@468: virtual void setSystemPlaybackBlockSize(int blockSize); 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: */ Chris@366: int getTargetBlockSize() const; Chris@43: Chris@43: /** Chris@43: * Set the playback latency of the target audio device, in frames Chris@43: * at the target 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@468: void setSystemPlaybackLatency(int); 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@468: void setSystemPlaybackSampleRate(int); 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: */ Chris@434: virtual sv_samplerate_t getTargetSampleRate() const; 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@546: * case where our requested channel count does not match its. Chris@546: */ Chris@546: void setSystemPlaybackChannelCount(int); Chris@546: Chris@546: /** Chris@43: * Set the current output levels for metering (for call from the Chris@43: * target) Chris@43: */ Chris@43: void setOutputLevels(float left, float right); Chris@43: Chris@43: /** Chris@43: * Return the current (or thereabouts) output levels in the range Chris@43: * 0.0 -> 1.0, for metering purposes. Chris@43: */ Chris@43: virtual bool getOutputLevels(float &left, float &right); 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@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 getTargetChannelCount() const; Chris@43: Chris@43: /** Chris@468: * ApplicationPlaybackSource equivalent of the above. Chris@468: */ Chris@468: virtual int getApplicationChannelCount() const { Chris@468: return getTargetChannelCount(); Chris@468: } Chris@468: Chris@468: /** Chris@43: * Get the actual sample rate of the source material. This may Chris@43: * safely be called from a realtime thread. Returns 0 if there is Chris@43: * no source yet available. Chris@43: */ Chris@434: virtual sv_samplerate_t getSourceSampleRate() const; Chris@43: Chris@43: /** Chris@468: * ApplicationPlaybackSource equivalent of the above. Chris@468: */ Chris@468: virtual int getApplicationSampleRate() const { Chris@468: return int(round(getSourceSampleRate())); 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@471: virtual int getSourceSamples(int count, float **buffer); 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@107: void setAuditioningEffect(Auditionable *plugin); Chris@43: Chris@43: /** Chris@43: * Specify that only the given set of models should be played. Chris@43: */ Chris@43: 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: Chris@468: std::string getClientName() const { return m_clientName; } Chris@57: Chris@43: signals: Chris@43: void modelReplaced(); Chris@43: 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@43: void audioOverloadPluginDisabled(); Chris@130: void audioTimeStretchMultiChannelDisabled(); Chris@43: Chris@158: void activity(QString); Chris@158: Chris@43: public slots: Chris@43: void audioProcessingOverload(); Chris@43: Chris@43: protected slots: Chris@43: void selectionChanged(); Chris@43: void playLoopModeChanged(); Chris@43: void playSelectionModeChanged(); Chris@43: void playParametersChanged(PlayParameters *); Chris@43: void preferenceChanged(PropertyContainer::PropertyName); Chris@435: void modelChangedWithin(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@43: virtual ~RingBufferVector() { Chris@43: while (!empty()) { Chris@43: delete *begin(); Chris@43: erase(begin()); Chris@43: } Chris@43: } Chris@43: }; Chris@43: Chris@43: 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@434: sv_samplerate_t m_targetSampleRate; 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@43: RealTimePluginInstance *m_auditioningPlugin; Chris@43: bool m_auditioningPluginBypassed; 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@366: if (m_writeBuffers && c < (int)m_writeBuffers->size()) { Chris@43: return (*m_writeBuffers)[c]; Chris@43: } else { Chris@43: return 0; Chris@43: } Chris@43: } Chris@43: Chris@366: RingBuffer *getReadRingBuffer(int c) { Chris@43: RingBufferVector *rb = m_readBuffers; Chris@366: if (rb && c < (int)rb->size()) { Chris@43: return (*rb)[c]; Chris@43: } else { Chris@43: return 0; Chris@43: } Chris@43: } Chris@43: Chris@366: void clearRingBuffers(bool haveLock = false, int count = 0); Chris@43: void unifyRingBuffers(); Chris@43: Chris@62: RubberBand::RubberBandStretcher *m_timeStretcher; Chris@130: RubberBand::RubberBandStretcher *m_monoStretcher; Chris@436: double m_stretchRatio; Chris@130: bool m_stretchMono; Chris@91: Chris@436: int m_stretcherInputCount; Chris@91: float **m_stretcherInputs; Chris@436: sv_frame_t *m_stretcherInputSizes; Chris@43: Chris@43: // Called from fill thread, m_playing true, mutex held Chris@43: // 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@43: // Called from getSourceSamples. Chris@434: void applyAuditioningEffect(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@43: FillThread(AudioCallbackPlaySource &source) : Chris@43: Thread(Thread::NonRTThread), Chris@43: m_source(source) { } Chris@43: Chris@43: virtual void run(); Chris@43: Chris@43: protected: Chris@43: AudioCallbackPlaySource &m_source; Chris@43: }; Chris@43: Chris@43: QMutex m_mutex; Chris@43: QWaitCondition m_condition; Chris@43: FillThread *m_fillThread; Chris@43: }; Chris@43: Chris@43: #endif Chris@43: Chris@43: