Varispeed with Libsndfile, Libsamplerate and Portaudio in C

467 views Asked by At

I'm working on an audio visualizer in C with OpenGL, Libsamplerate, portaudio, and libsndfile. I'm having difficulty using src_process correctly within my whole paradigm. My goal is to use src_process to achieve Vinyl Like varispeed in real time within the visualizer. Right now my implementation changes the pitch of the audio without changing the speed. It does so with lots of distortion due to what sounds like missing frames as when I lower the speed with the src_ratio it almost sounds granular like chopped up samples. Any help would be appreciated, I keep experimenting with my buffering chunks however 9 times out of 10 I get a libsamplerate error saying my input and output arrays are overlapping. I've also been looking at the speed change example that came with libsamplerate and I can't find where I went wrong. Any help would be appreciated.

Here's the code I believe is relevant. Thanks and let me know if I can be more specific, this semester was my first experience in C and programming.

#define FRAMES_PER_BUFFER       1024
#define ITEMS_PER_BUFFER        (FRAMES_PER_BUFFER * 2)
float src_inBuffer[ITEMS_PER_BUFFER];
float src_outBuffer[ITEMS_PER_BUFFER];

void initialize_SRC_DATA()
{
    data.src_ratio = 1;                             //Sets Default Playback Speed
    /*---------------*/
    data.src_data.data_in = data.src_inBuffer;      //Point to SRC inBuffer
    data.src_data.data_out = data.src_outBuffer;    //Point to SRC OutBuffer
    data.src_data.input_frames = 0;                 //Start with Zero to Force Load
    data.src_data.output_frames = ITEMS_PER_BUFFER
                         / data.sfinfo1.channels;   //Number of Frames to Write Out
    data.src_data.src_ratio = data.src_ratio;       //Sets Default Playback Speed
    }

/* Open audio stream */
err = Pa_OpenStream( &g_stream,
        NULL,
        &outputParameters,
        data.sfinfo1.samplerate, 
        FRAMES_PER_BUFFER, 
        paNoFlag, 
        paCallback, 
        &data );

/* Read FramesPerBuffer Amount of Data from inFile into buffer[] */
numberOfFrames = sf_readf_float(data->inFile, data->src_inBuffer, framesPerBuffer);

/* Looping of inFile if EOF is Reached */
if (numberOfFrames < framesPerBuffer) 
{
    sf_seek(data->inFile, 0, SEEK_SET);
    numberOfFrames = sf_readf_float(data->inFile, 
                                    data->src_inBuffer+(numberOfFrames*data->sfinfo1.channels), 
                                    framesPerBuffer-numberOfFrames);  
}

/* Inform SRC Data How Many Input Frames To Process */
data->src_data.end_of_input = 0;
data->src_data.input_frames = numberOfFrames;

/* Perform SRC Modulation, Processed Samples are in src_outBuffer[] */
if ((data->src_error = src_process (data->src_state, &data->src_data))) {   
    printf ("\nError : %s\n\n", src_strerror (data->src_error)) ;
    exit (1);
}

* Write Processed SRC Data to Audio Out and Visual Out */
for (i = 0; i < framesPerBuffer * data->sfinfo1.channels; i++)
{
    // gl_audioBuffer[i] = data->src_outBuffer[i] * data->amplitude;   
    out[i] = data->src_outBuffer[i] * data->amplitude;
}
1

There are 1 answers

0
syyc8A3QierDK4G On

I figured out a solution that works well enough for me and am just going to explain it best I can for anyone else with a similar issue. So to get the Varispeed to work, the way the API works is you give it a certain number of frames, and it spits out a certain number of frames. So for a SRC ratio of 0.5, if you process 512 frames per loop you are feeding in 512/0.5 frames = 1024 frames. That way when the API runs its src_process function, it compresses those 1024 frames into 512, speeding up the samples. So I dont fully understand why it solved my issue, but the problem was if the ratio is say 0.7, you end up with a float number which doesn't work with the arrays indexed int values. Therefore there's missing samples unless the src ratio is eqaully divisble by the framesperbuffer potentially at the end of each block. So what I did was add +2 frames to be read if the framesperbuffer%src.ratio != 0 and it seemed to fix 99% of the glitches.

/* This if Statement Ensures Smooth VariSpeed Output */
if (fmod((double)framesPerBuffer, data->src_data.src_ratio) == 0)
{
    numInFrames = framesPerBuffer;
}
else
    numInFrames = (framesPerBuffer/data->src_data.src_ratio) + 2;

/* Read FramesPerBuffer Amount of Data from inFile into buffer[] */
numberOfFrames = sf_readf_float(data->inFile, data->src_inBuffer, numInFrames);