summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp')
-rw-r--r--src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp487
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