Chris@0: /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@0: A waveform viewer and audio annotation editor. Chris@2: Chris Cannam, Queen Mary University of London, 2005-2006 Chris@0: Chris@0: This is experimental software. Not for distribution. 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@0: #include "base/Scavenger.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include 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 IntegerTimeStretcher; 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@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: 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 will be available. 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 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@0: 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@0: void setSlowdownFactor(size_t factor); Chris@0: Chris@0: signals: Chris@0: void modelReplaced(); Chris@0: Chris@4: void playStatusChanged(bool isPlaying); Chris@4: Chris@0: /// Just a warning Chris@0: void sampleRateMismatch(size_t requested, size_t available); Chris@0: Chris@3: protected slots: Chris@3: void selectionChanged(); Chris@3: void playLoopModeChanged(); Chris@3: void playSelectionModeChanged(); Chris@3: Chris@0: protected: Chris@0: ViewManager *m_viewManager; Chris@0: AudioGenerator *m_audioGenerator; Chris@0: Chris@0: std::set m_models; Chris@0: std::vector *> m_buffers; Chris@0: size_t m_bufferCount; Chris@0: size_t m_blockSize; Chris@0: size_t m_sourceSampleRate; Chris@0: size_t m_targetSampleRate; Chris@0: size_t m_playLatency; Chris@0: bool m_playing; Chris@0: bool m_exiting; Chris@0: size_t m_bufferedToFrame; Chris@4: size_t m_lastModelEndFrame; Chris@0: static const size_t m_ringBufferSize; Chris@0: float m_outputLeft; Chris@0: float m_outputRight; Chris@0: Chris@0: RingBuffer &getRingBuffer(size_t c) { Chris@0: return *m_buffers[c]; Chris@0: } Chris@0: Chris@0: class TimeStretcherData Chris@0: { Chris@0: public: Chris@0: TimeStretcherData(size_t channels, size_t factor, size_t blockSize); Chris@0: ~TimeStretcherData(); Chris@0: Chris@0: size_t getFactor() const { return m_factor; } Chris@0: IntegerTimeStretcher *getStretcher(size_t channel); Chris@0: double *getOutputBuffer(size_t channel); Chris@0: double *getInputBuffer(); Chris@0: Chris@0: void run(size_t channel); Chris@0: Chris@0: protected: Chris@0: TimeStretcherData(const TimeStretcherData &); // not provided Chris@0: TimeStretcherData &operator=(const TimeStretcherData &); // not provided Chris@0: Chris@0: typedef std::pair StretcherBuffer; Chris@0: std::map m_stretcher; Chris@0: double *m_stretchInputBuffer; Chris@0: size_t m_factor; Chris@0: size_t m_blockSize; Chris@0: }; Chris@0: Chris@0: size_t m_slowdownCounter; Chris@0: TimeStretcherData *m_timeStretcher; Chris@0: Scavenger m_timeStretcherScavenger; Chris@0: Chris@4: // Called from fill thread, m_playing true, mutex held Chris@4: void fillBuffers(); Chris@4: Chris@4: // Called from fillBuffers Chris@3: bool mixModels(size_t &frame, size_t count, float **buffers); Chris@0: Chris@0: class AudioCallbackPlaySourceFillThread : public QThread Chris@0: { Chris@0: public: Chris@0: AudioCallbackPlaySourceFillThread(AudioCallbackPlaySource &source) : 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@0: AudioCallbackPlaySourceFillThread *m_fillThread; Chris@0: SRC_STATE *m_converter; Chris@0: }; Chris@0: Chris@0: #endif Chris@0: Chris@0: