MacGamingFixes
macgamingfixes / фикс аудиобуфера

фикс аудиобуфера

часть macgamingfixes. та же статья есть в приложении, рядом с переключателем.

игры под crossover трещат, если устройство вывода не работает на 88.2 или 96 кгц, а bluetooth и большинство usb-устройств так не умеют. дело никогда не было в частоте. дело в длине буфера, и её можно исправить на любой частоте.

симптом

треск в звуке игры и рваный микрофон в голосовом чате. обычный совет — выставить 96 кгц в «настройке audio-midi». bluetooth-гарнитура предлагает только 44.1 кгц, usb-микрофон — 44.1 или 48.

причина

wine будит игру каждые 10 мс, и движок держит в очереди один-два таких периода. coreaudio забирает 512 кадров за цикл ввода-вывода: 11.6 мс при 44.1 кгц, 10.7 мс при 48 кгц. это длиннее одного периода, очередь пустеет, и wine дополняет пробел тишиной. при 96 кгц те же 512 кадров длятся 5.3 мс, и очередь не пустеет.

у записи зеркальная проблема: wine отдаёт один пакет в 10 мс за цикл и выбрасывает излишек.

замеры

проба играет и записывает так же, как sdl, по 15 с на прогон. для воспроизведения считается вставленная тишина, для записи — потерянный звук.

устройствобез фиксас фиксом
bluetooth-гарнитура, 44.1 кгц6.0–6.3 %0.1 %
usb-выход, 48 кгц1.8–2.2 %0.1–0.2 %
встроенные динамики, 48 кгц1.8–2.2 %0.0–0.1 %
встроенные динамики, 96 кгц0.1 %—
usb-микрофон, 48 кгц6.9 %0.2 %
usb-микрофон, 44.1 кгц6.4 %0.1 %
вставлено тишины, bluetooth-гарнитурадо6.3 %после0.1 %

вторая половина обычного совета — поставить микрофон на 44.1 кгц — не помогла: на 44.1 кгц терялось столько же, сколько на 48.

фикс

размер буфера ввода-вывода задаётся отдельно для каждого процесса, поэтому его нужно выставлять изнутри игры. обёртка заменяет winecoreaudio.so из wine, реэкспортирует настоящий драйвер и при загрузке просит у coreaudio буфер 5.33 мс на каждом устройстве: 256 кадров при 48 кгц, 235 при 44.1.

coreaudio_iobuf.c
Float64 hz;                          // текущая частота устройства
UInt32 frames = hz * io_ms / 1000;   // 5.33 мс на любой частоте
AudioObjectSetPropertyData(device, &buffer_frame_size,
                           0, NULL, sizeof frames, &frames);

что меняется на диске

ограничения