Chris@19: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@21: Sonic Visualiser Chris@21: An audio file viewer and annotation editor. Chris@21: Centre for Digital Music, Queen Mary, University of London. Chris@21: This file copyright 2006 Chris Cannam. Chris@0: Chris@21: This program is free software; you can redistribute it and/or Chris@21: modify it under the terms of the GNU General Public License as Chris@21: published by the Free Software Foundation; either version 2 of the Chris@21: License, or (at your option) any later version. See the file Chris@21: 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@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@12: class PlayParameters; 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@17: 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@13: * 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@13: * Get the number of channels of audio that will be provided Chris@13: * to the play target. This may be more than the source channel Chris@13: * count: for example, a mono source will provide 2 channels Chris@13: * after pan. Chris@13: * This may safely be called from a realtime thread. Returns 0 if Chris@13: * there is no source yet available. Chris@13: */ Chris@13: size_t getTargetChannelCount() const; Chris@13: Chris@13: /** 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@25: void sampleRateMismatch(size_t requested, size_t available, bool willResample); Chris@0: Chris@3: protected slots: Chris@3: void selectionChanged(); Chris@3: void playLoopModeChanged(); Chris@3: void playSelectionModeChanged(); Chris@12: void playParametersChanged(PlayParameters *); Chris@3: Chris@0: protected: Chris@0: ViewManager *m_viewManager; Chris@0: AudioGenerator *m_audioGenerator; Chris@0: Chris@6: class RingBufferVector : public std::vector *> { Chris@6: public: Chris@6: virtual ~RingBufferVector() { Chris@6: while (!empty()) { Chris@6: delete *begin(); Chris@6: erase(begin()); Chris@6: } Chris@6: } Chris@6: }; Chris@6: Chris@0: std::set m_models; Chris@6: RingBufferVector *m_readBuffers; Chris@6: RingBufferVector *m_writeBuffers; Chris@13: size_t m_readBufferFill; Chris@13: size_t m_writeBufferFill; Chris@6: Scavenger m_bufferScavenger; Chris@6: size_t m_sourceChannelCount; 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@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@6: RingBuffer *getWriteRingBuffer(size_t c) { Chris@6: if (m_writeBuffers && c < m_writeBuffers->size()) { Chris@6: return (*m_writeBuffers)[c]; Chris@6: } else { Chris@6: return 0; Chris@6: } Chris@0: } Chris@0: Chris@6: RingBuffer *getReadRingBuffer(size_t c) { Chris@6: RingBufferVector *rb = m_readBuffers; Chris@6: if (rb && c < rb->size()) { Chris@6: return (*rb)[c]; Chris@6: } else { Chris@6: return 0; Chris@6: } Chris@6: } Chris@6: Chris@6: void clearRingBuffers(bool haveLock = false, size_t count = 0); Chris@13: void unifyRingBuffers(); Chris@6: 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@7: // Return true if work done Chris@7: bool fillBuffers(); Chris@4: Chris@6: // Called from fillBuffers. Return the number of frames written, Chris@6: // which will be count or fewer. Return in the frame argument the Chris@6: // new buffered frame position (which may be earlier than the Chris@6: // frame argument passed in, in the case of looping). Chris@6: size_t 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: