void QPulseAudioInput::close() { if (!m_opened) return; m_timer->stop(); QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); if (m_stream) { pulseEngine->lock(); pa_stream_set_state_callback(m_stream, 0, 0); pa_stream_set_read_callback(m_stream, 0, 0); pa_stream_set_underflow_callback(m_stream, 0, 0); pa_stream_set_overflow_callback(m_stream, 0, 0); pa_stream_disconnect(m_stream); pa_stream_unref(m_stream); m_stream = 0; pulseEngine->unlock(); } disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed); if (!m_pullMode && m_audioSource) { delete m_audioSource; m_audioSource = 0; } m_opened = false; }
void QPulseAudioInput::suspend() { if (m_deviceState == QAudio::ActiveState) { setError(QAudio::NoError); setState(QAudio::SuspendedState); m_timer->stop(); QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); pa_operation *operation; pulseEngine->lock(); operation = pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, 0); pulseEngine->wait(operation); pa_operation_unref(operation); pulseEngine->unlock(); } }
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; }
bool QPulseAudioInput::open() { if (m_opened) return true; QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) { setError(QAudio::FatalError); setState(QAudio::StoppedState); return false; } pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); if (!pa_sample_spec_valid(&spec)) { setError(QAudio::OpenError); setState(QAudio::StoppedState); return false; } m_spec = spec; #ifdef DEBUG_PULSE // QTime now(QTime::currentTime()); // qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif if (m_streamName.isNull()) m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8(); #ifdef DEBUG_PULSE qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); qDebug() << "Rate: " << spec.rate; qDebug() << "Channels: " << spec.channels; qDebug() << "Frame size: " << pa_frame_size(&spec); #endif pulseEngine->lock(); pa_channel_map channel_map; pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT); if (!pa_channel_map_compatible(&channel_map, &spec)) qWarning() << "Channel map doesn't match sample specification!"; m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map); pa_stream_set_state_callback(m_stream, inputStreamStateCallback, this); pa_stream_set_read_callback(m_stream, inputStreamReadCallback, this); pa_stream_set_underflow_callback(m_stream, inputStreamUnderflowCallback, this); pa_stream_set_overflow_callback(m_stream, inputStreamOverflowCallback, this); m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec); int flags = 0; pa_buffer_attr buffer_attr; buffer_attr.maxlength = (uint32_t) -1; buffer_attr.prebuf = (uint32_t) -1; buffer_attr.tlength = (uint32_t) -1; buffer_attr.minreq = (uint32_t) -1; flags |= PA_STREAM_ADJUST_LATENCY; if (m_bufferSize > 0) buffer_attr.fragsize = (uint32_t) m_bufferSize; else buffer_attr.fragsize = (uint32_t) m_periodSize; if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) { qWarning() << "pa_stream_connect_record() failed!"; pa_stream_unref(m_stream); m_stream = 0; pulseEngine->unlock(); setError(QAudio::OpenError); setState(QAudio::StoppedState); return false; } while (pa_stream_get_state(m_stream) != PA_STREAM_READY) pa_threaded_mainloop_wait(pulseEngine->mainloop()); const pa_buffer_attr *actualBufferAttr = pa_stream_get_buffer_attr(m_stream); m_periodSize = actualBufferAttr->fragsize; m_periodTime = pa_bytes_to_usec(m_periodSize, &spec) / 1000; if (actualBufferAttr->tlength != (uint32_t)-1) m_bufferSize = actualBufferAttr->tlength; pulseEngine->unlock(); connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed); m_opened = true; m_timer->start(m_periodTime); m_clockStamp.restart(); m_timeStamp.restart(); m_elapsedTimeOffset = 0; m_totalTimeValue = 0; return true; }