SDL_OpenAudioDevice:从实时处理的源缓冲区连续播放

SDL_OpenAudioDevice:从实时处理的源缓冲区连续播放,c,audio,sdl-2,C,Audio,Sdl 2,我正在写一个模拟器到SDL的移植。有一种方法,在每一帧调用,为下一帧传递带有新音频样本的缓冲区 我用SDL_OpenAudioDevice打开了一个设备,在每一帧SDL回调方法都会从音频缓冲区复制样本 它可以工作,但声音并不完美,有些抽搐,一些金属噪音等等 声音是16位有符号的 编辑:好的,我找到了一个解决方案 用开场白的代码,我在当前帧实时播放下一帧的样本。这是错误的 所以,我实现了一个循环缓冲区,在该缓冲区中,我放置了底层代码在每个(当前)帧传递给我的下一帧的样本 在该缓冲区中有两个指针,一

我正在写一个模拟器到SDL的移植。有一种方法,在每一帧调用,为下一帧传递带有新音频样本的缓冲区

我用SDL_OpenAudioDevice打开了一个设备,在每一帧SDL回调方法都会从音频缓冲区复制样本

它可以工作,但声音并不完美,有些抽搐,一些金属噪音等等

声音是16位有符号的

编辑:好的,我找到了一个解决方案

用开场白的代码,我在当前帧实时播放下一帧的样本。这是错误的

所以,我实现了一个循环缓冲区,在该缓冲区中,我放置了底层代码在每个(当前)帧传递给我的下一帧的样本

在该缓冲区中有两个指针,一个用于读取点,另一个用于写入点。SDL在其音频流上没有更多数据可播放时调用回调函数;所以,当调用回调函数时,我在循环缓冲区上播放读取点的音频样本,然后更新读取指针

当底层代码为我提供下一帧的音频样本数据时,我会在写入点将它们写入循环缓冲区,然后更新写入指针

读和写指针根据每帧要播放的样本量进行移位

代码已更新,当SamplePerFrame不是int但可以工作时需要进行一些调整;-)

循环缓冲结构:

typedef struct circularBufferStruct
{
    short *buffer;
    int cells;
    short *readPoint;
    short *writePoint;
} circularBuffer;
在初始化时调用此方法:

int initialize_audio(int stereo)
{
    if (stereo)
        channel = 2;
    else
        channel = 1;

    // Check if sound is disabled
    if (sampleRate != 0)
    {
        // Initialize SDL Audio
        if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
        {
            SDL_Log("SDL fails to initialize audio subsystem!\n%s", SDL_GetError());
            return 1;
        }

        // Number of samples per frame
        samplesPerFrame = (double)sampleRate / (double)framesPerSecond * channel;

        audioSamplesSize = samplesPerFrame * bytesPerSample; // Bytes
        audioBufferSize = audioSamplesSize * 10; // Bytes

        // Set and clear circular buffer
        audioBuffer.buffer = malloc(audioBufferSize); // Bytes, must be a multiple of audioSamplesSize
        memset(audioBuffer.buffer, 0, audioBufferSize);

        audioBuffer.cells = (audioBufferSize) / sizeof(short); // Cells, not Bytes!
        audioBuffer.readPoint = audioBuffer.buffer;
        audioBuffer.writePoint = audioBuffer.readPoint + (short)samplesPerFrame;
    }
    else
        samplesPerFrame = 0;

    // First frame
    return samplesPerFrame;
}
在每个帧调用此方法(第一次通过后,我们只需要样本量):


我希望这个问题/答案将来会对其他人有用,因为我在网上几乎找不到SDL Audio的任何内容

用开场白的代码,我在当前帧实时播放下一帧的样本。这是错误的

所以,我实现了一个循环缓冲区,在该缓冲区中,我放置了底层代码在每个(当前)帧传递给我的下一帧的样本。从那个缓冲区,我在不同的位置读写,见开篇文章

void audioCallback(void *userdata, uint8_t *stream, int len)
{
    SDL_memset(stream, 0, len);

    if (audioSamplesSize == 0)
        return;

    if (len > audioSamplesSize)
    {
        len = audioSamplesSize;
    }

    SDL_MixAudioFormat(stream, (const Uint8 *)audioBuffer.readPoint, AUDIO_S16SYS, len, SDL_MIX_MAXVOLUME);
    audioBuffer.readPoint += (short)samplesPerFrame;

    if (audioBuffer.readPoint >= audioBuffer.buffer + audioBuffer.cells)
        audioBuffer.readPoint = audioBuffer.readPoint - audioBuffer.cells;
}
int update_audio(short *buffer)
{
    // Check if sound is disabled
    if (sampleRate != 0)
    {
        memcpy(audioBuffer.writePoint, buffer, audioSamplesSize); // Bytes
        audioBuffer.writePoint += (short)samplesPerFrame; // Cells

        if (audioBuffer.writePoint >= audioBuffer.buffer + audioBuffer.cells)
            audioBuffer.writePoint = audioBuffer.writePoint - audioBuffer.cells;

        if (firstTime)
        {
            // Set required audio specs
            want.freq = sampleRate;
            want.format = AUDIO_S16SYS;
            want.channels = channel;
            want.samples = samplesPerFrame / channel; // total samples divided by channel count
            want.padding = 0;
            want.callback = audioCallback;
            want.userdata = NULL;

            device = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0, 0), 0, &want, &have, 0);
            SDL_PauseAudioDevice(device, 0);

            firstTime = 0;
        }
    }
    else
        samplesPerFrame = 0;

    // Next frame
    return samplesPerFrame;
}