bool QPulseAudioInput::deviceReady() { if (m_pullMode) { // reads some audio data and writes it to QIODevice read(0,0); } else { // emits readyRead() so user will call read() on QIODevice to get some audio data if (m_audioSource != 0) { PulseInputPrivate *a = qobject_cast<PulseInputPrivate*>(m_audioSource); a->trigger(); } } m_bytesAvailable = checkBytesReady(); if (m_deviceState != QAudio::ActiveState) return true; if (m_intervalTime && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_intervalTime) { emit notify(); m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_intervalTime; m_timeStamp.restart(); } return true; }
bool QAlsaAudioInput::deviceReady() { if(pullMode) { // reads some audio data and writes it to QIODevice read(0, buffer_size); } else { // emits readyRead() so user will call read() on QIODevice to get some audio data AlsaInputPrivate* a = qobject_cast<AlsaInputPrivate*>(audioSource); a->trigger(); } bytesAvailable = checkBytesReady(); if(deviceState != QAudio::ActiveState) return true; if (bytesAvailable < 0) { // bytesAvailable as negative is error code, try to recover from it. xrun_recovery(bytesAvailable); bytesAvailable = checkBytesReady(); if (bytesAvailable < 0) { // recovery failed must stop and set error. close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return 0; } } if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { emit notify(); elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; timeStamp.restart(); } return true; }
int QAlsaAudioInput::xrun_recovery(int err) { int count = 0; bool reset = false; if(err == -EPIPE) { errorState = QAudio::UnderrunError; err = snd_pcm_prepare(handle); if(err < 0) reset = true; else { bytesAvailable = checkBytesReady(); if (bytesAvailable <= 0) reset = true; } } else if((err == -ESTRPIPE)||(err == -EIO)) { errorState = QAudio::IOError; while((err = snd_pcm_resume(handle)) == -EAGAIN){ usleep(100); count++; if(count > 5) { reset = true; break; } } if(err < 0) { err = snd_pcm_prepare(handle); if(err < 0) reset = true; } } if(reset) { close(); open(); snd_pcm_prepare(handle); return 0; } return err; }
qint64 QAlsaAudioInput::read(char* data, qint64 len) { // Read in some audio data and write it to QIODevice, pull mode if ( !handle ) return 0; int bytesRead = 0; int bytesInRingbufferBeforeRead = ringBuffer.bytesOfDataInBuffer(); if (ringBuffer.bytesOfDataInBuffer() < len) { // bytesAvaiable is saved as a side effect of checkBytesReady(). int bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // bytesAvailable as negative is error code, try to recover from it. xrun_recovery(bytesToRead); bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // recovery failed must stop and set error. close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return 0; } } bytesToRead = qMin<qint64>(len, bytesToRead); bytesToRead = qMin<qint64>(ringBuffer.freeBytes(), bytesToRead); bytesToRead -= bytesToRead % period_size; int count=0; int err = 0; while(count < 5 && bytesToRead > 0) { char buffer[bytesToRead]; int chunks = bytesToRead / period_size; int frames = chunks * period_frames; if (frames > (int)buffer_frames) frames = buffer_frames; int readFrames = snd_pcm_readi(handle, buffer, frames); bytesRead = snd_pcm_frames_to_bytes(handle, readFrames); if (m_volume < 1.0f) QAudioHelperInternal::qMultiplySamples(m_volume, settings, buffer, buffer, bytesRead); if (readFrames >= 0) { ringBuffer.write(buffer, bytesRead); #ifdef DEBUG_AUDIO qDebug() << QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(bytesRead).arg(readFrames).toLatin1().constData(); #endif break; } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { errorState = QAudio::IOError; err = 0; break; } else { if(readFrames == -EPIPE) { errorState = QAudio::UnderrunError; err = snd_pcm_prepare(handle); } else if(readFrames == -ESTRPIPE) { err = snd_pcm_prepare(handle); } if(err != 0) break; } count++; } } bytesRead += bytesInRingbufferBeforeRead; if (bytesRead > 0) { // got some send it onward #ifdef DEBUG_AUDIO qDebug() << "frames to write to QIODevice = " << snd_pcm_bytes_to_frames( handle, (int)bytesRead ) << " (" << bytesRead << ") bytes"; #endif if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) return 0; if (pullMode) { qint64 l = 0; qint64 bytesWritten = 0; while (ringBuffer.bytesOfDataInBuffer() > 0) { l = audioSource->write(ringBuffer.availableData(), ringBuffer.availableDataBlockSize()); if (l > 0) { ringBuffer.readBytes(l); bytesWritten += l; } else { break; } } if (l < 0) { close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); } else if (l == 0 && bytesWritten == 0) { if (deviceState != QAudio::IdleState) { errorState = QAudio::NoError; deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } else { bytesAvailable -= bytesWritten; totalTimeValue += bytesWritten; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } } return bytesWritten; } else { while (ringBuffer.bytesOfDataInBuffer() > 0) { int size = ringBuffer.availableDataBlockSize(); memcpy(data, ringBuffer.availableData(), size); data += size; ringBuffer.readBytes(size); } bytesAvailable -= bytesRead; totalTimeValue += bytesRead; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } return bytesRead; } } return 0; }
bool QAlsaAudioInput::open() { #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif clockStamp.restart(); timeStamp.restart(); elapsedTimeOffset = 0; int dir; int err = 0; int count=0; unsigned int sampleRate=settings.sampleRate(); if (!settings.isValid()) { qWarning("QAudioInput: open error, invalid format."); } else if (settings.sampleRate() <= 0) { qWarning("QAudioInput: open error, invalid sample rate (%d).", settings.sampleRate()); } else { err = -1; } if (err == 0) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit errorChanged(errorState); return false; } QString dev = QString(QLatin1String(m_device.constData())); QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput); if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) if (devices.size() > 0) dev = QLatin1String(devices.first()); else return false; #else dev = QLatin1String("hw:0,0"); #endif } else { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(m_device); #else int idx = 0; char *name; QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); while(snd_card_get_name(idx,&name) == 0) { if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) break; idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); #endif } // Step 1: try and open the device while((count < 5) && (err < 0)) { err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); if(err < 0) count++; } if (( err < 0)||(handle == 0)) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return false; } snd_pcm_nonblock( handle, 0 ); // Step 2: Set the desired HW parameters. snd_pcm_hw_params_alloca( &hwparams ); bool fatal = false; QString errMessage; unsigned int chunks = 8; err = snd_pcm_hw_params_any( handle, hwparams ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err); } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_access( handle, hwparams, access ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err); } } if ( !fatal ) { err = setFormat(); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params(handle, hwparams); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err); } } if( err < 0) { qWarning()<<errMessage; errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return false; } snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); period_size = snd_pcm_frames_to_bytes(handle,period_frames); snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); // Step 3: Set the desired SW parameters. snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(handle, swparams); snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); snd_pcm_sw_params(handle, swparams); // Step 4: Prepare audio ringBuffer.resize(buffer_size); snd_pcm_prepare( handle ); snd_pcm_start(handle); // Step 5: Setup timer bytesAvailable = checkBytesReady(); if(pullMode) connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); // Step 6: Start audio processing chunks = buffer_size/period_size; timer->start(period_time*chunks/2000); errorState = QAudio::NoError; totalTimeValue = 0; return true; }
qint64 QAudioInputPrivate::read(char* data, qint64 len) { // Read in some audio data and write it to QIODevice, pull mode if ( !handle ) return 0; // bytesAvaiable is saved as a side effect of checkBytesReady(). int bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // bytesAvailable as negative is error code, try to recover from it. xrun_recovery(bytesToRead); bytesToRead = checkBytesReady(); if (bytesToRead < 0) { // recovery failed must stop and set error. close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return 0; } } bytesToRead = qMin<qint64>(len, bytesToRead); bytesToRead -= bytesToRead % period_size; int count=0, err = 0; while(count < 5) { int chunks = bytesToRead/period_size; int frames = chunks*period_frames; if(frames > (int)buffer_frames) frames = buffer_frames; int readFrames = snd_pcm_readi(handle, audioBuffer, frames); if (readFrames >= 0) { err = snd_pcm_frames_to_bytes(handle, readFrames); #ifdef DEBUG_AUDIO qDebug()<<QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(err).arg(readFrames).toLatin1().constData(); #endif break; } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { errorState = QAudio::IOError; err = 0; break; } else { if(readFrames == -EPIPE) { errorState = QAudio::UnderrunError; err = snd_pcm_prepare(handle); } else if(readFrames == -ESTRPIPE) { err = snd_pcm_prepare(handle); } if(err != 0) break; } count++; } if(err > 0) { // got some send it onward #ifdef DEBUG_AUDIO qDebug()<<"frames to write to QIODevice = "<< snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes"; #endif if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) return 0; if (pullMode) { qint64 l = audioSource->write(audioBuffer,err); if(l < 0) { close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); } else if(l == 0) { if (deviceState != QAudio::IdleState) { errorState = QAudio::NoError; deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } else { bytesAvailable -= err; totalTimeValue += err; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } } return l; } else { memcpy(data,audioBuffer,err); bytesAvailable -= err; totalTimeValue += err; resuming = false; if (deviceState != QAudio::ActiveState) { errorState = QAudio::NoError; deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } return err; } } return 0; }
} int QAudioInputPrivate::bytesReady() const { return qMax(bytesAvailable, 0); } qint64 QAudioInputPrivate::read(char* data, qint64 len) { Q_UNUSED(len) // Read in some audio data and write it to QIODevice, pull mode if ( !handle ) return 0; bytesAvailable = checkBytesReady(); if (bytesAvailable < 0) { // bytesAvailable as negative is error code, try to recover from it. xrun_recovery(bytesAvailable); bytesAvailable = checkBytesReady(); if (bytesAvailable < 0) { // recovery failed must stop and set error. close(); errorState = QAudio::IOError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return 0; } }
qint64 QPulseAudioInput::read(char *data, qint64 len) { m_bytesAvailable = checkBytesReady(); setError(QAudio::NoError); setState(QAudio::ActiveState); int readBytes = 0; if (!m_pullMode && !m_tempBuffer.isEmpty()) { readBytes = qMin(static_cast<int>(len), m_tempBuffer.size()); memcpy(data, m_tempBuffer.constData(), readBytes); m_totalTimeValue += readBytes; if (readBytes < m_tempBuffer.size()) { m_tempBuffer.remove(0, readBytes); return readBytes; } m_tempBuffer.clear(); } while (pa_stream_readable_size(m_stream) > 0) { size_t readLength = 0; #ifdef DEBUG_PULSE qDebug() << "QPulseAudioInput::read -- " << pa_stream_readable_size(m_stream) << " bytes available from pulse audio"; #endif QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); pulseEngine->lock(); const void *audioBuffer; // Second and third parameters (audioBuffer and length) to pa_stream_peek are output parameters, // the audioBuffer pointer is set to point to the actual pulse audio data, // and the length is set to the length of this data. if (pa_stream_peek(m_stream, &audioBuffer, &readLength) < 0) { qWarning() << QString("pa_stream_peek() failed: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream)))); pulseEngine->unlock(); return 0; } qint64 actualLength = 0; if (m_pullMode) { QByteArray adjusted(readLength, Qt::Uninitialized); applyVolume(audioBuffer, adjusted.data(), readLength); actualLength = m_audioSource->write(adjusted); if (actualLength < qint64(readLength)) { pulseEngine->unlock(); setError(QAudio::UnderrunError); setState(QAudio::IdleState); return actualLength; } } else { actualLength = qMin(static_cast<int>(len - readBytes), static_cast<int>(readLength)); applyVolume(audioBuffer, data + readBytes, actualLength); } #ifdef DEBUG_PULSE qDebug() << "QPulseAudioInput::read -- wrote " << actualLength << " to client"; #endif if (actualLength < qint64(readLength)) { #ifdef DEBUG_PULSE qDebug() << "QPulseAudioInput::read -- appending " << readLength - actualLength << " bytes of data to temp buffer"; #endif int diff = readLength - actualLength; int oldSize = m_tempBuffer.size(); m_tempBuffer.resize(m_tempBuffer.size() + diff); applyVolume(static_cast<const char *>(audioBuffer) + actualLength, m_tempBuffer.data() + oldSize, diff); QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection); } m_totalTimeValue += actualLength; readBytes += actualLength; pa_stream_drop(m_stream); pulseEngine->unlock(); if (!m_pullMode && readBytes >= len) break; if (m_intervalTime && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_intervalTime) { emit notify(); m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_intervalTime; m_timeStamp.restart(); } } #ifdef DEBUG_PULSE qDebug() << "QPulseAudioInput::read -- returning after reading " << readBytes << " bytes"; #endif return readBytes; }