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
qqnxaudiosource.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Research In Motion
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <private/qaudiohelpers_p.h>
7
8#include <QDebug>
9
11
13 : QPlatformAudioSource(std::move(device), format, parent),
14 m_audioSource(0),
15 m_pcmNotifier(0),
16 m_state(QAudio::StoppedState),
17 m_bytesRead(0),
18 m_elapsedTimeOffset(0),
19 m_totalTimeValue(0),
20 m_bytesAvailable(0),
21 m_bufferSize(0),
22 m_periodSize(0),
23 m_pullMode(true)
24{
25}
26
28{
29 close();
30}
31
32void QQnxAudioSource::start(QIODevice *device)
33{
34 if (m_state != QAudio::StoppedState)
35 close();
36
37 if (!m_pullMode && m_audioSource)
38 delete m_audioSource;
39
40 m_pullMode = true;
41 m_audioSource = device;
42
43 if (open())
44 changeState(QAudio::ActiveState, QAudio::NoError);
45 else
46 changeState(QAudio::StoppedState, QAudio::OpenError);
47}
48
50{
51 if (m_state != QAudio::StoppedState)
52 close();
53
54 if (!m_pullMode && m_audioSource)
55 delete m_audioSource;
56
57 m_pullMode = false;
58 m_audioSource = new InputPrivate(this);
59 m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
60
61 if (open()) {
62 changeState(QAudio::IdleState, QAudio::NoError);
63 } else {
64 delete m_audioSource;
65 m_audioSource = 0;
66
67 changeState(QAudio::StoppedState, QAudio::OpenError);
68 }
69
70 return m_audioSource;
71}
72
74{
75 if (m_state == QAudio::StoppedState)
76 return;
77
78 changeState(QAudio::StoppedState, QAudio::NoError);
79 close();
80}
81
83{
84 stop();
85 m_bytesAvailable = 0;
86}
87
89{
90 if (m_state == QAudio::StoppedState)
91 return;
92
93 snd_pcm_capture_pause(m_pcmHandle.get());
94
95 if (m_pcmNotifier)
96 m_pcmNotifier->setEnabled(false);
97
98 changeState(QAudio::SuspendedState, QAudio::NoError);
99}
100
102{
103 if (m_state == QAudio::StoppedState)
104 return;
105
106 snd_pcm_capture_resume(m_pcmHandle.get());
107
108 if (m_pcmNotifier)
109 m_pcmNotifier->setEnabled(true);
110
111 if (m_pullMode)
112 changeState(QAudio::ActiveState, QAudio::NoError);
113 else
114 changeState(QAudio::IdleState, QAudio::NoError);
115}
116
118{
119 return qMax(m_bytesAvailable, 0);
120}
121
122void QQnxAudioSource::setBufferSize(qsizetype bufferSize)
123{
124 m_bufferSize = bufferSize;
125}
126
128{
129 return m_bufferSize;
130}
131
133{
134 return qint64(1000000) * m_format.framesForBytes(m_bytesRead) / m_format.sampleRate();
135}
136
138{
139 return m_state;
140}
141
142void QQnxAudioSource::userFeed()
143{
144 if (m_state == QAudio::StoppedState || m_state == QAudio::SuspendedState)
145 return;
146
147 deviceReady();
148}
149
150bool QQnxAudioSource::deviceReady()
151{
152 if (m_pullMode) {
153 // reads some audio data and writes it to QIODevice
154 read(0, 0);
155 } else {
156 m_bytesAvailable = m_periodSize;
157
158 // emits readyRead() so user will call read() on QIODevice to get some audio data
159 if (m_audioSource != 0) {
160 InputPrivate *input = qobject_cast<InputPrivate*>(m_audioSource);
161 input->trigger();
162 }
163 }
164
165 if (m_state != QAudio::ActiveState)
166 return true;
167
168 return true;
169}
170
171bool QQnxAudioSource::open()
172{
173 m_pcmHandle = QnxAudioUtils::openPcmDevice(m_audioDevice.id(), QAudioDevice::Input);
174
175 if (!m_pcmHandle)
176 return false;
177
178 int errorCode = 0;
179
180 // Necessary so that bytesFree() which uses the "free" member of the status struct works
181 snd_pcm_plugin_set_disable(m_pcmHandle.get(), PLUGIN_MMAP);
182
183 const std::optional<snd_pcm_channel_info_t> info = QnxAudioUtils::pcmChannelInfo(
184 m_pcmHandle.get(), QAudioDevice::Input);
185
186 if (!info) {
187 close();
188 return false;
189 }
190
191 snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(m_format,
192 QAudioDevice::Input, info->max_fragment_size);
193
194 if ((errorCode = snd_pcm_plugin_params(m_pcmHandle.get(), &params)) < 0) {
195 qWarning("QQnxAudioSource: open error, couldn't set channel params (0x%x)", -errorCode);
196 close();
197 return false;
198 }
199
200 if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle.get(), SND_PCM_CHANNEL_CAPTURE)) < 0) {
201 qWarning("QQnxAudioSource: open error, couldn't prepare channel (0x%x)", -errorCode);
202 close();
203 return false;
204 }
205
206 const std::optional<snd_pcm_channel_setup_t> setup = QnxAudioUtils::pcmChannelSetup(
207 m_pcmHandle.get(), QAudioDevice::Input);
208
209 m_periodSize = qMin(2048, setup->buf.block.frag_size);
210
211 m_elapsedTimeOffset = 0;
212 m_totalTimeValue = 0;
213 m_bytesRead = 0;
214
215 m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle.get(), SND_PCM_CHANNEL_CAPTURE),
216 QSocketNotifier::Read, this);
217 connect(m_pcmNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(userFeed()));
218
219 return true;
220}
221
222void QQnxAudioSource::close()
223{
224 if (m_pcmHandle) {
225#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2)
226 snd_pcm_plugin_flush(m_pcmHandle.get(), SND_PCM_CHANNEL_CAPTURE);
227#else
228 snd_pcm_plugin_drop(m_pcmHandle.get(), SND_PCM_CHANNEL_CAPTURE);
229#endif
230 m_pcmHandle = nullptr;
231 }
232
233 if (m_pcmNotifier) {
234 delete m_pcmNotifier;
235 m_pcmNotifier = 0;
236 }
237
238 if (!m_pullMode && m_audioSource) {
239 delete m_audioSource;
240 m_audioSource = 0;
241 }
242}
243
244qint64 QQnxAudioSource::read(char *data, qint64 len)
245{
246 if (!m_pullMode && m_bytesAvailable == 0)
247 return 0;
248
249 int errorCode = 0;
250 QByteArray tempBuffer(m_periodSize, 0);
251
252 const int actualRead = snd_pcm_plugin_read(m_pcmHandle.get(), tempBuffer.data(), m_periodSize);
253 if (actualRead < 1) {
254 snd_pcm_channel_status_t status;
255 memset(&status, 0, sizeof(status));
256 status.channel = SND_PCM_CHANNEL_CAPTURE;
257 if ((errorCode = snd_pcm_plugin_status(m_pcmHandle.get(), &status)) < 0) {
258 qWarning("QQnxAudioSource: read error, couldn't get plugin status (0x%x)", -errorCode);
259 close();
260 changeState(QAudio::StoppedState, QAudio::FatalError);
261 return -1;
262 }
263
264 if (status.status == SND_PCM_STATUS_READY
265 || status.status == SND_PCM_STATUS_OVERRUN) {
266 if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle.get(), SND_PCM_CHANNEL_CAPTURE)) < 0) {
267 qWarning("QQnxAudioSource: read error, couldn't prepare plugin (0x%x)", -errorCode);
268 close();
269 changeState(QAudio::StoppedState, QAudio::FatalError);
270 return -1;
271 }
272 }
273 } else {
274 changeState(QAudio::ActiveState, QAudio::NoError);
275 }
276
277 if (volume() < 1.0f)
278 QAudioHelperInternal::qMultiplySamples(volume(), m_format, tempBuffer.data(),
279 tempBuffer.data(), actualRead);
280
281 m_bytesRead += actualRead;
282
283 if (m_pullMode) {
284 m_audioSource->write(tempBuffer.data(), actualRead);
285 } else {
286 memcpy(data, tempBuffer.data(), qMin(static_cast<qint64>(actualRead), len));
287 }
288
289 m_bytesAvailable = 0;
290
291 return actualRead;
292}
293
294void QQnxAudioSource::changeState(QAudio::State state, QAudio::Error error)
295{
296 if (m_state != state) {
297 m_state = state;
298 emit stateChanged(state);
299 }
300
301 setError(error);
302}
303
304InputPrivate::InputPrivate(QQnxAudioSource *audio)
305 : m_audioDevice(audio)
306{
307}
308
309qint64 InputPrivate::readData(char *data, qint64 len)
310{
311 return m_audioDevice->read(data, len);
312}
313
314qint64 InputPrivate::writeData(const char *data, qint64 len)
315{
316 Q_UNUSED(data);
317 Q_UNUSED(len);
318 return 0;
319}
320
322{
323 return m_audioDevice->m_bytesAvailable + QIODevice::bytesAvailable();
324}
325
327{
328 return true;
329}
330
332{
333 emit readyRead();
334}
335
336QT_END_NAMESPACE
bool isSequential() const override
Returns true if this device is sequential; otherwise returns false.
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
qint64 readData(char *data, qint64 len) 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.
The QAudioDevice class provides an information about audio devices and their functionality.
void suspend() override
qsizetype bytesReady() const override
QAudio::State state() const override
QIODevice * start() override
void resume() override
void setBufferSize(qsizetype) override
void stop() override
qsizetype bufferSize() const override
void reset() override
qint64 processedUSecs() const override
void start(QIODevice *) override