static int do_write(connection *c) { pa_memchunk chunk; ssize_t r; void *p; connection_assert_ref(c); if (!c->source_output) return 0; if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) { /* pa_log("peek failed"); */ return 0; } pa_assert(chunk.memblock); pa_assert(chunk.length); p = pa_memblock_acquire(chunk.memblock); r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length); pa_memblock_release(chunk.memblock); pa_memblock_unref(chunk.memblock); if (r < 0) { pa_log("write(): %s", pa_cstrerror(errno)); return -1; } pa_memblockq_drop(c->output_memblockq, (size_t) r); return 1; }
void voice_append_chunk_to_file(struct userdata *u, const char *file_name, pa_memchunk *chunk) { int i; FILE *file = NULL; for (i = 0; i < file_table_size && file_table[i].name != NULL; i++) { if (0 == strcmp(file_name, file_table[i].name)) { file = file_table[i].file; } } if (i >= file_table_size) { pa_log("Can't open new files, not writing to file \"%s\"", file_name); return; } if (file == NULL) { file = file_table[i].file = fopen(file_name, "w"); if (file == NULL) { pa_log("Can't open file \"%s\": %s", file_name, strerror(errno)); return; } } char *p = pa_memblock_acquire(chunk->memblock); fwrite(p + chunk->index, 1, chunk->length, file); pa_memblock_release(chunk->memblock); }
void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn) { FILE *f; void *p; pa_assert(c); pa_assert(fn); /* Only for debugging purposes */ f = pa_fopen_cloexec(fn, "a"); if (!f) { pa_log_warn("Failed to open '%s': %s", fn, pa_cstrerror(errno)); return; } p = pa_memblock_acquire(c->memblock); if (fwrite((uint8_t*) p + c->index, 1, c->length, f) != c->length) pa_log_warn("Failed to write to '%s': %s", fn, pa_cstrerror(errno)); pa_memblock_release(c->memblock); fclose(f); }
static int do_read(connection *c) { pa_memchunk chunk; ssize_t r; size_t l; void *p; size_t space = 0; connection_assert_ref(c); if (!c->sink_input || (l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0) return 0; if (c->playback.current_memblock) { space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index; if (space <= 0) { pa_memblock_unref(c->playback.current_memblock); c->playback.current_memblock = NULL; } } if (!c->playback.current_memblock) { pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1)); c->playback.memblock_index = 0; space = pa_memblock_get_length(c->playback.current_memblock); } if (l > space) l = space; p = pa_memblock_acquire(c->playback.current_memblock); r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l); pa_memblock_release(c->playback.current_memblock); if (r <= 0) { if (r < 0 && (errno == EINTR || errno == EAGAIN)) return 0; pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno)); return -1; } chunk.memblock = c->playback.current_memblock; chunk.index = c->playback.memblock_index; chunk.length = (size_t) r; c->playback.memblock_index += (size_t) r; pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL); pa_atomic_sub(&c->playback.missing, (int) r); return 0; }
pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p) { int capacity; int readfd; struct srbheader *srh; pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel)); sr->mainloop = m; sr->memblock = pa_memblock_new_pool(p, -1); if (!sr->memblock) goto fail; srh = pa_memblock_acquire(sr->memblock); pa_zero(*srh); sr->rb_read.memory = (uint8_t*) srh + PA_ALIGN(sizeof(*srh)); srh->readbuf_offset = sr->rb_read.memory - (uint8_t*) srh; capacity = (pa_memblock_get_length(sr->memblock) - srh->readbuf_offset) / 2; sr->rb_write.memory = PA_ALIGN_PTR(sr->rb_read.memory + capacity); srh->writebuf_offset = sr->rb_write.memory - (uint8_t*) srh; capacity = PA_MIN(capacity, srh->writebuf_offset - srh->readbuf_offset); pa_log_debug("SHM block is %d bytes, ringbuffer capacity is 2 * %d bytes", (int) pa_memblock_get_length(sr->memblock), capacity); srh->capacity = sr->rb_read.capacity = sr->rb_write.capacity = capacity; sr->rb_read.count = &srh->read_count; sr->rb_write.count = &srh->write_count; sr->sem_read = pa_fdsem_new_shm(&srh->read_semdata); if (!sr->sem_read) goto fail; sr->sem_write = pa_fdsem_new_shm(&srh->write_semdata); if (!sr->sem_write) goto fail; readfd = pa_fdsem_get(sr->sem_read); #ifdef DEBUG_SRBCHANNEL pa_log("Enabling io event on fd %d", readfd); #endif sr->read_event = m->io_new(m, readfd, PA_IO_EVENT_INPUT, semread_cb, sr); m->io_enable(sr->read_event, PA_IO_EVENT_INPUT); return sr; fail: pa_srbchannel_free(sr); return NULL; }
pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) { void *p, *q; pa_assert(dst); pa_assert(src); pa_assert(dst->length == src->length); p = pa_memblock_acquire(dst->memblock); q = pa_memblock_acquire(src->memblock); memmove((uint8_t*) p + dst->index, (uint8_t*) q + src->index, dst->length); pa_memblock_release(dst->memblock); pa_memblock_release(src->memblock); return dst; }
static void do_read(struct userdata *u) { uint32_t free_frags; pa_memchunk memchunk; WAVEHDR *hdr; MMRESULT res; void *p; if (!u->source) return; if (!PA_SOURCE_IS_LINKED(u->source->state)) return; EnterCriticalSection(&u->crit); free_frags = u->free_ifrags; u->free_ifrags = 0; LeaveCriticalSection(&u->crit); if (free_frags == u->fragments) pa_log_debug("WaveIn overflow!"); while (free_frags) { hdr = &u->ihdrs[u->cur_ihdr]; if (hdr->dwFlags & WHDR_PREPARED) waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR)); if (hdr->dwBytesRecorded) { memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded); pa_assert(memchunk.memblock); p = pa_memblock_acquire(memchunk.memblock); memcpy((char*) p, hdr->lpData, hdr->dwBytesRecorded); pa_memblock_release(memchunk.memblock); memchunk.length = hdr->dwBytesRecorded; memchunk.index = 0; pa_source_post(u->source, &memchunk); pa_memblock_unref(memchunk.memblock); } res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR)); if (res != MMSYSERR_NOERROR) pa_log_error("Unable to prepare waveIn block: %d", res); res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR)); if (res != MMSYSERR_NOERROR) pa_log_error("Unable to add waveIn block: %d", res); free_frags--; u->cur_ihdr++; u->cur_ihdr %= u->fragments; } }
/* No lock necessary. */ pa_memblock *pa_memblock_will_need(pa_memblock *b) { void *p; pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) > 0); p = pa_memblock_acquire(b); pa_will_need(p, b->length); pa_memblock_release(b); return b; }
static void dump_chunk(const pa_memchunk *chunk) { size_t n; void *q; char *e; pa_assert(chunk); q = pa_memblock_acquire(chunk->memblock); for (e = (char*) q + chunk->index, n = 0; n < chunk->length; n++, e++) fprintf(stderr, "%c", *e); pa_memblock_release(chunk->memblock); }
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { void *data; pa_assert(b); pa_assert(spec); data = pa_memblock_acquire(b); pa_silence_memory(data, pa_memblock_get_length(b), spec); pa_memblock_release(b); return b; }
pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { void *data; pa_assert(c); pa_assert(c->memblock); pa_assert(spec); data = pa_memblock_acquire(c->memblock); pa_silence_memory((uint8_t*) data+c->index, c->length, spec); pa_memblock_release(c->memblock); return c; }
static int process_render(struct userdata *u) { pa_assert(u); if (u->memchunk.length <= 0) pa_sink_render(u->sink, ioring->usable_buffer_space, &u->memchunk); pa_assert(u->memchunk.length > 0); xc_evtchn_notify(xce, xen_evtchn_port); for (;;) { ssize_t l; void *p; p = pa_memblock_acquire(u->memchunk.memblock); /* xen: write data to ring buffer & notify backend */ l = ring_write(ioring, (uint8_t*)p + u->memchunk.index, u->memchunk.length); pa_memblock_release(u->memchunk.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) return 0; else { pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); return -1; } } else { u->memchunk.index += (size_t) l; u->memchunk.length -= (size_t) l; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } } return 0; } }
static int process_sink_render(struct userdata *u) { pa_assert(u); if (u->memchunk_sink.length <= 0) pa_sink_render(u->sink, libvchan_buffer_space(u->play_ctrl), &u->memchunk_sink); pa_assert(u->memchunk_sink.length > 0); for (;;) { ssize_t l; void *p; p = pa_memblock_acquire(u->memchunk_sink.memblock); l = write_to_vchan(u->play_ctrl, (char *) p + u->memchunk_sink.index, u->memchunk_sink.length); pa_memblock_release(u->memchunk_sink.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) return 0; else { pa_log ("Failed to write data to VCHAN: %s", pa_cstrerror(errno)); return -1; } } else { u->memchunk_sink.index += (size_t) l; u->memchunk_sink.length -= (size_t) l; if (u->memchunk_sink.length <= 0) { pa_memblock_unref(u->memchunk_sink.memblock); pa_memchunk_reset(&u->memchunk_sink); } } return 0; } }
/* Called from thread context */ static void cmtspeech_source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; uint8_t *buf; pa_assert(o); pa_assert_se(u = o->userdata); if (chunk->length != u->ul_frame_size) { pa_log_warn("Pushed UL audio frame has wrong size %zu", chunk->length); return; } buf = ((uint8_t *) pa_memblock_acquire(chunk->memblock)) + chunk->index; (void)cmtspeech_send_ul_frame(u, buf, chunk->length); pa_memblock_release(chunk->memblock); }
static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) { pa_memblock *b; size_t length; void *data; pa_assert(pool); length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX); b = pa_memblock_new(pool, length); data = pa_memblock_acquire(b); memset(data, c, length); pa_memblock_release(b); pa_memblock_set_is_silence(b, true); return b; }
pa_srbchannel* pa_srbchannel_new_from_template(pa_mainloop_api *m, pa_srbchannel_template *t) { int temp; struct srbheader *srh; pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel)); sr->mainloop = m; sr->memblock = t->memblock; pa_memblock_ref(sr->memblock); srh = pa_memblock_acquire(sr->memblock); sr->rb_read.capacity = sr->rb_write.capacity = srh->capacity; sr->rb_read.count = &srh->read_count; sr->rb_write.count = &srh->write_count; sr->rb_read.memory = (uint8_t*) srh + srh->readbuf_offset; sr->rb_write.memory = (uint8_t*) srh + srh->writebuf_offset; sr->sem_read = pa_fdsem_open_shm(&srh->read_semdata, t->readfd); if (!sr->sem_read) goto fail; sr->sem_write = pa_fdsem_open_shm(&srh->write_semdata, t->writefd); if (!sr->sem_write) goto fail; pa_srbchannel_swap(sr); temp = t->readfd; t->readfd = t->writefd; t->writefd = temp; #ifdef DEBUG_SRBCHANNEL pa_log("Enabling io event on fd %d", t->readfd); #endif sr->read_event = m->io_new(m, t->readfd, PA_IO_EVENT_INPUT, semread_cb, sr); m->io_enable(sr->read_event, PA_IO_EVENT_INPUT); return sr; fail: pa_srbchannel_free(sr); return NULL; }
static int process_render(struct userdata *u) { pa_assert(u); if (u->memchunk.length <= 0) pa_sink_render(u->sink, u->buffer_size, &u->memchunk); pa_assert(u->memchunk.length > 0); for (;;) { ssize_t l; void *p; p = pa_memblock_acquire(u->memchunk.memblock); l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->write_type); pa_memblock_release(u->memchunk.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) return 0; else { pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); return -1; } } else { u->memchunk.index += (size_t) l; u->memchunk.length -= (size_t) l; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } } return 0; } }
static void dump(pa_memblockq *bq) { printf(">"); for (;;) { pa_memchunk out; char *e; size_t n; void *q; if (pa_memblockq_peek(bq, &out) < 0) break; q = pa_memblock_acquire(out.memblock); for (e = (char*) q + out.index, n = 0; n < out.length; n++) printf("%c", *e); pa_memblock_release(out.memblock); pa_memblock_unref(out.memblock); pa_memblockq_drop(bq, out.length); } printf("<\n"); }
void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq) { size_t l; unsigned gcd, n; void *p; pa_memchunk_reset(c); gcd = pa_gcd(rate, freq); n = rate / gcd; l = pa_mempool_block_size_max(pool) / sizeof(float); l /= n; if (l <= 0) l = 1; l *= n; c->length = l * sizeof(float); c->memblock = pa_memblock_new(pool, c->length); p = pa_memblock_acquire(c->memblock); calc_sine(p, c->length, freq * l / rate); pa_memblock_release(c->memblock); }
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { case SINK_MESSAGE_RENDER: /* Handle the request from the JACK thread */ if (u->sink->thread_info.state == PA_SINK_RUNNING) { pa_memchunk chunk; size_t nbytes; void *p; pa_assert(offset > 0); nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec); pa_sink_render_full(u->sink, nbytes, &chunk); p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index; pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset); pa_memblock_release(chunk.memblock); pa_memblock_unref(chunk.memblock); } else { unsigned c; pa_sample_spec ss; /* Humm, we're not RUNNING, hence let's write some silence */ ss = u->sink->sample_spec; ss.channels = 1; for (c = 0; c < u->channels; c++) pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss); } u->frames_in_buffer = (jack_nframes_t) offset; u->saved_frame_time = * (jack_nframes_t*) data; u->saved_frame_time_valid = TRUE; return 0; case SINK_MESSAGE_BUFFER_SIZE: pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec)); return 0; case SINK_MESSAGE_ON_SHUTDOWN: pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); return 0; case PA_SINK_MESSAGE_GET_LATENCY: { jack_nframes_t l, ft, d; size_t n; /* This is the "worst-case" latency */ l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer; if (u->saved_frame_time_valid) { /* Adjust the worst case latency by the time that * passed since we last handed data to JACK */ ft = jack_frame_time(u->client); d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; l = l > d ? l - d : 0; } /* Convert it to usec */ n = l * pa_frame_size(&u->sink->sample_spec); *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); return 0; } } return pa_sink_process_msg(o, code, data, offset, memchunk); }
int pa_sound_file_load( pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk, pa_proplist *p) { SNDFILE *sf = NULL; SF_INFO sfi; int ret = -1; size_t l; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL; void *ptr = NULL; int fd; pa_assert(fname); pa_assert(ss); pa_assert(chunk); pa_memchunk_reset(chunk); if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) { pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno)); goto finish; } #ifdef HAVE_POSIX_FADVISE if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) { pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno)); goto finish; } else pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); #endif pa_zero(sfi); if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) { pa_log("Failed to open file %s", fname); goto finish; } fd = -1; if (pa_sndfile_read_sample_spec(sf, ss) < 0) { pa_log("Failed to determine file sample format."); goto finish; } if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) { if (ss->channels > 2) pa_log("Failed to determine file channel map, synthesizing one."); pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT); } if (p) pa_sndfile_init_proplist(sf, p); if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { pa_log("File too large"); goto finish; } chunk->memblock = pa_memblock_new(pool, l); chunk->index = 0; chunk->length = l; readf_function = pa_sndfile_readf_function(ss); ptr = pa_memblock_acquire(chunk->memblock); if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) || (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) { pa_log("Premature file end"); goto finish; } ret = 0; finish: if (sf) sf_close(sf); if (ptr) pa_memblock_release(chunk->memblock); if (ret != 0 && chunk->memblock) pa_memblock_unref(chunk->memblock); if (fd >= 0) pa_close(fd); return ret; }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_proplist *proplist; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(u->thread_mq); proplist = tunnel_new_proplist(u); u->context = pa_context_new_with_proplist(u->thread_mainloop_api, "PulseAudio", proplist); pa_proplist_free(proplist); if (!u->context) { pa_log("Failed to create libpulse context"); goto fail; } if (u->cookie_file && pa_context_load_cookie_from_file(u->context, u->cookie_file) != 0) { pa_log_error("Can not load cookie file!"); goto fail; } pa_context_set_state_callback(u->context, context_state_cb, u); if (pa_context_connect(u->context, u->remote_server, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { pa_log("Failed to connect libpulse context"); goto fail; } for (;;) { int ret; if (pa_mainloop_iterate(u->thread_mainloop, 1, &ret) < 0) { if (ret == 0) goto finish; else goto fail; } if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); if (u->connected && pa_stream_get_state(u->stream) == PA_STREAM_READY && PA_SINK_IS_LINKED(u->sink->thread_info.state)) { size_t writable; writable = pa_stream_writable_size(u->stream); if (writable > 0) { if(u->transcode.encoding != -1) { pa_memchunk memchunk; const void *p; size_t nbBytes; unsigned char *cbits; pa_sink_render_full(u->sink, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size, &memchunk); pa_assert(memchunk.length > 0); pa_assert(memchunk.length >= u->transcode.frame_size*u->transcode.channels); pa_log_debug("received memchunk length: %zu bytes", memchunk.length ); /* we have new data to write */ p = pa_memblock_acquire(memchunk.memblock); nbBytes = pa_transcode_encode(&u->transcode, (uint8_t*) p + memchunk.index, &cbits); pa_log_debug("encoded length: %zu bytes", nbBytes); /* TODO: Use pa_stream_begin_write() to reduce copying. */ ret = pa_stream_write_compressed(u->stream, (uint8_t*) cbits, nbBytes, NULL, /**< A cleanup routine for the data or NULL to request an internal copy */ 0, /** offset */ PA_SEEK_RELATIVE, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size); pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if(nbBytes > 0) free(cbits); if (ret != 0) { pa_log_error("Could not write data into the stream ... ret = %i", ret); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } } else { pa_memchunk memchunk; const void *p; pa_sink_render_full(u->sink, writable, &memchunk); pa_assert(memchunk.length > 0); /* we have new data to write */ p = pa_memblock_acquire(memchunk.memblock); /* TODO: Use pa_stream_begin_write() to reduce copying. */ ret = pa_stream_write(u->stream, (uint8_t*) p + memchunk.index, memchunk.length, NULL, /**< A cleanup routine for the data or NULL to request an internal copy */ 0, /** offset */ PA_SEEK_RELATIVE); pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if (ret != 0) { pa_log_error("Could not write data into the stream ... ret = %i", ret); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } } } } } fail: pa_asyncmsgq_post(u->thread_mq->outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq->inq, PA_MESSAGE_SHUTDOWN); finish: if (u->stream) { pa_stream_disconnect(u->stream); pa_stream_unref(u->stream); u->stream = NULL; } if (u->context) { pa_context_disconnect(u->context); pa_context_unref(u->context); u->context = NULL; } pa_log_debug("Thread shutting down"); }
static void thread_func(void *userdata) { struct userdata *u = userdata; int write_type = 0, read_type = 0; short revents = 0; pa_assert(u); pa_log_debug("Thread starting up"); if (u->core->realtime_scheduling) pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); for (;;) { int ret; /* pa_log("loop"); */ if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); /* Render some data and write it to the dsp */ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) { if (u->use_mmap) { if ((ret = mmap_write(u)) < 0) goto fail; revents &= ~POLLOUT; if (ret > 0) continue; } else { ssize_t l; pa_bool_t loop = FALSE, work_done = FALSE; l = (ssize_t) u->out_fragment_size; if (u->use_getospace) { audio_buf_info info; if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno)); u->use_getospace = FALSE; } else { l = info.bytes; /* We loop only if GETOSPACE worked and we * actually *know* that we can write more than * one fragment at a time */ loop = TRUE; } } /* Round down to multiples of the fragment size, * because OSS needs that (at least some versions * do) */ l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size; /* Hmm, so poll() signalled us that we can read * something, but GETOSPACE told us there was nothing? * Hmm, make the best of it, try to read some data, to * avoid spinning forever. */ if (l <= 0 && (revents & POLLOUT)) { l = (ssize_t) u->out_fragment_size; loop = FALSE; } while (l > 0) { void *p; ssize_t t; if (u->memchunk.length <= 0) pa_sink_render(u->sink, (size_t) l, &u->memchunk); pa_assert(u->memchunk.length > 0); p = pa_memblock_acquire(u->memchunk.memblock); t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); pa_memblock_release(u->memchunk.memblock); /* pa_log("wrote %i bytes of %u", t, l); */ pa_assert(t != 0); if (t < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) { pa_log_debug("EAGAIN"); revents &= ~POLLOUT; break; } else { pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); goto fail; } } else { u->memchunk.index += (size_t) t; u->memchunk.length -= (size_t) t; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } l -= t; revents &= ~POLLOUT; work_done = TRUE; } if (!loop) break; } if (work_done) continue; } } /* Try to read some data and pass it on to the source driver. */ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) { if (u->use_mmap) { if ((ret = mmap_read(u)) < 0) goto fail; revents &= ~POLLIN; if (ret > 0) continue; } else { void *p; ssize_t l; pa_memchunk memchunk; pa_bool_t loop = FALSE, work_done = FALSE; l = (ssize_t) u->in_fragment_size; if (u->use_getispace) { audio_buf_info info; if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno)); u->use_getispace = FALSE; } else { l = info.bytes; loop = TRUE; } } l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size; if (l <= 0 && (revents & POLLIN)) { l = (ssize_t) u->in_fragment_size; loop = FALSE; } while (l > 0) { ssize_t t; size_t k; pa_assert(l > 0); memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); k = pa_memblock_get_length(memchunk.memblock); if (k > (size_t) l) k = (size_t) l; k = (k/u->frame_size)*u->frame_size; p = pa_memblock_acquire(memchunk.memblock); t = pa_read(u->fd, p, k, &read_type); pa_memblock_release(memchunk.memblock); pa_assert(t != 0); /* EOF cannot happen */ /* pa_log("read %i bytes of %u", t, l); */ if (t < 0) { pa_memblock_unref(memchunk.memblock); if (errno == EINTR) continue; else if (errno == EAGAIN) { pa_log_debug("EAGAIN"); revents &= ~POLLIN; break; } else { pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); goto fail; } } else { memchunk.index = 0; memchunk.length = (size_t) t; pa_source_post(u->source, &memchunk); pa_memblock_unref(memchunk.memblock); l -= t; revents &= ~POLLIN; work_done = TRUE; } if (!loop) break; } if (work_done) continue; } } /* pa_log("loop2 revents=%i", revents); */ if (u->rtpoll_item) { struct pollfd *pollfd; pa_assert(u->fd >= 0); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->events = (short) (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0)); } /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~(POLLOUT|POLLIN)) { pa_log("DSP shutdown."); goto fail; } revents = pollfd->revents; } else revents = 0; } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) { pa_memblock *r; void *d; unsigned i; pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10)); d = pa_memblock_acquire(r); switch (ss->format) { case PA_SAMPLE_U8: case PA_SAMPLE_ULAW: case PA_SAMPLE_ALAW: { uint8_t *u = d; u[0] = 0x00; u[1] = 0xFF; u[2] = 0x7F; u[3] = 0x80; u[4] = 0x9f; u[5] = 0x3f; u[6] = 0x1; u[7] = 0xF0; u[8] = 0x20; u[9] = 0x21; break; } case PA_SAMPLE_S16NE: case PA_SAMPLE_S16RE: { uint16_t *u = d; u[0] = 0x0000; u[1] = 0xFFFF; u[2] = 0x7FFF; u[3] = 0x8000; u[4] = 0x9fff; u[5] = 0x3fff; u[6] = 0x1; u[7] = 0xF000; u[8] = 0x20; u[9] = 0x21; break; } case PA_SAMPLE_S32NE: case PA_SAMPLE_S32RE: { uint32_t *u = d; u[0] = 0x00000001; u[1] = 0xFFFF0002; u[2] = 0x7FFF0003; u[3] = 0x80000004; u[4] = 0x9fff0005; u[5] = 0x3fff0006; u[6] = 0x10007; u[7] = 0xF0000008; u[8] = 0x200009; u[9] = 0x21000A; break; } case PA_SAMPLE_S24_32NE: case PA_SAMPLE_S24_32RE: { uint32_t *u = d; u[0] = 0x000001; u[1] = 0xFF0002; u[2] = 0x7F0003; u[3] = 0x800004; u[4] = 0x9f0005; u[5] = 0x3f0006; u[6] = 0x107; u[7] = 0xF00008; u[8] = 0x2009; u[9] = 0x210A; break; } case PA_SAMPLE_FLOAT32NE: case PA_SAMPLE_FLOAT32RE: { float *u = d; u[0] = 0.0f; u[1] = -1.0f; u[2] = 1.0f; u[3] = 4711.0f; u[4] = 0.222f; u[5] = 0.33f; u[6] = -.3f; u[7] = 99.0f; u[8] = -0.555f; u[9] = -.123f; if (ss->format == PA_SAMPLE_FLOAT32RE) for (i = 0; i < 10; i++) u[i] = PA_FLOAT32_SWAP(u[i]); break; } case PA_SAMPLE_S24NE: case PA_SAMPLE_S24RE: { uint8_t *u = d; PA_WRITE24NE(u, 0x000001); PA_WRITE24NE(u+3, 0xFF0002); PA_WRITE24NE(u+6, 0x7F0003); PA_WRITE24NE(u+9, 0x800004); PA_WRITE24NE(u+12, 0x9f0005); PA_WRITE24NE(u+15, 0x3f0006); PA_WRITE24NE(u+18, 0x107); PA_WRITE24NE(u+21, 0xF00008); PA_WRITE24NE(u+24, 0x2009); PA_WRITE24NE(u+27, 0x210A); break; } default: pa_assert_not_reached(); } pa_memblock_release(r); return r; }
static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) { void *d; unsigned i; d = pa_memblock_acquire(chunk->memblock); switch (ss->format) { case PA_SAMPLE_U8: case PA_SAMPLE_ULAW: case PA_SAMPLE_ALAW: { uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) printf(" 0x%02x ", *(u++)); break; } case PA_SAMPLE_S16NE: case PA_SAMPLE_S16RE: { uint16_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) printf(" 0x%04x ", *(u++)); break; } case PA_SAMPLE_S32NE: case PA_SAMPLE_S32RE: { uint32_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) printf("0x%08x ", *(u++)); break; } case PA_SAMPLE_S24_32NE: case PA_SAMPLE_S24_32RE: { uint32_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) printf("0x%08x ", *(u++)); break; } case PA_SAMPLE_FLOAT32NE: case PA_SAMPLE_FLOAT32RE: { float *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { printf("%4.3g ", ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_FLOAT32_SWAP(*u)); u++; } break; } case PA_SAMPLE_S24LE: case PA_SAMPLE_S24BE: { uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { printf(" 0x%06x ", PA_READ24NE(u)); u += pa_frame_size(ss); } break; } default: pa_assert_not_reached(); } printf("\n"); pa_memblock_release(chunk->memblock); }
static void compare_block(const pa_sample_spec *ss, const pa_memchunk *chunk, int iter) { void *d; unsigned i; d = pa_memblock_acquire(chunk->memblock); switch (ss->format) { case PA_SAMPLE_U8: { const uint8_t *v = u8_result[iter]; uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_ALAW: { const uint8_t *v = alaw_result[iter]; uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_ULAW: { const uint8_t *v = ulaw_result[iter]; uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_S16LE: { const uint16_t *v = s16le_result[iter]; uint16_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_S16BE: { const uint16_t *v = s16be_result[iter]; uint16_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_FLOAT32LE: { const float *v = float32le_result[iter]; float *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { float uu = ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_FLOAT32_SWAP(*u); fail_unless(fabs(uu - *v) <= 1e-6, NULL); ++u; ++v; } break; } case PA_SAMPLE_FLOAT32BE: { const float *v = float32be_result[iter]; float *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { float uu = ss->format == PA_SAMPLE_FLOAT32NE ? *u : PA_FLOAT32_SWAP(*u); fail_unless(fabs(uu - *v) <= 1e-6, NULL); ++u; ++v; } break; } case PA_SAMPLE_S32LE: { const uint32_t *v = s32le_result[iter]; uint32_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_S32BE: { const uint32_t *v = s32be_result[iter]; uint32_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_S24_32LE: { const uint32_t *v = s24_32le_result[iter]; uint32_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_S24_32BE: { const uint32_t *v = s24_32be_result[iter]; uint32_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); ++u; ++v; } break; } case PA_SAMPLE_S24LE: { const uint8_t *v = s24le_result[iter]; uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); fail_unless(*(u+1) == *(v+1), NULL); fail_unless(*(u+2) == *(v+2), NULL); u += 3; v += 3; } break; } case PA_SAMPLE_S24BE: { const uint8_t *v = s24be_result[iter]; uint8_t *u = d; for (i = 0; i < chunk->length / pa_frame_size(ss); i++) { fail_unless(*u == *v, NULL); fail_unless(*(u+1) == *(v+1), NULL); fail_unless(*(u+2) == *(v+2), NULL); u += 3; v += 3; } break; } default: pa_assert_not_reached(); } pa_memblock_release(chunk->memblock); }
int main(int argc, char *argv[]) { pa_mempool *pool_a, *pool_b, *pool_c; unsigned id_a, id_b, id_c; pa_memexport *export_a, *export_b; pa_memimport *import_b, *import_c; pa_memblock *mb_a, *mb_b, *mb_c; int r, i; pa_memblock* blocks[5]; uint32_t id, shm_id; size_t offset, size; char *x; const char txt[] = "This is a test!"; pool_a = pa_mempool_new(1); pool_b = pa_mempool_new(1); pool_c = pa_mempool_new(1); pa_mempool_get_shm_id(pool_a, &id_a); pa_mempool_get_shm_id(pool_b, &id_b); pa_mempool_get_shm_id(pool_c, &id_c); pa_assert(pool_a && pool_b && pool_c); blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1); blocks[1] = pa_memblock_new(pool_a, sizeof(txt)); x = pa_memblock_acquire(blocks[1]); snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt); pa_memblock_release(blocks[1]); blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt)); x = pa_memblock_acquire(blocks[2]); snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt); pa_memblock_release(blocks[2]); blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt)); blocks[4] = NULL; for (i = 0; blocks[i]; i++) { printf("Memory block %u\n", i); mb_a = blocks[i]; pa_assert(mb_a); export_a = pa_memexport_new(pool_a, revoke_cb, (void*) "A"); export_b = pa_memexport_new(pool_b, revoke_cb, (void*) "B"); pa_assert(export_a && export_b); import_b = pa_memimport_new(pool_b, release_cb, (void*) "B"); import_c = pa_memimport_new(pool_c, release_cb, (void*) "C"); pa_assert(import_b && import_c); r = pa_memexport_put(export_a, mb_a, &id, &shm_id, &offset, &size); pa_assert(r >= 0); pa_assert(shm_id == id_a); printf("A: Memory block exported as %u\n", id); mb_b = pa_memimport_get(import_b, id, shm_id, offset, size); pa_assert(mb_b); r = pa_memexport_put(export_b, mb_b, &id, &shm_id, &offset, &size); pa_assert(r >= 0); pa_assert(shm_id == id_a || shm_id == id_b); pa_memblock_unref(mb_b); printf("B: Memory block exported as %u\n", id); mb_c = pa_memimport_get(import_c, id, shm_id, offset, size); pa_assert(mb_c); x = pa_memblock_acquire(mb_c); printf("1 data=%s\n", x); pa_memblock_release(mb_c); print_stats(pool_a, "A"); print_stats(pool_b, "B"); print_stats(pool_c, "C"); pa_memexport_free(export_b); x = pa_memblock_acquire(mb_c); printf("2 data=%s\n", x); pa_memblock_release(mb_c); pa_memblock_unref(mb_c); pa_memimport_free(import_b); pa_memblock_unref(mb_a); pa_memimport_free(import_c); pa_memexport_free(export_a); } printf("vaccuuming...\n"); pa_mempool_vacuum(pool_a); pa_mempool_vacuum(pool_b); pa_mempool_vacuum(pool_c); printf("vaccuuming done...\n"); pa_mempool_free(pool_a); pa_mempool_free(pool_b); pa_mempool_free(pool_c); return 0; }
static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) { pa_memblock *r; void *d; unsigned i; pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10)); d = pa_memblock_acquire(r); switch (ss->format) { case PA_SAMPLE_U8: case PA_SAMPLE_ULAW: case PA_SAMPLE_ALAW: { static const uint8_t u8_samples[] = { 0x00, 0xFF, 0x7F, 0x80, 0x9f, 0x3f, 0x01, 0xF0, 0x20, 0x21 }; memcpy(d, u8_samples, sizeof(u8_samples)); break; } case PA_SAMPLE_S16NE: case PA_SAMPLE_S16RE: { static const uint16_t u16_samples[] = { 0x0000, 0xFFFF, 0x7FFF, 0x8000, 0x9fff, 0x3fff, 0x0001, 0xF000, 0x0020, 0x0021 }; memcpy(d, u16_samples, sizeof(u16_samples)); break; } case PA_SAMPLE_S24_32NE: case PA_SAMPLE_S24_32RE: case PA_SAMPLE_S32NE: case PA_SAMPLE_S32RE: { static const uint32_t u32_samples[] = { 0x00000001, 0xFFFF0002, 0x7FFF0003, 0x80000004, 0x9fff0005, 0x3fff0006, 0x00010007, 0xF0000008, 0x00200009, 0x0021000A }; memcpy(d, u32_samples, sizeof(u32_samples)); break; } case PA_SAMPLE_S24NE: case PA_SAMPLE_S24RE: { /* Need to be on a byte array because they are not aligned */ static const uint8_t u24_samples[] = { 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x02, 0x7F, 0xFF, 0x03, 0x80, 0x00, 0x04, 0x9f, 0xff, 0x05, 0x3f, 0xff, 0x06, 0x01, 0x00, 0x07, 0xF0, 0x00, 0x08, 0x20, 0x00, 0x09, 0x21, 0x00, 0x0A }; memcpy(d, u24_samples, sizeof(u24_samples)); break; } case PA_SAMPLE_FLOAT32NE: case PA_SAMPLE_FLOAT32RE: { float *u = d; static const float float_samples[] = { 0.0f, -1.0f, 1.0f, 4711.0f, 0.222f, 0.33f, -.3f, 99.0f, -0.555f, -.123f }; if (ss->format == PA_SAMPLE_FLOAT32RE) { for (i = 0; i < 10; i++) u[i] = PA_FLOAT32_SWAP(float_samples[i]); } else memcpy(d, float_samples, sizeof(float_samples)); break; } default: pa_assert_not_reached(); } pa_memblock_release(r); return r; }
static void thread_func(void *userdata) { struct userdata *u = userdata; int read_type = 0; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); for (;;) { int ret; struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Try to read some data and pass it on to the source driver */ if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) { ssize_t l; void *p; if (!u->memchunk.memblock) { u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd)); u->memchunk.index = u->memchunk.length = 0; } pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index); p = pa_memblock_acquire(u->memchunk.memblock); l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type); pa_memblock_release(u->memchunk.memblock); pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */ if (l < 0) { if (errno == EINTR) continue; else if (errno != EAGAIN) { pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno)); goto fail; } } else { u->memchunk.length = (size_t) l; pa_source_post(u->source, &u->memchunk); u->memchunk.index += (size_t) l; if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } pollfd->revents = 0; } } /* Hmm, nothing to do. Let's sleep */ pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0); if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) goto fail; if (ret == 0) goto finish; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~POLLIN) { pa_log("FIFO shutdown."); goto fail; } } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
/* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; float *src, *dst; size_t fs; unsigned n, c; pa_memchunk tchunk; pa_usec_t current_latency PA_GCC_UNUSED; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); /* (1) IF YOU NEED A FIXED BLOCK SIZE USE * pa_memblockq_peek_fixed_size() HERE INSTEAD. NOTE THAT FILTERS * WHICH CAN DEAL WITH DYNAMIC BLOCK SIZES ARE HIGHLY * PREFERRED. */ while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; pa_sink_render(u->sink, nbytes, &nchunk); pa_memblockq_push(u->memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } /* (2) IF YOU NEED A FIXED BLOCK SIZE, THIS NEXT LINE IS NOT * NECESSARY */ tchunk.length = PA_MIN(nbytes, tchunk.length); pa_assert(tchunk.length > 0); fs = pa_frame_size(&i->sample_spec); n = (unsigned) (tchunk.length / fs); pa_assert(n > 0); chunk->index = 0; chunk->length = n*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); src = pa_memblock_acquire_chunk(&tchunk); dst = pa_memblock_acquire(chunk->memblock); /* (3) PUT YOUR CODE HERE TO DO SOMETHING WITH THE DATA */ /* As an example, copy input to output */ for (c = 0; c < u->channels; c++) { pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels * sizeof(float), src+c, u->channels * sizeof(float), n); } pa_memblock_release(tchunk.memblock); pa_memblock_release(chunk->memblock); pa_memblock_unref(tchunk.memblock); /* (4) IF YOU NEED THE LATENCY FOR SOMETHING ACQUIRE IT LIKE THIS: */ current_latency = /* Get the latency of the master sink */ pa_sink_get_latency_within_thread(i->sink) + /* Add the latency internal to our sink input on top */ pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); return 0; }