mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
module-equalizer-sink: resyncing with head and fix invalid writes
* pa_log->debug for default equalizer notification
* partially fixed infinite rewind bug
* set max_request to window_size first iteration
* swap order inside ROUND_UP calls
* resync pa_sink_input_new changes
* change pa_sample_clamp parameters to be correct to fix invalid writes
* reenable proper reset logic + proper request size
This commit is contained in:
parent
3053badf06
commit
a434f4c6af
1 changed files with 108 additions and 105 deletions
|
|
@ -99,10 +99,10 @@ struct userdata {
|
|||
* the latency of the filter, calculated from window_size
|
||||
* based on constraints of COLA and window function
|
||||
*/
|
||||
size_t latency;//Really just R but made into it's own variable
|
||||
//for twiddling with pulseaudio
|
||||
size_t overlap_size;//window_size-R
|
||||
size_t samples_gathered;
|
||||
size_t input_buffer_max;
|
||||
//message
|
||||
float *W;//windowing function (time domain)
|
||||
float *work_buffer, **input, **overlap_accum;
|
||||
|
|
@ -198,6 +198,34 @@ static int is_monotonic(const uint32_t *xs,size_t length){
|
|||
return 1;
|
||||
}
|
||||
|
||||
//ensure's memory allocated is a multiple of v_size
|
||||
//and aligned
|
||||
static void * alloc(size_t x,size_t s){
|
||||
size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
|
||||
float *t;
|
||||
pa_assert(f >= x*s);
|
||||
t = fftwf_malloc(f);
|
||||
memset(t, 0, f);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void alloc_input_buffers(struct userdata *u, size_t min_buffer_length){
|
||||
if(min_buffer_length <= u->input_buffer_max){
|
||||
return;
|
||||
}
|
||||
pa_assert(min_buffer_length >= u->window_size);
|
||||
for(size_t c = 0; c < u->channels; ++c){
|
||||
float *tmp = alloc(min_buffer_length, sizeof(float));
|
||||
if(u->input[c]){
|
||||
if(!u->first_iteration){
|
||||
memcpy(tmp, u->input[c], u->overlap_size * sizeof(float));
|
||||
}
|
||||
free(u->input[c]);
|
||||
}
|
||||
u->input[c] = tmp;
|
||||
}
|
||||
u->input_buffer_max = min_buffer_length;
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
|
||||
|
|
@ -359,7 +387,7 @@ static void dsp_logic(
|
|||
|
||||
//preseve the needed input for the next window's overlap
|
||||
memmove(src, src + u->R,
|
||||
u->overlap_size * sizeof(float)
|
||||
(u->samples_gathered - u->R) * sizeof(float)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -470,71 +498,64 @@ typedef union float_vector {
|
|||
//}
|
||||
|
||||
static void process_samples(struct userdata *u, pa_memchunk *tchunk){
|
||||
size_t fs=pa_frame_size(&(u->sink->sample_spec));
|
||||
size_t fs = pa_frame_size(&(u->sink->sample_spec));
|
||||
float *dst;
|
||||
unsigned a_i;
|
||||
float *H, X;
|
||||
pa_assert(u->samples_gathered >= u->R);
|
||||
size_t iterations, offset;
|
||||
pa_assert(u->samples_gathered >= u->window_size);
|
||||
iterations = (u->samples_gathered - u->overlap_size) / u->R;
|
||||
tchunk->index = 0;
|
||||
tchunk->length = u->R * fs;
|
||||
tchunk->length = iterations * u->R * fs;
|
||||
tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
|
||||
dst = ((float*)pa_memblock_acquire(tchunk->memblock));
|
||||
|
||||
for(size_t c=0;c < u->channels; c++) {
|
||||
a_i = pa_aupdate_read_begin(u->a_H[c]);
|
||||
X = u->Xs[c][a_i];
|
||||
H = u->Hs[c][a_i];
|
||||
dsp_logic(
|
||||
u->work_buffer,
|
||||
u->input[c],
|
||||
u->overlap_accum[c],
|
||||
X,
|
||||
H,
|
||||
u->W,
|
||||
u->output_window,
|
||||
u
|
||||
);
|
||||
pa_aupdate_read_end(u->a_H[c]);
|
||||
if(u->first_iteration){
|
||||
/* The windowing function will make the audio ramped in, as a cheap fix we can
|
||||
* undo the windowing (for non-zero window values)
|
||||
*/
|
||||
for(size_t i = 0;i < u->overlap_size; ++i){
|
||||
u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
|
||||
dst = ((float*) pa_memblock_acquire(tchunk->memblock));
|
||||
for(size_t iter = 0; iter < iterations; ++iter){
|
||||
offset = iter * u->R * fs;
|
||||
for(size_t c = 0;c < u->channels; c++) {
|
||||
a_i = pa_aupdate_read_begin(u->a_H[c]);
|
||||
X = u->Xs[c][a_i];
|
||||
H = u->Hs[c][a_i];
|
||||
dsp_logic(
|
||||
u->work_buffer,
|
||||
u->input[c],
|
||||
u->overlap_accum[c],
|
||||
X,
|
||||
H,
|
||||
u->W,
|
||||
u->output_window,
|
||||
u
|
||||
);
|
||||
pa_aupdate_read_end(u->a_H[c]);
|
||||
if(u->first_iteration){
|
||||
/* The windowing function will make the audio ramped in, as a cheap fix we can
|
||||
* undo the windowing (for non-zero window values)
|
||||
*/
|
||||
for(size_t i = 0; i < u->overlap_size; ++i){
|
||||
u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
|
||||
}
|
||||
}
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, (uint8_t *) (dst + c) + offset, fs, u->work_buffer, sizeof(float), u->R);
|
||||
}
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
|
||||
if(u->first_iteration){
|
||||
u->first_iteration = FALSE;
|
||||
}
|
||||
u->samples_gathered -= u->R;
|
||||
}
|
||||
pa_memblock_release(tchunk->memblock);
|
||||
u->samples_gathered -= u->R;
|
||||
}
|
||||
|
||||
static void initialize_buffer(struct userdata *u, pa_memchunk *in){
|
||||
size_t fs = pa_frame_size(&u->sink->sample_spec);
|
||||
size_t samples = in->length / fs;
|
||||
float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
|
||||
pa_assert_se(u->samples_gathered + samples <= u->window_size);
|
||||
for(size_t c = 0; c < u->channels; c++) {
|
||||
//buffer with an offset after the overlap from previous
|
||||
//iterations
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples);
|
||||
}
|
||||
u->samples_gathered += samples;
|
||||
pa_memblock_release(in->memblock);
|
||||
}
|
||||
|
||||
static void input_buffer(struct userdata *u, pa_memchunk *in){
|
||||
size_t fs = pa_frame_size(&(u->sink->sample_spec));
|
||||
size_t samples = in->length/fs;
|
||||
float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
|
||||
pa_assert_se(samples <= u->window_size - u->samples_gathered);
|
||||
pa_assert(u->samples_gathered + samples <= u->input_buffer_max);
|
||||
for(size_t c = 0; c < u->channels; c++) {
|
||||
//buffer with an offset after the overlap from previous
|
||||
//iterations
|
||||
pa_assert_se(
|
||||
u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size
|
||||
u->input[c] + u->samples_gathered + samples <= u->input[c] + u->input_buffer_max
|
||||
);
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples);
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples);
|
||||
}
|
||||
u->samples_gathered += samples;
|
||||
pa_memblock_release(in->memblock);
|
||||
|
|
@ -543,7 +564,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){
|
|||
/* 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;
|
||||
size_t fs;
|
||||
size_t fs, target_samples;
|
||||
struct timeval start, end;
|
||||
pa_memchunk tchunk;
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
|
@ -551,6 +572,16 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
|
|||
pa_assert(chunk);
|
||||
pa_assert(u->sink);
|
||||
fs = pa_frame_size(&(u->sink->sample_spec));
|
||||
target_samples = PA_ROUND_UP(nbytes / fs, u->R);
|
||||
if(u->first_iteration){
|
||||
//allocate request_size
|
||||
target_samples = PA_MAX(target_samples, u->window_size);
|
||||
}else{
|
||||
//allocate request_size + overlap
|
||||
target_samples += u->overlap_size;
|
||||
alloc_input_buffers(u, target_samples);
|
||||
}
|
||||
alloc_input_buffers(u, target_samples);
|
||||
chunk->memblock = NULL;
|
||||
|
||||
/* Hmm, process any rewind request that might be queued up */
|
||||
|
|
@ -559,18 +590,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
|
|||
//pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
|
||||
pa_rtclock_get(&start);
|
||||
do{
|
||||
size_t input_remaining = u->window_size - u->samples_gathered;
|
||||
size_t input_remaining = target_samples - u->samples_gathered;
|
||||
pa_assert(input_remaining > 0);
|
||||
//collect samples
|
||||
|
||||
//buffer = &u->conv_buffer;
|
||||
//buffer->length = input_remaining*fs;
|
||||
//buffer->index = 0;
|
||||
//pa_memblock_ref(buffer->memblock);
|
||||
//pa_sink_render_into(u->sink, buffer);
|
||||
while(pa_memblockq_peek(u->input_q, &tchunk) < 0){
|
||||
pa_sink_render(u->sink, input_remaining*fs, &tchunk);
|
||||
//pa_sink_render_full(u->sink, input_remaining*fs, &tchunk);
|
||||
//pa_sink_render(u->sink, input_remaining * fs, &tchunk);
|
||||
pa_sink_render_full(u->sink, input_remaining * fs, &tchunk);
|
||||
pa_assert(tchunk.memblock);
|
||||
pa_memblockq_push(u->input_q, &tchunk);
|
||||
pa_memblock_unref(tchunk.memblock);
|
||||
|
|
@ -581,15 +605,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
|
|||
//pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
|
||||
/* copy new input */
|
||||
//pa_rtclock_get(start);
|
||||
if(u->first_iteration){
|
||||
initialize_buffer(u, &tchunk);
|
||||
}else{
|
||||
input_buffer(u, &tchunk);
|
||||
}
|
||||
input_buffer(u, &tchunk);
|
||||
//pa_rtclock_get(&end);
|
||||
//pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC);
|
||||
pa_memblock_unref(tchunk.memblock);
|
||||
}while(u->samples_gathered < u->window_size);
|
||||
}while(u->samples_gathered < target_samples);
|
||||
|
||||
pa_rtclock_get(&end);
|
||||
pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
|
||||
|
||||
|
|
@ -605,9 +626,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
|
|||
pa_assert(chunk->memblock);
|
||||
//pa_log_debug("gave %ld", chunk->length/fs);
|
||||
//pa_log_debug("end pop");
|
||||
if(u->first_iteration){
|
||||
u->first_iteration = FALSE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -632,11 +650,17 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) {
|
|||
}
|
||||
|
||||
static void reset_filter(struct userdata *u){
|
||||
size_t fs = pa_frame_size(&u->sink->sample_spec);
|
||||
size_t max_request;
|
||||
u->samples_gathered = 0;
|
||||
for(size_t i = 0;i < u->channels; ++i){
|
||||
for(size_t i = 0; i < u->channels; ++i){
|
||||
memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float));
|
||||
}
|
||||
u->first_iteration = TRUE;
|
||||
//set buffer size to max request, no overlap copy
|
||||
max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R);
|
||||
max_request = PA_MAX(max_request, u->window_size);
|
||||
pa_sink_set_max_request_within_thread(u->sink, max_request * fs);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -658,11 +682,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
|||
u->sink->thread_info.rewind_nbytes = 0;
|
||||
|
||||
if (amount > 0) {
|
||||
//pa_sample_spec *ss = &u->sink->sample_spec;
|
||||
//invalidate the output q
|
||||
pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
|
||||
//pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q));
|
||||
//pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
|
||||
pa_log("Resetting filter");
|
||||
reset_filter(u);
|
||||
}
|
||||
|
|
@ -689,11 +710,11 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
|
|||
size_t fs;
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert_se(u = i->userdata);
|
||||
|
||||
//if(u->first_iteration){
|
||||
// return;
|
||||
//}
|
||||
fs = pa_frame_size(&(u->sink->sample_spec));
|
||||
//pa_sink_set_max_request_within_thread(u->sink, nbytes);
|
||||
//pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
|
||||
pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs));
|
||||
pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(nbytes / fs, u->R) * fs);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -703,8 +724,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
|
|||
pa_sink_input_assert_ref(i);
|
||||
pa_assert_se(u = i->userdata);
|
||||
|
||||
//pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
|
||||
//pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
|
||||
pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
|
||||
}
|
||||
|
||||
|
|
@ -733,7 +752,7 @@ static void sink_input_detach_cb(pa_sink_input *i) {
|
|||
/* Called from I/O thread context */
|
||||
static void sink_input_attach_cb(pa_sink_input *i) {
|
||||
struct userdata *u;
|
||||
size_t fs;
|
||||
size_t fs, max_request;
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert_se(u = i->userdata);
|
||||
|
||||
|
|
@ -741,19 +760,15 @@ static void sink_input_attach_cb(pa_sink_input *i) {
|
|||
pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
|
||||
|
||||
pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
|
||||
fs = pa_frame_size(&(u->sink->sample_spec));
|
||||
pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs));
|
||||
|
||||
//pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
|
||||
//pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
|
||||
//TODO: setting this guy minimizes drop outs but doesn't get rid
|
||||
//of them completely, figure out why
|
||||
//pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
|
||||
//TODO: this guy causes dropouts constantly+rewinds, it's unusable
|
||||
//pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
|
||||
fs = pa_frame_size(&u->sink->sample_spec);
|
||||
//set buffer size to max request, no overlap copy
|
||||
max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R);
|
||||
max_request = PA_MAX(max_request, u->window_size);
|
||||
pa_sink_set_max_request_within_thread(u->sink, max_request * fs);
|
||||
pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
|
||||
pa_sink_attach_within_thread(u->sink);
|
||||
if(u->set_default){
|
||||
pa_log("Setting default sink to %s", u->sink->name);
|
||||
pa_log_debug("Setting default sink to %s", u->sink->name);
|
||||
pa_namereg_set_default_sink(u->module->core, u->sink);
|
||||
}
|
||||
}
|
||||
|
|
@ -1005,17 +1020,6 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
|
|||
pa_sink_set_asyncmsgq(u->sink, NULL);
|
||||
}
|
||||
|
||||
//ensure's memory allocated is a multiple of v_size
|
||||
//and aligned
|
||||
static void * alloc(size_t x,size_t s){
|
||||
size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
|
||||
float *t;
|
||||
pa_assert(f >= x*s);
|
||||
t = fftwf_malloc(f);
|
||||
memset(t, 0, f);
|
||||
return t;
|
||||
}
|
||||
|
||||
int pa__init(pa_module*m) {
|
||||
struct userdata *u;
|
||||
pa_sample_spec ss;
|
||||
|
|
@ -1068,15 +1072,15 @@ int pa__init(pa_module*m) {
|
|||
u->R = (u->window_size + 1) / 2;
|
||||
u->overlap_size = u->window_size - u->R;
|
||||
u->samples_gathered = 0;
|
||||
u->input_buffer_max = 0;
|
||||
u->a_H = pa_xnew0(pa_aupdate *, u->channels);
|
||||
u->latency = u->window_size - u->R;
|
||||
u->Xs = pa_xnew0(float *, u->channels);
|
||||
u->Hs = pa_xnew0(float **, u->channels);
|
||||
for(size_t c = 0; c < u->channels; ++c){
|
||||
u->Xs[c] = pa_xnew0(float, 2);
|
||||
u->Hs[c] = pa_xnew0(float *, 2);
|
||||
for(size_t i = 0; i < 2; ++i){
|
||||
u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float));
|
||||
u->Hs[c][i] = alloc(FILTER_SIZE, sizeof(float));
|
||||
}
|
||||
}
|
||||
u->W = alloc(u->window_size, sizeof(float));
|
||||
|
|
@ -1086,8 +1090,7 @@ int pa__init(pa_module*m) {
|
|||
u->overlap_accum = pa_xnew0(float *, u->channels);
|
||||
for(size_t c = 0; c < u->channels; ++c){
|
||||
u->a_H[c] = pa_aupdate_new();
|
||||
u->input[c] = alloc(u->window_size, sizeof(float));
|
||||
memset(u->input[c], 0, (u->window_size)*sizeof(float));
|
||||
u->input[c] = NULL;
|
||||
u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
|
||||
memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float));
|
||||
}
|
||||
|
|
@ -1151,7 +1154,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
|
||||
pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
|
||||
|
||||
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0);
|
||||
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
|
||||
pa_sink_input_new_data_done(&sink_input_data);
|
||||
|
||||
if (!u->sink_input)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue