From 856d42d4319fd19bdc2c686b1e92d7926b81ad5c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 17 Oct 2019 09:42:42 +1100 Subject: [PATCH] documented AudioHybridDevice --- app/audio/audiohybriddevice.h | 92 ++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/app/audio/audiohybriddevice.h b/app/audio/audiohybriddevice.h index 20e3979f5..c106b8273 100644 --- a/app/audio/audiohybriddevice.h +++ b/app/audio/audiohybriddevice.h @@ -1,10 +1,43 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + #ifndef AUDIOHYBRIDDEVICE_H #define AUDIOHYBRIDDEVICE_H #include -typedef float sample; - +/** + * @brief A device that can be connected to QAudioOutput and provides both "push" and "pull" functionality + * + * By default, a QAudioOutput works in either "push" or "pull" mode. Either it can automatically "pull" from a + * QIODevice (reading from it whenever it requires samples), or we can "push" samples to it constantly (on a timer and + * often in another thread) to make sure the buffer never underruns. + * + * This class functions as a "pull" device for the QAudioOutput, so the audio device automatically reads from it, but + * then provides both "pull" and "push" functionality to the rest of the system. A QIODevice (like a raw PCM file) + * can be connected and pulled from (good for continuous audio playback), and any amount of samples can also be pushed + * (good for short bursts of sound, e.g. audio scrubbing). + * + * This does not support any "queuing", running ConnectDevice() or Push() will immediately discard anything that's + * currently being sent to the device and replace it. + */ class AudioHybridDevice : public QIODevice { Q_OBJECT @@ -13,17 +46,72 @@ public: void Push(const QByteArray &samples); + /** + * @brief Stop all audio output + * + * Whatever is being done (pulling from QIODevice or samples), it is stopped and cleared placing this into + * "idle" state. + * + * Note that audio playback may not stop immediately after calling this function as the audio output may still have + * samples in its buffer to output (QAudioOutput::stop() should be called as well for more immediate feedback). This + * will prevent any further samples from being sent however. + * + * \see IsIdle() + */ void Stop(); + /** + * @brief Connect a QIODevice (e.g. QFile) to start sending to the audio output + * + * This will clear any pushed samples or QIODevices currently being read and will start reading from this next time + * the audio output requests data. + */ void ConnectDevice(QIODevice* device); + /** + * @brief Returns true if there are no more samples to be sent + * + * This is true if a device was connected or samples were pushed but we reached the end and no more data is available + * to be sent. + * + * In this state, this device will continue returning an empty buffer (all zeroes) to the device to workaround + * QAudioOutput's buffer underrun prevention with pushed samples. Therefore the QAudioOutput will never be put into + * QAudio::IdleState. Therefore only way to determine whether the output is no longer receiving usable audio is to + * check this function. + */ bool IsIdle(); signals: + /** + * @brief Signal emitted when this leaves "idle" state and has valid audio data that is ready to be sent + */ void HasSamples(); protected: + /** + * @brief Internal QIODevice function for reading + * + * Returns either QIODevice::read() from the connected device, or samples from the pushed sample buffer if there is + * none. + * + * If there is neither, this function returns a complete buffer of zeroes (i.e. silence). This is to allow + * pushed samples that don't fulfill the QAudioOutput's internal buffer size. If it cannot fill its internal buffer + * with the samples we send, it won't play anything until it receives more samples (enough to fill the buffer) + * in an attempt to prevent buffer underrun). + * + * Since we don't care about buffer underrun with short bursts of sound, we return silent samples if the QAudioOutput + * requests it. However this does mean the QAudioOutput never returns to QAudio::IdleState (since we never know if a + * read is just to fill the "remainder" of a buffer - which needs zeroes - or just a general read - which technically + * doesn't). It's recommended to check IsIdle() in tandem with the QAudioOutput::notify() signal to + * determine whether the QAudioOutput is effectively idle and can be stopped. + */ virtual qint64 readData(char *data, qint64 maxSize) override; + + /** + * @brief Internal QIODevice function for writing + * + * This class does not support writing, so this always returns -1 (QIODevice's documented error code for writing) + */ virtual qint64 writeData(const char *data, qint64 maxSize) override; private: