🅱 bpplay: бескомпромиссная архитектура компьютерного аудио и минимализм программного обеспечения

Вниманию предлагается серия постов и экспериментальный плеер от Ferenc Koscsó (только Mac). Если вы все еще слушаете на компьютере и интересуетесь DSD, то некоторые интересные подробности только для вас! Впрочем, многое справедливо и для любого компьютерного аудио.

Если вы не хотите ничего читать, то просто забирайте и пробуйте софт Release v0.9.7 · ferenckoscso/bpplay · GitHub

bpplay: бескомпромиссная архитектура компьютерного аудио и минимализм программного обеспечения

Бит-перфектный музыкальный плеер для macOS и вопрос, который привёл к его созданию — часть 1

В эпоху громких, чрезмерно обработанных коммерческих записей мало что так резко противостоит мейнстриму, как обращение к практике звукозаписи 1950-х и 1960-х годов: зачастую лучшее, что можно сделать с записью, — вообще ничего с ней не делать. Мы стараемся сохранить исполнение в его первоначальном виде, используя всего несколько микрофонов, словно записываем голограмму. Для альбомов My Reel Club® это просто реальность: будь то LP, лента или даже цифровой релиз высокого разрешения, мы избегаем постобработки в любой форме, потому что каждое дополнительное вмешательство, каждое лишнее звено в цепи может лишь ухудшить результат.

Отсюда естественным образом возникает вопрос: можно ли перенести этот принцип в мир компьютерного цифрового воспроизведения?

bpplay — это эксперимент, призванный ответить на этот вопрос. Минималистичный бит-перфектный музыкальный плеер для macOS, написанный на C, с единственной целью: доставить сэмплы музыкального файла к ЦАП по максимально короткому и детерминированному пути, нетронутыми, в их исходном виде. О том, что это означает на практике и почему такой инструмент может иметь смысл — несмотря на то, что нечто подобное уже существует, — и пойдёт речь в этой статье.

Две вещи, которых мы никогда не знаем наверняка

Прежде чем перейти к технической стороне вопроса, стоит остановиться на парадоксе, который незримо присутствует во всём этом эксперименте.

Жизнь аудиофила протекает в условиях своеобразного противоречия. С одной стороны, огромные усилия направлены на то, чтобы сделать сигнал «чистым»: дорогие кабели, сетевые фильтры, генераторы тактовых импульсов без джиттера — всё это призвано защищать чистоту. С другой стороны, есть две вещи, которых мы никогда не знаем наверняка, — и именно они определяют, что вообще может означать чистота и точность воспроизведения.

Первое: мы не знаем, что именно слышали создатели записи, когда мастер был закончен. На каких мониторах, в какой комнате, при какой громкости было принято решение: «вот так правильно»? Запись представляет собой последовательность решений, принятых в конкретной ситуации прослушивания, и впоследствии получить доступ к этому эталону уже невозможно. Следовательно, точность воспроизведения — это точность по отношению к оригиналу, который никто не знает в точности.

Странно, не правда ли?

Второе: мы не знаем, что именно делает компьютер во время воспроизведения. Современная машина одновременно выполняет десятки процессов; в фоновом режиме операционная система индексирует данные, сканирует сеть, перемещает страницы памяти, а аудиомикшер выполняет ресэмплинг и микширование, как было описано несколькими строками выше. К тому моменту, когда сигнал достигает ЦАПа, он уже прошёл через непрозрачную, трудно предсказуемую программную машину, внутренние процессы которой практически невидимы для нас.

С первым парадоксом ничего не поделать. А вот второй можно устранить. Цепь нельзя сделать идеальной, но её можно сделать известной и предсказуемой. Если каждый шаг виден, можно точно проследить, что происходит между файлом и ЦАПом.

bpplay не обещает, что «звучит лучше». Он обещает, что вы можете знать, что происходит во время воспроизведения.

Что обычно делает приложение музыкального плеера?

Стоит уточнить, относительно чего именно bpplay является минималистичным.

Когда вы слушаете музыку через обычное приложение macOS, звук не поступает непосредственно в ЦАП; он проходит через системный аудиомикшер, являющийся частью подсистемы CoreAudio. Там с ним может происходить многое: ресэмплинг, микширование в формате с плавающей точкой, программная регулировка громкости. В большинстве ситуаций эти операции удобны и полезны — несколько приложений могут одновременно воспроизводить звук, громкость можно регулировать, и всё просто работает.

Поэтому, когда мы воспроизводим аудиофайл высокого разрешения на современной операционной системе, путь к цифро-аналоговому преобразователю обычно представляет собой непрозрачный чёрный ящик. В аудиофильских и инженерных кругах два фундаментальных вопроса почти никогда не получают детерминированного ответа:

  1. Является ли передача бит-перфектной? Достигают ли двоичные данные, хранящиеся в исходном файле (в нашем примере — сэмплы, закодированные в PCM), аппаратного интерфейса полностью и без изменений?

  2. Согласовано ли воспроизведение по времени? Вносят ли фоновые процессы операционной системы, операции с диском и планирование работы CPU микроскопические ошибки синхронизации — так называемый программный джиттер — по мере отправки сэмплов наружу?

Большинство коммерческих плееров жертвуют сквозной детерминированностью ради удобных функций. Связь между аппаратной и программной частью превращается в многоуровневый, асинхронный и непредсказуемый процесс. Чтобы понять, зачем нужен подход bpplay, полезно посмотреть, как работает «типичный» медиаплеер.

  • Непрерывная нагрузка на I/O: программа считывает аудиофайл с накопителя — пусть даже с NVMe SSD — небольшими буферами, фрагмент за фрагментом. Это порождает постоянные операции файловой системы и аппаратные прерывания (IRQ) на протяжении всего воспроизведения.

  • Декодирование в реальном времени: ядра процессора постоянно сталкиваются с небольшими скачками нагрузки, распаковывая сжатые форматы (например, FLAC, ALAC) в необработанный PCM-поток в реальном времени. Постоянное переключение контекста может влиять на системные часы и уровень шума на шинах питания.

  • Микшерные механизмы операционной системы: при настройках по умолчанию данные проходят через внутренний аудиомикшер системы (Windows Audio Engine, macOS CoreAudio или Linux PipeWire/PulseAudio). Эти программные уровни часто произвольно изменяют частоту дискретизации, применяют дитер или совместно используют аудиоресурсы со звуками системных уведомлений.

Если цель состоит в том, чтобы именно то, что было утверждено при производстве записи, достигало ЦАПа — бит в бит, сэмпл в сэмпл, — то эти же операции уже становятся помехой. Каждая из них представляет собой место, где сигнал может измениться, а обработка может породить побочный шум.

Поэтому bpplay обходит весь промежуточный слой. Он получает исключительный контроль над ЦАПом (hog mode), так что ни одно другое приложение не может получить к нему доступ, отключает микширование и передаёт сэмплы через прямой callback реального времени (IOProc), выполняя на критическом пути минимально возможное число операций. Никакой программной регулировки громкости, дитеринга или преобразования частоты дискретизации. Громкость и все дальнейшие операции передаются устройству, которое именно для этого и предназначено: ЦАПу и усилителю.

Когда преобразование всё же имеет смысл

Однако важно, чтобы это не превратилось в религиозную догму. «Полное отсутствие преобразований» само по себе не является целью; настоящая цель — чтобы ЦАП получал идеальные данные, идеальный сигнал, то есть формат, в котором он работает лучше всего. Для большинства современных ЦАПов это как раз нативный, нетронутый материал, и в таком случае подход bpplay очевиден.

Но не каждый ЦАП устроен так. Многие преобразователи всё равно выполняют внутреннее преобразование: например, delta-sigma ЦАП преобразует каждый входящий PCM-поток в собственное внутреннее представление с высокой частотой дискретизации, прежде чем превратить его в аналоговый сигнал. Если это внутреннее преобразование выполнено посредственно — вычислительные возможности чипа ЦАПа ограничены, — может оказаться, что тщательно выбранный апсемплинг на стороне компьютера или преобразование PCM→DSD даст лучший конечный результат, чем передача этой работы ЦАПу. Компьютер способен использовать на порядки больше ресурсов и более сложные алгоритмы, чем внутренняя схема ЦАПа.

На первый взгляд это противоречит принципу бит-перфектности, но на самом деле лишь переосмысливает его. Разница заключается не в наличии или отсутствии преобразования, а в том, является ли оно осознанным, контролируемым решением или скрытым, непредусмотренным вмешательством системы. Преобразование, выбранное намеренно, с учётом конкретного ЦАПа и позволяющее работать ему в оптимальном режиме, полностью соответствует философии детерминированного сигнального пути. bpplay представляет собой пуристский крайний вариант, поскольку его ценность заключается в проверяемости и эталонном характере; но более широкий принцип не состоит в отрицании преобразований. Он заключается в том, что в цепи ничего не должно происходить без нашего одобрения.

Вся музыкальная композиция загружается в память

Одно из важнейших свойств bpplay — и то, что отличает его от большинства плееров, — способ загрузки данных. Весь трек загружается в память ещё до того, как прозвучит первый звук. Нет буферизации «на лету», нет чтения с диска во время воспроизведения: весь трек (или плейлист) находится в оперативной памяти, причём закреплён там, чтобы операционная система не могла выгрузить его на диск (mlock), и оттуда поступает к ЦАПу с единственным копированием в памяти. Это можно назвать монолитной буферизацией на основе RAM.

Преимущество ощутимо. Во время воспроизведения диск молчит — нет операций I/O и мелких прерываний, вызванных активностью накопителя. В типичной тестовой конфигурации исходный SSD/HDD может находиться на той же самой цепочке Thunderbolt/USB, что и ЦАП, и это совершенно не имеет значения, поскольку во время воспроизведения нет необходимости читать данные с накопителя. К этому моменту музыка уже находится в памяти.

У всего этого есть цена, и расплачиваться приходится памятью: из-за согласования формата на этапе загрузки и, в случае DSD, упаковки DoP. Альбом высокого разрешения в DSD может занимать несколько гигабайт: файл DSD256 размером 4 ГБ после загрузки может занимать целых 7,5 ГБ. На первый взгляд это выглядит расточительством и наверняка удивляет многих. Но именно такова цена чистого, детерминированного сигнального пути — и, что особенно важно, это не ухудшает звук: сигнал остаётся нетронутым бит в бит. Почему размер увеличивается именно настолько и почему это не связано ни с какими потерями, будет одной из тем следующей, технической части этой серии.

Воспроизведение «сопровождается шумом»

Модель с полной загрузкой в RAM имеет следствие, выходящее за рамки простой предсказуемости.

Обычный плеер постоянно заставляет компьютер работать во время воспроизведения. Он читает данные с диска или SSD, декодирует сжатый формат, управляет буферами, а при нехватке памяти вынужден также выполнять подкачку. Каждая операция создаёт кратковременную импульсную нагрузку на процессор и накопитель, а через них — и на блок питания компьютера.

Здесь начинается самое интересное. Импульсные блоки питания современных компьютеров зависят от нагрузки: когда CPU внезапно начинает декодирование или SSD инициирует чтение, мгновенное потребление тока резко возрастает, а затем столь же быстро возвращается обратно.

Эти крутые переходные процессы в принципе способны генерировать высокочастотный шум — RF/EMI, — который может распространяться по шинам питания, через контур заземления или посредством излучения и становиться фактором, мешающим работе чувствительного ЦАПа, расположенного рядом с компьютером или питающегося от той же шины. Тот же эффект усиливается обработкой прерываний, пробуждением фоновых процессов и подкачкой памяти: каждая из этих операций создаёт небольшую нерегулярную нагрузку, влияющую на электрическое поведение машины.

Вызывает ли этот шум слышимую разницу в конкретной системе, в значительной степени зависит от конструкции ЦАПа. Гальванически хорошо изолированный ЦАП с собственным блоком питания как раз и создан для подавления подобных помех. Поэтому bpplay не утверждает, что «звучит лучше благодаря этому». Его утверждение скромнее и лучше защищено с технической точки зрения: модель с полной загрузкой в RAM устраняет сам источник шума, причём в самом его начале. Если трек уже находится в памяти, во время воспроизведения нет чтения с диска, декодирования или подкачки — а значит, не возникают и скачки нагрузки, которые изначально могли бы породить шум. Самый чистый способ устранить источник помех — не дать ему возникнуть вообще.

О порядках величины шума

Чтобы это явление не оставалось абстрактной спекуляцией, стоит посмотреть на реальные цифры. Возьмём современный высокоскоростной M.2 NVMe SSD — накопитель PCIe Gen 4 или Gen 5.

В энергосберегающем режиме простоя такое устройство потребляет всего около 50 милливатт, то есть примерно 0,05 Вт. Но в тот момент, когда плеер запрашивает новый блок данных, контроллер и микросхемы NAND Flash за микросекунды «раскручиваются» до полной скорости, и потребление подскакивает до 6–7 Вт, а при экстремальных пакетных операциях — даже до 9–10 Вт. Поскольку слот M.2 работает при фиксированном напряжении 3,3 В, в пересчёте на ток это означает:

  • Ток в режиме простоя: около 15 мА
  • Пиковый ток при активном чтении: около 2000 мА (2 А)

Однако ключевым является не само пиковое значение, а то, каким образом оно достигается. Этот примерно 130-кратный скачок происходит не постепенно, а в виде переходного процесса, практически мгновенно — примерно за одну микросекунду. Скорость изменения тока (di/dt) — решающий параметр для индуктивных колебаний напряжения и излучаемого электромагнитного шума — в таком случае составляет:

di/dt ≈ (2 A − 0.015 A) / 0.000001 s ≈ 1,985,000 A/s

Почти два миллиона ампер в секунду. С точки зрения материнской платы это мощный электромагнитный импульс, который может вызвать кратковременную просадку напряжения на локальных конденсаторах, в то время как высокочастотная составляющая, распространяющаяся по проводникам, способна модулировать окружающие цифровые и аналоговые схемы.

Именно здесь становится понятно, что даёт модель bpplay. Традиционный плеер во время одного трека продолжительностью 3–4 минуты — в зависимости от размера буфера — заставляет SSD десятки или даже сотни раз совершать резкие колебания между 15 и 2000 миллиамперами. С bpplay такой скачок по существу происходит только один раз: в первые мгновения после запуска трека, когда его содержимое считывается в память.

После этого потребление SSD стабилизируется на спокойном, почти постоянном уровне, di/dt стремится к нулю, и этот конкретный источник шума — переходные процессы накопителя — практически исчезает на время воспроизведения. Остальная часть компьютера, конечно, продолжает работать, но один из самых резких и наиболее часто повторяющихся источников электрических помех в цепи замолкает именно тогда, когда звучит музыка.

«Но это уже было сделано»

Существует как минимум ещё один плеер для macOS — VeraVox, который практически слово в слово придерживается той же философии: сигнал должен оставаться нетронутым. Hog mode, IOProc, DoP, отсутствие DSP. Однако картина сложнее, и по двум направлениям.

С одной стороны, VeraVox — отполированный, хотя всё ещё находящийся на стадии бета-тестирования продукт: графический интерфейс, измерительные инструменты, веб-управление, множество форматов — серьёзная и хорошо выполненная работа. bpplay, напротив, создавался не как продукт, а как эксперимент, proof of concept, ценность которого как раз и заключается в простоте. Один C-файл, ноль внешних зависимостей, каждая строка видна и может быть проверена. Любой, кто хочет понять, как воспроизводить музыку бит-перфектно на macOS, способен прочитать исходный код bpplay от начала до конца за один день. Важен не коммерческий успех, а понимание процессов и извлечение из них урока.

С другой стороны, у bpplay есть действительно иное и принципиально важное решение: уже упомянутая модель полной загрузки в RAM. Большинство плееров — включая VeraVox — используют так называемый кольцевой буфер и во время воспроизведения также считывают данные из файла, обычно блоками по 50–100 МБ. bpplay этого не делает: одним шагом, с заданными приоритетами, он загружает всё, после чего диск больше не выполняет никакой работы.

Это измеримое различие, а не просто вопрос вкуса.

Чего в bpplay нет?

Характер bpplay, пожалуй, проще всего понять, сравнив его с тем, что знакомо большинству пользователей. Roon, Audirvana и HQPlayer — зрелые приложения, выполняющие огромное количество задач, которых bpplay сознательно не делает:

  • Никакого управления библиотекой и медиаменеджмента. Никаких обложек, просмотра метаданных, функции «Discover». bpplay воспроизводит файлы и папки — и ничего больше.
  • Никакого графического интерфейса. Вместо визуального мира Roon и элегантности Audirvana — окно Terminal (а в соответствующем режиме воспроизведения и его может не быть).
  • Никакого сетевого стриминга и мультирум-воспроизведения. RAAT от Roon, сетевые рендереры — всего этого нет в Core.
  • Никакой обработки сигнала. Вся концепция HQPlayer построена вокруг апсемплинга, сложных цифровых фильтров и модуляторов — bpplay является полной противоположностью: нулевая обработка сигнала, и именно в этом заключается смысл.
  • Никакой регулировки громкости, темброблоков и микширования. Всё это передаётся ЦАПу и усилителю.

Список поначалу может даже выглядеть удивительно. Но в действительности картина иная: каждая отсутствующая функция одновременно является местом, где сигнал может измениться, или куда между файлом и ЦАПом может вклиниться непредсказуемый программный этап — со всеми его шумовыми последствиями. В bpplay этих функций нет не потому, что на них не хватило времени, а потому, что их отсутствие и является целью.

Эталон, а не конкурент

Здесь проявляется, пожалуй, самое интересное применение bpplay — то, что отличает его от перечисленных выше программ не по функциям, а по роли.

Представим ситуацию, когда вы знакомитесь с новым ЦАПом. Вы что-то слышите: характер, окраску, ощущение пространства. Но как определить, принадлежит ли это самому ЦАПу — или записи, — а не является следом какого-то скрытого этапа обработки в программном обеспечении плеера? Большинство программ создают непрозрачную завесу между файлом и ЦАПом, и если сам путь непредсказуем, то непредсказуемым становится и мнение, которое вы формируете о ЦАПе.

С bpplay этот фактор неопределённости исчезает. Поскольку путь от файла к ЦАПу известен, детерминирован и свободен от вмешательств, то результат, который вы слышите, гораздо в большей степени является результатом работы самого ЦАПа. Программа делает цепь предсказуемой, поэтому реальные возможности ЦАПа — и его ограничения — проявляются честнее.

Эта роль точно соответствует нашему подходу к выпуску записей. Записи My Reel Club® создаются известным способом, без постобработки: точно известно, что находится в канавке или на ленте, поскольку сама запись также производится нами, обычно перед аудиторией. Теперь к ним присоединяется программный плеер с такой же строгостью и детерминированным принципом работы. Вместе они образуют полностью контролируемую тестовую цепь: известный исходный материал, предсказуемый плеер и в конце единственный неизвестный элемент, который действительно стоит исследовать, — сам новый ЦАП.

Таким образом, bpplay отказывается от красивого интерфейса и удобных функций, но взамен предлагает нечто другое: эталон. Фиксированную точку, относительно которой можно проводить измерения. Именно поэтому он не является конкурентом Roon или HQPlayer — у него другая задача. Они предназначены для комфортного повседневного прослушивания; bpplay нужен для того, чтобы можно было понять, что делает остальная часть цепи после компьютера.

Второй эксперимент внутри эксперимента

У bpplay есть слой, который вообще не имеет отношения к звуку, но представляет собой поучительную часть этой истории.

В конце концов, за проектом в привычном смысле не стоит профессиональный опыт разработки программного обеспечения: отправной точкой были инженерный опыт и опыт работы с медиатехнологиями, а не написание кода для аудио реального времени внутри CoreAudio.

bpplay был создан при участии языковой модели Claude Opus 4.8.

НО!

Задание заключалось не в том, чтобы «создать аудиофильский бит-перфектный плеер».

Архитектура была задана заранее: модель полной загрузки в RAM, hog mode, сигнальный путь без вмешательств. Были заранее определены и функции: поддержка DoP, gapless-воспроизведение, сегментация по форматам. Эти решения следовали из принципов Hi-Fi и практического опыта, а не от модели. Модель была партнёром в реализации — в лабиринте API CoreAudio, в написании бит-точного кода и отладке.

Это само по себе эксперимент, другой эксперимент внутри плеера: что происходит, когда человек с твёрдыми представлениями, но без привычки к разработке, создаёт серьёзное программное обеспечение с помощью языковой модели? Движение DIY (Do-It-Yourself) всегда было популярно среди взыскательных любителей музыки, не являющихся инженерами.

Теперь, похоже, приходит время и для DIY-софта: мы можем адаптировать важные элементы компьютерного прослушивания музыки под собственные потребности.

Этот опыт показывает, что идея работает удивительно хорошо, если у человека есть видение и способность выносить суждения. Модель не заменяет видение, а лишь ускоряет его реализацию. Решения — что исключить, что относится к чистому сигнальному пути, а что является избыточным украшательством — на протяжении всего процесса должен принимать человек. И именно эти решения делают bpplay тем, чем он является. С момента первого промпта весь процесс занял примерно две недели.

Этот опыт, вероятно, заслуживает отдельной статьи, но он уместен и здесь, потому что bpplay — не просто плеер, но и моментальный снимок того, как сегодня можно создавать программное обеспечение, а также того, насколько разработчики операционных систем поддерживают — или, наоборот, затрудняют — требовательное компьютерное прослушивание музыки.

Для кого он предназначен?

Core-версия bpplay предназначена не для всех. Как уже отмечалось несколькими строками выше, у неё нет графического интерфейса — она запускается из Terminal и управляется клавишами. Взамен она делает ровно то, что обещает, и каждое её решение прозрачно.

Она предназначена для тех, кому интересно, что на самом деле происходит между файлом и ЦАПом; кто ценит инструмент, делающий ровно столько, сколько необходимо, и ничего больше; кому нравится возможность проверить сам код. И особенно для тех, кто сравнивает ЦАПы или знакомится с ними и хочет использовать плеер, который не влияет на оценку, — фиксированную эталонную точку в собственной системе.

Что bpplay Core умеет сегодня

Это сводка текущего, проверенного на реальном оборудовании состояния Core-версии. Всё перечисленное здесь подтверждено работой с реальными ЦАПами (XMOS и интерфейсом Amanero Combo 384/768), вплоть до DSD256.

Поддерживаемые форматы

  • WAV (целочисленный PCM, 16/24/32 бит)
  • FLAC (без потерь, через встроенный однозаголовочный декодер)
  • AIFF / AIF (PCM без потерь, big-endian, однократное преобразование при загрузке)
  • DSF (DSD), воспроизводимый через DoP — DSD64, DSD128 и DSD256
  • Поддерживаются варианты потоков с плавающей точкой и целочисленные варианты
  • Поддерживаются многоканальные WAV и .dsf
  • Поддержка MQA, включая обработку MQA на самом устройстве

Сигнальный путь

  • Эксклюзивный доступ к аппаратуре (hog mode) — ни одно другое приложение не может обратиться к ЦАПу
  • Прямой доступ к CoreAudio HAL через callback IOProc реального времени, выполняющий только memcpy
  • Никакой программной регулировки громкости, дитеринга, ресэмплинга или DSP любого рода
  • Автоматическое переключение частоты дискретизации в соответствии с файлом, поэтому системный ресэмплер не запускается
  • Однократное преобразование формата без потерь при загрузке; путь реального времени остаётся чистым копированием

Модель полной загрузки в RAM

  • Весь трек (или плейлист) загружается в память до начала воспроизведения
  • Буфер закрепляется с помощью mlock, поэтому ОС не может выгрузить его на диск
  • Нулевой дисковый I/O и нулевое декодирование во время воспроизведения

Работа с DSD

  • Автоматический DoP: расширение .dsf включает упаковку DoP без необходимости устанавливать какой-либо флаг
  • DSD64 / DSD128 / DSD256, независимо от частоты — частота несущей DoP масштабируется вместе с частотой
  • Предупреждение на очень высоких частотах DSD на случай, если ЦАП не сможет их обработать

Воспроизведение и удобство

  • Gapless-воспроизведение между треками одного формата и частоты
  • Рекурсивный обход папок: достаточно указать папку (или несколько), и программа воспроизведёт всё содержимое в альбомном порядке, на неограниченную глубину
  • Альбомы с разными частотами автоматически разделяются на чистые, отдельные сегменты воспроизведения
  • Короткий антищелчковый ramp в начале и конце для защиты цепи
  • Запрет перехода в режим сна во время воспроизведения (Mac не перейдёт в спящий режим посреди альбома)
  • Управление с клавиатуры из Terminal: следующий, предыдущий, пауза, выход (расширенный пульт на базе Siri появится позже)

Инженерная сторона

  • Один C-файл плюс несколько зависимостей, состоящих только из заголовков, — ноль внешних библиотек
  • Чистая компиляция одной командой make, без предупреждений
  • Каждая строка сигнального пути видна и доступна для аудита
  • Поддержка MacOS 10.15 и новее
  • Минимальный объём необходимой RAM: 8 ГБ

То, чего здесь намеренно нет, — графического интерфейса, управления библиотекой, сетевого стриминга и любой обработки сигнала — описано в статье выше; именно отсутствие этих функций и является сутью проекта.

Тем же, кто хочет функционально насыщенный плеер с красивым интерфейсом, множеством форматов для комфортного повседневного прослушивания, подойдут Roon, Audirvana, HQPlayer, VeraVox (бета-версия, но уже достаточно функциональная) или любой другой серьёзный плеер. bpplay с ними не конкурирует — его роль иная. В конечном счёте он является воплощением одного принципа: зачастую лучшее, что можно сделать с сигналом, — вообще ничего с ним не делать.

В следующей части автор заглянет под капот: разберёт, как на самом деле выглядит бит-перфектный сигнальный путь, что DoP означает для DSD, почему файл размером 4 ГБ превращается в памяти в 7,5 ГБ — и почему это не является потерей, — а также как bpplay работает с различными форматами, используя одну чистую цепь.

ИСТОЧНИК — https://mediaengineering.medium.com/bpplay-uncompromising-computer-audio-architecture-and-software-minimalism-fbcc30753b28

9 лайков

bpplay: Что происходит «под капотом»?

Идеальный битовый тракт, DoP и управление форматами

Бит-перфектный проигрыватель для macOS и вопрос, который привёл к его созданию — часть 2

В первой части мы пришли к выводу, что bpplay не обещает «лучшего звучания». Вместо этого он гарантирует полную прозрачность того, что происходит во время воспроизведения. Это не маркетинговый слоган, а результат целого ряда конкретных проектных решений, которые мы теперь разберём шаг за шагом:

  • Как на самом деле работает бит-перфектный тракт.
  • Что означает DoP (DSD over PCM) при воспроизведении DSD-файлов.
  • Почему файл размером 4 ГБ превращается в 7,5 ГБ в памяти, сохраняя при этом полную битовую точность.

Реальный тракт сигнала

Мы также рассмотрим работу с форматами, в частности то, как bpplay управляет смешанными плейлистами, где gapless-воспроизведение невозможно, а также разберём технические преимущества загрузки целых треков в память до начала воспроизведения.

В стандартной конфигурации macOS CoreAudio сигнал проходит через несколько уровней, на которых он может быть изменён:

Файл → Ресэмплинг → Float32-микшер → Программная регулировка громкости → ЦАП.

Архитектура bpplay полностью обходит этот путь, используя эксклюзивный тракт в режиме «hog mode»:

Файл → Декодирование → Согласование формата → IOProc → ЦАП.

Аллоцирование устройства:

При запуске bpplay получает эксклюзивный контроль над ЦАПом у системы CoreAudio посредством механизма macOS «hog mode». Это гарантирует, что никакое другое приложение не сможет вмешаться, системный микшер отключён, а системные звуки уведомлений принудительно направляются на другой аудиовыход. Это обязательное резервирование аппаратного ресурса на уровне системы. Терминал подтверждает это сообщением:

«Hog mode: exclusive access acquired»

Согласование формата с HAL: нижний уровень CoreAudio — это Hardware Abstraction Layer (HAL). Программа запрашивает у HAL возможности ЦАПа на целевой частоте дискретизации, в частности проверяя наличие физического формата с целочисленным представлением. Если такой формат доступен, bpplay выбирает его, гарантируя, что виртуальный формат также останется целочисленным и не потребует промежуточных преобразований в числа с плавающей точкой. Если HAL сообщает, что на данной частоте устройство поддерживает только формат float32, bpplay принимает этот вариант и записывает в журнал выбранный тракт.

Загрузка и однократное согласование формата: весь аудиофайл — WAV, FLAC, AIFF или DSF — целиком загружается непосредственно в память. Все необходимые преобразования без потерь для соответствия виртуальному формату ЦАПа выполняются на этом этапе, в потоке с обычным приоритетом и не в режиме реального времени, до запуска ЦАПа.

Блокировка памяти (mlock): чтобы операционная система не выгружала данные из памяти на диск, bpplay использует системный вызов mlock(). Это гарантирует, что аудиобуфер физически остаётся в оперативной памяти. Во время воспроизведения отсутствует вообще какой-либо дисковый ввод-вывод, активность SSD равна нулю, а следовательно, отсутствуют и электромагнитные помехи (EMI), возникающие из-за переходных процессов в NVMe-накопителе.

macOS Core Audio против прямого доступа Linux ALSA

Текущая версия bpplay для macOS взаимодействует с USB-ЦАПом через Core Audio. Предстоящая версия для Linux будет использовать прямой доступ ALSA, что приведёт к более глубокому архитектурному изменению того, как данные перемещаются из оперативной памяти в буфер USB-ЦАПа.

Рассмотрим, как две операционные системы обрабатывают путь данных от системной оперативной памяти до буфера USB-ЦАПа и что это означает с точки зрения шума, предсказуемости и контроля.

1. macOS — Core Audio HAL (текущая реализация)

Приложение регистрирует callback IOProc. Когда система вызывает эту функцию, код выполняет простой memcpy, копируя предварительно загруженные аудиоданные в область памяти, предоставленную HAL (outData->mBuffers[i].mData).

Однако в фоновом режиме работают несколько уровней:

  • memcpy записывает данные в промежуточный буфер меньшей ёмкости, управляемый CoreAudio.
  • HAL передаёт данные ядерному USB-аудиодрайверу через поток высокого приоритета, использующий политику Time Constraint.
  • Ядерный драйвер преобразует данные в USB Request Blocks (URB) и передаёт их USB-контроллеру.

Характеристики:

  • Высокий уровень абстракции («толстый» слой): это закрытая архитектура; она не позволяет напрямую вмешиваться в тракт, поэтому ни USB feedback, ни низкоуровневые временные параметры не могут контролироваться приложением bpplay.
  • Несколько операций копирования памяти: внутри системы происходит несколько операций копирования, хотя macOS оптимизирует этот процесс.
  • Управление через HAL: синхронизация и управление буферами в значительной степени осуществляются HAL, а не приложением. Это обеспечивает минимальную загрузку CPU, но за это приходится платить отсутствием прозрачности тракта.

Linux — прямой ALSA + mmap (планируемая архитектура)

В Linux использование ALSA в режиме mmap с прямым доступом значительно сокращает путь данных.

Ядерный драйвер ALSA выделяет DMA-буферы (Direct Memory Access). Они отображаются в адресное пространство приложения с помощью системного вызова mmap(). В результате операция memcpy записывает данные непосредственно в область памяти, из которой USB-контроллер считывает их через DMA.

Характеристики:

  • Низкий уровень абстракции («тонкий» слой) и потенциальный zero-copy: промежуточный буфер, используемый HAL, устраняется; адрес памяти, в который записывает программа, физически совпадает с адресом, из которого читает аппаратный интерфейс.
  • Детерминированная конфигурация: приоритет потока (SCHED_FIFO) и блокировка памяти (mlockall) полностью определяются приложением.
  • Расширенный контроль разработчика: управление временным циклом осуществляется непосредственно приложением. Это переносит архитектурную ответственность: именно разработчик, а не операционная система, должен определить оптимальную конфигурацию.

В современных асинхронных USB-ЦАПах устройство передаёт компьютеру информацию о скорости потребления данных из своего внутреннего буфера посредством feedback-пакетов. Этот механизм критически важен, поскольку локальные часы ЦАПа не синхронизированы идеально с тактовым генератором компьютера. На основе этой обратной связи компьютер регулирует скорость передачи аудиоданных, предотвращая переполнение или опустошение буфера (что проявлялось бы в виде слышимых артефактов или искажений).

  • В Linux низкоуровневый ядерный драйвер ALSA непосредственно получает и обрабатывает feedback от ЦАПа.
  • В macOS этим управляет значительно более высокий уровень абстракции — CoreAudio HAL.

В результате архитектура Linux находится ближе к аппаратуре, а между данными и процессами принятия решений имеется меньше уровней абстракции. Теоретически это позволяет добиться более точного тайминга и меньшего количества неявных системных операций.

Что это означает для архитектуры bpplay

Основная философия этой конструкции — минимизировать скрытые программные процессы во время воспроизведения. В macOS HAL неявно выполняет множество операций, включая управление временем, буферами и обратной связью. В Linux ALSA Direct эти операции перемещаются ближе к внешнему оборудованию, обеспечивая более высокий уровень контроля и потенциально уменьшая избыточную активность CPU и системы.

Реализация для macOS уже минимизирует программно создаваемый шум, предварительно загружая все данные в RAM и выполняя во время воспроизведения минимальное количество операций. Однако CoreAudio HAL остаётся высокоуровневым слоем, который взаимодействует с аппаратурой через собственные потоки и внутренние буферы.

Версия Linux ALSA Direct даёт возможность ещё сильнее упростить этот слой. Минимизация копирований памяти, более прямой доступ к DMA-буферу и больший контроль над таймингом потенциально позволяют добиться ещё меньшей и более предсказуемой системной активности во время воспроизведения.

На этом этапе использование SCHED_FIFO вместе с Linux-ядром реального времени (PREEMPT_RT) превращается из теоретической возможности в конкретное функциональное преимущество. Такой подход повышает вероятность точного воспроизведения того, что хранится в исходных файлах, снижая потенциальное влияние постороннего системного шума и дополнительного джиттера на процесс воспроизведения.

Иерархия буферов USB-интерфейса

Типичный USB-ЦАП содержит несколько уровней буферизации.

Почему буфер USB-интерфейса имеет ключевое значение

Работа асинхронного USB-аудио основана на том, что USB-интерфейс — например, чип XMOS или Amanero — непрерывно сообщает компьютеру скорость потребления данных из своего буфера. На основе этой обратной связи компьютер, посредством драйвера ALSA или CoreAudio, регулирует скорость передачи данных.

Следовательно, когда говорится о потенциальном «опустошении буфера ЦАПа», критической точкой отказа обычно является именно буфер USB-интерфейса (XMOS, Amanero и т. д.). Буфер непосредственно самого ЦАП-чипа расположен дальше по тракту, обычно имеет больший размер и выполняет другую функцию, например синхронизацию тактовой частоты и фильтрацию.

  • Буфер USB-ЦАПа: в данном контексте в первую очередь имеется в виду буфер USB-интерфейса. Именно этот буфер формирует петлю обратной связи с компьютером и подвержен опустошению или переполнению при неправильной работе механизма feedback.
  • Буфер ЦАП-чипа: буфер непосредственно ЦАП-чипа (ESS, AKM и т. д.) остаётся вторичным с точки зрения обратной связи при передаче данных от хоста.

IOProc: детерминизм реального времени

Аудиовыход в реальном времени каждого приложения на базе CoreAudio управляется IOProc: это callback-функция, вызываемая системой примерно раз в миллисекунду — причём частота вызовов определяется не приложением, а аппаратными аудиочасами. Этот вызов выполняется строго в реальном времени: он не может ждать, останавливаться или запрашивать ресурсы у системы.

IOProc в bpplay выполняет исключительно одну операцию: memcpy, копируя предварительно декодированные данные, уже находящиеся в памяти и согласованные с виртуальным форматом ЦАПа, в буфер, выделенный HAL для ЦАПа. Никаких вычислений с плавающей точкой, динамического выделения памяти, чтения файлов или блокировок. Только одно копирование памяти — ровно то, что необходимо для обеспечения детерминизма сигнального тракта.

Отсюда следует одно из важнейших архитектурных решений bpplay:

загрузка и воспроизведение — это две полностью раздельные фазы.

На этапе загрузки разрешены все операции: чтение файлов, декодирование FLAC, упаковка DoP и согласование формата. Всё это выполняется в основном потоке до запуска ЦАПа. На этапе воспроизведения — внутри IOProc — эти операции строго запрещены. Две фазы взаимодействуют через переменные: основной поток может читать текущую позицию, а пользователь может нажать «n», чтобы перейти к следующему треку, но IOProc никогда не ждёт и никогда не блокируется.

Остановка без щелчков и хлопков требует специальной обработки. Когда трек заканчивается, буфер IOProc необходимо чем-то заполнить. Очевидное решение — немедленно опустить сигнал до нуля — вызывает скачок напряжения, который на выходе ЦАПа воспринимается как слышимый щелчок. Поэтому bpplay реализует линейный fade-out длиной 1024 фрейма: от амплитуды последнего аудиофрейма сигнал постепенно уменьшается до нуля, кадр за кадром, примерно за 23 миллисекунды при 44,1 кГц. Этого достаточно, чтобы выход плавно достиг нулевого уровня — и всё происходит в области тишины, за пределами собственно музыкального содержания. Та же логика применяется при паузе: bpplay обрабатывает прерывание не резким отключением звука, которое вызвало бы скачок DC-смещения и щелчок на пике волны, а мгновенным коротким fade-out. Поэтому нажатие кнопки паузы («p») происходит плавно, без загрузки выхода и без щелчка.

DoP: упаковка DSD-потока

В первой части упоминалось, что bpplay также поддерживает DSD — обрабатывая файлы DSF в режиме DoP вплоть до DSD256. Внутри это одна из наиболее элегантных конструкций: протокол, который инкапсулирует данные в другом формате, не изменяя ни одного бита исходного содержимого.

Что такое DSD?

Аудиофайлы PCM (WAV, FLAC, AIFF) используют отсчёты, привязанные ко времени: в каждом интервале дискретизации определённое число — закодированное в 16, 24 или 32 битах — задаёт значение аналогового сигнала. DSD (Direct Stream Digital) принципиально отличается: он хранит 1-битные отсчёты с чрезвычайно высокой частотой, используя дельта-сигма-модуляцию. Для DSD64 частота дискретизации составляет 64 × 44,1 кГц, то есть 2,8224 МГц; для DSD128 — 5,6448 МГц; для DSD256 — 11,2896 МГц на канал.

В большинстве USB-аудиоинтерфейсов такую плотность битов нельзя передать напрямую: стандартный протокол передачи аудио ожидает PCM-данные с заданной разрядностью и частотой дискретизации. DoP решает эту проблему.

Структура стандарта DoP

Протокол DoP (DSD over PCM, v1.1) удивительно прост. Он упаковывает ровно 16 бит DSD в один 24-битный PCM-отсчёт: младшие 16 бит отсчёта содержат непосредственно полезную нагрузку DSD (8 бит из первого временного слота и 8 бит из второго), тогда как старшие 8 бит образуют байт-маркер, чередующийся между 0x05 и 0xFA.

Поскольку каждый PCM-отсчёт содержит 16 бит DSD, частота дискретизации несущего PCM ровно в 16 раз ниже исходной частоты DSD: 176,4 кГц для DSD64, 352,8 кГц для DSD128 и 705,6 кГц для DSD256. Именно это bpplay сообщает ЦАПу — не «DSD64», а «24-битный PCM, 176,4 кГц», внутри которого находится структура DoP.

Чередующийся байт-маркер выполняет роль предохранителя всей системы. Если ЦАП поддерживает DSD через DoP и обнаруживает строгое чередование 0x05 и 0xFA, он распаковывает DSD-биты и направляет их во внутренний DSD-декодер. Затем аудиосодержимое напрямую поступает на аналоговый каскад без промежуточного преобразования. Если чередование по какой-либо причине нарушено — например, если ЦАП получает не-DSD файл, — DoP-декодер остаётся пассивным и воспринимает входящий поток как обычный PCM. Никаких внезапных всплесков статики или громкого гула не возникает: DSD просто не распаковывается. Таким образом, маркер — не просто идентификатор формата, а надёжный самопроверяющийся handshake.

Почему не Native DSD?

Архитектура macOS CoreAudio не распознаёт транспорт Native DSD; API, позволяющего сообщить системе «передавай это как DSD-биты в ЦАП», не существует. Поэтому DoP — не компромисс, а физически единственный жизнеспособный путь через CoreAudio. Упаковка DoP действительно создаёт избыточность, но, что принципиально важно, не за счёт качества сигнала. 16 бит DSD, которые bpplay помещает в 24-битный фрейм, побитово идентичны данным внутри DSF-файла. DoP-фрейм служит исключительно транспортным протоколом, оставляя исходное содержимое полностью неизменным.

Почему 4 ГБ превращаются в 7,5 ГБ?

Как отмечалось в первой части, bpplay требует необычно большого объёма памяти, особенно при работе с DSD-файлами. Пример с DSD256 был весьма конкретным: файл размером 4 ГБ после загрузки может занимать в RAM до 7,5 ГБ. Это не ошибка измерения и не неэффективное управление ресурсами — это совокупный результат двух последовательных, математически предсказуемых и полностью без потерь преобразований.

Шаг 1: упаковка DoP (×1,5)

Битрейт исходного DSD256 составляет 11,2896 МГц на канал. Для двух каналов скорость передачи данных рассчитывается следующим образом: 11 289 600 × 2 канала / 8 бит = 2 822 400 байт/с.

После упаковки DoP поток несущего PCM представляет собой 24-битные данные с частотой 705 600 Гц для двух каналов: 705 600 Гц × 3 байта/отсчёт × 2 канала = 4 233 600 байт/с. Соотношение составляет: 4 233 600 / 2 822 400 = увеличение в 1,5 раза. Это и есть «избыточность» байта DoP-маркера: один дополнительный байт-индикатор добавляется на каждые два байта DSD. Сам DSD-поток остаётся неизменным — увеличивается только размер транспортной инкапсуляции.

Шаг 2: путь HAL Float32 (×1,333)

Строка, которую мы видели на экране терминала в предыдущем разделе:

«Signal path: 32-bit float source → float32 virtual format: direct copy,
zero conversion on our side; the HAL does the final step to the DAC.»

Это сообщение является результатом согласования между ЦАПом и CoreAudio HAL. bpplay пытается получить целочисленный режим: в этом случае 24-битные DoP-отсчёты отправляются непосредственно в ЦАП без промежуточного преобразования в float. Если HAL сообщает, что устройство на данной частоте предоставляет только виртуальный формат float32 — что особенно часто встречается при 705,6 кГц, — bpplay принимает этот путь. Он преобразует 24-битные целочисленные отсчёты в float32 в соответствии со стандартом IEEE 754 — без потерь, поскольку мантисса float32 содержит 24 бита, а значит, 16- или 24-битное целое число может быть представлено точно.

Однако это преобразование увеличивает размер данных: с 3 байт/отсчёт до 4 байт/отсчёт. Соотношение составляет 4/3 = увеличение в 1,333 раза.

Полная формула

Произведение двух этапов: 1,5 × 1,333 = ровно двукратное увеличение по сравнению с исходными DSD-данными.

В 4-гигабайтном альбоме DSF собственно DSD-данные занимают немного меньше 4 ГБ: заголовки — DSD chunk, fmt chunk и data chunk, суммарно несколько сотен байт — а также выравнивание блоков, предусмотренное стандартом DSF, занимают часть пространства. Поэтому фактический DSD-поток составляет примерно 3,75 ГБ, что превращается в 3,75 × 2,0 = 7,5 ГБ в памяти. Эта корреляция не случайна.

Ещё раз важно подчеркнуть: оба этапа — упаковка DoP и преобразование float32 — выполняются ровно один раз во время загрузки, полностью за пределами потока реального времени. Во время воспроизведения обработанные данные просто находятся в памяти, а IOProc считывает их посредством memcpy. Исходное DSD-содержимое остаётся побитово неизменным.

Сегменты форматов: когда gapless перестаёт быть gapless

Одно из наиболее прагматичных и наименее заметных решений в bpplay — обработка смешанных плейлистов.

Проблема

Gapless-воспроизведение подразумевает полное отсутствие щелчков, пауз или моментов тишины между двумя треками. В bpplay это встроенная функция: когда данные одного трека заканчиваются, IOProc немедленно начинает копировать данные следующего. Никаких накладных расходов и никакого промежутка.

Однако такой непрерывный поток возможен только в том случае, если соседние файлы имеют идентичные форматы: одинаковую частоту дискретизации, одинаковую разрядность и одинаковое количество каналов. Если частота внезапно меняется — например, за FLAC 44,1 кГц следует WAV 96 кГц, — ЦАП необходимо перенастроить. Это аппаратная операция: CoreAudio должен остановить активный аудиопоток, изменить частоту дискретизации, а затем снова запустить поток. Такой перезапуск физически неизбежен и по своей природе вызывает короткую паузу.

Решение: сегментация

Перед загрузкой bpplay сканирует плейлист, считывая заголовки файлов, и делит его на отдельные сегменты формата. Внутри одного сегмента все файлы имеют одинаковую частоту дискретизации, разрядность и количество каналов — следовательно, эти треки воспроизводятся gapless. На границе сегментов происходит корректное изменение конфигурации, о котором терминал немедленно сообщает:

«Format change in queue (Nothing.flac: 44100 Hz/16-bit) — gapless chain breaks here.
A sample-rate switch cannot be gapless. Start the remainder separately.»

Это сообщение отражает саму суть bpplay: он не скрывает то, что невозможно обойти, а прямо указывает, почему и где происходит прерывание.

Чтение заголовков как лёгкое предварительное сканирование

Чтобы определить границы сегментов формата, bpplay считывает только заголовки файлов — не весь аудиопоток. Для WAV это первые несколько десятков байт; для FLAC — блок метаданных STREAMINFO (34 байта); для DSF — DSD chunk и fmt chunk (в сумме 80 байт); для AIFF — COMM chunk. Эта операция выполняется практически мгновенно и почти не увеличивает время загрузки. Полное декодирование строго ограничено файлами следующего сегмента и начинается только после завершения воспроизведения предыдущего сегмента.

Что происходит, если .flac-файл содержит MQA-обработанный материал?

Абсолютно ничего. На этапе чтения заголовков никакой специфической обработки MQA не происходит. Система просто идентифицирует его как стандартный FLAC-файл.

Почему?

Объяснение связано со структурой FLAC и механизмом работы MQA. bpplay извлекает исключительно 34-байтовый блок STREAMINFO. Этот блок, являющийся частью стандартной спецификации FLAC, содержит основные параметры: частоту дискретизации, разрядность, количество каналов и общее количество отсчётов. В нём нет специфических MQA-маркеров. STREAMINFO в MQA-FLAC просто сообщает стандартные параметры, например 44,1 кГц/16 бит или 48 кГц/24 бит, — поэтому на этом этапе программа корректно воспринимает его как обычный стандартный FLAC-файл.

  • Чтобы программа определила, что FLAC-файл фактически содержит данные MQA, требуется одно из двух:
  • Чтение метаданных ID3v2 или Vorbis comment, расположенных в конце файла, где MQA-кодер размещает идентификатор.

Декодирование первых нескольких аудиоотсчётов. MQA использует принцип «оригами»: информация высокого разрешения встраивается в младшие биты 24-битного потока, маскируется шумом и сопровождается специальным watermark в начале файла, который можно обнаружить только во время декодирования.

Поскольку bpplay на начальном этапе строго читает только заголовок, текущий процесс ограничивается определением формата. Метаданные не разбираются и аудиоотсчёты не декодируются.

Следствие: на начальном этапе чтения заголовков система определяет MQA как обычный 16- или 24-битный FLAC-файл. Фактическое обнаружение MQA происходит позже, на этапе полного декодирования, на основании метаданных. Однако unfolding (декодирование) MQA внутри bpplay не выполняется; им занимается ЦАП, если он поддерживает декодирование MQA.

Этап сегментации bpplay намеренно сделан лёгким: программа читает только заголовки файлов, чтобы быстро определить, какие файлы относятся к одному gapless-сегменту. Поэтому на этой ранней стадии MQA не распознаётся и рассматривается как стандартный 16- или 24-битный FLAC. Обнаружение MQA происходит позднее, во время полного декодирования, когда система разбирает метаданные Vorbis comment. Поэтому bpplay не утверждает, что полностью анализирует файл при загрузке плейлиста; он извлекает только те данные, которые строго необходимы для корректного управления сегментами.

Особый случай AIFF: перестановка байтов

AIFF (Audio Interchange File Format) структурно эквивалентен WAV и использует похожую архитектуру на основе чанков для несжатого PCM, но с одним фундаментальным отличием: AIFF хранит отсчёты в порядке big-endian. Старший байт (MSB) располагается первым, тогда как WAV и CoreAudio используют little-endian, где первым идёт младший байт (LSB).

bpplay обрабатывает это во время загрузки, меняя порядок байтов каждого отсчёта. Перестановка трёх байтов 24-битного отсчёта (A–B–C → C–B–A) математически выполняется без потерь: это то же самое числовое значение, прочитанное в другом порядке. Хотя этот шаг обязателен, он не нарушает битовую точность. Исходная последовательность битов остаётся идентичной; изменяется только физический порядок байтов, чтобы соответствовать тому, что ожидает CoreAudio HAL. Поэтому определение «бит-перфектный» остаётся корректным.

Целочисленный путь против Float32

Читатель закономерно может спросить: при каких условиях тракт работает в целочисленном режиме, а когда вынужден использовать float32?

Ответ зависит от прошивки ЦАПа и внутренних решений CoreAudio HAL. bpplay запрашивает у HAL доступные физические форматы на целевой частоте дискретизации и определяет минимальную целочисленную разрядность, которая как минимум соответствует разрядности исходного файла. Если ЦАП поддерживает её и виртуальный формат также может быть установлен как целочисленный, цепочка остаётся полностью целочисленной: вычислений с плавающей точкой не происходит, что обеспечивает побитово точную передачу данных.

Если HAL принудительно использует виртуальный формат float32, bpplay сообщает об этом:

Physical format: 32-bit integer.
Virtual format: float, 32-bit, 8 B/frame — float virtual format: the HAL gave no integer path
Signal path: 32-bit float source → float32 virtual format: direct copy,
zero conversion on our side; the HAL does the final step to the DAC.

Float32 не вносит потерь для 16- или 24-битного PCM, поскольку мантисса float32 имеет 24 бита, то есть диапазон 16/24-битных целых чисел может быть представлен в ней точно, без ошибок округления. Однако настоящий побитово точный целочисленный тракт технически отличается от этого: в целочисленном режиме после загрузки не выполняется вообще ни одного преобразования — ни в исходном формате, ни в транспортном.

Известное ограничение — macOS USB и 32-битное целое

В macOS аудиосистема CoreAudio предоставляет приложениям промежуточный формат с плавающей точкой float32 для ЦАПов, подключённых по USB, даже если сам ЦАП также предлагает целочисленный формат. Это определяется на уровне системы, а не проигрывателем. bpplay всегда честно сообщает об этом в строке сигнального тракта: строка Virtual format: float показывает, когда выходной виртуальный формат является плавающей точкой. На практике:

16- и 24-битный материал (WAV, FLAC, AIFF, DSD/DoP, MQA): побитово точный, поскольку полностью укладывается в 24 бита эффективной точности промежуточного float32. Сигнализация MQA проходит без изменений, а DSD отображается на дисплее ЦАПа как DSD.

32-битный целочисленный исходник: младшие несколько бит могут быть потеряны в промежуточном float. Это одно детерминированное, контролируемое преобразование — в данном случае строка Signal path: сообщает об одном контролируемом преобразовании, а не о полной битовой точности. Это затрагивает небольшое число пользователей, поскольку 32-битные целочисленные исходники встречаются редко (большинство студийных мастер-файлов имеют 24-битное целочисленное или 32-битное float-представление).

Доступные форматы ЦАПа можно проверить с помощью опции -fmts — она выводит физические форматы, предлагаемые устройством, и текущий виртуальный формат. Ограничение относится к USB-аудиосистеме macOS. На путях без USB — например, при использовании сетевого аудиоинтерфейса Ravenna/AES67 — оно отсутствует: там весь сигнал проходит без изменений.

За пределами битов: компьютер как источник шума

До этого обсуждение было сосредоточено на сигнальном тракте — битах, форматах и преобразованиях. Однако модель полного хранения в RAM имеет следствие, проявляющееся не в битах, а в электронах. Как обсуждалось в первой части, современные импульсные блоки питания зависят от нагрузки, и скачок потребления при чтении NVMe SSD может за микросекунды вырасти с 15 миллиампер до 2 ампер — это соответствует переходному току ($di/dt$) почти в два миллиона ампер в секунду. Такой резкий переход способен создавать высокочастотный RF/EMI-шум в силовых цепях и через излучаемые пути, что может стать мешающим фактором в окружении ЦАПа, расположенного рядом с компьютером или питающегося по шине.

Однако SSD — лишь один и, возможно, наиболее наглядный источник шума. Чтобы полностью понять смысл модели полного хранения в RAM, стоит рассмотреть, что компьютер делает во время воспроизведения и какие из этих процессов можно или нельзя адаптировать под требования аудиофильского воспроизведения.

Колебания нагрузки процессора

Обычный проигрыватель декодирует сжатый формат в реальном времени. Непрерывная распаковка FLAC или ALAC создаёт небольшие повторяющиеся пики нагрузки на ядрах процессора. Хотя сама по себе эта нагрузка потребляет немного энергии, динамическое масштабирование напряжения и частоты (DVFS, turbo boost) современных CPU превращает каждый такой пик нагрузки в переход между частотами и напряжениями. Ядро переключается между тактовыми состояниями, и каждый переход создаёт переходную нагрузку на линии питания — то же явление, которое наблюдается у SSD, хотя с меньшей амплитудой и большей частотой.

Модель полного хранения в RAM устраняет и этот источник шума. После загрузки и декодирования трека процессору практически больше нечего делать: IOProc выполняет только memcpy, одну из простейших операций, которую можно запросить у CPU. Ядра могут перейти в низкое, стабильное тактовое состояние и оставаться в нём — колебаний нагрузки при декодировании больше нет, которые заставляли бы частоту то повышаться, то снижаться. Нагрузка создаётся предсказуемо, ровно тогда, когда этого требует музыка.

Прерывания и переключения контекста

Каждое чтение с диска генерирует аппаратное прерывание (IRQ): накопитель сообщает процессору, что данные готовы, и процессор прерывает текущую работу, чтобы их обработать. Каждое прерывание вызывает переключение контекста, а каждое переключение контекста создаёт небольшой нерегулярный профиль нагрузки. Обычный проигрыватель, который читает данные с накопителя порциями в течение 3–4-минутного трека, поддерживает этот поток прерываний на протяжении всего воспроизведения.

Если во время воспроизведения нет операций с диском, этих прерываний просто не возникает. Не то чтобы bpplay «лучше обрабатывал прерывания» — он просто не задействует оборудование, которое эти прерывания порождает. Самый чистый способ устранить источник шума — не создавать его изначально. Основная уязвимость широко используемой архитектуры с кольцевым буфером — так называемое опустошение буфера, когда даже один пропущенный запрос чтения с диска или пик загрузки CPU может вызвать слышимый щелчок или кратковременную тишину. Модель полного хранения в RAM физически исключает такую возможность отказа: после успешной загрузки во время воспроизведения не может возникнуть программного или аппаратного события, которое задержало бы использование данных.

Пейджинг памяти (Swap)

Когда памяти не хватает, операционная система записывает редко используемые страницы на накопитель (paging/swap). Это снова приводит к активности диска и связанным с ней переходным процессам. Вызов mlock() в bpplay гарантирует, что аудиобуфер физически остаётся в RAM и не может быть выгружен. Минимальное требование в 8 ГБ памяти объясняется именно этим: весь трек вместе с выполненным при загрузке согласованием формата — включая расширенный DoP+float32-буфер в случае DSD — должен свободно помещаться в памяти, чтобы системе не приходилось использовать paging.

Однако paging не полностью контролируется bpplay: другие системные процессы всё ещё могут инициировать выгрузку страниц. Что bpplay может и делает — это исключает собственные аудиоданные из этого уравнения. Разумеется, пользователь также должен обеспечивать общую стабильность системы: при желании получить оптимальный результат не следует запускать ненужные фоновые процессы.

Дисплей и GPU — цена графического интерфейса

Именно здесь решение отказаться от графического интерфейса перестаёт быть просто эстетическим или философским выбором. Проигрыватель, отображающий VU-метры, спектроанализаторы или анимированную обложку альбома, обновляет экран как минимум 60 раз в секунду. Это постоянно держит GPU и графический конвейер активными, создавая собственное энергопотребление и колебания нагрузки — ещё один регулярный и небезначительный источник шума в электрическом поведении компьютера.

Строка состояния bpplay в терминале обновляется пять раз в секунду (внутренний цикл выполняется каждые 40 миллисекунд и записывает данные каждый пятый цикл). Пять текстовых обновлений в секунду без участия GPU вместо перерисовки шестидесяти полноценных кадров. Разница составляет несколько порядков. Поэтому «непривлекательное окно терминала» — не просто отказ от удобных функций, а сознательное уменьшение активности дисплея как источника шума.

Что невозможно заглушить внутри компьютера

Фоновая работа операционной системы — индексация, сетевые демоны, системное обслуживание — продолжается. Всё это не зависит от bpplay. bpplay не переводит систему в режим сна (он лишь предотвращает автоматический idle sleep во время воспроизведения по соображениям надёжности) и не вмешивается в системные процессы. Он просто гарантирует, что сам не добавляет шума: во время воспроизведения нет чтения с диска, декодирования и отрисовки экрана.

Исходный код bpplay содержит решение для этого: основному потоку присваивается QOS_CLASS_UTILITY, чтобы он не конкурировал с аудиопотоком реального времени — не для ускорения реакции кнопок, а именно для подавления фоновой активности.

pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0);

Сам комментарий в коде отмечает, что это исключительно управление ресурсами, а не вмешательство, влияющее на качество звука; бит-перфектные данные остаются идентичными, а практическое влияние на macOS минимально, поскольку поток реального времени и так защищён. Управление приоритетами будет иметь реальное значение в планируемой headless-сборке для Linux, где SCHED_FIFO обеспечивает настоящий приоритет планирования. Такая сдержанность — указание того, что на самом деле не имеет значения, — столь же характерна для подхода bpplay, как и конкретные цифры.

Активность шины — периодический трафик изохронной передачи USB-аудио — также остаётся: программное обеспечение не может устранить её, поскольку это сам канал передачи. Ответственность за связанный с этим шум лежит на ЦАПе; гальванически хорошо изолированный ЦАП с автономным питанием как раз и предназначен для подавления таких помех. Таким образом, bpplay не решает каждую проблему; он лишь подавляет те источники, которые можно устранить программно на стороне компьютера.

После загрузки музыкального материала в память и завершения согласования формата практически вся активность, способная вызывать регулярные или резкие изменения нагрузки, во время воспроизведения прекращается. Нет постоянного дискового ввода-вывода, декодирования или графической перерисовки. IOProc выполняет одну операцию: копирование из памяти в память. Эта операция создаёт настолько низкую и предсказуемую нагрузку, что ядра процессора могут оставаться в стабильном тактовом состоянии. Компьютер становится электрически «тише».

Эта стабильность особенно интересна с точки зрения тактирования. Хотя bpplay не может непосредственно влиять на внутренние часы ЦАПа, минимальная системная нагрузка косвенно может положительно влиять на джиттер. Большие и быстрые изменения нагрузки не только вызывают колебания питания, но и могут создавать небольшие нестабильности в распределении системного тактового сигнала — особенно при отсутствии гальванической изоляции между компьютером и ЦАПом. Минималистичная работа bpplay пытается подавить и этот источник шума. Он не заявляет об устранении джиттера — большая часть джиттера всё равно возникает внутри собственной тактовой схемы ЦАПа, — но по крайней мере не добавляет лишних внешних возмущений.

Невозможно содержательно обсуждать джиттер, не прояснив фундаментальный вопрос: откуда во время воспроизведения берётся тактовый сигнал, определяющий момент преобразования цифровых отсчётов в аналоговое напряжение? Для большинства современных высококачественных USB-ЦАПов — и в тракте, использованном для проверки bpplay, где тестировались ЦАПы TT DAC и Gustard через интерфейсы XMOS и Amanero Combo384/768, — ответ однозначен: ведущими являются часы ЦАПа, а не компьютера.

Эти USB-интерфейсы используют асинхронную передачу USB. Суть асинхронного режима заключается в том, что время цифро-аналогового преобразования контролируется собственным локальным кварцевым генератором ЦАПа — обычно двумя генераторами, раздельными для базовых семейств 44,1 и 48 кГц. Компьютер не задаёт темп преобразования; наоборот, ЦАП непрерывно сообщает компьютеру через feedback endpoint скорость, с которой он потребляет данные, а компьютер соответствующим образом регулирует передачу, чтобы входной буфер ЦАПа не опустошался и не переполнялся.

Это имеет далеко идущие последствия. Частота дискретизации, установленная в CoreAudio, фактически является лишь номинальным значением; реальный темп, определяющий звучание, задаётся тактовым генератором ЦАПа. Более того, частота самих вызовов IOProc — ранее описанная как вызовы «на основе аппаратных аудиочасов» — в конечном счёте также определяется ЦАПом: асинхронная feedback-частота задаёт момент, когда HAL запрашивает новую порцию данных. Поэтому даже темп, с которым данные запрашиваются у bpplay, синхронизирован с часами ЦАПа, а не компьютера.

Отсюда следует, что колебания тайминга в тракте данных — тот вид «программного джиттера», о котором говорилось в первой части, — не достигают тактового генератора преобразования в хорошо спроектированном асинхронном ЦАПе. Входной буфер ЦАПа и его локальный генератор изолируют все ошибки планирования компьютера во временной области.

Если ЦАП является ведущим по тактированию, а асинхронный буфер поглощает временные ошибки в тракте данных, возникает закономерный вопрос: имеет ли практически нулевая мощность нагрузки во время воспроизведения вообще какое-либо отношение к джиттеру? Да, но не там и не так, как можно было бы предположить изначально. Здесь необходимо разделить разные типы джиттера.

Случайный (гауссов) джиттер возникает из-за собственного фазового шума кварцевого генератора и теплового шума. Это внутреннее свойство тактового генератора ЦАПа — bpplay не может на него влиять и не претендует на это.

Интерфейсный или транспортный джиттер представляет собой временную неопределённость прихода данных. Она изолируется асинхронным USB-буфером. bpplay гарантирует, что буфер никогда не останется пустым, но время преобразования определяется ЦАПом, а не программным обеспечением.

Третья категория — детерминированный, а именно периодический, обусловленный питанием (коррелированный) джиттер — и именно здесь ситуация становится интересной. Большие и быстрые изменения нагрузки — переходные процессы SSD и декодирования, обсуждавшиеся в первой части, с di/dt, приближающимся к двум миллионам А/с, — не просто искажают напряжение питания: высокочастотная составляющая, распространяющаяся по силовым линиям и излучаемым путям, может проникать в цепи питания и опорные напряжения тактовой схемы ЦАПа. Загрязнённая линия питания может измеримо ухудшить фазовый шум кварцевого генератора и модулировать шумовой фон аналогового каскада. Это не джиттер в тракте передачи данных во временной области, а электрическое загрязнение, способное ухудшить работу собственных часов ЦАПа и его аналоговых опорных цепей — при условии существования пути связи: общей земли, питания по шине или недостаточной гальванической изоляции.

Именно здесь модель полного хранения в RAM имеет реальное значение для джиттера. Меньшее количество и меньшая амплитуда di/dt-переходов на стороне компьютера означают, что к ЦАПу распространяется меньше широкополосного EMI и шума по силовым линиям, что уменьшает вероятность загрязнения тактовой схемы ЦАПа и его шумового фона. Формулировка должна быть точной: bpplay не снижает джиттер собственных часов ЦАПа. Он уменьшает внешний источник электрических возмущений, который в противном случае мог бы ухудшить работу этих часов и шумовой фон.

Будет ли это иметь слышимый эффект, полностью зависит от конструкции ЦАПа. Гальванически хорошо изолированный ЦАП с автономным питанием полностью разрывает этот путь связи: для такого устройства шум со стороны компьютера в значительной степени не имеет значения, и практическая польза bpplay в этом отношении стремится к нулю — что совершенно нормально, поскольку ЦАП уже выполнил эту задачу. Напротив, у питаемого от шины или недостаточно изолированного ЦАПа, расположенного рядом с компьютером, путь связи остаётся открытым, и электрическая тишина может дать измеримое — а иногда и слышимое — различие. bpplay подавляет шум в источнике; изоляция блокирует его остаток. Это не конкурирующие подходы, а два конца одной и той же проблемы.

При этом модель полного хранения в RAM гарантирует один критически важный аспект тракта данных: буфер никогда не опустошается. Классический недостаток архитектур с кольцевым буфером — buffer underrun, когда пропущенное чтение с диска или пик нагрузки CPU опустошает буфер, вызывая слышимый щелчок или секунду тишины, — физически невозможен во время воспроизведения bpplay. bpplay подаёт данные в асинхронный буфер ЦАПа детерминированно и без прерываний.

Подход bpplay к поддерживаемым форматам столь же прагматичен. Для DSD в настоящее время поддерживается DSF в сочетании с протоколом DoP. bpplay обрабатывает контейнер .dsf (DSD Stream File, Sony), используя упаковку DoP. Важно понимать, что сам DoP-бэкенд не зависит от формата: после получения исходного 1-битного DSD-потока упаковка его в 24-битный PCM-фрейм с маркером 0x05/0xFA идентична независимо от происхождения битового потока. Ограничение связано не с DoP-трактом, а с разбором контейнера.

Формат .dff (DSDIFF, Philips) — контейнер профессионального мира и SACD-мастеринга, используемый во многих SACD-ри́пах и рабочих процессах на базе Pyramix. В несжатом виде воспроизведение .dff — хорошо определённая задача: после упаковки DoP весь оставшийся тракт идентичен текущему и требует лишь другой интерпретации контейнера. Существуют два конкретных различия. Первое — структура чанков и порядок байтов big-endian. Второе, более существенное, — порядок битов: стандарт DoP ожидает DSD-биты в порядке MSB-first внутри PCM-фрейма; .dsf хранит данные LSB-first, поэтому текущий путь DSF→DoP меняет порядок битов внутри байта, тогда как .dff изначально использует MSB-first и не требует этого разворота. Концепция та же, но деталь обратная. Однако .dff может содержать сжатие DST (Direct Stream Transfer) — формат сжатия DSD без потерь, — обработка которого потребовала бы отдельного DST-декодера и представляла бы значительную дополнительную сложность.

.wsd (Wideband Single-bit Data) — также необработанный 1-битный DSD-формат, обычно встречающийся в архивных и профессиональных системах. Теоретических препятствий для его воспроизведения нет, но на практике он встречается редко.

Поэтому решение здесь связано не с архитектурой, а с приоритетами. Формат .dsf охватывает подавляющее большинство потребительских и полупрофессиональных коллекций DSD; добавление несжатого .dff — чётко определённая задача, которую можно построить на существующем независимом от формата DoP-бэкенде; сжатый DST .dff и .wsd относятся к нишевым библиотекам и имеют более низкий приоритет. Согласно философии bpplay, новый формат добавляется тогда, когда имеет реальное практическое значение для целевой аудитории, а не просто ради полноты списка.

Что касается формата ALAC, стоит прояснить распространённое заблуждение: эталонная реализация кодека ALAC с 2011 года является открытым исходным кодом под лицензией Apache 2.0.

Почему же bpplay не воспроизводит ALAC? Главная причина — битовая избыточность. И ALAC, и FLAC являются форматами сжатия без потерь; декодированный PCM в обоих случаях побитово идентичен. Поэтому ALAC-файл не имеет никакого преимущества перед FLAC ни по качеству звука, ни по битовой точности — оба приводят к абсолютно одинаковой последовательности отсчётов. В минималистичном проигрывателе, ориентированном на проверяемость, параллельная поддержка двух избыточных lossless-кодеков лишь увеличивает объём кода без какого-либо слышимого или измеримого выигрыша.

Вторая причина — сложность контейнера. ALAC находится внутри контейнера MP4/M4A (ISO BMFF), чья структурная архитектура атомов (boxes) и таблиц отсчётов значительно сложнее заголовка RIFF/WAVE или потока FLAC. Интеграция MP4-демультиплексора противоречит фундаментальной цели bpplay: сделать исходный код полностью читаемым и проверяемым за один день.

Третья причина — ловушка «простого пути». Подсистема CoreAudio могла бы декодировать ALAC нативно (AudioConverter / ExtAudioFile), но этот путь ведёт непосредственно через непрозрачные системные слои, которые bpplay как раз существует для обхода, и где битовую точность уже нельзя гарантировать столь же прозрачно. Поэтому «дешёвое» решение противоречило бы принципу детерминированного сигнального тракта.

Наконец, практическая альтернатива: пользователи с библиотекой ALAC, происходящей из экосистемы Apple, могут без потерь преобразовать её в FLAC, не потеряв ни одного бита. Поэтому приоритет FLAC — не идеологический спор об открытости форматов, а результат прямых инженерных соображений: меньше кода, более прозрачный тракт и тот же бит-перфектный результат.

Итог: один пик — затем тишина

Энергопотребление обычного проигрывателя остаётся импульсным на протяжении всего воспроизведения: чтение SSD, пики декодирования, прерывания и обновление дисплея создают нагрузку на силовые линии десятки или сотни раз за 3–4-минутный трек.

В bpplay множество пиков сворачивается в один дискретный пик нагрузки в начале трека, когда содержимое считывается и декодируется в память. После этого энергопотребление стабилизируется на низком, почти постоянном уровне, di/dt приближается к нулю, а компьютер становится электрически тихим. Именно во время воспроизведения музыки.

Имеет ли это слышимое значение в конкретном тракте воспроизведения, как отмечалось в первой части, в значительной степени зависит от конструкции ЦАПа. bpplay не утверждает, что «звучит лучше благодаря этому». Он утверждает другое: источники шума на стороне компьютера, которые можно устранить программными средствами, устраняются в самом источнике, а оставшийся шум — присущий аппаратуре и каналу передачи — остаётся компоненту, ответственному за его обработку: ЦАПу.

Прослеживаемый, честный сигнальный тракт

Всё изложенное выше — не абстрактное архитектурное описание, а цепочка конкретного кода, определённых чисел и осознанных проектных решений. Каждый шаг имеет своё назначение и обоснование:

  • Hog mode: необходим, поскольку обход микшера требует резервирования аппаратного ресурса на уровне системы.
  • Однократное согласование формата при загрузке: критически важно, поскольку перенос любой операции, связанной с I/O, в поток реального времени строго запрещён.
  • mlock: используется потому, что paging вызывает дисковую активность и последующие переходные процессы питания.
  • Упаковка DoP: необходима, поскольку внутри CoreAudio не существует другого пути передачи DSD в ЦАП.
  • Расширение float32: принимается потому, что HAL иногда принудительно использует путь с плавающей точкой вместо целочисленного.
  • Сегменты форматов: используются потому, что переключение частоты дискретизации не может быть gapless; архитектура не скрывает этот факт, а просто сообщает о нём.

Та же логика распространяется ниже уровня битов — вплоть до самих электронов. Модель полного хранения в RAM не только делает сигнальный тракт детерминированным, но и минимизирует электрический след компьютера: переходные процессы дискового I/O, колебания нагрузки процессора из-за декодирования в реальном времени, поток прерываний и — благодаря отсутствию графического интерфейса — активность дисплея устраняются в самом источнике. Всё оставшееся — фоновое системное обслуживание и трафик по шине передачи — выходит за пределы программного обеспечения и оставляется на совести конструкции хорошо спроектированного изолированного ЦАПа.

Соответствие принципам минималистичной записи

В записях My Reel Club® тот же принцип применяется на другом уровне. С момента записи точно известно, что именно попадает в канавку или на ленту: минимальная микрофонная конфигурация, прямая стереосхема и отсутствие постобработки. bpplay соответствует этому источнику проигрывателем, спроектированным в том же пуристском духе: всё, что ему не нужно делать, он не делает. Всё, что он обязан сделать — упаковку DoP, преобразование float32 и перестановку байтов в случае AIFF, — он выполняет однократно и проверяемо ещё до начала воспроизведения.

Результат — не обещание, а состояние: программная сторона известна, и единственной переменной остаётся сторона ЦАПа. Именно такого состояния многие — возможно, неосознанно — годами добивались с помощью различных инструментов. bpplay не является единственным ответом, но это один из наиболее прозрачных вариантов.

Топология и аппаратная проверка

В ходе разработки и проверки тракт воспроизведения тестировался в трёх различных вариантах подключения:

  • ЦАП напрямую подключался к порту MacBook Air.
  • Подключение через хаб Sonnet Tech Thunderbolt 5.
  • Подключение через обычный безымянный USB-хаб.

В процессе проверки использовались как качественные, так и бюджетные 4-метровые кабели. Бит-перфектное поведение — включая корректный выбор формата, hog mode, memcpy через IOProc и загрузку всего материала в RAM — оставалось идентичным во всех трёх сценариях: битовый тракт полностью независим от способа подключения. Качество хаба, кабелей и гальванической изоляции влияет на профиль шума и EMI, однако, как отмечалось выше, это определяется топологией и самим ЦАПом, а не программным обеспечением.

(Продолжение следует в части 3, где будет рассмотрено непосредственное пересечение bpplay и аналоговых записей My Reel Club®: что такой тракт показывает на примере альбома Native DSD256 и с чем не способно справиться даже самое проверяемое программное обеспечение.)

Технические ссылки

  • Исходный код bpplay.c: один C-файл, использующий API CoreAudio HAL, совместимый с macOS 10.15+.
  • Стандарт DoP v1.1: базовый протокол инкапсуляции DSD в PCM.
  • IEEE 754 float32: содержит 23-битную мантиссу + 1 неявный бит, позволяя точно представить 24-битное целое число без ошибок округления.
  • Страница руководства mlock(2): справочная документация по закреплению страниц памяти в физической RAM.

ОРИГИНАЛ — https://mediaengineering.medium.com/bpplay-what-happens-under-the-hood-85d5159bb739

4 лайка

Побитово точное, основанное на памяти воспроизведение аудиофайлов в 2026 году

Платформы, языки и реальная разработка — часть 3

Несколько недель назад я начал большой эксперимент: решил, несмотря на то что не являюсь специалистом, написать программное обеспечение — такое, которое соответствует ожиданиям аудиофилов.

Как стало ясно из двух предыдущих материалов, bpplay представляет собой совершенно новое поколение DIY (do-it-yourself, «сделай сам») программного обеспечения, практически не существовавшее ранее. Одна из важнейших мотиваций проекта заключается именно в демонстрации смены парадигмы: с помощью больших языковых моделей (LLM) сегодня практически любой человек, скорее всего, может создавать подобные приложения, адаптированные под конкретные потребности, — без профессионального опыта программирования или проектирования программных архитектур. В эпоху разработки с помощью ИИ чистая инженерная концепция и точное распознавание проблемы стали важнее рутинного написания кода на уровне синтаксиса.

Когда инженер или целеустремлённый DIY-разработчик начинает проектировать более серьёзный аудиофильский музыкальный проигрыватель, почти сразу возникают два фундаментальных вопроса:

  • Какая операционная система обладает внутренней архитектурой, обеспечивающей прозрачный, прямой путь для аудиобитов вплоть до физического выхода?
  • На каком языке и на какой базе разработчика стоит строить приложение, чтобы оно было не просто работающим прототипом, а программой, которую можно поддерживать в долгосрочной перспективе, сделать безопасной и расширяемой на несколько платформ?

Прототип bpplay был написан на чистом C — как с точки зрения языка, так и концепции. Этот выбор идеально отражает классический консервативный подход, отдающий приоритет кратчайшему пути сигнала и прямому доступу к аппаратному обеспечению. Хотя подход на базе C в целом по-прежнему актуален, современная архитектура программного обеспечения предлагает альтернативы, способные вывести эффективность и безопасность разработки на новый уровень при минимальных компромиссах. В этой статье мы одновременно рассмотрим аудиоподсистемы современных операционных систем, внутреннее устройство наиболее известных аудиофильских проигрывателей на рынке и возможности сред разработки в области работы в реальном времени.

Программные критерии для побитово точного, полностью основанного на памяти воспроизведения

Независимо от того, с какой операционной системой мы работаем, программная архитектура должна соответствовать трём жёстким условиям, чтобы исключить скачки потребления тока аппаратным обеспечением и любые ошибки синхронизации, которые могут ими вызываться:

  • Монолитное выделение оперативной памяти и блокировка памяти: весь аудиоматериал должен быть загружен в системную память до начала воспроизведения. Затем блок памяти должен быть заблокирован на уровне ядра операционной системы (например, с помощью mlock() или mlockall() в POSIX-системах либо VirtualLock в Windows). Это не позволяет менеджеру виртуальной памяти ОС переместить блок в медленный файл подкачки (swap), что во время воспроизведения вызвало бы критические задержки — так называемые page faults.
  • Эксклюзивный доступ к аппаратному обеспечению: внутренние программные микшеры системы должны быть полностью обойдены, обеспечивая прямой доступ к внутренним буферам аппаратного интерфейса (ЦАП).
  • Неблокирующий поток реального времени: непосредственно в потоке, отвечающем за рендеринг аудио (audio thread), во время воспроизведения категорически запрещены любые операции ввода-вывода, сетевые вызовы или динамическое выделение памяти.

Различия платформ: macOS vs. Linux vs. Windows

Чтобы понять, где и каким образом можно выполнить эти требования, стоит сравнить три основные платформы на системном уровне.

Вывод однозначен: Linux обеспечивает наиболее прямой и детерминированный контроль (особенно на целевых устройствах без графического интерфейса), тогда как подсистема Core Audio в macOS также предоставляет стабильную и унифицированную высокоуровневую абстракцию аппаратного обеспечения.

Дилемма integer mode против 32-bit float — macOS, Linux и Windows

В разработке аудиофильского программного обеспечения регулярно возникает вопрос о различии между передачей в целочисленном формате и 32-битном floating point. Долгое время существовало заблуждение, будто если программное обеспечение или система преобразует сигнал в формат с плавающей точкой, побитовая точность теряется. Реальность радикально различается в зависимости от операционной системы и архитектуры драйвера.

macOS: прозрачная работа с плавающей точкой. Core Audio изначально работает с архитектурой 32-bit floating point. При активации Hog Mode программное обеспечение получает эксклюзивное владение физическим устройством и отключает системный микшер. В таком режиме 32-битный floating-point выход на практике остаётся побитово точным при условии отсутствия программной регулировки громкости или ресэмплинга в цепочке.

Стандарт 32-битного формата с плавающей точкой (IEEE 754) имеет 24-битную мантиссу. Это означает, что любые 16- или 24-битные музыкальные данные с фиксированной точкой (integer) могут быть без потерь, бит в бит, отображены в область floating point, а затем — после поступления в аппаратный драйвер ЦАПа — преобразованы обратно в исходный целочисленный формат без искажений. Хотя нативный integer mode (полностью обходящий преобразование в float) теоретически является наиболее чистым путём, передача в float через Hog Mode также гарантированно даёт побитово точный результат. Это верно вплоть до момента, когда мы захотим воспроизвести аудиофайлы, использующие 32-битную целочисленную арифметику, — но такие файлы коммерчески практически недоступны (за исключением нескольких специализированных лейблов).

Linux: аппаратная реальность integer. В мире Linux, если мы стремимся к настоящей побитовой точности и обходим подсистемы PipeWire или PulseAudio через прямой аппаратный интерфейс ALSA (hw:X,Y), вопрос решается сразу. Пользовательские и профессиональные ЦАП-интерфейсы просто не поддерживают декодирование floating point на аппаратном уровне. Аппаратное обеспечение принимает только нативные форматы с фиксированной точкой (integer), такие как S16_LE, S24_3LE или S32_LE.

Хотя современная подсистема PipeWire внутренне выполняет микширование и дитеринг в 32-bit float для сохранения динамического диапазона, если, как bpplay, мы записываем непосредственно в аппаратный буфер ALSA, передача float физически невозможна: программное обеспечение должно отправлять сэмплы точно в том целочисленном формате, который ожидает ЦАП, иначе аппаратное обеспечение отклонит поток. Поэтому на прямом аппаратном пути сигнала альтернативы float нет.

Windows: ASIO integer против гибридного подхода WASAPI. Микшер Windows Audio Engine также использует формат 32-bit float в shared mode. Однако в эксклюзивных режимах архитектура разделяется на два варианта:

  • ASIO (Audio Stream Input/Output): эта профессиональная, зависящая от производителя модель драйвера строго основана на integer (ASIOSTInt16LSB, ASIOSTInt32LSB и т. д.). Она напрямую отображает блоки памяти программного обеспечения, по принципу копирования, в целочисленные буферы аппаратного обеспечения, поэтому вопрос преобразования в float вообще не возникает.
  • WASAPI Exclusive: полностью обходя системный микшер, этот режим устанавливает прямое соединение с аудиодрайвером. WASAPI Exclusive может договариваться с драйвером: если драйвер ЦАПа заявляет поддержку 32-bit float в качестве программного контейнера, передача также может осуществляться через float-путь, который, как и в macOS, остаётся побитово точным благодаря стандарту IEEE 754. Тем не менее и здесь наиболее чистый подход — запросить и использовать непосредственно нативную целочисленную разрядность аппаратного обеспечения (например, 24-bit или 32-bit integer).

Великая тайна DSD: Native DSD против DoP (DSD over PCM)

В то время как PCM (Pulse Code Modulation) описывает звуковую волну с помощью частоты дискретизации и битовой глубины, DSD (Direct Stream Digital) использует совершенно другой принцип — 1-битную импульсно-плотностную модуляцию (PDM) с гигантской частотой дискретизации (например, 2,8224 МГц для DSD64). Физическая передача этого 1-битного потока от источника к ЦАПу создаёт серьёзную границу между операционными системами.

В чём разница между Native DSD и DoP?

  • Native DSD: программное обеспечение проигрывателя и драйвер операционной системы передают 1-битный поток данных, содержащийся в музыкальном файле, как необработанный битовый поток, без какого-либо преобразования или структурной модификации, через USB-интерфейс непосредственно на выделенный DSD-декодирующий чип ЦАПа.
  • DoP (DSD over PCM): здесь важно уточнить: DoP не преобразует DSD в PCM-аудиосигнал. DoP — это всего лишь хитроумный транспортный контейнер (способ упаковки), созданный потому, что программная архитектура некоторых операционных систем не способна переносить необработанные 1-битные потоки. В качестве носителя используется 24-битный PCM-кадр: в верхние 8 бит помещается специальный постоянно чередующийся идентификатор (так называемый marker: 0x05 и 0xFA, чередующиеся от кадра к кадру), а необработанные данные DSD упаковываются в нижние 16 бит. Когда этот кадр поступает на ЦАП, совместимый с DoP, аппаратное обеспечение распознаёт маркер в верхних 8 битах, сразу понимает, что это не PCM-аудио, удаляет «упаковку» и передаёт оставшийся чистый 16-битный поток DSD в DSD-ядро.

Цена DoP — избыточность пропускной способности. В 24-битном кадре 8 бит занимают маркер, поэтому одна треть (33%) DoP-кадра является служебными данными; относительно нативной передачи, где маркера нет, это означает ровно на 50% больше пропускной способности (24/16 = 1,5×). Следовательно, для передачи потока DSD64 компьютер должен имитировать скорость передачи данных 24-битного PCM-потока с частотой 176,4 кГц.

Работа с DSD в разных операционных системах

macOS: подсистема Core Audio компании Apple жёстко ориентирована на PCM, и её API не поддерживает напрямую передачу нативных 1-битных данных. Поэтому при прямом USB-соединении нативный DSD в среде Mac невозможен; единственный побитово точный путь — DoP. Поскольку DoP расходует пропускную способность, а заводской предел частоты дискретизации Core Audio в macOS принято считать равным 768 кГц, разрешение, передаваемое через DoP, теоретически должно ограничиваться DSD256 (для которого требуется PCM-несущая с частотой 705,6 кГц).

Mac и реальность сверхвысоких частот дискретизации: хотя широко распространено мнение, что Mac не способен напрямую отправлять более высокие разрешения (DSD512, DSD1024) по USB-кабелю, специализированное программное обеспечение вроде HQPlayer способно преодолеть этот барьер. Например, если посмотреть на USB-интерфейсный чип XMOS внутри знаменитого ЦАПа Holo Spring II, который официально не поддерживает нативную 1-битную передачу выше DSD256 из-за отсутствия соответствующего драйвера Mac, система всё же способна принимать DSD1024 напрямую по USB — без какого-либо сетевого протокола (NAA) — от HQPlayer, работающего на Mac.

Holo Audio и скрытая магия XMOS

Чипы XMOS (такие как XU208 или специализированные варианты) не являются фиксированными, жёстко запрограммированными схемами. На самом деле XMOS — это программируемый программным обеспечением многоядерный RISC-микроконтроллер реального времени. Сам по себе чип XMOS представляет собой чистый лист: его возможности и дескрипторы конечных точек определяются специализированной прошивкой, которую производитель записывает в него.

Джефф Чжу (конструктор Holo Audio) во время разработки ЦАПа Spring II написал чрезвычайно оптимизированную специализированную прошивку для чипа XMOS — способную прозрачно взаимодействовать со встроенным заводским драйвером USB Audio Class 2 (UAC2) в macOS. Когда мы подключаем Spring II к Mac (к слову, получившему разъём формата USB 3.0 Type-B, хотя само соединение работает на скорости USB 2.0 High Speed), чип XMOS сообщает подсистеме Core Audio, что является стандартным устройством UAC2, способным работать также с ультравысокими частотами PCM.

Цифры и физика не лгут:

Тактовая частота DSD1024 чудовищно высока: 45,1584 МГц.

Поскольку Mac не может напрямую передавать 1-битный поток, HQPlayer пришлось упаковать его в контейнер DoP. DoP добавляет 8-битный идентификационный маркер к каждые 16 бит данных DSD, создавая 24-битный PCM-кадр.

Чтобы передать это через 16-битную полезную нагрузку кадра, Mac должен имитировать PCM-несущую с частотой 2,8224 МГц (2822,4 кГц):

45,1584 МГц / 16 бит = 2,8224 МГц

Достаточна ли пропускная способность протокола USB 2.0 для этого?

Да. Физическая потребность в пропускной способности для стереопотока PCM 2,8224 МГц, 24 бит составляет около 135,5 Мбит/с. Поскольку теоретический максимум USB 2.0 High Speed составляет 480 Мбит/с (в реальности примерно 280–320 Мбит/с стабильной пропускной способности), физический объём потока DSD1024 DoP легко передаётся по USB-кабелю.

Поскольку HAL macOS (Hardware Abstraction Layer) и встроенный заводской драйвер UAC2 не содержат жёстко заданного программного ограничения сверху (которое говорило бы «остановиться на 768 кГц»), если подключённая прошивка XMOS сообщает о способности принимать пакет 2,8224 МГц, Mac без каких-либо возражений открывает этот гигантский канал передачи данных. Третьим ключевым участником этой истории является разработчик HQPlayer Юсси Лаако, который не использовал стандартные высокоуровневые оконные API macOS, а отправлял кадры — нарезанные на 16-битные блоки и снабжённые маркером DoP — непосредственно в оболочку устройства HAL. Чип XMOS благодаря точно согласованным внутренним часам затем передавал эти данные без задержки непосредственно во внутреннюю FPGA-систему R-2R ЦАПа Spring II и специализированный DSD-модуль.

Linux: ALSA поддерживает нативные DSD-потоки на уровне ядра (например, через регистры формата SNDRV_PCM_FORMAT_DSD_U32_LE). Если используемый USB-ЦАП и его Linux-драйвер (модуль snd-usb-audio) содержат соответствующее сопоставление нативного DSD для конкретного устройства, Linux отправляет DSD нативно, без каких-либо потерь пропускной способности, вплоть до гигантских уровней DSD512 или DSD1024. Если драйвер не знает нативное сопоставление ЦАПа, ALSA может перейти в режим DoP.

Windows: WASAPI Exclusive изначально не работает с 1-битным потоком, поэтому здесь пределом также является DoP. Однако если установить официальный ASIO-драйвер производителя ЦАПа в Windows, ASIO полностью обходит аудиоподсистемы Windows и позволяет программному обеспечению проигрывателя размещать необработанные 1-битные данные непосредственно в нативных целочисленных/DSD-буферах аппаратного обеспечения. Таким образом, в Windows с ASIO возможно полноценное нативное воспроизведение DSD512/1024.

Сравнение известных аудиофильских платформ

Чтобы поставить аскетичный дизайн bpplay в контекст, стоит рассмотреть, как наиболее популярные коммерческие и high-end-программы на рынке работают с управлением памятью, чистотой сигнала и транспортом DSD.

Методологическое замечание: для этого сравнения мы в первую очередь рассматривали известные премиальные аудиофильские платформы, способные работать на разных операционных системах. Это важно, поскольку долгосрочная цель bpplay также заключается в мультиплатформенной работе, а архитектурные компромиссы крупных разработчиков программного обеспечения дают прямые уроки для проектирования собственной архитектуры.

Анализ архитектуры

  • Roon: хотя Roon использует собственный протокол RAAT (Roon Advanced Audio Transport) для побитово точной передачи, его философия проектирования противоположна bpplay. Roon Core постоянно индексирует данные, обрабатывает сетевые пакеты и рендерит изображения. Музыка поступает на конечное устройство в потоковом режиме, порциями, поэтому сетевой интерфейс и I/O постоянно активны. Блокировки памяти нет, а операции с базой данных могут в любой момент вызывать скачки нагрузки на CPU.
  • Audirvana: с инженерной точки зрения она ближе к bpplay, если активировать RAM buffering. В этом случае программа пытается загрузить файл в память, уменьшая активность диска. Однако Audirvana по-прежнему работает как стандартное пользовательское приложение: GUI и сетевые потоки фонового удалённого приложения (Remote App) постоянно заставляют CPU выполнять переключения контекста. При отсутствии kernel-level mlock система может обратиться к swap в критический момент.
  • JRiver: функция «Play from memory» загружает исходные PCM-данные в RAM, фактически снижая нагрузку на накопитель во время воспроизведения. Одновременно JRiver представляет собой огромный монолитный программный пакет (медиацентр, видеоплеер, сетевой сервер). Из-за фоновых плагинов и сложного графического движка пульсации энергопотребления и уровни электромагнитных помех могут оставаться высокими.
  • HQPlayer: уникальное, радикальное решение. Он может воспроизводить весь файл из памяти, но в отличие от bpplay не стремится к программному аскетизму — его цель заключается в максимальной вычислительной мощности. HQPlayer способен в реальном времени преобразовывать всё в DSD или PCM колоссального разрешения, используя сложные полифазные фильтры. Эта гигантская нагрузка на CPU создаёт огромные скачки потребления тока и тепловыделения, из-за чего компьютер может становиться электромагнитно «шумным», даже когда дисковые операции упали до нуля.

Языки программирования и среды выполнения реального времени

Текущий подход bpplay на базе C гарантирует минимально возможный программный след. Однако ручное управление памятью (malloc, free, арифметика указателей) с точки зрения современных требований к разработке и безопасности несёт определённые риски. Современная экосистема языков программирования уже предлагает целый ряд альтернатив.

C — классический минимализм.

  • Плюсы: нулевые накладные расходы, абсолютная прозрачность, отсутствие скрытой абстракции.
  • Минусы: более низкий уровень безопасности управления памятью, сложный перенос на другие платформы, а поддерживаемость кода в сложных системах ухудшается экспоненциально.

Rust — современный эталон, поддерживающий также воспроизведение в реальном времени.

  • Плюсы: модель владения Rust гарантирует безопасность памяти на этапе компиляции без использования сборщика мусора (GC) во время выполнения. Поскольку GC отсутствует, нет непредсказуемых пауз во время выполнения, что делает Rust идеально подходящим для создания критически важных аудиодвижков реального времени. Поддержка нескольких платформ также превосходна.
  • Минусы: крутая кривая обучения и сложный синтаксис при работе с указателями на уровне, близком к аппаратному обеспечению.

Swift & SwiftUI — чемпион пользовательского опыта.

  • Плюсы: красивые нативные интерфейсы macOS можно создавать с огромной скоростью. Современные инструменты разработки с поддержкой ИИ чрезвычайно эффективно генерируют код SwiftUI.
  • Минусы: Swift использует Automatic Reference Counting (ARC). ARC может вызывать скрытые атомарные операции и освобождение памяти в фоновом режиме, что крайне нежелательно в строгом потоке рендеринга аудио реального времени, поскольку способно вызывать непредсказуемые задержки длительностью в микросекунды (jitter).

Экосистема LLM на практике — с чего начать DIY-разработку?

Если мы не являемся профессиональными разработчиками программного обеспечения (я тоже не являюсь), но хотим создать приложение-проигрыватель с побитовой точностью, адаптированное под конкретные потребности, как bpplay, современные ИИ-инструменты открывают перед нами совершенно новые горизонты. Нет необходимости с нуля изучать API Core Audio HAL или конфигурации ALSA; ключ заключается в выборе правильной вспомогательной среды:

  • Google AI Studio (модели Gemini): отличная отправная точка для концептуального проектирования и понимания сложных заводских аудиоархитектур. Огромное контекстное окно Gemini позволяет целиком вставлять документацию по аудиоподсистемам операционной системы или даже полные исходные файлы существующих open-source C-проектов. Модель способна за один шаг охватить всю структуру и сформировать логический план — даже для движка на Rust.
  • Cursor / Windsurf (AI-native IDE): на сегодняшний день наиболее эффективные среды для непосредственного написания кода. Эти редакторы, изначально построенные вокруг LLM (Claude Sonnet/Opus 4.x, GPT, GLM 5.2), способны самостоятельно ориентироваться в структуре папок проекта. Если дать команду: «Реализуй блокировку памяти POSIX mlockall перед запуском аудиопотока Rust», IDE не только напишет код, но и настроит необходимые зависимости в фоновом режиме.
  • Claude Code (Agentic CLI): радикально новый подход к агентному программированию. Работая непосредственно из терминала, он способен тестировать программу, интерпретировать ошибки компиляции (например, сообщения строгого компилятора Rust при работе с потоками) и самостоятельно изменять исходный код до тех пор, пока тот не скомпилируется без ошибок.

При реализации bpplay я сознательно не использовал ни одно из многочисленных агентных решений. Именно намеренно — чтобы видеть и отслеживать сообщения во время написания и отладки кода, пока LLM проговаривала свои действия и задачи. Такой подход был значительно медленнее и также стоил дороже, но дал мне огромное количество опыта и новой информации не только о программировании, но и о принципах работы различных архитектур. Я хотел учиться; я никуда не спешил с разработкой.

DIY-дистрибуция — как передать программу друзьям?

Когда программа готова и мы хотим поделиться ею с друзьями, мы сразу сталкиваемся с защитными механизмами современных операционных систем. В macOS незарегистрированное приложение мгновенно блокируется Gatekeeper, а официальная подпись (Code Signing и Notarization) требует лицензии Apple Developer стоимостью $99 в год. Для некоммерческого дружеского DIY-проекта это ненужная финансовая нагрузка.

LLM незаменимы и на этом этапе, поскольку они могут написать скрипты для самостоятельной дистрибуции:

  • Автоматическая сборка: можно попросить Cursor / Claude Opus написать автоматизированный Bash-скрипт (или Makefile), который одной командой компилирует в фоновом режиме код на C или Rust, связывает его с интерфейсом SwiftUI и упаковывает всё в стандартный пакет macOS .app или аккуратный образ диска .dmg.
  • Генерация ad-hoc подписей: ИИ может сгенерировать локальную команду (например, codesign --force --deep --sign - bpplay.app), которая создаёт для приложения локальную цифровую подпись (ad-hoc). Хотя она не удовлетворяет требованиям официальных серверов Apple, это позволяет приложению работать стабильнее.
  • Инструкция по установке с помощью ИИ: наконец, можно попросить LLM написать предельно простую инструкцию для друзей, не требующую никаких знаний программирования. Она объясняет, как обойти системную защиту на Mac через правый клик → Open, либо автоматически генерирует однострочную команду Terminal (например, xattr -cr /Applications/bpplay.app), которая за секунду удаляет флаг карантина безопасности, устанавливаемый на файлы, загруженные из интернета.

Таким образом, распространение программного обеспечения и обмен им внутри круга друзей становится полностью автоматизированным и бесплатным, сохраняя чистый DIY-дух проекта.

Победившая архитектура — гибридный подход (Rust + SwiftUI)

Если мы хотим создать современный аудиоплеер, пригодный для долгосрочного использования, безопасный и потенциально мультиплатформенный, разумным решением будет гибридная архитектура. Эта модель не означает отказ от первоначальной философии программного минимализма; она лишь разделяет зоны ответственности:

[ SwiftUI User Interface ]      ← (UI events and state)
            │
[ C-FFI / UniFFI ]              ← (Zero-overhead bridge)
            │
[ Rust Core / Audio Engine ]    ← (RAM loading, mlockall, Hog Mode)

Ядро (Audio Engine) — Rust: вся логика процесса — чтение файлов, буферизация в RAM, блокировка памяти и низкоуровневый доступ к Core Audio HAL (или ALSA в Linux) — выполняется в движке Rust. Это гарантирует детерминированный, побитово точный и безопасный путь сигнала.

Интерфейс (UI) — SwiftUI: обработка графического интерфейса и пользовательских взаимодействий полностью отделена от движка. SwiftUI взаимодействует с бэкендом Rust (и с macOS) через тонкий слой абстракции (C-FFI или UniFFI).

Такая организация гарантирует, что отрисовка графического интерфейса, выполнение сгенерированного ИИ кода интерфейса или события оконного менеджера физически и логически независимы от аудиопотока реального времени, защищая цифровой поток сигнала от помех, порождённых программным обеспечением.

В итоге

Современный инженерный подход к аудиофильскому DIY и разработке программного обеспечения с помощью LLM требует защищать код от ошибок памяти и управлять системной памятью с микросекундной (или более высокой) точностью, предотвращая page faults, в то время как путь сигнала к аппаратному обеспечению должен оставаться строго прозрачным и детерминированным.

История разработки bpplay показывает, как минимализм, начавшийся с чистого C, может быть перенесён в современную гибридную экосистему. В противовес перенасыщенности функциями и возможному фоновому компьютерному шуму крупных мультиплатформенных коммерческих проигрывателей (Roon, Audirvana, JRiver) сочетание безопасности Rust и нативной изоляции SwiftUI представляет собой тот бескомпромиссный путь, который способен сохранить акустическую и электрическую чистоту побитово точного воспроизведения из RAM — и сделать его доступным практически каждому благодаря современным ИИ-средам вроде Cursor или Google AI Studio, открывая заново возможность профессиональной, индивидуализированной и легко распространяемой внутри собственного круга аудиопрограмм.

И сколько это стоило?

Оценка стоимости разработки на базе LLM

Хотя традиционно разработка программного обеспечения была одним из самых дорогих инженерных процессов, экосистема LLM радикально снизила порог входа. Ежемесячные расходы на профессиональные инструменты, использовавшиеся во время разработки bpplay (и ещё не представленного конвертера аудиофайлов HQConv), распределились следующим образом:

Итого за два месяца работы получилось около $350 — примерно 120 000 форинтов. Разработка программного обеспечения HQConv может занять ещё несколько недель.

Оно того стоило?

Определённо.

Это чрезвычайно познавательный процесс, если вам интересно не только, звучит ли X лучше Y, но и понять, что именно происходит внутри компьютера во время воспроизведения.

Я решил сделать программу доступной в виде открытого исходного кода, бесплатно для всех желающих.

ОРИГИНАЛ — https://mediaengineering.medium.com/bit-perfect-memory-based-audio-file-playback-in-2026-7199a2921b53

2 лайка

Кажется, очень небезынтересные наблюдение. И можно проверить Льва Толстого лично — пробуйте софт Release v0.9.7 · ferenckoscso/bpplay · GitHub

Я уже вошел в режим долгой прослушки.

3 лайка

Неплохой курсовик или диплом…

В свое время усвоил - диплом/кандидатская должны нести в себе “научную новизну”, которая в свою очередь, это либо известный метод, примененный в нестандартной отрасли, либо новый метод, примененный в традиционной области…

ИИ сейчас такие дипломы фигачит! Только держись.
Скоро перестанем понимать.

Без своих аудиофильских плееров теперь даже слушать вас никто не будет.

1 лайк

Вот это сформулировать, а дальше ИИ))

Формально всё это прекрасно было описано (поэтому ИИ и втянул всё знание в эту разработку) еще в начале 2000х.

Ранние версии Audirvana имели HOG-mode и разумеется кэширование в память, и не только Audirvana. Затем в какой-то момент плееры начали лишаться HOG, потому это Эппл… Потому что “сломать” систему на уровне обхода части Core Audio становилось все сложнее — с каждой новой MacOS на разных форумах типа Audiophile в ветках плееров были простыни стенаний что “апятьвсёсламалась”.

Если решение научилось снова включать HOG, для современной системы, это прекрасно. Единственное совершенно наивное, что всё это вместе с playback из RAM как-то спасет процессор от выполнения миллиардов операций :))) Не спасёт. Он будет всегда чем-то занят.

И это одна из причин переходить на идеологию сетевой развязки с endpoint. И никогда больше не возвращаться к USB/MacOS или иному полноценному очень занятому компу.

6 лайков

Я пока просто слушаю.

Это гуд. Но берегись MacOS 27 :)))))

Я не купил пока новый мак, а на старом у меня 15.5

При таком подходе не удивлюсь услышать зависимость звука от того,какие сайты в браузере открыты и в каком количестве…

1 лайк

Главное лишь звука, а не самой музыки :slight_smile:

зато нет берегов :index_pointing_up:

А ещё “за свои деньги”)))

Ради результата не жаль ни берегов, ни рек, ни лесов, ни полей)

4 лайка

@dmitre сильно ухи не ломай, там особо ничего не выслушаешь, кроме цифрита.
Всё расписанное в статье - технически верно, но это только подготовка, а главных шагов он так и не сделал. Это как плясать вокруг с тряпочкой, и протирать фары, но не залить бенза, и так и не поехать никуда.
Изменений не будет ровно никаких. Так, на уровне “показалось”.
Описанное в статье - лишь пару %-ов от нужных условий.
Откуда?
Потому что я сам сделал похожий макет, но довёл до предела, что можно вообще выжать из коммерческого железа из магазина: играет биты прямо в мозг usb чип, мимо вообще всего софтового барахла, идущего в комплекте с ПК.
Целью макета было выяснение предельного потенциала цифры.
OS? Alsa-шмалса, чё? Драйверы какие-то? Зачем оно, нету этого.
Bare-metal, сразу после POST, CPU в сыром виде, захват чипа за яйки, и прямые записи туда, real time до последнго цикла, а не псевдо-линуксы какие-то, с кучей барахла ихнего.
Краткий вердикт - звук преобразился весьма сильно, цифрит покинул систему: теперь понятно, что именно это было. То, что вызывает и утомляемость, и скучность, и плоско-мертвость звука
Пушистый и мягкий, очень аналоговый, интересный, без каши, с кучей вдруг-откуда-ни-возьмись неслышимых ранее окрасок, и с рисующимися образами. Всё это вдобавок происходило на модифицированном биосе, с некоторыми вырезанными оттуда паразитами, и другими исправленными параметрами (внесло 5-10% вклада)

1 лайк

А поконкретнее — железо и софт

Видимо, еще более супер-секретный плеер. )

1 лайк