4#include <QtCore/qcoreapplication.h>
5#include <QtCore/qvarlengtharray.h>
6#include <QtMultimedia/private/qaudiohelpers_p.h>
14 : QPlatformAudioSource(std::move(device), fmt, parent)
18 access = SND_PCM_ACCESS_RW_INTERLEAVED;
19 pcmformat = SND_PCM_FORMAT_S16;
25 deviceState = QAudio::StoppedState;
30 timer =
new QTimer(
this);
31 connect(timer, &QTimer::timeout,
this, &QAlsaAudioSource::userFeed);
37 disconnect(timer, &QTimer::timeout,
this, &QAlsaAudioSource::userFeed);
58 setError(QAudio::UnderrunError);
59 err = snd_pcm_prepare(
handle);
63 bytesAvailable = checkBytesReady();
64 if (bytesAvailable <= 0)
67 }
else if ((err == -estrpipe)||(err == -EIO)) {
68 setError(QAudio::IOError);
69 while((err = snd_pcm_resume(
handle)) == -EAGAIN){
78 err = snd_pcm_prepare(
handle);
94 snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN;
96 switch (m_format.sampleFormat()) {
97 case QAudioFormat::UInt8:
98 pcmformat = SND_PCM_FORMAT_U8;
100 case QAudioFormat::Int16:
101 if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
102 pcmformat = SND_PCM_FORMAT_S16_BE;
104 pcmformat = SND_PCM_FORMAT_S16_LE;
106 case QAudioFormat::Int32:
107 if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
108 pcmformat = SND_PCM_FORMAT_S32_BE;
110 pcmformat = SND_PCM_FORMAT_S32_LE;
112 case QAudioFormat::Float:
113 if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
114 pcmformat = SND_PCM_FORMAT_FLOAT_BE;
116 pcmformat = SND_PCM_FORMAT_FLOAT_LE;
122 return pcmformat != SND_PCM_FORMAT_UNKNOWN
123 ? snd_pcm_hw_params_set_format(
handle, hwparams, pcmformat)
129 if(deviceState != QAudio::StoppedState)
132 if(!pullMode && audioSource)
136 audioSource = device;
138 deviceState = QAudio::ActiveState;
143 emit stateChanged(deviceState);
148 if(deviceState != QAudio::StoppedState)
151 if(!pullMode && audioSource)
155 audioSource =
new AlsaInputPrivate(
this);
156 audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
158 deviceState = QAudio::IdleState;
163 emit stateChanged(deviceState);
170 if(deviceState == QAudio::StoppedState)
173 deviceState = QAudio::StoppedState;
176 emit stateChanged(deviceState);
182 QTime now(QTime::currentTime());
183 qDebug()<<now.second()<<
"s "<<now.msec()<<
"ms :open()";
185 elapsedTimeOffset = 0;
190 unsigned int sampleRate=m_format.sampleRate();
193 while((count < 5) && (err < 0)) {
194 err = snd_pcm_open(&handle, m_audioDevice.id().constData(), SND_PCM_STREAM_CAPTURE, 0);
198 if (( err < 0)||(
handle == 0)) {
199 setError(QAudio::OpenError);
200 deviceState = QAudio::StoppedState;
201 emit stateChanged(deviceState);
204 snd_pcm_nonblock(
handle, 0 );
207 snd_pcm_hw_params_alloca( &hwparams );
211 unsigned int chunks = 8;
213 err = snd_pcm_hw_params_any(
handle, hwparams );
216 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_any: err = %1").arg(err);
219 err = snd_pcm_hw_params_set_rate_resample(
handle, hwparams, 1 );
222 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
226 err = snd_pcm_hw_params_set_access(
handle, hwparams, access );
229 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_access: err = %1").arg(err);
236 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_format: err = %1").arg(err);
240 err = snd_pcm_hw_params_set_channels( handle, hwparams, (
unsigned int)m_format.channelCount() );
243 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_channels: err = %1").arg(err);
247 err = snd_pcm_hw_params_set_rate_near(
handle, hwparams, &sampleRate, 0 );
250 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
254 err = snd_pcm_hw_params_set_buffer_time_near(
handle, hwparams, &buffer_time, &dir);
257 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
261 err = snd_pcm_hw_params_set_period_time_near(
handle, hwparams, &period_time, &dir);
264 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
268 err = snd_pcm_hw_params_set_periods_near(
handle, hwparams, &chunks, &dir);
271 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
275 err = snd_pcm_hw_params(
handle, hwparams);
278 errMessage = QString::fromLatin1(
"QAudioSource: snd_pcm_hw_params: err = %1").arg(err);
282 qWarning()<<errMessage;
283 setError(QAudio::OpenError);
284 deviceState = QAudio::StoppedState;
285 emit stateChanged(deviceState);
288 snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
289 buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
290 snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
291 period_size = snd_pcm_frames_to_bytes(
handle,period_frames);
292 snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
293 snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
296 snd_pcm_sw_params_t *swparams;
297 snd_pcm_sw_params_alloca(&swparams);
298 snd_pcm_sw_params_current(
handle, swparams);
299 snd_pcm_sw_params_set_start_threshold(
handle,swparams,period_frames);
300 snd_pcm_sw_params_set_stop_threshold(
handle,swparams,buffer_frames);
301 snd_pcm_sw_params_set_avail_min(
handle, swparams,period_frames);
302 snd_pcm_sw_params(
handle, swparams);
305 ringBuffer.resize(buffer_size);
306 snd_pcm_prepare(
handle );
310 bytesAvailable = checkBytesReady();
313 connect(audioSource, &QIODevice::readyRead,
this, &QAlsaAudioSource::userFeed);
316 chunks = buffer_size/period_size;
317 timer->start(period_time*chunks/2000);
319 setError(QAudio::NoError);
340 bytesAvailable = period_size;
341 else if(deviceState != QAudio::ActiveState
342 && deviceState != QAudio::IdleState)
345 int frames = snd_pcm_avail_update(
handle);
347 bytesAvailable = frames;
349 if((
int)frames > (
int)buffer_frames)
350 frames = buffer_frames;
351 bytesAvailable = snd_pcm_frames_to_bytes(handle, frames);
354 return bytesAvailable;
359 return qMax(bytesAvailable, 0);
369 int bytesInRingbufferBeforeRead = ringBuffer.bytesOfDataInBuffer();
371 if (ringBuffer.bytesOfDataInBuffer() < len) {
374 int bytesToRead = checkBytesReady();
376 if (bytesToRead < 0) {
378 xrun_recovery(bytesToRead);
379 bytesToRead = checkBytesReady();
380 if (bytesToRead < 0) {
383 setError(QAudio::IOError);
384 deviceState = QAudio::StoppedState;
385 emit stateChanged(deviceState);
390 bytesToRead = qMin<qint64>(len, bytesToRead);
391 bytesToRead = qMin<qint64>(ringBuffer.freeBytes(), bytesToRead);
392 bytesToRead -= bytesToRead % period_size;
396 QVarLengthArray<
char, 4096> buffer(bytesToRead);
397 while(count < 5 && bytesToRead > 0) {
398 int chunks = bytesToRead / period_size;
399 int frames = chunks * period_frames;
400 if (frames > (
int)buffer_frames)
401 frames = buffer_frames;
403 int readFrames = snd_pcm_readi(handle, buffer.data(), frames);
404 bytesRead = snd_pcm_frames_to_bytes(
handle, readFrames);
406 QAudioHelperInternal::qMultiplySamples(volume(), m_format, buffer.constData(),
407 buffer.data(), bytesRead);
409 if (readFrames >= 0) {
410 ringBuffer.write(buffer.data(), bytesRead);
412 qDebug() << QString::fromLatin1(
"read in bytes = %1 (frames=%2)").arg(bytesRead).arg(readFrames).toLatin1().constData();
415 }
else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) {
416 setError(QAudio::IOError);
420 if(readFrames == -EPIPE) {
421 setError(QAudio::UnderrunError);
422 err = snd_pcm_prepare(
handle);
424 }
else if(readFrames == -ESTRPIPE) {
425 err = snd_pcm_prepare(
handle);
435 bytesRead += bytesInRingbufferBeforeRead;
440 qDebug() <<
"frames to write to QIODevice = " <<
441 snd_pcm_bytes_to_frames( handle, (
int)bytesRead ) <<
" (" << bytesRead <<
") bytes";
443 if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
448 qint64 bytesWritten = 0;
449 while (ringBuffer.bytesOfDataInBuffer() > 0) {
450 l = audioSource->write(ringBuffer.availableData(), ringBuffer.availableDataBlockSize());
452 ringBuffer.readBytes(l);
461 setError(QAudio::IOError);
462 deviceState = QAudio::StoppedState;
463 emit stateChanged(deviceState);
464 }
else if (l == 0 && bytesWritten == 0) {
465 if (deviceState != QAudio::IdleState) {
466 setError(QAudio::NoError);
467 deviceState = QAudio::IdleState;
468 emit stateChanged(deviceState);
471 bytesAvailable -= bytesWritten;
472 totalTimeValue += bytesWritten;
474 if (deviceState != QAudio::ActiveState) {
475 setError(QAudio::NoError);
476 deviceState = QAudio::ActiveState;
477 emit stateChanged(deviceState);
483 while (ringBuffer.bytesOfDataInBuffer() > 0) {
484 int size = ringBuffer.availableDataBlockSize();
485 memcpy(data, ringBuffer.availableData(), size);
487 ringBuffer.readBytes(size);
490 bytesAvailable -= bytesRead;
491 totalTimeValue += bytesRead;
493 if (deviceState != QAudio::ActiveState) {
494 setError(QAudio::NoError);
495 deviceState = QAudio::ActiveState;
496 emit stateChanged(deviceState);
508 if(deviceState == QAudio::SuspendedState) {
512 err = snd_pcm_prepare(
handle );
516 err = snd_pcm_start(
handle);
520 bytesAvailable = buffer_size;
523 deviceState = QAudio::ActiveState;
524 int chunks = buffer_size/period_size;
525 timer->start(period_time*chunks/2000);
526 emit stateChanged(deviceState);
542 qint64 result = qint64(1000000) * totalTimeValue /
543 m_format.bytesPerFrame() /
544 m_format.sampleRate();
551 if(deviceState == QAudio::ActiveState||resuming) {
554 deviceState = QAudio::SuspendedState;
555 emit stateChanged(deviceState);
561 if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
564 QTime now(QTime::currentTime());
565 qDebug()<<now.second()<<
"s "<<now.msec()<<
"ms :userFeed() IN";
574 read(0, buffer_size);
580 bytesAvailable = checkBytesReady();
582 if(deviceState != QAudio::ActiveState)
585 if (bytesAvailable < 0) {
587 xrun_recovery(bytesAvailable);
588 bytesAvailable = checkBytesReady();
589 if (bytesAvailable < 0) {
592 setError(QAudio::IOError);
593 deviceState = QAudio::StoppedState;
594 emit stateChanged(deviceState);
627 return audioDevice->read(data,len);
656 return m_tail - m_head;
657 else if (m_tail < m_head)
658 return m_data.size() + m_tail - m_head;
666 return m_head - m_tail - 1;
667 else if (m_tail > m_head)
668 return m_data.size() - m_tail + m_head - 1;
670 return m_data.size() - 1;
675 return (m_data.constData() + m_head);
681 return m_data.size() - m_head;
682 else if (m_tail > m_head)
683 return m_tail - m_head;
690 m_head = (m_head + bytes) % m_data.size();
695 if (m_tail + len < m_data.size()) {
696 memcpy(m_data.data() + m_tail, data, len);
699 int bytesUntilEnd = m_data.size() - m_tail;
700 memcpy(m_data.data() + m_tail, data, bytesUntilEnd);
701 if (len - bytesUntilEnd > 0)
702 memcpy(m_data.data(), data + bytesUntilEnd, len - bytesUntilEnd);
703 m_tail = len - bytesUntilEnd;
709#include "moc_qalsaaudiosource_p.cpp"
qsizetype bytesReady() const override
QIODevice * start() override
void setBufferSize(qsizetype value) override
QAudio::State state() const override
qsizetype bufferSize() const override
qint64 processedUSecs() const override
void start(QIODevice *device) override
qint64 read(char *data, qint64 len)
The QAudioDevice class provides an information about audio devices and their functionality.
const char * availableData() const
int availableDataBlockSize() const
void write(char *data, int len)
void readBytes(int bytes)
int bytesOfDataInBuffer() const