Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange sound in the first seconds of streaming audio #12059

Closed
renebarto opened this issue Jan 22, 2025 · 6 comments
Closed

Strange sound in the first seconds of streaming audio #12059

renebarto opened this issue Jan 22, 2025 · 6 comments
Assignees
Milestone

Comments

@renebarto
Copy link

renebarto commented Jan 22, 2025

I have an issue similar to #11724 on Windows.
I'm placing two sine waves of different frequencies on each channel of a stereo pair.
When I create the stream (16 bit setero) I get callbacks for 1920 bytes per call, and fill up the buffer then add 480 sample frames of 4 bytes each with sine wave (using a continuing phase to make sure the audio is smooth).
In the first seconds I hear a strange deformation on both channels, which disappears after a few seconds, first on the left, and a few seconds later on the right channel. After that the sounds is what I'd expect.
I tried clearing the buffer first, but that does not help.
I have the feeling either SDL_PutAudioStreamData writes more or less data to the buffer than specified, or that later on something else messes up the audio.

To be clear, I do not open a device first, I just create as stream:

SDL_AudioSpec spec { SDL_AUDIO_S16LE, 2, 48000 };
m_stream = SDL_OpenAudioDeviceStream(id, &spec, callback, nullptr);

Callback:

static uint8_t audioBuffer[65536];
float phaseL = 0.0F;
float phaseR = 0.0F;
float sampleFreq = 48000.0F;
float freqL = 1000.0F;
float freqR = 2000.0F;
float twoPI = 2 * SDL_PI_D;
float stepL = freqL / sampleFreq * twoPI;
float stepR = freqR / sampleFreq * twoPI;

void audio_callback(void */*userdata*/, SDL_AudioStream *stream, int additional_amount, int total_amount)
{
    size_t offset{};
    int sampleFrames = additional_amount / 4;
    for (int i = 0; i < sampleFrames; ++i)
    {
        int16_t sampleL = static_cast<int16_t>(32768.0F * sin(phaseL) + 0.5F);
        int16_t sampleR = static_cast<int16_t>(32768.0F * sin(phaseR) + 0.5F);
        memcpy(audioBuffer + offset, &sampleL, 2);
        offset += 2;
        memcpy(audioBuffer + offset, &sampleR, 2);
        offset += 2;
        phaseL += stepL;
        phaseR += stepR;
        if (phaseL > twoPI)
            phaseL -= twoPI;
        if (phaseR > twoPI)
            phaseR -= twoPI;
    }
    // Put the audio data into the stream
    //std::cout << "Get audio, size " << additional_amount << "\r\n";
    SDL_PutAudioStreamData(stream, audioBuffer, sampleFrames * 4);
}

I play sound for about 10 secs, then pause and destroy the stream.

After about 2 seconds the left channel sounds normal, after about 4 seconds the right is also normal.

@renebarto
Copy link
Author

Sorry, forgot to mention I'm on SDL3.1.9

@slouken slouken added this to the 3.2.2 milestone Jan 22, 2025
@renebarto
Copy link
Author

For additional information, I just checked out the 3.2.0 commit and rebuilt. Same issue.

@milq
Copy link

milq commented Jan 22, 2025

@renebarto, same for me using Debian GNU/Linux.

@ManuBlack
Copy link

ManuBlack commented Jan 29, 2025

Hello, I took a quick look in this in case it was related to another issue in audio I was investigating. It turned out it is not, but I observed in your callback function:

int16_t sampleL = static_cast<int16_t>(32768.0F * sin(phaseL) + 0.5F);
int16_t sampleR = static_cast<int16_t>(32768.0F * sin(phaseR) + 0.5F);

This definitely overflows, since int16_t range is [-32,768 .. 32,767] and this is responsible, at least partially, for the initial distortion. By trying a lower value it improves the situation.

@renebarto
Copy link
Author

renebarto commented Jan 30, 2025 via email

@slouken slouken modified the milestones: 3.2.2, 3.2.4, 3.2.6 Feb 2, 2025
@icculus
Copy link
Collaborator

icculus commented Feb 28, 2025

It does, actually. Here's what the code generates (top), vs the same thing multiplying by 32700.0F instead of 32768.0F (bottom):

Image

When it overflows, you get exactly one totally wrong sample in the wave.

As for why the noise disappeared later? Here's the original code, around 2.89 seconds in:

Image

Something goes just enough out of step to stop hitting an overflow value at the top of each wave. I'm guessing it's accumulating floating point precision problems from the repeated addition of stepR, but I could be totally wrong.

Here's the complete program, which is mostly the posted code with a mainline so it'll compile. Feel free to play with SAMPLE_MULTIPLIER at the top. The pictures were generated by running the program with the SDL_AUDIO_DRIVER=disk environment variable set, and then loading the resulting sdlaudio.raw file into Audacity.

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

#define SAMPLE_MULTIPLIER 32700.0F
//#define SAMPLE_MULTIPLIER 32768.0F

static uint8_t audioBuffer[65536];
float phaseL = 0.0F;
float phaseR = 0.0F;
float sampleFreq = 48000.0F;
float freqL = 1000.0F;
float freqR = 2000.0F;
float twoPI = 2 * SDL_PI_D;
float stepL = freqL / sampleFreq * twoPI;
float stepR = freqR / sampleFreq * twoPI;

static void SDLCALL audio_callback(void */*userdata*/, SDL_AudioStream *stream, int additional_amount, int total_amount)
{
    size_t offset{};
    int sampleFrames = additional_amount / 4;
    for (int i = 0; i < sampleFrames; ++i)
    {
        int16_t sampleL = static_cast<int16_t>(SAMPLE_MULTIPLIER * SDL_sin(phaseL) + 0.5F);
        int16_t sampleR = static_cast<int16_t>(SAMPLE_MULTIPLIER * SDL_sin(phaseR) + 0.5F);
        memcpy(audioBuffer + offset, &sampleL, 2);
        offset += 2;
        memcpy(audioBuffer + offset, &sampleR, 2);
        offset += 2;
        phaseL += stepL;
        phaseR += stepR;
        if (phaseL > twoPI)
            phaseL -= twoPI;
        if (phaseR > twoPI)
            phaseR -= twoPI;
    }
    // Put the audio data into the stream
    //std::cout << "Get audio, size " << additional_amount << "\r\n";
    SDL_PutAudioStreamData(stream, audioBuffer, sampleFrames * 4);
}

int main(int argc, char **argv)
{
    SDL_Init(SDL_INIT_AUDIO);
    SDL_AudioSpec spec { SDL_AUDIO_S16LE, 2, 48000 };
    SDL_AudioStream *m_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, audio_callback, nullptr);
    SDL_ResumeAudioStreamDevice(m_stream);
    SDL_Delay(10000);
    SDL_DestroyAudioStream(m_stream);
    SDL_Quit();
    return 0;
}

At any rate, I'm confident this is not an SDL bug at this point, so I'm closing this. If there's more to examine, let me know and I'll reopen it, though!

@icculus icculus closed this as completed Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants