0

With SDL2 I am trying to play a frequency that will be changed on a keydown event. There is a clicking sound when the frequency is changed, that i would like to get rid of.

The wave with the new frequency is phase shifted and the values of sin(2*pi*time*freq) and sin(2*pi*time*new_freq + phase_shift) is equal, but it has no impact on the clicking sound.

I have also tried to do a fade-out fade-in on two frequencies without any luck.

Here is the code with phase-shift.

#include <stdio.h>
#include <math.h>
#include <SDL2/SDL.h>

#define PI 3.14159265

typedef void (*periodic_function)(double time, Sint16 *sample, int max);
typedef struct callback_struct {
    int *sample_nr;
    float *frequency;
    float *new_frequency;
    periodic_function periodic_function;
} callback_struct;

void sinewave(double time, Sint16 *sample, int max) {
    double val = sin(time)*max;
    *sample = val;
}

void audio_callback(void *userdata, Uint8 *stream_, int len) {
    Sint16 *stream = (Sint16*)stream_;
    callback_struct *user_data = userdata;

    float sample_len = len / sizeof(Sint16);
    int *sample_nr = (*user_data).sample_nr;

    float *frequency = (*user_data).frequency;
    float *new_frequency = (*user_data).new_frequency;

    float deg;
    Sint16 sample;

    for (int i = 0; i < sample_len; i++, (*sample_nr)++) {
        float time = (*sample_nr) / 48000.0;

        if (*new_frequency != *frequency) {
            // Frequency has changed, add phase.
            double phase_shift = 2 * PI * time * (*frequency - *new_frequency);
            deg = 2 * PI * time * *new_frequency + phase_shift;
            *frequency = *new_frequency;
        } else {
            // Frequency has not changed.
            deg = 2 * PI * time * *frequency;
        }

        (*user_data).periodic_function(deg, &sample, 28000);
        *stream++ = sample;
    }
}

int main(int argc, char* argv[])
{
    SDL_Window *window;
    SDL_Renderer *renderer;

    if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) < 0)
    {
        printf("Error initializing sdl\n");
        return -1;
    }

    SDL_CreateWindowAndRenderer(680,440, 0, &window, &renderer);
    if(!window)
    {
        printf("Failed to create window\n");
        return -1;
    }

    // Set up user_data struct
    struct callback_struct user_data;
    int sample_nr = 0;
    float frequency = 240.0;
    float new_frequency = 240.0;
    user_data.sample_nr = &sample_nr;
    user_data.frequency = &frequency;
    user_data.new_frequency = &new_frequency;
    user_data.periodic_function = sinewave;

    SDL_AudioSpec want,have;
    SDL_AudioDeviceID dev;

    SDL_memset(&want, 0, sizeof(want));
    want.freq = 48000;
    want.format = AUDIO_S16SYS;
    want.channels = 1;
    want.samples = 1024;
    want.callback = audio_callback;
    want.userdata = &user_data;
    if((dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE)) < 0) {
        printf("Error open audio device\n");
        return -1;
    }

    // Play audio
    SDL_PauseAudioDevice(dev, 0);

    // Main loop
    int run_program = 1;
    while(run_program) {
        SDL_Event e;
        while(SDL_PollEvent(&e) > 0)
        {
            switch(e.type) {
                case SDL_QUIT:
                    run_program = 0;
                    break;
                case SDL_KEYDOWN:
                    char key = (char) *SDL_GetKeyName(e.key.keysym.sym);
                    if(key == 'P') {
                        new_frequency = frequency + 1;
                    } else if(key == 'O') {
                        new_frequency = frequency - 1;
                    }
                    continue;
                default:
                    break;
            }       
        }   
    }

    // Stop playing audio
    SDL_PauseAudioDevice(dev, 1);
    
    // Clean up and quit
    SDL_CloseAudioDevice(dev);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
marius627
  • 1
  • 1
  • Did you try recording the audio, and looking at the wave shape? Maybe your phase shift is wrong? – HolyBlackCat Jul 23 '23 at 17:55
  • The phase shift looks ok to me, but you only use it once when the frequency changes. In the next iteration, you don't add the shift anymore, so you end up with the same problem. Keep the phase shift in `userdata` like `frequency` and always use it. – Nelfeal Jul 23 '23 at 23:09
  • @marius627, `deg = 2 * PI * ...` is confusing as it looks like an object named `deg` (for _degrees_) is assigned a _radian_ amount with the `2 * PI * ...`. – chux - Reinstate Monica Jul 25 '23 at 18:50
  • @marius627, Why is code using `sample_len = len / sizeof(Sint16)`? I suspect `sample_len = len / sizeof sample_nr[0];` makes more sense. – chux - Reinstate Monica Jul 25 '23 at 19:08
  • Thanks, @Nelfeal, that fixed the problem! – marius627 Jul 26 '23 at 07:49

1 Answers1

0

Correct goal implementation?

Goal is "values of sin(2*pi*time*freq) and sin(2*pi*time*new_freq + phase_shift) is equal", but I am not sure code does that. double phase_shift = 2 * PI * time * (*frequency - *new_frequency); deg = 2 * PI * time * *new_frequency + phase_shift; is like deg = 2 * PI * time * *frequency;, so independent of *new_frequency. Certainly amiss.

Following may or may not have an audible effect

Code does a lot of converting float to double and also double to float. Code may intend to do that, but that is not clear. Enable more warning to see when narrowing occurs.

For perhaps smoother calculations, change all to double.

For likely more efficient code, change all to float, use float constants and functions.


48000.0 and PI (3.14159265) are double. sin() returns double.

48000.0f and 3.14159265f are float. sinf() returns float.


Perhaps simplify calculations?

        // double phase_shift = 2 * PI * time * (*frequency - *new_frequency);
        // deg = 2 * PI * time * *new_frequency + phase_shift;

        deg = 2 * PI * time * *frequency;

and

    //float time = (*sample_nr) / 48000.0;
    float time2pi = (*sample_nr) * ((2 * PI) / 48000.0);

        // 2 * PI * time * ...
        time2pi * ...

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256