Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: Sonic Visualiser Chris@0: An audio file viewer and annotation editor. Chris@0: Centre for Digital Music, Queen Mary, University of London. Chris@77: This file copyright 2006 Chris Cannam and QMUL. Chris@0: Chris@0: This program is free software; you can redistribute it and/or Chris@0: modify it under the terms of the GNU General Public License as Chris@0: published by the Free Software Foundation; either version 2 of the Chris@0: License, or (at your option) any later version. See the file Chris@0: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: #ifndef _AUDIO_CALLBACK_PLAY_SOURCE_H_ Chris@0: #define _AUDIO_CALLBACK_PLAY_SOURCE_H_ Chris@0: Chris@0: #include "base/RingBuffer.h" Chris@0: #include "base/AudioPlaySource.h" Chris@32: #include "base/PropertyContainer.h" Chris@0: #include "base/Scavenger.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include "base/Thread.h" Chris@0: Chris@0: #include Chris@0: Chris@0: #include Chris@0: #include Chris@0: Chris@0: class Model; Chris@0: class ViewManager; Chris@0: class AudioGenerator; Chris@0: class PlayParameters; Chris@14: class PhaseVocoderTimeStretcher; Chris@41: class RealTimePluginInstance; Chris@0: Chris@0: /** Chris@0: * AudioCallbackPlaySource manages audio data supply to callback-based Chris@0: * audio APIs such as JACK or CoreAudio. It maintains one ring buffer Chris@0: * per channel, filled during playback by a non-realtime thread, and Chris@0: * provides a method for a realtime thread to pick up the latest Chris@0: * available sample data from these buffers. Chris@0: */ Chris@0: class AudioCallbackPlaySource : public virtual QObject, Chris@0: public AudioPlaySource Chris@0: { Chris@0: Q_OBJECT Chris@0: Chris@0: public: Chris@0: AudioCallbackPlaySource(ViewManager *); Chris@0: virtual ~AudioCallbackPlaySource(); Chris@0: Chris@0: /** Chris@0: * Add a data model to be played from. The source can mix Chris@0: * playback from a number of sources including dense and sparse Chris@0: * models. The models must match in sample rate, but they don't Chris@0: * have to have identical numbers of channels. Chris@0: */ Chris@0: virtual void addModel(Model *model); Chris@0: Chris@0: /** Chris@0: * Remove a model. Chris@0: */ Chris@0: virtual void removeModel(Model *model); Chris@0: Chris@0: /** Chris@0: * Remove all models. (Silence will ensue.) Chris@0: */ Chris@0: virtual void clearModels(); Chris@0: Chris@0: /** Chris@0: * Start making data available in the ring buffers for playback, Chris@0: * from the given frame. If playback is already under way, reseek Chris@0: * to the given frame and continue. Chris@0: */ Chris@0: virtual void play(size_t startFrame); Chris@0: Chris@0: /** Chris@0: * Stop playback and ensure that no more data is returned. Chris@0: */ Chris@0: virtual void stop(); Chris@0: Chris@0: /** Chris@0: * Return whether playback is currently supposed to be happening. Chris@0: */ Chris@0: virtual bool isPlaying() const { return m_playing; } Chris@0: Chris@0: /** Chris@0: * Return the frame number that is currently expected to be coming Chris@0: * out of the speakers. (i.e. compensating for playback latency.) Chris@0: */ Chris@0: virtual size_t getCurrentPlayingFrame(); Chris@0: Chris@0: /** Chris@116: * Return the frame at which playback is expected to end (if not looping). Chris@116: */ Chris@116: virtual size_t getPlayEndFrame() { return m_lastModelEndFrame; } Chris@116: Chris@116: /** Chris@0: * Set the block size of the target audio device. This should Chris@0: * be called by the target class. Chris@0: */ Chris@0: void setTargetBlockSize(size_t); Chris@0: Chris@0: /** Chris@0: * Get the block size of the target audio device. Chris@0: */ Chris@0: size_t getTargetBlockSize() const; Chris@0: Chris@0: /** Chris@0: * Set the playback latency of the target audio device, in frames Chris@0: * at the target sample rate. This is the difference between the Chris@0: * frame currently "leaving the speakers" and the last frame (or Chris@0: * highest last frame across all channels) requested via Chris@0: * getSamples(). The default is zero. Chris@0: */ Chris@0: void setTargetPlayLatency(size_t); Chris@0: Chris@0: /** Chris@0: * Get the playback latency of the target audio device. Chris@0: */ Chris@0: size_t getTargetPlayLatency() const; Chris@0: Chris@0: /** Chris@0: * Specify that the target audio device has a fixed sample rate Chris@0: * (i.e. cannot accommodate arbitrary sample rates based on the Chris@0: * source). If the target sets this to something other than the Chris@0: * source sample rate, this class will resample automatically to Chris@0: * fit. Chris@0: */ Chris@0: void setTargetSampleRate(size_t); Chris@0: Chris@0: /** Chris@0: * Return the sample rate set by the target audio device (or the Chris@0: * source sample rate if the target hasn't set one). Chris@0: */ Chris@0: virtual size_t getTargetSampleRate() const; Chris@0: Chris@0: /** Chris@0: * Set the current output levels for metering (for call from the Chris@0: * target) Chris@0: */ Chris@0: void setOutputLevels(float left, float right); Chris@0: Chris@0: /** Chris@0: * Return the current (or thereabouts) output levels in the range Chris@0: * 0.0 -> 1.0, for metering purposes. Chris@0: */ Chris@0: virtual bool getOutputLevels(float &left, float &right); Chris@0: Chris@0: /** Chris@0: * Get the number of channels of audio that in the source models. Chris@0: * This may safely be called from a realtime thread. Returns 0 if Chris@0: * there is no source yet available. Chris@0: */ Chris@0: size_t getSourceChannelCount() const; Chris@0: Chris@0: /** Chris@0: * Get the number of channels of audio that will be provided Chris@0: * to the play target. This may be more than the source channel Chris@0: * count: for example, a mono source will provide 2 channels Chris@0: * after pan. Chris@0: * This may safely be called from a realtime thread. Returns 0 if Chris@0: * there is no source yet available. Chris@0: */ Chris@0: size_t getTargetChannelCount() const; Chris@0: Chris@0: /** Chris@0: * Get the actual sample rate of the source material. This may Chris@0: * safely be called from a realtime thread. Returns 0 if there is Chris@0: * no source yet available. Chris@0: */ Chris@118: virtual size_t getSourceSampleRate() const; Chris@0: Chris@0: /** Chris@0: * Get "count" samples (at the target sample rate) of the mixed Chris@0: * audio data, in all channels. This may safely be called from a Chris@0: * realtime thread. Chris@0: */ Chris@0: size_t getSourceSamples(size_t count, float **buffer); Chris@0: Chris@32: /** Chris@32: * Set the time stretcher factor (i.e. playback speed). Also Chris@32: * specify whether the time stretcher will be variable rate Chris@32: * (sharpening transients), and whether time stretching will be Chris@32: * carried out on data mixed down to mono for speed. Chris@32: */ Chris@26: void setTimeStretch(float factor, bool sharpen, bool mono); Chris@0: Chris@32: /** Chris@32: * Set the resampler quality, 0 - 2 where 0 is fastest and 2 is Chris@32: * highest quality. Chris@32: */ Chris@32: void setResampleQuality(int q); Chris@32: Chris@41: /** Chris@41: * Set a single real-time plugin as a processing effect for Chris@41: * auditioning during playback. Chris@41: * Chris@41: * The plugin must have been initialised with Chris@41: * getTargetChannelCount() channels and a getTargetBlockSize() Chris@41: * sample frame processing block size. Chris@41: * Chris@41: * This playback source takes ownership of the plugin, which will Chris@41: * be deleted at some point after the following call to Chris@41: * setAuditioningPlugin (depending on real-time constraints). Chris@41: * Chris@41: * Pass a null pointer to remove the current auditioning plugin, Chris@41: * if any. Chris@41: */ Chris@41: void setAuditioningPlugin(RealTimePluginInstance *plugin); Chris@41: Chris@180: /** Chris@180: * Specify that only the given set of models should be played. Chris@180: */ Chris@180: void setSoloModelSet(std::sets); Chris@180: Chris@180: /** Chris@180: * Specify that all models should be played as normal (if not Chris@180: * muted). Chris@180: */ Chris@180: void clearSoloModelSet(); Chris@180: Chris@0: signals: Chris@0: void modelReplaced(); Chris@0: Chris@0: void playStatusChanged(bool isPlaying); Chris@0: Chris@0: void sampleRateMismatch(size_t requested, size_t available, bool willResample); Chris@0: Chris@42: void audioOverloadPluginDisabled(); Chris@42: Chris@42: public slots: Chris@42: void audioProcessingOverload(); Chris@42: Chris@0: protected slots: Chris@0: void selectionChanged(); Chris@0: void playLoopModeChanged(); Chris@0: void playSelectionModeChanged(); Chris@0: void playParametersChanged(PlayParameters *); Chris@32: void preferenceChanged(PropertyContainer::PropertyName); Chris@148: void modelChanged(size_t startFrame, size_t endFrame); Chris@0: Chris@0: protected: Chris@0: ViewManager *m_viewManager; Chris@0: AudioGenerator *m_audioGenerator; Chris@0: Chris@0: class RingBufferVector : public std::vector *> { Chris@0: public: Chris@0: virtual ~RingBufferVector() { Chris@0: while (!empty()) { Chris@0: delete *begin(); Chris@0: erase(begin()); Chris@0: } Chris@0: } Chris@0: }; Chris@0: Chris@41: std::set m_models; Chris@41: RingBufferVector *m_readBuffers; Chris@41: RingBufferVector *m_writeBuffers; Chris@41: size_t m_readBufferFill; Chris@41: size_t m_writeBufferFill; Chris@41: Scavenger m_bufferScavenger; Chris@41: size_t m_sourceChannelCount; Chris@41: size_t m_blockSize; Chris@41: size_t m_sourceSampleRate; Chris@41: size_t m_targetSampleRate; Chris@41: size_t m_playLatency; Chris@41: bool m_playing; Chris@41: bool m_exiting; Chris@41: size_t m_lastModelEndFrame; Chris@41: static const size_t m_ringBufferSize; Chris@41: float m_outputLeft; Chris@41: float m_outputRight; Chris@41: RealTimePluginInstance *m_auditioningPlugin; Chris@42: bool m_auditioningPluginBypassed; Chris@41: Scavenger m_pluginScavenger; Chris@0: Chris@0: RingBuffer *getWriteRingBuffer(size_t c) { Chris@0: if (m_writeBuffers && c < m_writeBuffers->size()) { Chris@0: return (*m_writeBuffers)[c]; Chris@0: } else { Chris@0: return 0; Chris@0: } Chris@0: } Chris@0: Chris@0: RingBuffer *getReadRingBuffer(size_t c) { Chris@0: RingBufferVector *rb = m_readBuffers; Chris@0: if (rb && c < rb->size()) { Chris@0: return (*rb)[c]; Chris@0: } else { Chris@0: return 0; Chris@0: } Chris@0: } Chris@0: Chris@0: void clearRingBuffers(bool haveLock = false, size_t count = 0); Chris@0: void unifyRingBuffers(); Chris@0: Chris@16: PhaseVocoderTimeStretcher *m_timeStretcher; Chris@16: Scavenger m_timeStretcherScavenger; Chris@0: Chris@0: // Called from fill thread, m_playing true, mutex held Chris@0: // Return true if work done Chris@0: bool fillBuffers(); Chris@0: Chris@0: // Called from fillBuffers. Return the number of frames written, Chris@0: // which will be count or fewer. Return in the frame argument the Chris@0: // new buffered frame position (which may be earlier than the Chris@0: // frame argument passed in, in the case of looping). Chris@0: size_t mixModels(size_t &frame, size_t count, float **buffers); Chris@0: Chris@41: // Called from getSourceSamples. Chris@41: void applyAuditioningEffect(size_t count, float **buffers); Chris@41: Chris@127: class FillThread : public Thread Chris@0: { Chris@0: public: Chris@127: FillThread(AudioCallbackPlaySource &source) : Chris@0: Thread(Thread::NonRTThread), Chris@0: m_source(source) { } Chris@0: Chris@0: virtual void run(); Chris@0: Chris@0: protected: Chris@0: AudioCallbackPlaySource &m_source; Chris@0: }; Chris@0: Chris@0: QMutex m_mutex; Chris@0: QWaitCondition m_condition; Chris@127: FillThread *m_fillThread; Chris@0: SRC_STATE *m_converter; Chris@32: SRC_STATE *m_crapConverter; // for use when playing very fast Chris@32: int m_resampleQuality; Chris@32: void initialiseConverter(); Chris@0: }; Chris@0: Chris@0: #endif Chris@0: Chris@0: