SDL_OpenAudioDevice:从实时处理的源缓冲区连续播放
SDL_OpenAudioDevice: Continuous play from real time processed source buffer
我正在编写一个模拟器到 SDL 的移植。有一种在每一帧调用的方法,它传递一个缓冲区,其中包含下一帧的新音频样本。
我用 SDL_OpenAudioDevice 打开了一个设备,在每一帧,SDL 回调方法从音频缓冲区中再现样本。
能用,但声音不完美,有些抽动,金属噪音等等。
声音是 16 位符号。
编辑:好的,我找到了解决方案!
使用开头的代码post我在当前帧实时播放下一帧的样本。错了!
因此,我实现了一个循环缓冲区,我在其中放置了底层代码在每个(当前)帧传递给我的下一帧的样本。
在该缓冲区中有2个指针,一个用于读取指针,另一个用于写入指针。 SDL 在其音频流上没有更多数据可播放时调用回调函数;所以当调用回调函数时,我从循环缓冲区上的读取点播放音频样本,然后更新读取指针。
当底层代码为我提供下一帧的音频样本数据时,我在写入点将它们写入循环缓冲区,然后更新写入指针。
读写指针移动每帧要播放的样本量。
代码已更新,当 samplesPerFrame 不是 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;
}
这是来自 want.callback 的 SDL 方法回调:
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;
}
我希望这个 question/answer 将来对其他人有用,因为我在网上几乎找不到任何关于 SDL Audio 的东西
好的,我找到了解决办法!
使用开头的代码post我在当前帧实时播放下一帧的样本。错了!
所以,我实现了一个循环缓冲区,我在其中放置了底层代码在每个(当前)帧传递给我的下一帧的样本。从那个缓冲区我在不同的位置读取和写入,看到打开 post
我正在编写一个模拟器到 SDL 的移植。有一种在每一帧调用的方法,它传递一个缓冲区,其中包含下一帧的新音频样本。
我用 SDL_OpenAudioDevice 打开了一个设备,在每一帧,SDL 回调方法从音频缓冲区中再现样本。
能用,但声音不完美,有些抽动,金属噪音等等。
声音是 16 位符号。
编辑:好的,我找到了解决方案!
使用开头的代码post我在当前帧实时播放下一帧的样本。错了!
因此,我实现了一个循环缓冲区,我在其中放置了底层代码在每个(当前)帧传递给我的下一帧的样本。
在该缓冲区中有2个指针,一个用于读取指针,另一个用于写入指针。 SDL 在其音频流上没有更多数据可播放时调用回调函数;所以当调用回调函数时,我从循环缓冲区上的读取点播放音频样本,然后更新读取指针。
当底层代码为我提供下一帧的音频样本数据时,我在写入点将它们写入循环缓冲区,然后更新写入指针。
读写指针移动每帧要播放的样本量。
代码已更新,当 samplesPerFrame 不是 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;
}
这是来自 want.callback 的 SDL 方法回调:
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;
}
我希望这个 question/answer 将来对其他人有用,因为我在网上几乎找不到任何关于 SDL Audio 的东西
好的,我找到了解决办法!
使用开头的代码post我在当前帧实时播放下一帧的样本。错了!
所以,我实现了一个循环缓冲区,我在其中放置了底层代码在每个(当前)帧传递给我的下一帧的样本。从那个缓冲区我在不同的位置读取和写入,看到打开 post