Пишу портирование эмулятора на SDL. Существует метод, вызываемый в каждом кадре, который передает буфер с новыми аудиосэмплами для следующего кадра.
Я открыл устройство с помощью SDL_OpenAudioDevice и в каждом кадре метод обратного вызова SDL воспроизводит сэмплы из аудиобуфера.
Работает, но звук не идеален, некоторые ti c, некоторые металлические c шумы и т. Д.
Звук имеет 16-битную подпись.
РЕДАКТИРОВАТЬ: Хорошо, я нашел решение!
С кодом открытия поста я воспроизводил сэмплы для следующего кадра в текущем кадре в режиме реального времени. Это было неправильно!
Итак, я реализовал кольцевой буфер, в который я помещаю выборки для следующего кадра, который базовый код передает мне в каждом (текущем) кадре.
В этом буфере есть 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;
}
Это обратный вызов метода SDL из want.callback :
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;
}
Я ожидаю, что этот вопрос / ответ будет полезен для другие в будущем, потому что я не нашел почти ничего на net для SDL Audio