APULSE_EXPORT size_t pa_frame_size(const pa_sample_spec *spec) { return spec->channels * pa_sample_size(spec); }
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); }
static void * audio_capture_thr(void *args) { int error; PSAUDIORECTHRPARAMS params = (PSAUDIORECTHRPARAMS)args; pa_simple *pa_context = NULL; uint8_t active = 1; uint32_t buffer_size; SSAMPLEHEADER sample_header = {0}; HBUF sample; TIMING_MEASURE_AREA; buffer_size = HEADER_SIZE + params->sample_spec->channels * params->buffer_size * pa_sample_size(params->sample_spec); sample_header.number = 0; sample_header.buf_type = BUF_TYPE_INTERLEAVED; sample_header.sample_size = pa_sample_size(params->sample_spec); sample_header.samples = params->buffer_size; sample_header.channels = params->sample_spec->channels; sample_header.samplerate = params->sample_spec->rate; if ( !(pa_context = pa_simple_new(NULL, params->argv[0], PA_STREAM_RECORD, NULL, "record", params->sample_spec, NULL, NULL, &error)) ) { fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); goto audio_thr_finish; } printf("[source] audio thr\n"); while (active) { TIMING_START(); sample = buf_alloc(NULL); buf_resize(sample, buffer_size); sample->full_size = sample->size = sample->alloced_size; sample_zero_buffer(sample); memcpy(sample->buf, &sample_header, sizeof(SSAMPLEHEADER)); if (pa_simple_read(pa_context, sample->buf + HEADER_SIZE, sample->alloced_size - HEADER_SIZE, &error) < 0) { fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); goto audio_thr_finish; } pa_gettimeofday(&(sample_header.timestamp)); sample_header.number += 1; /* printf("[audio] read %p\n", sample); */ if ( (error = write(params->pipefd, &sample, sizeof(sample))) != sizeof(sample)) { if (error == -1) { handle_error("[audio] write()"); } buf_free(sample); perror("[audio] "); printf("[audio] uverrun. free buffer\n"); } active = params->active; TIMING_END(" audio"); } audio_thr_finish: pa_simple_free(pa_context); return NULL; }
static void audio_callback(void* data) { sa_stream_t* s = (sa_stream_t*)data; unsigned int bytes_per_frame = s->sample_spec.channels * pa_sample_size(&s->sample_spec); size_t buffer_size = s->sample_spec.rate * bytes_per_frame; char* buffer = malloc(buffer_size); while(1) { char* dst = buffer; size_t bytes_to_copy, bytes; pa_threaded_mainloop_lock(s->m); while(1) { if (s == NULL || s->stream == NULL) { if (s != NULL && s->m != NULL) pa_threaded_mainloop_unlock(s->m); goto free_buffer; } if ((bytes_to_copy = pa_stream_writable_size(s->stream)) == (size_t) -1) { fprintf(stderr, "pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(s->context))); pa_threaded_mainloop_unlock(s->m); goto free_buffer; } if(bytes_to_copy > 0) break; pa_threaded_mainloop_wait(s->m); } pa_threaded_mainloop_unlock(s->m); if (bytes_to_copy > buffer_size) bytes_to_copy = buffer_size; bytes = bytes_to_copy; pthread_mutex_lock(&s->mutex); if (!s->thread_id) { pthread_mutex_unlock(&s->mutex); break; } /* * Consume data from the start of the buffer list. */ while (1) { unsigned int avail = s->bl_head->end - s->bl_head->start; assert(s->bl_head->start <= s->bl_head->end); if (avail >= bytes_to_copy) { /* * We have all we need in the head buffer, so just grab it and go. */ memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy); s->bl_head->start += bytes_to_copy; break; } else { sa_buf* next = 0; /* * Copy what we can from the head and move on to the next buffer. */ memcpy(dst, s->bl_head->data + s->bl_head->start, avail); s->bl_head->start += avail; dst += avail; bytes_to_copy -= avail; /* * We want to free the now-empty buffer, but not if it's also the * current tail. If it is the tail, we don't have enough data to fill * the destination buffer, so we write less and give up. */ next = s->bl_head->next; if (next == NULL) { bytes = bytes-bytes_to_copy; break; } free(s->bl_head); s->bl_head = next; s->n_bufs--; } /* if (avail >= bytes_to_copy), else */ } /* while (1) */ if(bytes > 0) { pa_threaded_mainloop_lock(s->m); if (pa_stream_write(s->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE) < 0) { fprintf(stderr, "pa_stream_write() failed: %s", pa_strerror(pa_context_errno(s->context))); pa_threaded_mainloop_unlock(s->m); return; } pa_stream_update_timing_info(s->stream, NULL, NULL); s->bytes_written += bytes; pa_threaded_mainloop_unlock(s->m); } pthread_mutex_unlock(&s->mutex); } free_buffer: free(buffer); }
/** Return the size of a frame with the specific sample type */ size_t pa_frame_size(const pa_sample_spec *spec) { if ( spec == NULL ) return 0; return pa_sample_size(spec) * spec->channels; }