Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qaudio_qiodevice_support_p.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QAUDIO_QIODEVICE_SUPPORT_P_H
5#define QAUDIO_QIODEVICE_SUPPORT_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/qdebug.h>
19#include <QtCore/qglobal.h>
20#include <QtCore/qiodevice.h>
21#include <QtCore/qmutex.h>
22#include <QtCore/qspan.h>
23
24#include <QtMultimedia/private/qaudio_alignment_support_p.h>
25#include <QtMultimedia/private/qaudio_qspan_support_p.h>
26#include <QtMultimedia/private/qaudioringbuffer_p.h>
27
28#include <deque>
29#include <mutex>
30
31QT_BEGIN_NAMESPACE
32
33namespace QtPrivate {
34
35// QIODevice writing to a QAudioRingBuffer
36template <typename SampleType>
38{
39public:
41
42 explicit QIODeviceRingBufferWriter(Ringbuffer *rb, QObject *parent = nullptr)
44 {
45 Q_ASSERT(rb);
46 }
47
48private:
49 qint64 readData(char * /*data*/, qint64 /*maxlen*/) override { return -1; }
50
51 qint64 writeData(const char *data, qint64 len) override
52 {
53 using namespace QtMultimediaPrivate; // take/drop
54
55 // we don't write fractional samples
56 int64_t usableLength = alignDown(len, sizeof(SampleType));
57
58 QSpan<const std::byte> dataRegion = as_bytes(QSpan{ data, qsizetype(usableLength) });
59 qint64 totalBytesWritten = 0;
60
61 do {
62 int64_t remainingSamples = dataRegion.size() / sizeof(SampleType);
63 auto writeRegion = m_ringbuffer->acquireWriteRegion(remainingSamples);
64 if (writeRegion.isEmpty())
65 break; // no space in buffer
66
67 QSpan<std::byte> writeByteRegion = as_writable_bytes(writeRegion);
68 int64_t bytesToWrite = std::min(dataRegion.size(), writeByteRegion.size());
69
70 QSpan<const std::byte> dataForChunk = take(dataRegion, bytesToWrite);
71 std::copy(dataForChunk.begin(), dataForChunk.end(), writeByteRegion.begin());
72
73 totalBytesWritten += bytesToWrite;
74 dataRegion = drop(dataRegion, bytesToWrite);
75
76 m_ringbuffer->releaseWriteRegion(int(bytesToWrite / sizeof(SampleType)));
77 } while (!dataRegion.isEmpty());
78
79 if (totalBytesWritten)
80 emit readyRead();
81
82 return totalBytesWritten;
83 }
84
85 qint64 bytesToWrite() const override { return m_ringbuffer->free() * sizeof(SampleType); }
86
87 Ringbuffer *m_ringbuffer;
88};
89
90// QIODevice reading from a QAudioRingBuffer
91template <typename SampleType>
93{
94public:
96
97 explicit QIODeviceRingBufferReader(Ringbuffer *rb, QObject *parent = nullptr)
99 {
100 Q_ASSERT(rb);
101 }
102
103private:
104 qint64 readData(char *data, qint64 maxlen) override
105 {
106 using namespace QtMultimediaPrivate; // drop
107
108 QSpan<std::byte> outputRegion = as_writable_bytes(QSpan{ data, qsizetype(maxlen) });
109
110 qint64 totalBytesRead = 0;
111
112 while (!outputRegion.isEmpty()) {
113 qsizetype maxSizeToRead = outputRegion.size_bytes() / sizeof(SampleType);
114 QSpan readRegion = m_ringbuffer->acquireReadRegion(int(maxSizeToRead));
115 if (readRegion.isEmpty())
116 return totalBytesRead;
117
118 QSpan readByteRegion = as_bytes(readRegion);
119 std::copy(readByteRegion.begin(), readByteRegion.end(), outputRegion.begin());
120
121 outputRegion = drop(outputRegion, readByteRegion.size());
122 totalBytesRead += readByteRegion.size();
123
124 m_ringbuffer->releaseReadRegion(readRegion.size());
125 }
126
127 return totalBytesRead;
128 }
129
130 qint64 writeData(const char * /*data*/, qint64 /*len*/) override { return -1; }
131 qint64 bytesAvailable() const override { return m_ringbuffer->used() * sizeof(SampleType); }
132
133 Ringbuffer *m_ringbuffer;
134};
135
136// QIODevice backed by a std::deque
138{
139public:
140 using Deque = std::deque<char>;
141
142 explicit QDequeIODevice(QObject *parent = nullptr) : QIODevice(parent) { }
143
144 qint64 bytesAvailable() const override { return qint64(m_deque.size()); }
145
146private:
147 qint64 readData(char *data, qint64 maxlen) override
148 {
149 std::lock_guard guard{ m_mutex };
150
151 size_t bytesToRead = std::min<size_t>(m_deque.size(), maxlen);
152 std::copy_n(m_deque.begin(), bytesToRead, data);
153
154 m_deque.erase(m_deque.begin(), m_deque.begin() + bytesToRead);
155 return qint64(bytesToRead);
156 }
157
158 qint64 writeData(const char *data, qint64 len) override
159 {
160 std::lock_guard guard{ m_mutex };
161 m_deque.insert(m_deque.end(), data, data + len);
162 return len;
163 }
164
165 QMutex m_mutex;
166 Deque m_deque;
167};
168
169inline qint64 writeToDevice(QIODevice &device, QSpan<const std::byte> data)
170{
171 return device.write(reinterpret_cast<const char *>(data.data()), data.size());
172}
173
174inline qint64 readFromDevice(QIODevice &device, QSpan<std::byte> outputBuffer)
175{
176 return device.read(reinterpret_cast<char *>(outputBuffer.data()), outputBuffer.size());
177}
178
179template <typename SampleType>
180qsizetype pullFromQIODeviceToRingbuffer(QIODevice &device, QAudioRingBuffer<SampleType> &ringbuffer)
181{
182 using namespace QtMultimediaPrivate;
183
184 qsizetype totalBytesWritten = 0;
185
186 for (;;) {
187 qint64 bytesAvailableInDevice = alignDown(device.bytesAvailable(), sizeof(SampleType));
188 if (!bytesAvailableInDevice)
189 return totalBytesWritten; // no data in iodevice
190
191 qint64 samplesAvailableInDevice = bytesAvailableInDevice / sizeof(SampleType);
192
193 auto writeRegion = ringbuffer.acquireWriteRegion(samplesAvailableInDevice);
194 if (writeRegion.empty())
195 return totalBytesWritten;
196
197 qint64 bytesRead = readFromDevice(device, as_writable_bytes(writeRegion));
198 if (bytesRead < 0) {
199 qWarning() << "pullFromQIODeviceToRingbuffer cannot read from QIODevice:"
200 << device.errorString();
201 return totalBytesWritten;
202 }
203
204 Q_ASSERT(bytesRead == writeRegion.size_bytes());
205 ringbuffer.releaseWriteRegion(writeRegion.size());
206
207 totalBytesWritten += writeRegion.size_bytes();
208 }
209}
210
211template <typename SampleType>
212qsizetype pushToQIODeviceFromRingbuffer(QIODevice &device, QAudioRingBuffer<SampleType> &ringbuffer)
213{
214 using namespace QtMultimediaPrivate;
215 qsizetype totalBytesWritten = 0;
216
217 for (;;) {
218 auto ringbufferRegion = ringbuffer.acquireReadRegion(ringbuffer.size());
219 if (ringbufferRegion.empty())
220 return totalBytesWritten;
221 QSpan bufferByteRegion = as_bytes(ringbufferRegion);
222
223 int deviceBytesToWrite = device.bytesToWrite();
224 if (deviceBytesToWrite > 0) {
225 // we do our best effort and only push full samples to the device
226 int bytesToWrite = alignDown(deviceBytesToWrite, sizeof(SampleType));
227 bufferByteRegion = take(bufferByteRegion, bytesToWrite);
228
229 if (bufferByteRegion.empty())
230 return totalBytesWritten;
231 }
232
233 int bytesWritten = writeToDevice(device, bufferByteRegion);
234 if (bytesWritten < 0) {
235 qWarning() << "pushToQIODeviceFromRingbuffer cannot push data to QIODevice:"
236 << device.errorString();
237 return totalBytesWritten;
238 }
239 if (bytesWritten == 0)
240 return totalBytesWritten;
241
242 totalBytesWritten += bytesWritten;
243 Q_ASSERT(isAligned(bytesWritten, sizeof(SampleType)));
244 int samplesWritten = bytesWritten / sizeof(SampleType);
245 ringbuffer.releaseReadRegion(samplesWritten);
246 }
247}
248
249} // namespace QtPrivate
250
251QT_END_NAMESPACE
252
253#endif // QAUDIO_QIODEVICE_SUPPORT_P_H
qint64 readData(char *data, qint64 maxlen) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
QDequeIODevice(QObject *parent=nullptr)
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
qint64 readData(char *data, qint64 maxlen) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
QIODeviceRingBufferReader(Ringbuffer *rb, QObject *parent=nullptr)
qint64 writeData(const char *, qint64) override
Writes up to maxSize bytes from data to the device.
qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
QIODeviceRingBufferWriter(Ringbuffer *rb, QObject *parent=nullptr)
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
qint64 bytesToWrite() const override
For buffered devices, this function returns the number of bytes waiting to be written.
qint64 readData(char *, qint64) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
qsizetype pullFromQIODeviceToRingbuffer(QIODevice &device, QAudioRingBuffer< SampleType > &ringbuffer)
qint64 readFromDevice(QIODevice &device, QSpan< std::byte > outputBuffer)
qint64 writeToDevice(QIODevice &device, QSpan< const std::byte > data)
qsizetype pushToQIODeviceFromRingbuffer(QIODevice &device, QAudioRingBuffer< SampleType > &ringbuffer)