diff audioio/AudioCallbackPlaySource.h @ 0:cd5d7ff8ef38

* Reorganising code base. This revision will not compile.
author Chris Cannam
date Mon, 31 Jul 2006 12:03:45 +0000
parents
children ee967635c728
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audioio/AudioCallbackPlaySource.h	Mon Jul 31 12:03:45 2006 +0000
@@ -0,0 +1,306 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#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 "base/Thread.h"
+
+#include <samplerate.h>
+
+#include <set>
+#include <map>
+
+class Model;
+class ViewManager;
+class AudioGenerator;
+class PlayParameters;
+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).
+     */
+    virtual 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 in the source models.
+     * This may safely be called from a realtime thread.  Returns 0 if
+     * there is no source yet available.
+     */
+    size_t getSourceChannelCount() const;
+
+    /**
+     * Get the number of channels of audio that will be provided
+     * to the play target.  This may be more than the source channel
+     * count: for example, a mono source will provide 2 channels
+     * after pan.
+     * This may safely be called from a realtime thread.  Returns 0 if
+     * there is no source yet available.
+     */
+    size_t getTargetChannelCount() 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();
+
+    void playStatusChanged(bool isPlaying);
+
+    void sampleRateMismatch(size_t requested, size_t available, bool willResample);
+
+protected slots:
+    void selectionChanged();
+    void playLoopModeChanged();
+    void playSelectionModeChanged();
+    void playParametersChanged(PlayParameters *);
+
+protected:
+    ViewManager                     *m_viewManager;
+    AudioGenerator                  *m_audioGenerator;
+
+    class RingBufferVector : public std::vector<RingBuffer<float> *> {
+    public:
+	virtual ~RingBufferVector() {
+	    while (!empty()) {
+		delete *begin();
+		erase(begin());
+	    }
+	}
+    };
+
+    std::set<Model *>                m_models;
+    RingBufferVector                *m_readBuffers;
+    RingBufferVector                *m_writeBuffers;
+    size_t                           m_readBufferFill;
+    size_t                           m_writeBufferFill;
+    Scavenger<RingBufferVector>      m_bufferScavenger;
+    size_t                           m_sourceChannelCount;
+    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_lastModelEndFrame;
+    static const size_t              m_ringBufferSize;
+    float                            m_outputLeft;
+    float                            m_outputRight;
+
+    RingBuffer<float> *getWriteRingBuffer(size_t c) {
+	if (m_writeBuffers && c < m_writeBuffers->size()) {
+	    return (*m_writeBuffers)[c];
+	} else {
+	    return 0;
+	}
+    }
+
+    RingBuffer<float> *getReadRingBuffer(size_t c) {
+	RingBufferVector *rb = m_readBuffers;
+	if (rb && c < rb->size()) {
+	    return (*rb)[c];
+	} else {
+	    return 0;
+	}
+    }
+
+    void clearRingBuffers(bool haveLock = false, size_t count = 0);
+    void unifyRingBuffers();
+
+    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);
+	float *getOutputBuffer(size_t channel);
+	float *getInputBuffer();
+	
+	void run(size_t channel);
+
+    protected:
+	TimeStretcherData(const TimeStretcherData &); // not provided
+	TimeStretcherData &operator=(const TimeStretcherData &); // not provided
+
+	typedef std::pair<IntegerTimeStretcher *, float *> StretcherBuffer;
+	std::map<size_t, StretcherBuffer> m_stretcher;
+	float *m_stretchInputBuffer;
+	size_t m_factor;
+	size_t m_blockSize;
+    };
+
+    size_t m_slowdownCounter;
+    TimeStretcherData *m_timeStretcher;
+    Scavenger<TimeStretcherData> m_timeStretcherScavenger;
+
+    // Called from fill thread, m_playing true, mutex held
+    // Return true if work done
+    bool fillBuffers();
+    
+    // Called from fillBuffers.  Return the number of frames written,
+    // which will be count or fewer.  Return in the frame argument the
+    // new buffered frame position (which may be earlier than the
+    // frame argument passed in, in the case of looping).
+    size_t mixModels(size_t &frame, size_t count, float **buffers);
+
+    class AudioCallbackPlaySourceFillThread : public Thread
+    {
+    public:
+	AudioCallbackPlaySourceFillThread(AudioCallbackPlaySource &source) :
+            Thread(Thread::NonRTThread),
+	    m_source(source) { }
+
+	virtual void run();
+
+    protected:
+	AudioCallbackPlaySource &m_source;
+    };
+
+    QMutex m_mutex;
+    QWaitCondition m_condition;
+    AudioCallbackPlaySourceFillThread *m_fillThread;
+    SRC_STATE *m_converter;
+};
+
+#endif
+
+