diff audioio/AudioCallbackPlaySource.h @ 0:db6fcbd4405c

initial import
author Chris Cannam
date Tue, 10 Jan 2006 16:33:16 +0000
parents
children df5923e33d01
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audioio/AudioCallbackPlaySource.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,242 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _AUDIO_CALLBACK_PLAY_SOURCE_H_
+#define _AUDIO_CALLBACK_PLAY_SOURCE_H_
+
+#include "base/RingBuffer.h"
+#include "base/AudioPlaySource.h"
+#include "base/Scavenger.h"
+
+#include <QObject>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QThread>
+
+#include <samplerate.h>
+
+#include <set>
+#include <map>
+
+class Model;
+class ViewManager;
+class AudioGenerator;
+class IntegerTimeStretcher;
+
+/**
+ * AudioCallbackPlaySource manages audio data supply to callback-based
+ * audio APIs such as JACK or CoreAudio.  It maintains one ring buffer
+ * per channel, filled during playback by a non-realtime thread, and
+ * provides a method for a realtime thread to pick up the latest
+ * available sample data from these buffers.
+ */
+class AudioCallbackPlaySource : public virtual QObject,
+				public AudioPlaySource
+{
+    Q_OBJECT
+
+public:
+    AudioCallbackPlaySource(ViewManager *);
+    virtual ~AudioCallbackPlaySource();
+    
+    /**
+     * Add a data model to be played from.  The source can mix
+     * playback from a number of sources including dense and sparse
+     * models.  The models must match in sample rate, but they don't
+     * have to have identical numbers of channels.
+     */
+    virtual void addModel(Model *model);
+
+    /**
+     * Remove a model.
+     */
+    virtual void removeModel(Model *model);
+
+    /**
+     * Remove all models.  (Silence will ensue.)
+     */
+    virtual void clearModels();
+
+    /**
+     * Start making data available in the ring buffers for playback,
+     * from the given frame.  If playback is already under way, reseek
+     * to the given frame and continue.
+     */
+    virtual void play(size_t startFrame);
+
+    /**
+     * Stop playback and ensure that no more data is returned.
+     */
+    virtual void stop();
+
+    /**
+     * Return whether playback is currently supposed to be happening.
+     */
+    virtual bool isPlaying() const { return m_playing; }
+
+    /**
+     * Return the frame number that is currently expected to be coming
+     * out of the speakers.  (i.e. compensating for playback latency.)
+     */
+    virtual size_t getCurrentPlayingFrame();
+
+    /**
+     * Set the block size of the target audio device.  This should
+     * be called by the target class.
+     */
+    void setTargetBlockSize(size_t);
+
+    /**
+     * Get the block size of the target audio device.
+     */
+    size_t getTargetBlockSize() const;
+
+    /**
+     * Set the playback latency of the target audio device, in frames
+     * at the target sample rate.  This is the difference between the
+     * frame currently "leaving the speakers" and the last frame (or
+     * highest last frame across all channels) requested via
+     * getSamples().  The default is zero.
+     */
+    void setTargetPlayLatency(size_t);
+
+    /**
+     * Get the playback latency of the target audio device.
+     */
+    size_t getTargetPlayLatency() const;
+
+    /**
+     * Specify that the target audio device has a fixed sample rate
+     * (i.e. cannot accommodate arbitrary sample rates based on the
+     * source).  If the target sets this to something other than the
+     * source sample rate, this class will resample automatically to
+     * fit.
+     */
+    void setTargetSampleRate(size_t);
+
+    /**
+     * Return the sample rate set by the target audio device (or the
+     * source sample rate if the target hasn't set one).
+     */
+    size_t getTargetSampleRate() const;
+
+    /**
+     * Set the current output levels for metering (for call from the
+     * target)
+     */
+    void setOutputLevels(float left, float right);
+
+    /**
+     * Return the current (or thereabouts) output levels in the range
+     * 0.0 -> 1.0, for metering purposes.
+     */
+    virtual bool getOutputLevels(float &left, float &right);
+
+    /**
+     * Get the number of channels of audio that will be available.
+     * This may safely be called from a realtime thread.  Returns 0 if
+     * there is no source yet available.
+     */
+    size_t getSourceChannelCount() const;
+
+    /**
+     * Get the actual sample rate of the source material.  This may
+     * safely be called from a realtime thread.  Returns 0 if there is
+     * no source yet available.
+     */
+    size_t getSourceSampleRate() const;
+
+    /**
+     * Get "count" samples (at the target sample rate) of the mixed
+     * audio data, in all channels.  This may safely be called from a
+     * realtime thread.
+     */
+    size_t getSourceSamples(size_t count, float **buffer);
+
+    void setSlowdownFactor(size_t factor);
+
+signals:
+    void modelReplaced();
+
+    /// Just a warning
+    void sampleRateMismatch(size_t requested, size_t available);
+
+protected:
+    ViewManager                     *m_viewManager;
+    AudioGenerator                  *m_audioGenerator;
+
+    std::set<Model *>                m_models;
+    std::vector<RingBuffer<float> *> m_buffers;
+    size_t                           m_bufferCount;
+    size_t                           m_blockSize;
+    size_t                           m_sourceSampleRate;
+    size_t                           m_targetSampleRate;
+    size_t                           m_playLatency;
+    bool                             m_playing;
+    bool                             m_exiting;
+    size_t                           m_bufferedToFrame;
+    static const size_t              m_ringBufferSize;
+    float                            m_outputLeft;
+    float                            m_outputRight;
+
+    RingBuffer<float> &getRingBuffer(size_t c) {
+	return *m_buffers[c];
+    }
+
+    class TimeStretcherData
+    {
+    public:
+	TimeStretcherData(size_t channels, size_t factor, size_t blockSize);
+	~TimeStretcherData();
+
+	size_t getFactor() const { return m_factor; }
+	IntegerTimeStretcher *getStretcher(size_t channel);
+	double *getOutputBuffer(size_t channel);
+	double *getInputBuffer();
+	
+	void run(size_t channel);
+
+    protected:
+	TimeStretcherData(const TimeStretcherData &); // not provided
+	TimeStretcherData &operator=(const TimeStretcherData &); // not provided
+
+	typedef std::pair<IntegerTimeStretcher *, double *> StretcherBuffer;
+	std::map<size_t, StretcherBuffer> m_stretcher;
+	double *m_stretchInputBuffer;
+	size_t m_factor;
+	size_t m_blockSize;
+    };
+
+    size_t m_slowdownCounter;
+    TimeStretcherData *m_timeStretcher;
+    Scavenger<TimeStretcherData> m_timeStretcherScavenger;
+
+    void fillBuffers(); // Called from fill thread, m_playing true, mutex held
+
+    class AudioCallbackPlaySourceFillThread : public QThread
+    {
+    public:
+	AudioCallbackPlaySourceFillThread(AudioCallbackPlaySource &source) :
+	    m_source(source) { }
+
+	virtual void run();
+
+    protected:
+	AudioCallbackPlaySource &m_source;
+    };
+
+    QMutex m_mutex;
+    QWaitCondition m_condition;
+    AudioCallbackPlaySourceFillThread *m_fillThread;
+    SRC_STATE *m_converter;
+};
+
+#endif
+
+