libsndfile / libsamplerate

An audio Sample Rate Conversion library
http://libsndfile.github.io/libsamplerate/
BSD 2-Clause "Simplified" License
600 stars 167 forks source link

src_process - zipper noise #185

Closed wictorsson closed 1 year ago

wictorsson commented 1 year ago

Hi, I'm trying to do samplerate conversion in the callback function. The conversion works but I am getting zipper noise. I guess something is wrong with the input/output buffers but just can't figure it out. Since I am in the callback function I assume src_process is the correct way to use it. Playing files without libsamplerate or setting the ratio to 1 plays the audio without any problems. I am using RTaudio, libsndfile and libsamplerate


                  double streamTime, RtAudioStreamStatus status, void *userData)
{
    SRC_STATE *state = src_new(SRC_SINC_MEDIUM_QUALITY, sfinfo.channels, NULL);
       if (state == NULL) {
           // Handle error
           qDebug() << "state error";
       }
    // Get the input and output buffers from the user data
    sf_count_t num_frames = sfinfo.frames;
      // Allocate a buffer to hold the audio frames
    float *inbuffer = (float*)malloc(nBufferFrames * 2.0 * sfinfo.channels * sizeof(float));
    float *outbuffer = (float*)malloc(nBufferFrames * 2.0 * sfinfo.channels * sizeof(float));
    float *output_buffer = (float*)outputBuffer;
    sf_read_float(file, inbuffer, nBufferFrames * sfinfo.channels);
      // Initialize the SRC_DATA structure
    SRC_DATA src_data;
    src_data.data_in = inbuffer;
    src_data.data_out = outbuffer;
    src_data.src_ratio = 48000.0 / 44100.0;
   // src_data.src_ratio = 1;
    src_data.input_frames = nBufferFrames * sfinfo.channels;
    src_data.output_frames = nBufferFrames  * sfinfo.channels ;
    src_data.end_of_input = 1;
     //  Perform sample rate conversion
    int error = src_process(state, &src_data);
    if (error != 0) {
          qDebug() << "ERROR";
          // Handle error
    }
//  memcpy(output_buffer, src_data.data_out, src_data.output_frames_gen * sizeof(float));
    for (size_t i = 0; i < src_data.output_frames_gen; i++) {
                output_buffer[i] = src_data.data_out[i];
            }
    free(inbuffer);
    free(outbuffer);
    src_delete(state);
    playPosition = dac.getStreamTime();
    return 0;
}```
erikd commented 1 year ago

You seem to to be calling the "callback" function with individual chunks of some single stream of audio. Within the callback you are calling src_new which creates a new SRC_STATE object and then you are deleting it before the callback returns.

The problem is that you are failing to persist the SRC_STATE object between calls to the callback instead of creating a new one for each call.

Psuedo code:

   src_state = src_new (.....);
   for loop :
      callback (src_state, ....);
   src_delete (src_state);
wictorsson commented 1 year ago

You seem to to be calling the "callback" function with individual chunks of some single stream of audio. Within the callback you are calling src_new which creates a new SRC_STATE object and then you are deleting it before the callback returns.

The problem is that you are failing to persist the SRC_STATE object between calls to the callback instead of creating a new one for each call.

Psuedo code:

   src_state = src_new (.....);
   for loop :
      callback (src_state, ....);
   src_delete (src_state);

Ok, thanks for checking but I am still getting zipper noise (discontinuity) in the output. I changed the code, sending a pointer from main function into the callback function instead, but now I am getting the same result even when the ratio is set to 1.


int callback(void *outputBuffer, void *input_buffer, unsigned int nBufferFrames,
                  double streamTime, RtAudioStreamStatus status, void *userData)
{

    // Get the input and output buffers from the user data
    float *output_buffer = (float *)outputBuffer;

    // Get the sample rate converter state from the user data
    SRC_STATE *state = (SRC_STATE *)userData;

    // Set up the sample rate conversion data
    SRC_DATA src_data;
    src_data.src_ratio = 48000.0 / 44100.0;
    src_data.end_of_input = 0;

    // Allocate a buffer to hold the audio frames
    float *inbuffer = (float *)malloc(nBufferFrames * 4.0 * sfinfo.channels * sizeof(float));
    float *outbuffer = (float *)malloc(nBufferFrames * 4.0 * sfinfo.channels * sizeof(float));

    // Loop until we reach the end of the output buffer
    long output_frames_remaining = nBufferFrames * sfinfo.channels;
    while (output_frames_remaining > 0)
    {
      // Read a chunk of audio from the input file
      sf_count_t input_frames_read = sf_read_float(file, inbuffer, nBufferFrames * sfinfo.channels);

      // Set up the sample rate conversion data
      src_data.data_in = inbuffer;
      src_data.data_out = outbuffer;
      src_data.input_frames = input_frames_read;
      src_data.output_frames = input_frames_read * src_data.src_ratio;

      // Check if we have reached the end of the input file
      if (input_frames_read < nBufferFrames * sfinfo.channels)
      {
          src_data.end_of_input = 1;
      }

      // Perform sample rate conversion
      int error = src_process(state, &src_data);
      if (error != 0)
      {
        // Handle error
      }

      // Copy the generated frames to the output buffer
      size_t copy_frames = std::min(src_data.output_frames_gen, output_frames_remaining);
      for (size_t i = 0; i < copy_frames; i++)
      {
        output_buffer[i] = static_cast<float>(src_data.data_out[i]);
      }
      output_buffer += copy_frames;
      output_frames_remaining -= copy_frames;
    }

    // Update the play position
    playPosition = dac.getStreamTime();

    // Clean up
    free(inbuffer);
    free(outbuffer);

    return 0;
}```
wictorsson commented 1 year ago

You seem to to be calling the "callback" function with individual chunks of some single stream of audio. Within the callback you are calling src_new which creates a new SRC_STATE object and then you are deleting it before the callback returns.

The problem is that you are failing to persist the SRC_STATE object between calls to the callback instead of creating a new one for each call.

Psuedo code:

   src_state = src_new (.....);
   for loop :
      callback (src_state, ....);
   src_delete (src_state);

Solved! Followed these instructions and I also needed to calculate the amount of input frames to get the expected output frames to be used within the callback in RTaudio.