static ALCuint pulse_available_samples(ALCdevice *device) //{{{ { pulse_data *data = device->ExtraData; size_t samples; pa_threaded_mainloop_lock(data->loop); /* Capture is done in fragment-sized chunks, so we loop until we get all * that's available */ samples = (device->Connected ? pa_stream_readable_size(data->stream) : 0); while(samples > 0) { const void *buf; size_t length; if(pa_stream_peek(data->stream, &buf, &length) < 0) { ERR("pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(data->context))); break; } WriteRingBuffer(data->ring, buf, length/data->frame_size); samples -= length; pa_stream_drop(data->stream); } pa_threaded_mainloop_unlock(data->loop); return RingBufferSize(data->ring); } //}}}
int QPulseAudioInput::checkBytesReady() { if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) { m_bytesAvailable = 0; } else { m_bytesAvailable = pa_stream_readable_size(m_stream); } return m_bytesAvailable; }
static void stream_read_cb(pa_stream *p, size_t nbytes, void *userdata) { /* We don't care about the data, just drop it */ for (;;) { const void *data; pa_assert_se((nbytes = pa_stream_readable_size(p)) != (size_t) -1); if (nbytes <= 0) break; fail_unless(pa_stream_peek(p, &data, &nbytes) == 0); fail_unless(pa_stream_drop(p) == 0); } }
static void deh_stream_first_readwrite_callback(pa_mainloop_api *api, pa_defer_event *de, void *userdata) { pa_stream *s = userdata; if (s->direction == PA_STREAM_PLAYBACK) { size_t writable_size = pa_stream_writable_size(s); if (s->write_cb && writable_size > 0) s->write_cb(s, writable_size, s->write_cb_userdata); } else if (s->direction == PA_STREAM_RECORD) { size_t readable_size = pa_stream_readable_size(s); if (s->read_cb && readable_size > 0) s->read_cb(s, readable_size, s->read_cb_userdata); } pa_stream_unref(s); }
static ALCuint pulse_available_samples(ALCdevice *device) { pulse_data *data = device->ExtraData; size_t readable = data->cap_remain; if(device->Connected) { ssize_t got = pa_stream_readable_size(data->stream); if(got < 0) { ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got)); aluHandleDisconnect(device); } else if((size_t)got > data->cap_len) readable += got - data->cap_len; } if(data->last_readable < readable) data->last_readable = readable; return data->last_readable / pa_frame_size(&data->spec); }
/* This is called whenever new data may is available */ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) { pa_assert(s); pa_assert(length > 0); if (raw) { pa_assert(!sndfile); if (stdio_event) mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT); while (pa_stream_readable_size(s) > 0) { const void *data; if (pa_stream_peek(s, &data, &length) < 0) { pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context))); quit(1); return; } pa_assert(data); pa_assert(length > 0); if (buffer) { buffer = pa_xrealloc(buffer, buffer_length + length); memcpy((uint8_t*) buffer + buffer_length, data, length); buffer_length += length; } else { buffer = pa_xmalloc(length); memcpy(buffer, data, length); buffer_length = length; buffer_index = 0; } pa_stream_drop(s); } } else { pa_assert(sndfile); while (pa_stream_readable_size(s) > 0) { sf_count_t bytes; const void *data; if (pa_stream_peek(s, &data, &length) < 0) { pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context))); quit(1); return; } pa_assert(data); pa_assert(length > 0); if (writef_function) { size_t k = pa_frame_size(&sample_spec); if ((bytes = writef_function(sndfile, data, (sf_count_t) (length/k))) > 0) bytes *= (sf_count_t) k; } else bytes = sf_write_raw(sndfile, data, (sf_count_t) length); if (bytes < (sf_count_t) length) quit(1); pa_stream_drop(s); } } }
/* This is called whenever new data may is available */ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) { pa_assert(s); pa_assert(length > 0); if (raw) { pa_assert(!sndfile); if (stdio_event) mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT); while (pa_stream_readable_size(s) > 0) { const void *data; if (pa_stream_peek(s, &data, &length) < 0) { pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context))); quit(1); return; } pa_assert(length > 0); /* If there is a hole in the stream, we generate silence, except * if it's a passthrough stream in which case we skip the hole. */ if (data || !(flags & PA_STREAM_PASSTHROUGH)) { buffer = pa_xrealloc(buffer, buffer_length + length); if (data) memcpy((uint8_t *) buffer + buffer_length, data, length); else pa_silence_memory((uint8_t *) buffer + buffer_length, length, &sample_spec); buffer_length += length; } pa_stream_drop(s); } } else { pa_assert(sndfile); while (pa_stream_readable_size(s) > 0) { sf_count_t bytes; const void *data; if (pa_stream_peek(s, &data, &length) < 0) { pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context))); quit(1); return; } pa_assert(length > 0); if (!data && (flags & PA_STREAM_PASSTHROUGH)) { pa_stream_drop(s); continue; } if (!data && length > silence_buffer_length) { silence_buffer = pa_xrealloc(silence_buffer, length); pa_silence_memory((uint8_t *) silence_buffer + silence_buffer_length, length - silence_buffer_length, &sample_spec); silence_buffer_length = length; } if (writef_function) { size_t k = pa_frame_size(&sample_spec); if ((bytes = writef_function(sndfile, data ? data : silence_buffer, (sf_count_t) (length/k))) > 0) bytes *= (sf_count_t) k; } else bytes = sf_write_raw(sndfile, data ? data : silence_buffer, (sf_count_t) length); if (bytes < (sf_count_t) length) quit(1); pa_stream_drop(s); } } }
/* * audio record callback * args: * s - pointer to pa_stream * length - buffer length * data - pointer to user data * * asserts: * none * * returns: none */ static void stream_request_cb(pa_stream *s, size_t length, void *data) { audio_context_t *audio_ctx = (audio_context_t *) data; if(audio_ctx->channels == 0) { fprintf(stderr, "AUDIO: (pulseaudio) stream_request_cb failed: channels = 0\n"); return; } if(audio_ctx->samprate == 0) { fprintf(stderr, "AUDIO: (pulseaudio) stream_request_cb failed: samprate = 0\n"); return; } uint64_t frame_length = NSEC_PER_SEC / audio_ctx->samprate; /*in nanosec*/ int64_t ts = 0; int64_t buff_ts = 0; uint32_t i = 0; while (pa_stream_readable_size(s) > 0) { const void *inputBuffer; size_t length; /*read from stream*/ if (pa_stream_peek(s, &inputBuffer, &length) < 0) { fprintf(stderr, "AUDIO: (pulseaudio) pa_stream_peek() failed\n"); return; } if(length == 0) { fprintf(stderr, "AUDIO: (pulseaudio) empty buffer!\n"); return; /*buffer is empty*/ } get_latency(s); ts = ns_time_monotonic() - (latency * 1000); if(audio_ctx->last_ts <= 0) audio_ctx->last_ts = ts; uint32_t numSamples = (uint32_t) length / sizeof(sample_t); const sample_t *rptr = (const sample_t*) inputBuffer; sample_t *capture_buff = (sample_t *) audio_ctx->capture_buff; int chan = 0; /*store capture samples or silence if inputBuffer == NULL (hole)*/ for( i = 0; i < numSamples; ++i ) { capture_buff[sample_index] = inputBuffer ? *rptr++ : 0; sample_index++; /*store peak value*/ if(audio_ctx->capture_buff_level[chan] < capture_buff[sample_index]) audio_ctx->capture_buff_level[chan] = capture_buff[sample_index]; chan++; if(chan >= audio_ctx->channels) chan = 0; if(sample_index >= audio_ctx->capture_buff_size) { buff_ts = ts + ( i / audio_ctx->channels ) * frame_length; audio_fill_buffer(audio_ctx, buff_ts); /*reset*/ audio_ctx->capture_buff_level[0] = 0; audio_ctx->capture_buff_level[1] = 0; sample_index = 0; } } pa_stream_drop(s); /*clean the samples*/ } }
JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_pulseaudio_PA_stream_1readable_1size (JNIEnv *env, jclass clazz, jlong s) { return pa_stream_readable_size((pa_stream *) (intptr_t) s); }
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; }
static void data_available_for_stream(pa_mainloop_api *a, pa_io_event *ioe, int fd, pa_io_event_flags_t events, void *userdata) { pa_stream *s = userdata; snd_pcm_sframes_t frame_count; size_t frame_size = pa_frame_size(&s->ss); char buf[16 * 1024]; int paused = g_atomic_int_get(&s->paused); if (events & (PA_IO_EVENT_INPUT | PA_IO_EVENT_OUTPUT)) { #if HAVE_SND_PCM_AVAIL frame_count = snd_pcm_avail(s->ph); #else snd_pcm_hwsync(s->ph); frame_count = snd_pcm_avail_update(s->ph); #endif if (frame_count < 0) { if (frame_count == -EBADFD) { // stream was closed return; } int cnt = 0, ret; do { cnt ++; ret = snd_pcm_recover(s->ph, frame_count, 1); } while (ret == -1 && errno == EINTR && cnt < 5); #if HAVE_SND_PCM_AVAIL frame_count = snd_pcm_avail(s->ph); #else snd_pcm_hwsync(s->ph); frame_count = snd_pcm_avail_update(s->ph); #endif if (frame_count < 0) { trace_error("%s, can't recover after failed snd_pcm_avail (%d)\n", __func__, (int)frame_count); return; } } } else { return; } if (events & PA_IO_EVENT_OUTPUT) { if (paused) { // client stream is corked. Pass silence to ALSA size_t bytecnt = MIN(sizeof(buf), frame_count * frame_size); memset(buf, 0, bytecnt); snd_pcm_writei(s->ph, buf, bytecnt / frame_size); } else { size_t writable_size = pa_stream_writable_size(s); if (s->write_cb && writable_size > 0) s->write_cb(s, writable_size, s->write_cb_userdata); size_t bytecnt = MIN(sizeof(buf), frame_count * frame_size); bytecnt = ringbuffer_read(s->rb, buf, bytecnt); if (bytecnt == 0) { // application is not ready yet, play silence bytecnt = MIN(sizeof(buf), frame_count * frame_size); memset(buf, 0, bytecnt); } snd_pcm_writei(s->ph, buf, bytecnt / frame_size); } } if (events & PA_IO_EVENT_INPUT) { if (paused) { // client stream is corked. Read data from ALSA and discard them size_t bytecnt = MIN(sizeof(buf), frame_count * frame_size); snd_pcm_readi(s->ph, buf, bytecnt / frame_size); } else { size_t bytecnt = ringbuffer_writable_size(s->rb); if (bytecnt == 0) { // ringbuffer is full because app doesn't read data fast enough. // Make some room ringbuffer_drop(s->rb, frame_count * frame_size); bytecnt = ringbuffer_writable_size(s->rb); } bytecnt = MIN(bytecnt, frame_count * frame_size); bytecnt = MIN(bytecnt, sizeof(buf)); if (bytecnt > 0) { snd_pcm_readi(s->ph, buf, bytecnt / frame_size); ringbuffer_write(s->rb, buf, bytecnt); } size_t readable_size = pa_stream_readable_size(s); if (s->read_cb && readable_size > 0) s->read_cb(s, readable_size, s->read_cb_userdata); } } }
/* Read samples from the PulseAudio device. * Samples are converted to complex form based upon format, counted * and returned via cSamples pointer. * Returns the number of samples placed into cSamples */ int quisk_read_pulseaudio(struct sound_dev *dev, complex double *cSamples) { int i, nSamples; int read_frames; // A frame is a sample from each channel const void * fbuffer; pa_stream *s = dev->handle; size_t read_bytes; if (!dev) return 0; if (dev->cork_status) { if (dev->read_frames != 0) { WaitForPoll(); } return 0; } // Note: Access to PulseAudio data from our sound thread requires locking the threaded mainloop. if (dev->read_frames == 0) { // non-blocking: read available frames pa_threaded_mainloop_lock(pa_ml); read_frames = pa_stream_readable_size(s) / dev->num_channels / dev->sample_bytes; if (read_frames == 0) { pa_threaded_mainloop_unlock(pa_ml); return 0; } dev->dev_latency = read_frames * dev->num_channels * 1000 / (dev->sample_rate / 1000); } else { // Blocking read for dev->read_frames total frames WaitForPoll(); pa_threaded_mainloop_lock(pa_ml); read_frames = pa_stream_readable_size(s) / dev->num_channels / dev->sample_bytes; if (read_frames == 0) { pa_threaded_mainloop_unlock(pa_ml); return 0; } dev->dev_latency = read_frames * dev->num_channels * 1000 / (dev->sample_rate / 1000); } nSamples = 0; while (nSamples < read_frames) { // read samples in chunks until we have enough samples if (pa_stream_peek (s, &fbuffer, &read_bytes) < 0) { printf("Failure of pa_stream_peek in quisk_read_pulseaudio\n"); pa_threaded_mainloop_unlock(pa_ml); return nSamples; } if (fbuffer == NULL && read_bytes == 0) { // buffer is empty break; } if (fbuffer == NULL) { // there is a hole in the buffer pa_stream_drop(s); continue; } if (nSamples * dev->num_channels * dev->sample_bytes + read_bytes >= SAMP_BUFFER_SIZE) { if (quisk_sound_state.verbose_pulse) printf("buffer full on %s\n", dev->name); pa_stream_drop(s); // limit read request to buffer size break; } // Convert sampled data to complex data. dev->num_channels must be 2. if (dev->sample_bytes == 4) { //float32 float fi, fq; for( i = 0; i < read_bytes; i += (dev->num_channels * 4)) { memcpy(&fi, fbuffer + i + (dev->channel_I * 4), 4); memcpy(&fq, fbuffer + i + (dev->channel_Q * 4), 4); if (fi >= 1.0 || fi <= -1.0) dev->overrange++; if (fq >= 1.0 || fq <= -1.0) dev->overrange++; cSamples[nSamples++] = (fi + I * fq) * CLIP32; } } else if (dev->sample_bytes == 2) { //16bit integer little-endian int16_t si, sq; for( i = 0; i < read_bytes; i += (dev->num_channels * 2)) { memcpy(&si, fbuffer + i + (dev->channel_I * 2), 2); memcpy(&sq, fbuffer + i + (dev->channel_Q * 2), 2); if (si >= CLIP16 || si <= -CLIP16) dev->overrange++; if (sq >= CLIP16 || sq <= -CLIP16) dev->overrange++; int ii = si << 16; int qq = sq << 16; cSamples[nSamples++] = ii + I * qq; } } else { printf("Unknown sample size for %s", dev->name); } pa_stream_drop(s); } pa_threaded_mainloop_unlock(pa_ml); // DC removal; R.G. Lyons page 553 complex double c; for (i = 0; i < nSamples; i++) { c = cSamples[i] + dev->dc_remove * 0.95; cSamples[i] = c - dev->dc_remove; dev->dc_remove = c; } return nSamples; }