Sladky Player

Многопоточный аудиовывод и аппаратный DSP

Портативный плеер на системном языке Rust. Аппаратный рендеринг GUI через WGPU API, низкоуровневый аудиовывод на базе кроссплатформенной библиотеки Cubeb, асинхронный декодер Symphonia и полностью настраиваемый 32-bit Float DSP-конвейер.

СПЕЦИФИКАЦИЯ АРХИТЕКТУРЫ
Sladky Player Interface

Сигнальный тракт и низкоуровневый DSP

Обработка аудиосигнала происходит полностью в 32-bit Float стерео-режиме. Конвейер DSP состоит из последовательных независимых модулей, выполняющих точную математическую коррекцию исходных сэмплов.

⚙️

1. Коррекция и фильтрация (Pre-processing)

Модуль SignalCorrector выполняет выравнивание задержки каналов (Time Alignment) в кольцевом буфере с точностью до сэмпла, полярную инверсию фазы и моно-сведение. Модуль DynamicsPre осуществляет срез инфрабаса (Subsonic Filter, 20Hz HPF, Linkwitz-Riley 4-го порядка на каскадных биквадратных фильтрах) и ультразвука (22kHz LPF), а также автоматическую регулировку усиления (AGC) с RMS-детектором (целевой уровень -16 dBFS).

📉

2. Эквализация и многополосная сатурация

Параметрический 12-полосный эквалайзер использует peaking-фильтры со стабильной добротностью Q=1.41 на частотах от 16 Гц до 16 кГц. Модуль Goodizer осуществляет разделение сигнала на 3 полосы (кроссовер LPF на 250 Гц и HPF на 4000 Гц) с независимой компрессией (соотношение атаки/спада, порога и коэффициента сжатия) и нелинейным насыщением (tanh-волносинтез) в зависимости от выбранного режима (A, B, C, D).

🧠

3. Психоакустический апскейлинг SoundUP RT

Многокомпонентный движок: субгармонический резонатор (LPF на 100 Гц) генерирует низкочастотные гармоники по методу ADAA (Anti-Aliasing Double Approximation) для исключения алиасинга. Декоррелятор Хааса (HPF на 800 Гц, задержка 1.2 мс) расширяет стереобазу. Модуль ATR выделяет транзиенты (полоса 6.5 кГц) с помощью быстрого и медленного детекторов огибающей и динамически усиливает их атаку. HF-эксайтер осуществляет полиномиальный синтез чётных и нечётных гармоник выше 11 кГц с подмешиванием шума.

🎧

4. Пространственный бинауральный рендеринг

Модуль Hrtf3DVirtualizer осуществляет Mid/Side декомпозицию. Mid-канал пропускается через peaking-фильтр (3 кГц, Q=1.0) для симуляции резонанса ушного канала и notch-фильтр (6.5 кГц, Q=2.0). Акустическая дистанция эмулируется ранним отражением (задержка 0.9 мс, уровень -15.5 дБ). Side-канал корректируется режекторным фильтром на 8 кГц. Кросс-ток задерживается на ITD (0.28 мс, 13 сэмплов при 48 кГц) и фильтруется high-shelf фильтром акустической тени головы (-10 дБ выше 1.5 кГц).

RUST
// Lock-Free real-time audio pipeline on Cubeb SPSC ring buffers
pub fn process_audio_callback(
output_buffer: &mut [f32],
cons_main: &mut Consumer<f32>,
cons_amb: &mut Consumer<f32>,
dsp: &mut DspProcessor,
quick_dsp: &mut QuickDspState,
settings: &SharedAudioSettings
) -> isize {
let frames_requested = output_buffer.len() / 2;
let mut amb_buffer = vec![0.0f32; output_buffer.len()];

let amb_popped = cons_amb.pop_slice(&mut amb_buffer);
let popped = cons_main.pop_slice(output_buffer);

let vol = settings.get_float(&settings.volume);
let bal = settings.get_float(&settings.balance_lr);
let pitch = settings.get_float(&settings.pitch);
let reverb_wet = settings.get_float(&settings.reverb_wet);
let reverb_room = settings.get_float(&settings.reverb_room);
let crossfeed = settings.get_float(&settings.crossfeed);

let l_gain = if bal > 0.0 { 1.0 - bal } else { 1.0 };
let r_gain = if bal < 0.0 { 1.0 + bal } else { 1.0 };

for i in (0..popped).step_by(2) {
    let mut l = output_buffer[i];
    let mut r = output_buffer[i+1];

    let (l_pt, r_pt) = quick_dsp.pitch_shifter.process(l, r, pitch);
    let (l_rev, r_rev) = quick_dsp.reverb.process(l_pt, r_pt, reverb_wet, reverb_room);
    let (l_cf, r_cf) = quick_dsp.crossfeed.process(l_rev, r_rev, crossfeed);
    let (proc_l, proc_r) = dsp.process_stereo(l_cf, r_cf);

    l = proc_l * l_gain; 
    r = proc_r * r_gain;
    if i < amb_popped {
        l += amb_buffer[i];
        r += amb_buffer[i+1];
    }
    output_buffer[i] = l * vol;
    output_buffer[i+1] = r * vol;
}
frames_requested as isize
}
// 3-Band Linkwitz-Riley Crossover with Envelope Tracked Compression
impl Goodizer {
pub fn process_frame(&mut self, mut l: f32, mut r: f32) -> (f32, f32) {
    if self.amount <= 0.0 { return (l, r); }
    let (dry_l, dry_r) = (l, r);

    // 1. Поканальный кроссовер (LPF на 250 Гц, HPF на 4000 Гц)
    let low_l = self.lpf_l.process(l);
    let low_r = self.lpf_r.process(r);

    let high_l = self.hpf_l.process(l);
    let high_r = self.hpf_r.process(r);

    let mid_l = l - low_l - high_l;
    let mid_r = r - low_r - high_r;

    // 2. Поканальная компрессия выделенных частотных диапазонов
    let comp_low_l = self.c_low_l.process(low_l);
    let comp_low_r = self.c_low_r.process(low_r);

    let comp_mid_l = self.c_mid_l.process(mid_l);
    let comp_mid_r = self.c_mid_r.process(mid_r);

    let comp_high_l = self.c_high_l.process(high_l);
    let comp_high_r = self.c_high_r.process(high_r);

    // 3. Рекомпозиция и смешивание обработанного сигнала с сухим
    let mix_l = comp_low_l + comp_mid_l + comp_high_l;
    let mix_r = comp_low_r + comp_mid_r + comp_high_r;

    (
        dry_l * (1.0 - self.amount) + mix_l * self.amount,
        dry_r * (1.0 - self.amount) + mix_r * self.amount
    )
}
}
// ADAA Non-Linear Waveshaping (Anti-Aliasing Double Approximation)
fn ln_cosh(x: f32) -> f32 {
let ax = x.abs();
if ax > 4.0 {
    ax - 0.69314718
} else {
    let x2 = x * x;
    x2 * (6.0 + x2) / (3.0 * x2)
}
}

fn adaa_tanh(x: f32, x_prev: f32) -> f32 {
let diff = x - x_prev;
if diff.abs() < 1e-5 {
    x.tanh()
} else {
    // Вычисление первой производной первообразной ln(cosh(x))
    (ln_cosh(x) - ln_cosh(x_prev)) / diff
}
}
AUDIO ENGINE STREAM MONITOR
Interface/API:
WASAPI Exclusive (Bit-Perfect)
Output Format:
32-bit Float @ 384000 Hz
Hardware Buffer:
50 samples (~1 ms latency)
Resampling DSP:
Sinc Interpolation (64000 Taps)
Quantize Dither:
NS9 Noise Shaping
PCM-to-DSD SDM:
ASDM7EC (DSD512)
Clipping Guard:
Soft Saturation ON (Thresh: -1.0 dB)
GUI Backend:
WGPU Vulkan 144Hz

Многопоточная SPSC-архитектура без блокировок

Поток аудиовывода (Real-time Thread) полностью изолирован от потока GUI. Декодирование, ресемплинг, динамический кроссфейд по настраиваемым кривым Безье и Ambient-синтез выполняются в параллельных потоках, обмениваясь аудиоданными через неблокирующие кольцевые буферы (SPSC Ring Buffer) на куче. Это гарантирует стабильную доставку аудиопакетов на системную звуковую карту без риска опустошения буфера (buffer underrun) и заиканий звука.

Crystallization of Sound

CRYSTALLIZATION

Апскейл звука

Реконструкция звука на основе алгоритмов SoundUP.
Оптимизировано для режима 'В реальном времени'.

Демонстрация

Оригинал SoundUP
0:00 / 0:00
Интенсивность апскейла
MIN
MAX

Портативность и производительность

Плеер компилируется в единый статический исполняемый файл без внешних зависимостей. Настройки и конфигурация DSP-конвейера сохраняются локально в settings.json. Аппаратное ускорение графики через WGPU API обеспечивает мгновенную отрисовку интерфейса с нулевой нагружкой на центральный процессор.

Технический FAQ по архитектуре Sladky Player

Вывод звука и задержки

В чем разница между режимами WASAPI Shared и Exclusive в плеере?
В режиме Shared плеер передает декодированный аудиопоток системному микшеру Windows, который принудительно ресемплирует его под настройки ОС и смешивает со сторонними звуками. В режиме Exclusive аудиодвижок захватывает физическое устройство напрямую. Происходит блокировка системного аудиостека, поток идет в обход микшера Windows напрямую на ЦАП без стороннего вмешательства (Bit-Perfect), исключая фазовые искажения и принудительный апсемплинг.
Почему Exclusive режим выдает ошибку на некоторых частотах дискретизации?
В эксклюзивном режиме аппаратный драйвер аудиокарты принимает только те параметры (Sample Rate и Bit Depth), которые физически поддерживает микросхема вашего ЦАП. Если в настройках плеера выбрана частота 384 кГц, а устройство поддерживает максимум 192 кГц, инициализация потока завершится аппаратным сбоем. В этом случае плеер безопасно перенаправит поток в режим Shared.
Как устроена многопоточная SPSC-схема и почему в ней нет щелчков?
Сигнальный тракт разделен на три изолированных потока: поток декодирования/ресемплирования, поток фоновых звуков (Ambient) и низкоуровневый поток вывода. Они связаны через кольцевые буферы без блокировок, выделенные в куче. Аудиоколлбэк забирает готовые сэмплы из буфера за микросекунды, не дожидаясь операций чтения с диска или тяжелой математики декодирования, что предотвращает пропуски буфера (underrun) и сопутствующие щелчки.
Зачем нужен встроенный дельта-сигма модулятор (PCM-to-DSD)?
Модулятор преобразует PCM-поток в 1-битный формат DSD (вплоть до DSD512) в реальном времени. Математический алгоритм вытесняет шум квантования далеко вверх за границу слышимости (Noise Shaping), сохраняя полезный сигнал в идеальной линейной фазе. Схемы с пост-фильтрацией 'EC' (Error Correction) также подмешивают слабый аналоговый дизер для сглаживания нелинейностей ЦАП, нагружая процессор сильнее стандартных алгоритмов.

DSP-тракт и психоакустика

Что на самом деле делает алгоритм SoundUP RT при апскейле?
В отличие от простых эквалайзеров, SoundUP RT детерминированно синтезирует новые высокие частоты. Модуль анализирует тональную основу СЧ/НЧ-диапазона и проецирует её обертоны в ВЧ-область с помощью нелинейного полиномиального синтеза. Модуль Clarity (ATR) вычисляет динамическую огибающую транзиентов для подчеркивания атак, декоррелятор Хааса вносит фазовую задержку 1.2 мс в ВЧ-спектр одного из каналов для расширения стереобазы, а эксайтер подмешивает модулированный розовый шум для создания ощущения воздушности.
Что такое ADAA в резонаторе суббаса и зачем оно нужно?
При создании субгармоник методом нелинейного насыщения (сатурации tanh) в цифровой среде неизбежно возникает алиасинг — зеркальное отражение паразитных частот от частоты Найквиста обратно в слышимый спектр, засоряющее бас. Алгоритм ADAA (Anti-Aliasing Double Approximation) решает эту проблему за счет численного интегрирования нелинейной функции. Вычисляя значение первой производной первообразной сигнала, ADAA практически полностью подавляет спектральные наложения, сохраняя глубокий и чистый суббас.
Как работает бинауральный 3D-звук (HRTF)?
Активация режима HRTF разделяет стереосигнал на Mid- (центр) и Side- (стерео-ширина) компоненты. Центральный канал фильтруется каскадом для имитации анатомического резонанса ушной раковины (подъем на 3 кГц и спад на 6.5 кГц) и дополняется задержкой ранних отражений (0.9 мс, -15.5 дБ) для удаления эффекта звучания внутри головы. Боковые каналы задерживаются на время межушного интервала ITD (0.28 мс) и обрабатываются фильтром акустического затенения головы (ослабление ВЧ-спектра на 10 дБ выше 1.5 кГц для противоположного уха), создавая полноценное бинауральное пространство.
Каким образом пиковый лимитер защищает от клиппинга?
Пиковый лимитер использует упреждающий буфер задержки на 2 мс. Плеер задерживает основной звуковой поток на это время, успевая проанализировать амплитуду пиков до того, как они попадут на выход. При обнаружении превышения порога включается быстрое подавление уровня (атака 0.5 мс) без резкого среза полуволн. Если перегрузка слишком велика, включается функция мягкого насыщения (Soft Saturation), плавно скругляющая пики по кривой гиперболического тангенса (tanh), что предотвращает появление жестких цифровых искажений.

Бесшовность и база данных

Как реализовано бесшовное воспроизведение (Gapless) и плавные переходы (Crossfade)?
Декодер работает на опережение. При приближении текущего трека к завершению запускается фоновое декодирование следующего файла. Если включен Crossfade, плеер плавно снижает громкость затухающего аудиопотока и повышает громкость нового по таблицам кубических кривых Безье, заданных в настройках. Это гарантирует отсутствие микропауз, щелчков и задержек при смене файлов.
В чем разница между алгоритмами WSOLA и Phase Vocoder при изменении тональности?
Алгоритм WSOLA работает во временной области: он нарезает сигнал на перекрывающиеся сегменты, находит точки максимальной кросс-корреляции (подобия волн) и накладывает их друг на друга со сдвигом. Этот метод идеален для вокала и инструментов, так как сохраняет фазовую структуру атак. Алгоритм Phase Vocoder переводит сигнал в частотную область через преобразование Фурье, корректирует фазы бинов для компенсации временного сдвига и выполняет обратное преобразование. Он лучше подходит для сложных полифонических миксов, но может вызывать эффект фазового размытия (smearing).
Как устроена схема базы данных плейлистов и зачем нужны рекурсивные запросы?
База данных (music.db) содержит реляционные таблицы связей. Плейлисты поддерживают иерархическую структуру через поле родительского плейлиста. При выборе родительского плейлиста плеер выполняет рекурсивный запрос, который автоматически обходит дерево вложенности вниз, собирает идентификаторы всех дочерних плейлистов и возвращает полный плоский список треков с сохранением их пользовательского порядка воспроизведения.

Графика и визуализация

Почему плеер написан на WGPU, а не на Electron, и каков реальный расход ОЗУ?
Electron запускает полноценный экземпляр Chromium, потребляя от 150-300 МБ оперативной памяти только под нужды браузерного движка. Наш графический движок на базе WGPU общается с видеокартой напрямую через низкоуровневые API. Исполняемый файл плеера автономен и потребляет около 6-8 МБ ОЗУ на уровне прикладного кода. Тем не менее, общий расход оперативной памяти процесса в системе будет выше (в районе 40-70 МБ) из-за особенностей работы современных графических API (DirectX 12 или Vulkan): видеодрайвер принудительно резервирует значительные блоки системной памяти под нужды Swapchain, компиляцию конвейеров шейдеров и кэширование текстур.
Как работает визуализатор спектра и нагружает ли он систему?
Моно-сумма сэмплов из аудиопотока копируется в кольцевой буфер реального времени. Выделенный фоновый поток забирает блоки (размером от 512 до 8192 сэмплов в зависимости от настроек FFT в интерфейсе), накладывает на них окно Ханна и вычисляет спектрограмму методом быстрого преобразования Фурье. Полученные амплитуды частот асинхронно упаковываются в текстуру. Отрисовка спектрограммы и спад полос происходят на GPU с помощью кастомных WGSL-шейдеров, что сводит нагрузку на центральный процессор к минимуму.
Что означает тонкая синяя линия на спектрограмме?
Это визуальный индикатор реальной частоты среза (Cutoff Frequency) исходного аудиофайла. Плеер сканирует спектр кадра, находит диапазон, выше которого уровень энергии падает ниже -96 дБ, и отображает эту границу. Это позволяет мгновенно отличать оригинальные файлы высокого качества от искусственно растянутых низкокачественных апскейлов (например, MP3 128 kbps, сохраненный в FLAC 96 kHz, где срез наглядно застынет на отметке 16 кГц).