diff options
Diffstat (limited to 'src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp')
-rw-r--r-- | src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp | 487 |
1 files changed, 0 insertions, 487 deletions
diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp b/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp deleted file mode 100644 index 82456e0b6..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwasmaudiosink_p.h" - - -#include <emscripten.h> -#include <AL/al.h> -#include <AL/alc.h> -#include <QDebug> -#include <QtMath> -#include <QIODevice> - -// non native al formats (AL_EXT_float32) -#define AL_FORMAT_MONO_FLOAT32 0x10010 -#define AL_FORMAT_STEREO_FLOAT32 0x10011 - -constexpr unsigned int DEFAULT_BUFFER_DURATION = 50'000; - -class ALData { -public: - ALCcontext *context = nullptr; - ALCdevice *device = nullptr; - ALuint source; - ALuint *buffers = nullptr; - ALuint *buffer = nullptr; - ALenum format; -}; - -QT_BEGIN_NAMESPACE - -class QWasmAudioSinkDevice : public QIODevice { - - QWasmAudioSink *m_out; - -public: - QWasmAudioSinkDevice(QWasmAudioSink *parent); - -protected: - qint64 readData(char *data, qint64 maxlen) override; - qint64 writeData(const char *data, qint64 len) override; -}; - -QWasmAudioSink::QWasmAudioSink(const QByteArray &device) : m_name(device) -{ - m_timer.setSingleShot(false); - aldata = new ALData(); - connect(&m_timer, &QTimer::timeout, this, [this](){ - if (m_pullMode) - nextALBuffers(); - else { - unloadALBuffers(); - m_device->write(nullptr, 0); - updateState(); - } - }); -} - -QWasmAudioSink::~QWasmAudioSink() -{ - delete aldata; - if (m_tmpData) - delete[] m_tmpData; -} - -void QWasmAudioSink::start(QIODevice *device) -{ - Q_ASSERT(device); - Q_ASSERT(device->openMode().testFlag(QIODevice::ReadOnly)); - m_device = device; - start(true); -} - -QIODevice *QWasmAudioSink::start() -{ - m_device = new QWasmAudioSinkDevice(this); - m_device->open(QIODevice::WriteOnly); - start(false); - return m_device; -} - -void QWasmAudioSink::start(bool mode) -{ - auto formatError = [this](){ - qWarning() << "Unsupported audio format " << m_format; - setError(QAudio::OpenError); - }; - switch (m_format.sampleFormat()) { - case QAudioFormat::UInt8: - switch (m_format.channelCount()) { - case 1: - aldata->format = AL_FORMAT_MONO8; - break; - case 2: - aldata->format = AL_FORMAT_STEREO8; - break; - default: - return formatError(); - } - break; - case QAudioFormat::Int16: - switch (m_format.channelCount()) { - case 1: - aldata->format = AL_FORMAT_MONO16; - break; - case 2: - aldata->format = AL_FORMAT_STEREO16; - break; - default: - return formatError(); - } - break; - case QAudioFormat::Float: - switch (m_format.channelCount()) { - case 1: - aldata->format = AL_FORMAT_MONO_FLOAT32; - break; - case 2: - aldata->format = AL_FORMAT_STEREO_FLOAT32; - break; - default: - return formatError(); - } - break; - default: - return formatError(); - } - - alGetError(); - aldata->device = alcOpenDevice(m_name.data()); - if (!aldata->device) { - qWarning() << "Failed to open audio device" << alGetString(alGetError()); - return setError(QAudio::OpenError); - } - ALint attrlist[] = {ALC_FREQUENCY, m_format.sampleRate(), 0}; - aldata->context = alcCreateContext(aldata->device, attrlist); - - if (!aldata->context) { - qWarning() << "Failed to create audio context" << alGetString(alGetError()); - return setError(QAudio::OpenError); - } - alcMakeContextCurrent(aldata->context); - - - alGenSources(1, &aldata->source); - - if (m_bufferSize > 0) - m_bufferFragmentsCount = qMax(2,qCeil((qreal)m_bufferSize/(m_bufferFragmentSize))); - m_bufferSize = m_bufferFragmentsCount * m_bufferFragmentSize; - aldata->buffers = new ALuint[m_bufferFragmentsCount]; - aldata->buffer = aldata->buffers; - alGenBuffers(m_bufferFragmentsCount, aldata->buffers); - m_processed = 0; - m_tmpDataOffset = 0; - m_pullMode = mode; - alSourcef(aldata->source, AL_GAIN, m_volume); - if (m_pullMode) - loadALBuffers(); - m_timer.setInterval(DEFAULT_BUFFER_DURATION / 3000); - m_timer.start(); - if (m_pullMode) - alSourcePlay(aldata->source); - m_running = true; - m_elapsedTimer.start(); - updateState(); -} - -void QWasmAudioSink::stop() -{ - if (!m_running) - return; - m_elapsedTimer.invalidate(); - alSourceStop(aldata->source); - alSourceRewind(aldata->source); - m_timer.stop(); - m_bufferFragmentsBusyCount = 0; - alDeleteSources(1, &aldata->source); - alDeleteBuffers(m_bufferFragmentsCount, aldata->buffers); - delete[] aldata->buffers; - alcMakeContextCurrent(nullptr); - alcDestroyContext(aldata->context); - alcCloseDevice(aldata->device); - m_running = false; - m_processed = 0; - if (!m_pullMode) - m_device->deleteLater(); - updateState(); -} - -void QWasmAudioSink::reset() -{ - stop(); - m_error = QAudio::NoError; -} - -void QWasmAudioSink::suspend() -{ - if (!m_running) - return; - - alSourcePause(aldata->source); -} - -void QWasmAudioSink::resume() -{ - if (!m_running) - return; - - alSourcePlay(aldata->source); -} - -int QWasmAudioSink::bytesFree() const -{ - int processed; - alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed); - return m_running ? m_bufferFragmentSize * (m_bufferFragmentsCount - m_bufferFragmentsBusyCount - + processed) : 0; -} - -void QWasmAudioSink::setBufferSize(int value) -{ - if (m_running) - return; - - m_bufferSize = value; -} - -int QWasmAudioSink::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QWasmAudioSink::processedUSecs() const -{ - int processed; - alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed); - return m_format.durationForBytes(m_processed + m_format.bytesForDuration( - DEFAULT_BUFFER_DURATION * processed)); -} - -QAudio::Error QWasmAudioSink::error() const -{ - return m_error; -} - -QAudio::State QWasmAudioSink::state() const -{ - if (!m_running) - return QAudio::StoppedState; - ALint state; - alGetSourcei(aldata->source, AL_SOURCE_STATE, &state); - switch (state) { - case AL_INITIAL: - return QAudio::IdleState; - case AL_PLAYING: - return QAudio::ActiveState; - case AL_PAUSED: - return QAudio::SuspendedState; - case AL_STOPPED: - return m_running ? QAudio::IdleState : QAudio::StoppedState; - } - return QAudio::StoppedState; -} - -void QWasmAudioSink::setFormat(const QAudioFormat &fmt) -{ - if (m_running) - return; - m_format = fmt; - if (m_tmpData) - delete[] m_tmpData; - m_bufferFragmentSize = m_format.bytesForDuration(DEFAULT_BUFFER_DURATION); - m_bufferSize = m_bufferFragmentSize * m_bufferFragmentsCount; - m_tmpData = new char[m_bufferFragmentSize]; -} - -QAudioFormat QWasmAudioSink::format() const -{ - return m_format; -} - -void QWasmAudioSink::setVolume(qreal volume) -{ - if (m_volume == volume) - return; - m_volume = volume; - if (m_running) - alSourcef(aldata->source, AL_GAIN, volume); -} - -qreal QWasmAudioSink::volume() const -{ - return m_volume; -} - -void QWasmAudioSink::loadALBuffers() -{ - if (m_bufferFragmentsBusyCount == m_bufferFragmentsCount) - return; - - auto size = m_device->read(m_tmpData + m_tmpDataOffset, m_bufferFragmentSize - - m_tmpDataOffset); - m_tmpDataOffset += size; - if (!m_tmpDataOffset || (m_tmpDataOffset != m_bufferFragmentSize && - m_bufferFragmentsBusyCount >= m_bufferFragmentsCount * 2 / 3)) - return; - - alBufferData(*aldata->buffer, aldata->format, m_tmpData, m_tmpDataOffset, - m_format.sampleRate()); - m_tmpDataOffset = 0; - alGetError(); - alSourceQueueBuffers(aldata->source, 1, aldata->buffer); - if (alGetError()) - return; - - m_bufferFragmentsBusyCount++; - m_processed += size; - if (++aldata->buffer == aldata->buffers + m_bufferFragmentsCount) - aldata->buffer = aldata->buffers; -} - -void QWasmAudioSink::unloadALBuffers() -{ - int processed; - alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed); - - if (processed) { - auto head = aldata->buffer - m_bufferFragmentsBusyCount; - if (head < aldata->buffers) { - if (head + processed > aldata->buffers) { - auto batch = m_bufferFragmentsBusyCount - (aldata->buffer - aldata->buffers); - alGetError(); - alSourceUnqueueBuffers(aldata->source, batch, head + m_bufferFragmentsCount); - if (!alGetError()) { - m_bufferFragmentsBusyCount -= batch; - m_processed += m_bufferFragmentSize*batch; - } - processed -= batch; - if (!processed) - return; - head = aldata->buffers; - } else { - head += m_bufferFragmentsCount; - } - } - alGetError(); - alSourceUnqueueBuffers(aldata->source, processed, head); - if (!alGetError()) - m_bufferFragmentsBusyCount -= processed; - } -} - -void QWasmAudioSink::nextALBuffers() -{ - updateState(); - unloadALBuffers(); - loadALBuffers(); - ALint state; - alGetSourcei(aldata->source, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING) - alSourcePlay(aldata->source); - updateState(); -} - -void QWasmAudioSink::updateState() -{ - auto current = state(); - if (m_state == current) - return; - m_state = current; - - if (m_state == QAudio::IdleState && m_running) - setError(QAudio::UnderrunError); - - emit stateChanged(m_state); - -} - -void QWasmAudioSink::setError(QAudio::Error error) -{ - if (error == m_error) - return; - m_error = error; - emit errorChanged(error); -} - -QWasmAudioSinkDevice::QWasmAudioSinkDevice(QWasmAudioSink *parent) : QIODevice(parent), - m_out(parent) -{ -} - -qint64 QWasmAudioSinkDevice::readData(char *data, qint64 maxlen) -{ - Q_UNUSED(data) - Q_UNUSED(maxlen) - return 0; -} - - -qint64 QWasmAudioSinkDevice::writeData(const char *data, qint64 len) -{ - ALint state; - alGetSourcei(m_out->aldata->source, AL_SOURCE_STATE, &state); - if (state != AL_INITIAL) - m_out->unloadALBuffers(); - if (m_out->m_bufferFragmentsBusyCount < m_out->m_bufferFragmentsCount) { - bool exceeds = m_out->m_tmpDataOffset + len > m_out->m_bufferFragmentSize; - bool flush = m_out->m_bufferFragmentsBusyCount < m_out->m_bufferFragmentsCount * 2 / 3 || - m_out->m_tmpDataOffset + len >= m_out->m_bufferFragmentSize; - const char *read; - char *tmp = m_out->m_tmpData; - int size = 0; - if (m_out->m_tmpDataOffset && exceeds) { - size = m_out->m_tmpDataOffset + len; - tmp = new char[m_out->m_tmpDataOffset + len]; - std::memcpy(tmp, m_out->m_tmpData, m_out->m_tmpDataOffset); - } - if (flush && !m_out->m_tmpDataOffset) { - read = data; - size = len; - } else { - std::memcpy(tmp + m_out->m_tmpDataOffset, data, len); - read = tmp; - if (!exceeds) { - m_out->m_tmpDataOffset += len; - size = m_out->m_tmpDataOffset; - } - } - m_out->m_processed += size; - if (size && flush) { - alBufferData(*m_out->aldata->buffer, m_out->aldata->format, read, size, - m_out->m_format.sampleRate()); - if (tmp && tmp != m_out->m_tmpData) - delete[] tmp; - m_out->m_tmpDataOffset = 0; - alGetError(); - alSourceQueueBuffers(m_out->aldata->source, 1, m_out->aldata->buffer); - if (alGetError()) - return 0; - m_out->m_bufferFragmentsBusyCount++; - if (++m_out->aldata->buffer == m_out->aldata->buffers + m_out->m_bufferFragmentsCount) - m_out->aldata->buffer = m_out->aldata->buffers; - if (state != AL_PLAYING) - alSourcePlay(m_out->aldata->source); - } - return len; - } - return 0; -} - -QT_END_NAMESPACE |