Хочу представить вам плеер и синтезатор для системного спикера, написанный на Rust.
Поддерживает воспроизведение MP3, WAV, FLAC, трекерной музыки - и вообще всего, что может быть сконвертировано библиотекой ffmpeg в WAV-PCM. Для улучшения качества звука поддерживает обработку звука с помощью фильтров: например, фильтры высоких и низких частот, а также извлечение из сигнала самых значимых гармоник с помощью преобразования Фурье.
Также поддерживается многоканальное воспроизведение собственной музыки, написанной в текстовом виде в специальном формате.
Ссылка на GitHub: https://github.com/HoShiMin/BeeSynth
Как это работает: доступ к спикеру осуществляется с помощью так называемых портов ввода-вывода - специального интерфейса в процессоре, выделенного для работы с чипсетом и периферийными устройствами. Этот интерфейс сводится к двум машинным инструкциям: in и out, которые обычно доступны только в режиме ядра (Ring0) - в привилегированном режиме, к которому у пользовательских программ доступа нет. А значит, нам нужен драйвер, который или откроет для нашей программы доступ к портам в пользовательский режим (юзермод, он же Ring3), или будет служить «мостиком» между Ring3 и Ring0, позволяя юзермоду отправлять запросы в ядро и работать с портами оттуда.
В проекте поддерживаются оба способа при использовании драйвера InpOut:
1. Отправляем ему запросы на работу с портами.
2. С его помощью патчим уровень привилегий, с которым наш поток может работать с портами, с Ring0 на Ring3 - таким образом, поток получает возможность работать с портами из юзермода напрямую - без необходимости запрашивать драйвер.
Научились работать со спикером: теперь необходимо понять, что играть. Самый удобный формат для воспроизведения - WAV, т.к. представляет собой массив сэмплов фиксированной длительности. Каждый сэмпл - амплитуда сигнала в момент времени, соответствующий номеру сэмпла в массиве. Поэтому все музыкальные форматы мы предварительно конвертируем в WAV с помощью библиотеки ffmpeg.
Спикер имеет только два состояния: напряжение приложено (мембрана поднята вверх) и напряжение снято (мембрана опущена). Таким образом, мы можем воспроизводить звук с глубиной дискретизации всего в 1 бит, в отличие от типовых WAV-файлов с глубиной дискретизации в 16 бит, поэтому нужен такой алгоритм ресэмплинга, который позволит добиться приемлемого качества звука. И здесь возможны варианты: можно использовать широтно-импульсную модуляцию (PWM), чтобы научить мембрану занимать промежуточные положения между 0 и 1, настолько быстро подавая и снимая напряжение, чтобы мембрана не успевала доходить до граничных положений, но сделать это очень сложно из-за различий в физических свойствах разных спикеров в разных компьютерах. Поэтому в проекте реализован другой подход: положение переключается на каждый амплитудный пик или на каждую амплитудную впадину в сигнале, что даёт уверенное качество звука и хорошую громкость.
Остался последний штрих: можно улучшить качество звука, отрезав самые низы, которые спикер не воспроизведёт, и верхи, которые приводят к шуму. Сделать это можно, используя фильтры низких и высоких частот.
В итоге мы можем воспроизводить любой звук в относительно хорошем качестве. Технические детали и более подробное описание можно найти в README на страничке проекта на гитхабе.