libsamplerate icon indicating copy to clipboard operation
libsamplerate copied to clipboard

Calculate in advance the number of samples needed for generating a fixed size

Open scjurgen opened this issue 3 years ago • 5 comments

Is there any (easy) possibility to obtain in advance the samples needed for generating a certain amount of data? e.g.: Convert from 48000 to 44100 with SINC. To obtain 64 samples I need to feed i.e. 69 or 70 samples (a sequence like 70,69,70,70,69,70,70). I would like to know in advance how many samples I actually have to provide to obtain deterministic 64 samples without having too much buffering going on in the sinc filter it self. This would save some buffering overhead in a host application and is needed to maintain parallel control data to the sample data in realtime applications. I know that I can obtain usable data by peeking into the filter:

   SINC_FILTER *sinc =  (SINC_FILTER *) state->private_data;  
   int buffered = sinc->b_end - sinc->b_current;  
   int delay = sinc->coeff_half_len / (sinc->index_inc * (state->last_ratio < 1.0 ? state->last_ratio : 1.0)). 

scjurgen avatar Jan 13 '22 08:01 scjurgen

Hi @scjurgen . @SoapGentoo , @arthurt, do you have any ideas?

evpobr avatar Jan 13 '22 16:01 evpobr

If the internal SRC state is currently unbuffered, then perhaps this is the answer:

int required_input_samples = round(desired_output_samples / src_ratio)

This would seem to match the observed pattern you provided for the first few blocks of 64 output samples. Note that it's independent of the size of the sinc filter.

As noted in similar discussion in #6, I'm not confident in my understanding of this since the source code is a bit intimidating.

arlofaria avatar Jan 13 '22 16:01 arlofaria

Thanks to a thread here on github by @arlofaria I started looking deeper into the implementation and I did a patch today that tests ok. I needed only to expose some information from SINC_FILTER

// in src_sinc.c:
 
int sinc_get_input_delay(const SRC_STATE *state) 
{
    SINC_FILTER *sinc =  (SINC_FILTER *) state->private_data;
    return sinc->coeff_half_len / (sinc->index_inc * (state->last_ratio < 1.0 ? state->last_ratio : 1.0));
}

// calculate current remaining data in buffer
int sinc_get_buffererd(const SRC_STATE *state)
{
    SINC_FILTER *sinc =  (SINC_FILTER *) state->private_data;
    int delta = sinc->b_end - sinc->b_current;
    if (delta < 0) // not sure this ever happened, looks like handled in prepare_data
        delta += sinc->b_len;
    return delta;
}

I used it than like this (fixed ratio only):

   // init
   int maxBuffered = sinc_get_input_delay(src) + ceil(ratio) + 1; // needs to be called after feeding some data to src_process
   // ...
   // in a processing loop I can now calculate the data needed:
   int produceSamplesCount = frameSize / ratio + 1;
   int buffered = sinc_get_buffererd(src); // peek into the buffer count
   if (buffered > maxBuffered) // buffer is filling up, we provide one sample less
         produceSamplesCount -= 1;
   // produceSamplesCount is the number of samples I need to provide for SRC_DATA
   // to obtain exactly frameSize

I tested this for all 3 SINC's and for various ratios and framesize. Some still have problems with not consuming all input data due to the handling in prepare_data where it loads data at the current end position.

scjurgen avatar Jan 13 '22 16:01 scjurgen

Hi @arlofaria

I started with your approach, but soon I realised that the handling is a bit more complex. Your answer in the other thread was helpful to dig into it.

If the internal SRC state is currently unbuffered, then perhaps this is the answer:

int required_input_samples = round(desired_output_samples / src_ratio)

scjurgen avatar Jan 13 '22 16:01 scjurgen

Nice! Great to see that someone is willing to dig deeper into this :-)

arlofaria avatar Jan 13 '22 16:01 arlofaria