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; }
/* Called from IO context */ static int unsuspend(struct userdata *u) { int m; pa_sample_spec ss, *ss_original; int frag_size, in_frag_size, out_frag_size; int in_nfrags, out_nfrags; struct audio_buf_info info; pa_assert(u); pa_assert(u->fd < 0); m = u->mode; pa_log_info("Trying resume..."); if ((u->fd = pa_oss_open(u->device_name, &m, NULL)) < 0) { pa_log_warn("Resume failed, device busy (%s)", pa_cstrerror(errno)); return -1; } if (m != u->mode) { pa_log_warn("Resume failed, couldn't open device with original access mode."); goto fail; } if (u->nfrags >= 2 && u->frag_size >= 1) if (pa_oss_set_fragments(u->fd, u->nfrags, u->orig_frag_size) < 0) { pa_log_warn("Resume failed, couldn't set original fragment settings."); goto fail; } ss = *(ss_original = u->sink ? &u->sink->sample_spec : &u->source->sample_spec); if (pa_oss_auto_format(u->fd, &ss) < 0 || !pa_sample_spec_equal(&ss, ss_original)) { pa_log_warn("Resume failed, couldn't set original sample format settings."); goto fail; } if (ioctl(u->fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { pa_log_warn("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno)); goto fail; } in_frag_size = out_frag_size = frag_size; in_nfrags = out_nfrags = u->nfrags; if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { in_frag_size = info.fragsize; in_nfrags = info.fragstotal; } if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { out_frag_size = info.fragsize; out_nfrags = info.fragstotal; } if ((u->source && (in_frag_size != (int) u->in_fragment_size || in_nfrags != (int) u->in_nfrags)) || (u->sink && (out_frag_size != (int) u->out_fragment_size || out_nfrags != (int) u->out_nfrags))) { pa_log_warn("Resume failed, input fragment settings don't match."); goto fail; } if (u->use_mmap) { if (u->source) { if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno)); goto fail; } } if (u->sink) { if ((u->out_mmap = mmap(NULL, u->out_hwbuf_size, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { pa_log("Resume failed, mmap(): %s", pa_cstrerror(errno)); if (u->in_mmap && u->in_mmap != MAP_FAILED) { munmap(u->in_mmap, u->in_hwbuf_size); u->in_mmap = NULL; } goto fail; } pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &ss); } } u->out_mmap_current = u->in_mmap_current = 0; u->out_mmap_saved_nfrags = u->in_mmap_saved_nfrags = 0; pa_assert(!u->rtpoll_item); build_pollfd(u); if (u->sink && u->sink->get_volume) u->sink->get_volume(u->sink); if (u->source && u->source->get_volume) u->source->get_volume(u->source); pa_log_info("Resumed successfully..."); return 0; fail: pa_close(u->fd); u->fd = -1; return -1; }
static int trigger(struct userdata *u, pa_bool_t quick) { int enable_bits = 0, zero = 0; pa_assert(u); if (u->fd < 0) return 0; pa_log_debug("trigger"); if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) enable_bits |= PCM_ENABLE_INPUT; if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) enable_bits |= PCM_ENABLE_OUTPUT; pa_log_debug("trigger: %i", enable_bits); if (u->use_mmap) { if (!quick) ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero); #ifdef SNDCTL_DSP_HALT if (enable_bits == 0) if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0) pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno)); #endif if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno)); if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) { pa_log_debug("clearing playback buffer"); pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec); } } else { if (enable_bits) if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0) pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno)); if (!quick) { /* * Some crappy drivers do not start the recording until we * read something. Without this snippet, poll will never * register the fd as ready. */ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size); if (pa_read(u->fd, buf, u->in_fragment_size, NULL) < 0) { pa_log("pa_read() failed: %s", pa_cstrerror(errno)); pa_xfree(buf); return -1; } pa_xfree(buf); } } } return 0; }
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); }
/* This is called whenever new data may is available */ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) { pa_assert(s); pa_assert(length > 0); if (raw) { pa_assert(!sndfile); if (stdio_event) mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT); while (pa_stream_readable_size(s) > 0) { const void *data; if (pa_stream_peek(s, &data, &length) < 0) { pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context))); quit(1); return; } pa_assert(length > 0); /* If there is a hole in the stream, we generate silence, except * if it's a passthrough stream in which case we skip the hole. */ if (data || !(flags & PA_STREAM_PASSTHROUGH)) { buffer = pa_xrealloc(buffer, buffer_length + length); if (data) memcpy((uint8_t *) buffer + buffer_length, data, length); else pa_silence_memory((uint8_t *) buffer + buffer_length, length, &sample_spec); buffer_length += length; } pa_stream_drop(s); } } else { pa_assert(sndfile); while (pa_stream_readable_size(s) > 0) { sf_count_t bytes; const void *data; if (pa_stream_peek(s, &data, &length) < 0) { pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context))); quit(1); return; } pa_assert(length > 0); if (!data && (flags & PA_STREAM_PASSTHROUGH)) { pa_stream_drop(s); continue; } if (!data && length > silence_buffer_length) { silence_buffer = pa_xrealloc(silence_buffer, length); pa_silence_memory((uint8_t *) silence_buffer + silence_buffer_length, length - silence_buffer_length, &sample_spec); silence_buffer_length = length; } if (writef_function) { size_t k = pa_frame_size(&sample_spec); if ((bytes = writef_function(sndfile, data ? data : silence_buffer, (sf_count_t) (length/k))) > 0) bytes *= (sf_count_t) k; } else bytes = sf_write_raw(sndfile, data ? data : silence_buffer, (sf_count_t) length); if (bytes < (sf_count_t) length) quit(1); pa_stream_drop(s); } } }
/* Sink and source states are passed as arguments, because this is called * during state changes, and we need the new state, but thread_info.state * has not yet been updated. */ static void trigger(struct userdata *u, pa_sink_state_t sink_state, pa_source_state_t source_state, bool quick) { int enable_bits = 0, zero = 0; pa_assert(u); if (u->fd < 0) return; pa_log_debug("trigger"); if (u->source && PA_SOURCE_IS_OPENED(source_state)) enable_bits |= PCM_ENABLE_INPUT; if (u->sink && PA_SINK_IS_OPENED(sink_state)) enable_bits |= PCM_ENABLE_OUTPUT; pa_log_debug("trigger: %i", enable_bits); if (u->use_mmap) { if (!quick) ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero); #ifdef SNDCTL_DSP_HALT if (enable_bits == 0) if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0) pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno)); #endif if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno)); if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) { pa_log_debug("clearing playback buffer"); pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec); } } else { if (enable_bits) if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0) pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno)); if (!quick) { /* * Some crappy drivers do not start the recording until we * read something. Without this snippet, poll will never * register the fd as ready. */ if (u->source && PA_SOURCE_IS_OPENED(source_state)) { uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size); /* XXX: Shouldn't this be done only when resuming the source? * Currently this code path is executed also when resuming the * sink while the source is already running. */ if (pa_read(u->fd, buf, u->in_fragment_size, NULL) < 0) pa_log("pa_read() failed: %s", pa_cstrerror(errno)); pa_xfree(buf); } } } }