module-equalizer-sink: resyncing with head and fix invalid writes
authorJason Newton <nevion@gmail.com>
Wed, 26 Aug 2009 08:15:49 +0000 (01:15 -0700)
committerJason Newton <nevion@gmail.com>
Fri, 2 Oct 2009 09:14:20 +0000 (02:14 -0700)
    * 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

src/modules/module-equalizer-sink.c

index f634d5e..f556be7 100755 (executable)
@@ -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)