void mi6k_frame_new(pa_memchunk *out, unsigned char **chars_out, struct mi6k_dim_command **dim_command_out) { unsigned char *clear_cmd; unsigned char *chars; struct mi6k_dim_command *dim_command; out->memblock = pa_memblock_new(mi6k.pool, FRAME_SIZE); assert(out->memblock); out->index = 0; out->length = out->memblock->length; clear_cmd = out->memblock->data; dim_command = (void*) (clear_cmd + 1); chars = (void*) (dim_command + 1); *clear_cmd = MI6K_HOME; dim_command->cmd[0] = MI6K_SET_DIM_1; dim_command->cmd[1] = MI6K_SET_DIM_2; dim_command->column = 0xFF; dim_command->level = MI6K_BRIGHTNESS_MIN; memset(chars, ' ', MI6K_WIDTH * MI6K_HEIGHT); if (chars_out) { *chars_out = chars; } if (dim_command_out) { *dim_command_out = dim_command; } }
pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) { pa_memblock *n; size_t l; void *tdata, *sdata; pa_assert(c); pa_assert(c->memblock); if (pa_memblock_ref_is_one(c->memblock) && !pa_memblock_is_read_only(c->memblock) && pa_memblock_get_length(c->memblock) >= c->index+min) return c; l = PA_MAX(c->length, min); n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l); sdata = pa_memblock_acquire(c->memblock); tdata = pa_memblock_acquire(n); memcpy(tdata, (uint8_t*) sdata + c->index, c->length); pa_memblock_release(c->memblock); pa_memblock_release(n); pa_memblock_unref(c->memblock); c->memblock = n; c->index = 0; return c; }
static int process_source_data(struct userdata *u) { ssize_t l; void *p; if (!u->memchunk_source.memblock) { u->memchunk_source.memblock = pa_memblock_new(u->core->mempool, 16*1024); // at least vchan buffer size u->memchunk_source.index = u->memchunk_source.length = 0; } pa_assert(pa_memblock_get_length(u->memchunk_source.memblock) > u->memchunk_source.index); p = pa_memblock_acquire(u->memchunk_source.memblock); l = libvchan_read(u->rec_ctrl, p + u->memchunk_source.index, pa_memblock_get_length(u->memchunk_source.memblock) - u->memchunk_source.index); pa_memblock_release(u->memchunk_source.memblock); pa_log_debug("process_source_data %lu", l); if (l <= 0) { /* vchan disconnected/error */ pa_log("Failed to read data from vchan"); return -1; } else { u->memchunk_source.length = (size_t) l; pa_source_post(u->source, &u->memchunk_source); u->memchunk_source.index += (size_t) l; if (u->memchunk_source.index >= pa_memblock_get_length(u->memchunk_source.memblock)) { pa_memblock_unref(u->memchunk_source.memblock); pa_memchunk_reset(&u->memchunk_source); } } return 0; }
pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) { pa_memchunk *packet = NULL; size_t i; pa_assert(pb); pa_assert(pb->packets); if (seq == 0) { /* 0 means seq reached UINT16_MAX and has been wrapped... */ pa_assert(pb->seq == UINT16_MAX); pb->seq = 0; } else { /* ...otherwise, seq MUST have be increased! */ pa_assert(seq == pb->seq + 1); pb->seq++; } i = (pb->pos + 1) % pb->size; if (pb->packets[i].memblock) pa_memblock_unref(pb->packets[i].memblock); pa_memchunk_reset(&pb->packets[i]); pb->packets[i].memblock = pa_memblock_new(pb->mempool, size); pb->packets[i].length = size; pb->packets[i].index = 0; packet = &pb->packets[i]; if (pb->count < pb->size) pb->count++; pb->pos = i; return packet; }
static int thread_read(struct userdata *u) { void *p; ssize_t readd; pa_memchunk chunk; chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size); p = pa_memblock_acquire(chunk.memblock); readd = u->stream->read(u->stream, (uint8_t*) p, pa_memblock_get_length(chunk.memblock)); pa_memblock_release(chunk.memblock); if (readd < 0) { pa_log("Failed to read from stream. (err %i)", readd); goto end; } u->timestamp += pa_bytes_to_usec(readd, &u->source->sample_spec); chunk.index = 0; chunk.length = readd; if (chunk.length > 0) pa_source_post(u->source, &chunk); end: pa_memblock_unref(chunk.memblock); return 0; }
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; }
/* 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, h, c; pa_memchunk tchunk; 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); 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); } tchunk.length = PA_MIN(nbytes, tchunk.length); pa_assert(tchunk.length > 0); fs = pa_frame_size(&i->sample_spec); n = (unsigned) (PA_MIN(tchunk.length, u->block_size) / 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 = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); dst = (float*) pa_memblock_acquire(chunk->memblock); for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) { for (c = 0; c < u->input_count; c++) pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c], sizeof(float), src+ h*u->max_ladspaport_count + c, u->channels*sizeof(float), n); u->descriptor->run(u->handle[h], n); for (c = 0; c < u->output_count; c++) pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + h*u->max_ladspaport_count + c, u->channels*sizeof(float), u->output[c], sizeof(float), n); } pa_memblock_release(tchunk.memblock); pa_memblock_release(chunk->memblock); pa_memblock_unref(tchunk.memblock); return 0; }
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; } }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); u->timestamp = pa_rtclock_now(); for (;;) { int ret; /* Generate some null data */ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { pa_usec_t now; pa_memchunk chunk; now = pa_rtclock_now(); if ((chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) { chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); /* or chunk.length? */ chunk.index = 0; pa_source_post(u->source, &chunk); pa_memblock_unref(chunk.memblock); u->timestamp = now; } pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->latency_time * PA_USEC_PER_MSEC); } else pa_rtpoll_set_timer_disabled(u->rtpoll); /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; } 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 *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; }
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); }
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 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 write_type = 0; pa_memchunk silence; uint32_t silence_overhead = 0; double silence_ratio = 0; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); /* Create a chunk of memory that is our encoded silence sample. */ pa_memchunk_reset(&silence); for (;;) { int ret; if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) { pa_usec_t usec; int64_t n; void *p; if (!silence.memblock) { pa_memchunk silence_tmp; pa_memchunk_reset(&silence_tmp); silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096); silence_tmp.length = 4096; p = pa_memblock_acquire(silence_tmp.memblock); memset(p, 0, 4096); pa_memblock_release(silence_tmp.memblock); pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence); pa_assert(0 == silence_tmp.length); silence_overhead = silence_tmp.length - 4096; silence_ratio = silence_tmp.length / 4096; pa_memblock_unref(silence_tmp.memblock); } for (;;) { ssize_t l; if (u->encoded_memchunk.length <= 0) { if (u->encoded_memchunk.memblock) pa_memblock_unref(u->encoded_memchunk.memblock); if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { size_t rl; /* We render real data */ if (u->raw_memchunk.length <= 0) { if (u->raw_memchunk.memblock) pa_memblock_unref(u->raw_memchunk.memblock); pa_memchunk_reset(&u->raw_memchunk); /* Grab unencoded data */ pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); } pa_assert(u->raw_memchunk.length > 0); /* Encode it */ rl = u->raw_memchunk.length; u->encoding_overhead += u->next_encoding_overhead; pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } else { /* We render some silence into our memchunk */ memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); pa_memblock_ref(silence.memblock); /* Calculate/store some values to be used with the smoother */ u->next_encoding_overhead = silence_overhead; u->encoding_ratio = silence_ratio; } } pa_assert(u->encoded_memchunk.length > 0); p = pa_memblock_acquire(u->encoded_memchunk.memblock); l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type); pa_memblock_release(u->encoded_memchunk.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) { /* OK, we filled all socket buffers up * now. */ goto filled_up; } else { pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); goto fail; } } else { u->offset += l; u->encoded_memchunk.index += l; u->encoded_memchunk.length -= l; pollfd->revents = 0; if (u->encoded_memchunk.length > 0) { /* we've completely written the encoded data, so update our overhead */ u->encoding_overhead += u->next_encoding_overhead; /* OK, we wrote less that we asked for, * hence we can assume that the socket * buffers are full now */ goto filled_up; } } } filled_up: /* At this spot we know that the socket buffers are * fully filled up. This is the best time to estimate * the playback position of the server */ n = u->offset - u->encoding_overhead; #ifdef SIOCOUTQ { int l; if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) n -= (l / u->encoding_ratio); } #endif usec = pa_bytes_to_usec(n, &u->sink->sample_spec); if (usec > u->latency) usec -= u->latency; else usec = 0; pa_smoother_put(u->smoother, pa_rtclock_now(), usec); } /* Hmm, nothing to do. Let's sleep */ pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ } if ((ret = pa_rtpoll_run(u->rtpoll)) < 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) { if (u->sink->thread_info.state != PA_SINK_SUSPENDED) { pa_log("FIFO shutdown."); goto fail; } /* We expect this to happen on occasion if we are not sending data. It's perfectly natural and normal and natural */ if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); u->rtpoll_item = NULL; } } } 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: if (silence.memblock) pa_memblock_unref(silence.memblock); pa_log_debug("Thread shutting down"); }
static void thread_func(void *userdata) { struct userdata *u = userdata; bool timer_elapsed = false; size_t max_block_size; pa_assert(u); pa_log_debug("Thread starting up"); if (u->core->realtime_scheduling) pa_thread_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); max_block_size = pa_frame_align(pa_mempool_block_size_max(u->core->mempool), &u->source->sample_spec); u->timestamp = pa_rtclock_now(); for (;;) { int ret; /* Generate some null data */ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { pa_usec_t now; pa_memchunk chunk; now = pa_rtclock_now(); if (timer_elapsed && (chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) { chunk.length = PA_MIN(max_block_size, chunk.length); chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); chunk.index = 0; pa_silence_memchunk(&chunk, &u->source->sample_spec); pa_source_post(u->source, &chunk); pa_memblock_unref(chunk.memblock); u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec); } pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->block_usec); } else pa_rtpoll_set_timer_disabled(u->rtpoll); /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) goto fail; timer_elapsed = pa_rtpoll_timer_elapsed(u->rtpoll); if (ret == 0) goto finish; } 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"); }
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: { memcpy(d, u8_result[0], sizeof(u8_result[0])); break; } case PA_SAMPLE_S16NE: case PA_SAMPLE_S16RE: { if (ss->format == PA_SAMPLE_S16RE) { uint16_t *u = d; for (i = 0; i < 10; i++) u[i] = PA_UINT16_SWAP(s16ne_result[0][i]); } else memcpy(d, s16ne_result[0], sizeof(s16ne_result[0])); break; } case PA_SAMPLE_S24_32NE: case PA_SAMPLE_S24_32RE: case PA_SAMPLE_S32NE: case PA_SAMPLE_S32RE: { if (ss->format == PA_SAMPLE_S24_32RE || ss->format == PA_SAMPLE_S32RE) { uint32_t *u = d; for (i = 0; i < 10; i++) u[i] = PA_UINT32_SWAP(s32ne_result[0][i]); } else memcpy(d, s32ne_result[0], sizeof(s32ne_result[0])); break; } case PA_SAMPLE_S24NE: case PA_SAMPLE_S24RE: if (ss->format == PA_SAMPLE_S24LE) { uint8_t *u = d; for (i = 0; i < 30; i += 3) PA_WRITE24LE(&u[i], PA_READ24BE(&s24be_result[0][i])); } else memcpy(d, s24be_result[0], sizeof(s24be_result[0])); break; case PA_SAMPLE_FLOAT32NE: case PA_SAMPLE_FLOAT32RE: { if (ss->format == PA_SAMPLE_FLOAT32RE) { float *u = d; for (i = 0; i < 10; i++) PA_WRITE_FLOAT32RE(&u[i], float32ne_result[0][i]); } else memcpy(d, float32ne_result[0], sizeof(float32ne_result[0])); break; } default: pa_assert_not_reached(); } pa_memblock_release(r); return r; }
/* 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; }
static void thread_func(void *userdata) { struct userdata *u = userdata; unsigned short revents = 0; int ret, err; audio_info_t info; 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); pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); for (;;) { /* Render some data and write it to the dsp */ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) process_rewind(u); if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) { pa_usec_t xtime0, ysleep_interval, xsleep_interval; uint64_t buffered_bytes; err = ioctl(u->fd, AUDIO_GETINFO, &info); if (err < 0) { pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno)); goto fail; } if (info.play.error) { pa_log_debug("buffer under-run!"); AUDIO_INITINFO(&info); info.play.error = 0; if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); pa_smoother_reset(u->smoother, pa_rtclock_now(), true); } for (;;) { void *p; ssize_t w; size_t len; int write_type = 1; /* * Since we cannot modify the size of the output buffer we fake it * by not filling it more than u->buffer_size. */ xtime0 = pa_rtclock_now(); buffered_bytes = get_playback_buffered_bytes(u); if (buffered_bytes >= (uint64_t)u->buffer_size) break; len = u->buffer_size - buffered_bytes; len -= len % u->frame_size; if (len < (size_t) u->minimum_request) break; if (!u->memchunk.length) pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk); len = PA_MIN(u->memchunk.length, len); p = pa_memblock_acquire(u->memchunk.memblock); w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type); pa_memblock_release(u->memchunk.memblock); if (w <= 0) { if (errno == EINTR) { continue; } else if (errno == EAGAIN) { /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */ pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes); break; } else { pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); goto fail; } } else { pa_assert(w % u->frame_size == 0); u->written_bytes += w; u->memchunk.index += w; u->memchunk.length -= w; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } } } ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec); xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval); pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval)); } else pa_rtpoll_set_timer_disabled(u->rtpoll); /* 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)) { pa_memchunk memchunk; void *p; ssize_t r; size_t len; err = ioctl(u->fd, AUDIO_GETINFO, &info); pa_assert(err >= 0); if (info.record.error) { pa_log_debug("buffer overflow!"); AUDIO_INITINFO(&info); info.record.error = 0; if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); } err = ioctl(u->fd, I_NREAD, &len); pa_assert(err >= 0); if (len > 0) { memchunk.memblock = pa_memblock_new(u->core->mempool, len); pa_assert(memchunk.memblock); p = pa_memblock_acquire(memchunk.memblock); r = pa_read(u->fd, p, len, NULL); pa_memblock_release(memchunk.memblock); if (r < 0) { pa_memblock_unref(memchunk.memblock); if (errno == EAGAIN) break; else { pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); goto fail; } } else { u->read_bytes += r; memchunk.index = 0; memchunk.length = r; pa_source_post(u->source, &memchunk); pa_memblock_unref(memchunk.memblock); revents &= ~POLLIN; } } } if (u->rtpoll_item) { struct pollfd *pollfd; pa_assert(u->fd >= 0); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 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: /* We have to continue processing messages until we receive the * SHUTDOWN message */ 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; }
/* Called from IO thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { file_stream *u; pa_sink_input_assert_ref(i); pa_assert(chunk); u = FILE_STREAM(i->userdata); file_stream_assert_ref(u); if (!u->memblockq) return -1; for (;;) { pa_memchunk tchunk; size_t fs; void *p; sf_count_t n; if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { chunk->length = PA_MIN(chunk->length, length); pa_memblockq_drop(u->memblockq, chunk->length); return 0; } if (!u->sndfile) break; tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); tchunk.index = 0; p = pa_memblock_acquire(tchunk.memblock); if (u->readf_function) { fs = pa_frame_size(&i->sample_spec); n = u->readf_function(u->sndfile, p, (sf_count_t) (length/fs)); } else { fs = 1; n = sf_read_raw(u->sndfile, p, (sf_count_t) length); } pa_memblock_release(tchunk.memblock); if (n <= 0) { pa_memblock_unref(tchunk.memblock); sf_close(u->sndfile); u->sndfile = NULL; break; } tchunk.length = (size_t) n * fs; pa_memblockq_push(u->memblockq, &tchunk); pa_memblock_unref(tchunk.memblock); } if (pa_sink_input_safe_to_remove(i)) { pa_memblockq_free(u->memblockq); u->memblockq = NULL; pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); } return -1; }
int main(int argc, char *argv[]) { pa_mempool *pool = NULL; pa_sample_spec a, b; int ret = 1, c; bool all_formats = true; pa_resample_method_t method; int seconds; unsigned crossover_freq = 120; static const struct option long_options[] = { {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, ARG_VERSION}, {"from-rate", 1, NULL, ARG_FROM_SAMPLERATE}, {"from-format", 1, NULL, ARG_FROM_SAMPLEFORMAT}, {"from-channels", 1, NULL, ARG_FROM_CHANNELS}, {"to-rate", 1, NULL, ARG_TO_SAMPLERATE}, {"to-format", 1, NULL, ARG_TO_SAMPLEFORMAT}, {"to-channels", 1, NULL, ARG_TO_CHANNELS}, {"seconds", 1, NULL, ARG_SECONDS}, {"resample-method", 1, NULL, ARG_RESAMPLE_METHOD}, {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS}, {NULL, 0, NULL, 0} }; setlocale(LC_ALL, ""); #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); #endif pa_log_set_level(PA_LOG_WARN); if (!getenv("MAKE_CHECK")) pa_log_set_level(PA_LOG_INFO); pa_assert_se(pool = pa_mempool_new(false, 0)); a.channels = b.channels = 1; a.rate = b.rate = 44100; a.format = b.format = PA_SAMPLE_S16LE; method = PA_RESAMPLER_AUTO; seconds = 60; while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) { switch (c) { case 'h' : help(argv[0]); ret = 0; goto quit; case 'v': pa_log_set_level(PA_LOG_DEBUG); break; case ARG_VERSION: printf(_("%s %s\n"), argv[0], PACKAGE_VERSION); ret = 0; goto quit; case ARG_DUMP_RESAMPLE_METHODS: dump_resample_methods(); ret = 0; goto quit; case ARG_FROM_CHANNELS: a.channels = (uint8_t) atoi(optarg); break; case ARG_FROM_SAMPLEFORMAT: a.format = pa_parse_sample_format(optarg); all_formats = false; break; case ARG_FROM_SAMPLERATE: a.rate = (uint32_t) atoi(optarg); break; case ARG_TO_CHANNELS: b.channels = (uint8_t) atoi(optarg); break; case ARG_TO_SAMPLEFORMAT: b.format = pa_parse_sample_format(optarg); all_formats = false; break; case ARG_TO_SAMPLERATE: b.rate = (uint32_t) atoi(optarg); break; case ARG_SECONDS: seconds = atoi(optarg); break; case ARG_RESAMPLE_METHOD: if (*optarg == '\0' || pa_streq(optarg, "help")) { dump_resample_methods(); ret = 0; goto quit; } method = pa_parse_resample_method(optarg); break; default: goto quit; } } ret = 0; pa_assert_se(pool = pa_mempool_new(false, 0)); if (!all_formats) { pa_resampler *resampler; pa_memchunk i, j; pa_usec_t ts; pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS); pa_log_debug("=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)", seconds, a.rate, a.channels, pa_sample_format_to_string(a.format), b.rate, b.channels, pa_sample_format_to_string(b.format)); ts = pa_rtclock_now(); pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0)); pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts)); i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a)); ts = pa_rtclock_now(); i.length = pa_memblock_get_length(i.memblock); i.index = 0; while (seconds--) { pa_resampler_run(resampler, &i, &j); if (j.memblock) pa_memblock_unref(j.memblock); } pa_log_info("resampling: %llu", (long long unsigned)(pa_rtclock_now() - ts)); pa_memblock_unref(i.memblock); pa_resampler_free(resampler); goto quit; } for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) { for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) { pa_resampler *forth, *back; pa_memchunk i, j, k; pa_log_debug("=== %s -> %s -> %s -> /2", pa_sample_format_to_string(a.format), pa_sample_format_to_string(b.format), pa_sample_format_to_string(a.format)); pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0)); pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0)); i.memblock = generate_block(pool, &a); i.length = pa_memblock_get_length(i.memblock); i.index = 0; pa_resampler_run(forth, &i, &j); pa_resampler_run(back, &j, &k); dump_block("before", &a, &i); dump_block("after", &b, &j); dump_block("reverse", &a, &k); pa_memblock_unref(i.memblock); pa_memblock_unref(j.memblock); pa_memblock_unref(k.memblock); pa_resampler_free(forth); pa_resampler_free(back); } } quit: if (pool) pa_mempool_free(pool); return ret; }
static void thread_func(void *userdata) { struct userdata *u = userdata; unsigned short revents = 0; int ret; pa_assert(u); pa_log_debug("Thread starting up"); if (u->core->high_priority) pa_make_realtime(); pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); for (;;) { /* Render some data and write it to the dsp */ if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) { audio_info_t info; int err; size_t len; err = ioctl(u->fd, AUDIO_GETINFO, &info); pa_assert(err >= 0); /* * Since we cannot modify the size of the output buffer we fake it * by not filling it more than u->buffer_size. */ len = u->buffer_size; len -= u->written_bytes - (info.play.samples * u->frame_size); /* The sample counter can sometimes go backwards :( */ if (len > u->buffer_size) len = 0; if (info.play.error) { pa_log_debug("Solaris buffer underflow!"); clear_underflow(u); } len -= len % u->frame_size; while (len) { void *p; ssize_t r; if (!u->memchunk.length) pa_sink_render(u->sink, len, &u->memchunk); pa_assert(u->memchunk.length); p = pa_memblock_acquire(u->memchunk.memblock); r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL); pa_memblock_release(u->memchunk.memblock); if (r < 0) { if (errno == EINTR) continue; else if (errno != EAGAIN) { pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); goto fail; } } else { pa_assert(r % u->frame_size == 0); u->memchunk.index += r; u->memchunk.length -= r; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } len -= r; u->written_bytes += r; } } } /* Try to read some data and pass it on to the source driver */ if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) { pa_memchunk memchunk; int err; size_t l; void *p; ssize_t r; audio_info_t info; err = ioctl(u->fd, AUDIO_GETINFO, &info); pa_assert(err >= 0); if (info.record.error) { pa_log_debug("Solaris buffer overflow!"); clear_overflow(u); } err = ioctl(u->fd, I_NREAD, &l); pa_assert(err >= 0); if (l > 0) { /* This is to make sure it fits in the memory pool. Also, a page should be the most efficient transfer size. */ if (l > u->page_size) l = u->page_size; memchunk.memblock = pa_memblock_new(u->core->mempool, l); pa_assert(memchunk.memblock); p = pa_memblock_acquire(memchunk.memblock); r = pa_read(u->fd, p, l, NULL); pa_memblock_release(memchunk.memblock); if (r < 0) { pa_memblock_unref(memchunk.memblock); if (errno != EAGAIN) { pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); goto fail; } } else { memchunk.index = 0; memchunk.length = r; pa_source_post(u->source, &memchunk); pa_memblock_unref(memchunk.memblock); u->read_bytes += r; revents &= ~POLLIN; } } } if (u->fd >= 0) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->events = ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0); } /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) goto fail; if (ret == 0) goto finish; if (u->fd >= 0) { 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: /* We have to continue processing messages until we receive the * SHUTDOWN message */ 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 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"); }
/* Called from input thread context */ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert_se(u = o->userdata); if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) { pa_log("push when no link?"); return; } /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */ /* if uplink sink exists, pull data from there; simplify by using same length as chunk provided by source */ if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) { pa_memchunk tchunk; size_t nbytes = chunk->length; pa_mix_info streams[2]; pa_memchunk target_chunk; void *target; int ch; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); /* get data from the sink */ while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) { pa_memchunk nchunk; /* make sure we get nbytes from the sink with render_full, otherwise we cannot mix with the uplink */ pa_sink_render_full(u->sink, nbytes, &nchunk); pa_memblockq_push(u->sink_memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } pa_assert(tchunk.length == chunk->length); /* move the read pointer for sink memblockq */ pa_memblockq_drop(u->sink_memblockq, tchunk.length); /* allocate target chunk */ /* this could probably be done in-place, but having chunk as both the input and output creates issues with reference counts */ target_chunk.index = 0; target_chunk.length = chunk->length; pa_assert(target_chunk.length == chunk->length); target_chunk.memblock = pa_memblock_new(o->source->core->mempool, target_chunk.length); pa_assert( target_chunk.memblock ); /* get target pointer */ target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock) + target_chunk.index); /* set-up mixing structure volume was taken care of in sink and source already */ streams[0].chunk = *chunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[0].volume.channels = o->sample_spec.channels; streams[1].chunk = tchunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[1].volume.channels = o->sample_spec.channels; /* do mixing */ pa_mix(streams, /* 2 streams to be mixed */ 2, target, /* put result in target chunk */ chunk->length, /* same length as input */ (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */ NULL, /* no volume information */ FALSE); /* no mute */ pa_memblock_release(target_chunk.memblock); pa_memblock_unref(tchunk.memblock); /* clean-up */ /* forward the data to the virtual source */ pa_source_post(u->source, &target_chunk); pa_memblock_unref(target_chunk.memblock); /* clean-up */ } else { /* forward the data to the virtual source */ pa_source_post(u->source, chunk); } }
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; unsigned n; pa_memchunk tchunk; unsigned j, k, l; float sum_right, sum_left; float current_sample; 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); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; pa_sink_render(u->sink, nbytes * u->sink_fs / u->fs, &nchunk); pa_memblockq_push(u->memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } tchunk.length = PA_MIN(nbytes * u->sink_fs / u->fs, tchunk.length); pa_assert(tchunk.length > 0); n = (unsigned) (tchunk.length / u->sink_fs); pa_assert(n > 0); chunk->index = 0; chunk->length = n * u->fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, n * u->sink_fs); src = pa_memblock_acquire_chunk(&tchunk); dst = pa_memblock_acquire(chunk->memblock); for (l = 0; l < n; l++) { memcpy(((char*) u->input_buffer) + u->input_buffer_offset * u->sink_fs, ((char *) src) + l * u->sink_fs, u->sink_fs); sum_right = 0; sum_left = 0; /* fold the input buffer with the impulse response */ for (j = 0; j < u->hrir_samples; j++) { for (k = 0; k < u->channels; k++) { current_sample = u->input_buffer[((u->input_buffer_offset + j) % u->hrir_samples) * u->channels + k]; sum_left += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_left[k]]; sum_right += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_right[k]]; } } dst[2 * l] = PA_CLAMP_UNLIKELY(sum_left, -1.0f, 1.0f); dst[2 * l + 1] = PA_CLAMP_UNLIKELY(sum_right, -1.0f, 1.0f); u->input_buffer_offset--; if (u->input_buffer_offset < 0) u->input_buffer_offset += u->hrir_samples; } pa_memblock_release(tchunk.memblock); pa_memblock_release(chunk->memblock); pa_memblock_unref(tchunk.memblock); return 0; }