Мультиплатформенный аудио плеер на C++ и OpenAL

    Так сложилось, что большую часть жизни я пользовался Windows и привык воспроизводить аудио файлы с помощью Winamp. Он очень удобно интегрируется с командной строкой — запустил любой аудио файл и готово. После перехода на Linux и OS X (в основном по работе, но Mac использую и дома вместе с виндой) возникла острая необходимость найти альтернативу. Перепробывал большое количестко крафических плееров. Основная их проблема — это отсутствие нормальной интеграции с командной строкой и часто поддержка только одной из платформ: либо Linux, либо OS X. С консольными плеерами ситуация получше: mpg123 и mpg321 практически идеально делают именно то, что надо. Вот только появилось одно большое «но». Они не умеют играть .ogg и трекерную музыку (.it, .mod, .xm, .s3m и прочие), которой тоже накопилось достаточно и расставаться с ней совершенно не хотелось.

    Дело в том, что за свою программистскую карьеру мне пришлось написать пару мультиплатформных аудио систем для игровых движков: для Linderdaum Engine и для Blippar и ещу одну небольшую для вот этой книжки. Почему бы не применить накопленный опыт, чтобы самому написать проигрыватель? Требования для плеера получились вот такими:

    • работать на Windows, Linux и OS X;
    • проигрывать MP3, Vorbis, WAV и максимальное разумное кол-во модульных аудио форматов;
    • удобная интеграция с командной строкой;
    • использовать максимум сторонних библиотех, чтобы не превращать проект в долгострой;

    На самом деле первая версия, которая заменила mpg123, была написана за 3 дня. А версия, которая могла проиграть всю музыкальную коллекцию из ~12 тысяч файлов потребовала ровно месяц. В качестве бэк-энда для вывода звука было решено использовать OpenAL (OpenAL Soft на Linux и официальная поддержка на OS X). Для декодинга звуковых форматов используются libogg, libvorbis, minimp3, libmodplug и id3v2lib. Написание плеера «немного» отличается от написания аудиосистемы для игры (кроме того, что необходим только один единственный источник звука без всякого 3D позиционирования и эффектов). По сути главное отличие в том, что звуковые форматы на воле это совсем не то же самое, что звуковые ассеты для игрового проекта. Могут быть битые файлы, странные тэги, нестандартные добавки в конце файла, необычные .mp3 в которых сэмплинг рейт меняется от фрейма к фрейму, могут быть контейнеры .wav у которых внутри сидит .mp3 стрим.

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

    class iAudioSource
    {
    public:
    	iAudioSource()
    	: m_Looping( false )
    	{}
    	virtual void BindDataProvider( const std::shared_ptr<iWaveDataProvider>& Provider ) = 0;
    
    	virtual void Play() = 0;
    	virtual void Stop() = 0;
    	virtual bool IsPlaying() const = 0;
    	virtual bool IsLooping() const { return m_Looping; }
    	virtual void SetLooping( bool Looping ) { m_Looping = Looping; }
    
    private:
    	bool m_Looping;
    };
    

    class iAudioSubsystem
    {
    public:
    	virtual ~iAudioSubsystem() {};
    
    	virtual void Start() = 0;
    	virtual void Stop() = 0;
    
    	virtual std::shared_ptr<iAudioSource> CreateAudioSource() = 0;
    
    	virtual void SetListenerGain( float Gain ) = 0;
    };
    

    Единственные реализации этих интерфейсов используют OpenAL для вывода звука, поскольку поддержка это API на всех трех платформах вполне достойная.

    Чтобы декодировать различные форматы в PCM создаем интерфейс iWaveDataProvider.

    class iWaveDataProvider
    {
    public:
    	virtual ~iWaveDataProvider() {};
    
    	virtual const sWaveDataFormat& GetWaveDataFormat() const = 0;
    
    	virtual const uint8_t* GetWaveData() const = 0;
    	virtual size_t GetWaveDataSize() const = 0;
    
    	virtual bool IsStreaming() const { return false; }
    	virtual bool IsEndOfStream() const { return false; }
    	virtual void Seek( float Seconds ) {}
    	virtual size_t StreamWaveData( size_t Size ) { return 0; }
    };
    

    И для удобства вот такую фабрику:

    std::shared_ptr<iWaveDataProvider> CreateWaveDataProvider( const char* FileName, const std::shared_ptr<clBlob>& Data );
    

    Различные реализации iWaveDataProvider используют сторонние библиотеки для декодирования аудио форматов. Получилось весьма компактно и пригодно для дальнейшего расширения функциональности.

    Проект с исходниками доступен здесь: github.com/corporateshark/PortAMP

    Возможно однажды добавлю поддержку FLAC, но пока совершенно нет стимула — в домашней коллекции файлов такого формата нет.

    Поддержка FLAC теперь тоже есть.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 32

      +1
      Если не секрет, зачем Вам очень плотная интеграция с командной строкой? И зачем под все платформы обязательно один и тот же плеер? Под каждую операционку имею свой, наиболее удобный. Но везде настроены одинаковые глобальные клавиши, большую часть времени интерфейса плеера не вижу вообще, дискомфорта не ощущаю.
        +1
        Дело привычки. Очень удобно когда на всех платформах все одинаково на уровне рефлексов. Начинал писал исключительно для себя. Когда увидел, что проект работает, то решил выложить в публичный доступ — может кому-то тоже пригодится.
        +1
        Я как раз пытаюсь найти что-то в этом духе для себя под Windows. Я правда искал что-нить в духе cmus. То есть с текстовым UI.
        Как вы пользуетесь плеером? Я посмотрел код и создаётся впечатление, что он умеет только проигрывать файлы по одному. Или я чего-то не так понял?
          0
          Да, пока только по одному. Плейлисты редко использую, обычно просто запускаю одну песню и слушаю с автоповтором пока не надоест. Это не значит, что пулл-риквест с реализацией плейлистов не будет принят :)
            0
            Ну, может для начала прикрутить возможность передачи списка файлов в качестве аргумента? И играть их по очереди?
              0
              По сложности примерно одно и то же, но всё равно будет день нужен, чтобы это сделать. Пока в приоритете поддержка FLAC.
                0
                Может воспроизведение списков лучше оставить другому модулю, который будет в соответствии со списком дергать этот движок из командной строки? Так сохранится концепция модульности. Даже с фейдингом проблем не будет. Общение (паузу, перемотку) можно сделать через пайпы. Или нет?
                  0
                  В таком случае, по-моему, лучше делать библиотеку, чем консольное приложение. Оно ведь потом будет плохо дёргаться через пайпы как минимум в Windows…
                    +1
                    Если плохо дергать, то да.
                  0
                  Добавил поддержку нескольких файлов и формата APE.
                  0
                  У вас треки длинные наверное?
                    0
                    Обычные. Просто бесконечный луп ставлю.
                      +2
                      Если я возьму свою любимую песню и поставлю играться в бесконечный луп, то через час эта песня станет моей самой нелюбимой песней. У вас такого эффекта нет? Или может быть у вас треки без слов?
                        +2
                        Неа, не замечал такого. Это же на любителя.
                0
                Спасибо за наводку на MiniMP3, приятная штука. Ещё бы что-то подобное, но под public domain (и ещё б и для OGG)… И соответственно сразу вопрос. Ваш плеер лицензирован под MIT, но использует LGPL-библиотеку. Значит, по идее не должен распространяться в бинарниках, статически слинкованных с MiniMP3?

                ModPlug чуть ли не единственный в Linux у меня воспроизвёл vibe-fs.it без щелчков. DUMB щелчки добавляет даже под вендой. XMP под вендой не тестил, но в Linux щёлкает жутко. Да даже некоторые трекеры этим не гнушаются… Так что, возможно, тоже буду юзать. Спасибо за ещё один плеер. И да, FLAC — нужен! ^_^
                  0
                  Там LGPL 2.1. Читаем: «A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a „work that uses the Library“. Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.». Вот здесь комментарий: en.wikipedia.org/wiki/GNU_Lesser_General_Public_License Итого: если доступны сырцы для самостоятельной сборки (чтобы использовать новую версию LGPL библиотеки), то можно статически линковаться и распространять бинари программы, которая использует LGPL библиотеку. Если сырцы вообще не давать (не наш случай), то можно линковаться только динамически.

                  FLAC будет по мере наличия свободного времени. А еще на GitHub есть кнопка Fork ;)
                    +1
                    FLAC оказался проще, чем я думал. Пока прототип: github.com/corporateshark/PortAMP/tree/flac
                      +1
                      Спасибо за Flac! Проверил в линуксе, работает!
                        0
                        На здоровье! Влил в основую ветку. Теперь можно подумать и о плейлистах.
                    0
                    С консольными плеерами ситуация получше: mpg123 и mpg321 практически идеально делают именно то, что надо. Вот только появилось одно большое «но». Они не умеют играть .ogg и трекерную музыку (.it, .mod, .xm, .s3m и прочие), которой тоже накопилось достаточно и расставаться с ней совершенно не хотелось.

                    Извольте, а чем, тогда, вам не угодили консольные mplayer или ffplay из комплекта FFmpeg. Последний точно под мак есть. Ровно как и под Linux и Windows.
                      0
                      Не играют трекерную музыку, также как и mpg123/321.
                        0
                        а можно пример трека?
                          0
                            0
                            Скачал оттуда xm, mod и it. Проигралось всё. Логи: pastebin.com/Hk58TCz2

                            там же видно какая версия FFmpeg и с какими библиотеками собиралась. Насколько я понимаю, поддержку обеспечивает libmodplug, эта бинарная сборка (ссылка с главного сайта ffmpeg.org): evermeet.cx/ffmpeg тоже собрана с libmodplug. Эта (ссылка тоже с официального сайта): ffmpegmac.net — непонятно.

                            mplayer, действительно, отказался хавать. cvlc (консольная версия vlc) тоже переварила. mpv не проверял.

                            Единственно, что за FLAC, который в одном файле куча треков не скажу, а во всём остальном FFmpeg достаточно неплохо переваривает, что ему подсунут.
                              0
                              Круто. Насчет ffmpeg в mpg123/321 не знал.
                                0
                                Пардон, но что значает «ffmpeg _В_ mpg123/321»? В составе FFmpeg есть проигрыватель ffplay — я им играл :)
                                  0
                                  Имею в виду
                                  brew install mpg123
                                  и потом запускать
                                  mpg123
                                    0
                                    так при чём тут ffmpeg В mpg123/321? :-) т.е. я вашего хода мыслей не понял.
                      0
                      Давно собираюсь написать «свой» плеер. Из существующих для Windows меня устроил только 1by1, но он иногда папки проскакивает, да и кое-какого функционала нет в нём. Собирался использовать библиотеку bass (её уже приходилось использовать, а производительность меня мало волнует в контексте плеера), но почитаю и про упомянутые вами библиотеки. Мне нужно лишь mp3 и ogg.

                      А ваша книжка на русском будет издаваться? С удовольствием бы почитал. В ней есть что-нибудь про трекерную музыку под андроид?
                        0
                        Да, там есть код для работы с libmodplug под Андроид. Насчет русской версии книжки ничего сказать не могу, все права на перевод книжки у издателя.
                          0
                          Мне evilplayer.net нравился, я его лет 8 назад даже на Delphi повторял, когда учился.
                          +1
                          Я везде использую консольный mplayer или fplay или mpv.
                          Работают одинаково на всех платформах. :)

                          Only users with full accounts can post comments. Log in, please.