Я провел некоторые исследования по синтезу звука и сумел написать очень простой синтезатор. Я использовал Microsoft WaveOut API. Я написал интерфейс, чтобы упростить весь процесс. Теперь я могу инициализировать (моно) аудиопоток и вызвать setSample. Таким образом, при частоте дискретизации 44100 выборок в секунду setSample необходимо вызывать как минимум 44100 раз в секунду.
Это прекрасно работает на моем (четырехъядерном) ноутбуке, но на старой двухъядерной картине моих родителей это действительно плохо заикается. Это довольно странно: FL Studio работает довольно плавно на компьютере моих родителей, даже когда я использую несколько эффектов на более сложном синтезаторе, в то время как мой код довольно прост.
Я понятия не имею, что вызывает такое поведение. Мой код далеко не оптимизирован, но настолько прост, что я не могу себе представить, что проблема заключается только в оптимизации (если я не делаю что-то, что действительно замедляет синтез).
Кусок кода, который может быть проблемой:
void AudioStream::setSample(float sample)
{
unsigned int discreteSample = ((sampleSize > 1) ? 0 : amplitude) + ((float)amplitude * sample);
for (unsigned int i = 0; i < sampleSize; i++)
{
data[pointr++] = (char)(discreteSample & 255);
discreteSample = discreteSample >> 8;
}
if (pointr >= maxSize)
{
if (waveOutWrite(hWaveOut, firstHeader ? &header1 : &header2, firstHeader ? sizeof(header1) : sizeof(header2)) != MMSYSERR_NOERROR)
{ throw("Error writing to sound card!"); return; }
pointr = 0;
firstHeader = !firstHeader;
if (WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0) { throw("An error occured while waiting for sound to finish"); return; }
unsigned char *temp;
temp = data;
data = play;
play = temp;
first = false;
}
}
Я думаю, что преобразование значения с плавающей запятой в дискретную выборку может быть проблематичным. Я использую это для обработки образцов нескольких размеров.
Я также слышал, что waveOut может быть программно эмулированным (что могло бы многое объяснить), но я не уверен, что (или когда, например, в каких версиях Windows или при каких обстоятельствах) это имеет место и какова производительность разница это будет иметь.
Я надеюсь, что кто-то может помочь мне.
WaveOut API и хорошая производительность невозможны вместе. Используйте что-нибудь более подходящее, ASIO или WASAPI, если вы используете Windows, или попробуйте Portaudio, если вы хотите быть кроссплатформенным.
Передача по одному образцу за раз в waveOutWrite будет ужасно неэффективной, как на уровне вашего кода, так и в драйвере. Он устанавливает DMA-передачу буфера, и если этот буфер имеет длину в один байт, он переносит все накладные расходы на переключение буферов для каждой выборки. Я бы отправлял по крайней мере одну десятую секунды отсчетов при каждом вызове waveOutWrite.
Если вы ищете очень простую аудио-библиотеку для создания простого синтезатора, я настоятельно рекомендую audiere http://audiere.sourceforge.net/ или же BASS http://www.un4seen.com/.
Первый довольно устарел, но в то же время один из самых простых (что не делает его плохим). Я смог написать не такой простой синтезатор. Второй, BASS, пойдет вам на пользу и в более сложных проектах. Это идет с очень легкими примерами (и синтезатор — один из них). Это очень легко настроить тоже.