Так сложилось, что большую часть жизни я пользовался 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 теперь тоже есть.

Комментарии (31)


  1. DeXPeriX
    04.09.2015 15:07
    +1

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


    1. CorporateShark
      04.09.2015 15:18
      +1

      Дело привычки. Очень удобно когда на всех платформах все одинаково на уровне рефлексов. Начинал писал исключительно для себя. Когда увидел, что проект работает, то решил выложить в публичный доступ — может кому-то тоже пригодится.


  1. poxu
    04.09.2015 15:40
    +1

    Я как раз пытаюсь найти что-то в этом духе для себя под Windows. Я правда искал что-нить в духе cmus. То есть с текстовым UI.
    Как вы пользуетесь плеером? Я посмотрел код и создаётся впечатление, что он умеет только проигрывать файлы по одному. Или я чего-то не так понял?


    1. CorporateShark
      04.09.2015 16:03

      Да, пока только по одному. Плейлисты редко использую, обычно просто запускаю одну песню и слушаю с автоповтором пока не надоест. Это не значит, что пулл-риквест с реализацией плейлистов не будет принят :)


      1. poxu
        04.09.2015 16:11

        Ну, может для начала прикрутить возможность передачи списка файлов в качестве аргумента? И играть их по очереди?


        1. CorporateShark
          04.09.2015 16:30

          По сложности примерно одно и то же, но всё равно будет день нужен, чтобы это сделать. Пока в приоритете поддержка FLAC.


        1. AlexBin
          04.09.2015 17:13

          Может воспроизведение списков лучше оставить другому модулю, который будет в соответствии со списком дергать этот движок из командной строки? Так сохранится концепция модульности. Даже с фейдингом проблем не будет. Общение (паузу, перемотку) можно сделать через пайпы. Или нет?


          1. DeXPeriX
            04.09.2015 17:21

            В таком случае, по-моему, лучше делать библиотеку, чем консольное приложение. Оно ведь потом будет плохо дёргаться через пайпы как минимум в Windows…


            1. AlexBin
              05.09.2015 01:16
              +1

              Если плохо дергать, то да.


      1. poxu
        04.09.2015 17:03

        У вас треки длинные наверное?


        1. CorporateShark
          04.09.2015 17:08

          Обычные. Просто бесконечный луп ставлю.


          1. poxu
            04.09.2015 17:42
            +2

            Если я возьму свою любимую песню и поставлю играться в бесконечный луп, то через час эта песня станет моей самой нелюбимой песней. У вас такого эффекта нет? Или может быть у вас треки без слов?


            1. CorporateShark
              04.09.2015 17:43
              +2

              Неа, не замечал такого. Это же на любителя.


  1. DeXPeriX
    04.09.2015 15:40

    Спасибо за наводку на MiniMP3, приятная штука. Ещё бы что-то подобное, но под public domain (и ещё б и для OGG)… И соответственно сразу вопрос. Ваш плеер лицензирован под MIT, но использует LGPL-библиотеку. Значит, по идее не должен распространяться в бинарниках, статически слинкованных с MiniMP3?

    ModPlug чуть ли не единственный в Linux у меня воспроизвёл vibe-fs.it без щелчков. DUMB щелчки добавляет даже под вендой. XMP под вендой не тестил, но в Linux щёлкает жутко. Да даже некоторые трекеры этим не гнушаются… Так что, возможно, тоже буду юзать. Спасибо за ещё один плеер. И да, FLAC — нужен! ^_^


    1. CorporateShark
      04.09.2015 16:08

      Там 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. CorporateShark
      04.09.2015 19:40
      +1

      FLAC оказался проще, чем я думал. Пока прототип: github.com/corporateshark/PortAMP/tree/flac


      1. DeXPeriX
        05.09.2015 18:15
        +1

        Спасибо за Flac! Проверил в линуксе, работает!


        1. CorporateShark
          05.09.2015 18:17

          На здоровье! Влил в основую ветку. Теперь можно подумать и о плейлистах.


  1. monah_tuk
    05.09.2015 00:49

    С консольными плеерами ситуация получше: mpg123 и mpg321 практически идеально делают именно то, что надо. Вот только появилось одно большое «но». Они не умеют играть .ogg и трекерную музыку (.it, .mod, .xm, .s3m и прочие), которой тоже накопилось достаточно и расставаться с ней совершенно не хотелось.

    Извольте, а чем, тогда, вам не угодили консольные mplayer или ffplay из комплекта FFmpeg. Последний точно под мак есть. Ровно как и под Linux и Windows.


    1. CorporateShark
      05.09.2015 00:59

      Не играют трекерную музыку, также как и mpg123/321.


      1. monah_tuk
        06.09.2015 04:40

        а можно пример трека?


        1. CorporateShark
          06.09.2015 14:56

          1. monah_tuk
            06.09.2015 16:58

            Скачал оттуда xm, mod и it. Проигралось всё. Логи: pastebin.com/Hk58TCz2

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

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

            Единственно, что за FLAC, который в одном файле куча треков не скажу, а во всём остальном FFmpeg достаточно неплохо переваривает, что ему подсунут.


            1. CorporateShark
              06.09.2015 17:08

              Круто. Насчет ffmpeg в mpg123/321 не знал.


              1. monah_tuk
                06.09.2015 17:43

                Пардон, но что значает «ffmpeg _В_ mpg123/321»? В составе FFmpeg есть проигрыватель ffplay — я им играл :)


                1. CorporateShark
                  07.09.2015 00:39

                  Имею в виду

                  brew install mpg123
                  и потом запускать
                  mpg123


                  1. monah_tuk
                    07.09.2015 15:51

                    так при чём тут ffmpeg В mpg123/321? :-) т.е. я вашего хода мыслей не понял.


  1. maaGames
    06.09.2015 09:07

    Давно собираюсь написать «свой» плеер. Из существующих для Windows меня устроил только 1by1, но он иногда папки проскакивает, да и кое-какого функционала нет в нём. Собирался использовать библиотеку bass (её уже приходилось использовать, а производительность меня мало волнует в контексте плеера), но почитаю и про упомянутые вами библиотеки. Мне нужно лишь mp3 и ogg.

    А ваша книжка на русском будет издаваться? С удовольствием бы почитал. В ней есть что-нибудь про трекерную музыку под андроид?


    1. CorporateShark
      06.09.2015 14:55

      Да, там есть код для работы с libmodplug под Андроид. Насчет русской версии книжки ничего сказать не могу, все права на перевод книжки у издателя.


    1. Nikobraz
      07.09.2015 04:44

      Мне evilplayer.net нравился, я его лет 8 назад даже на Delphi повторял, когда учился.


  1. stalkerg
    07.09.2015 11:29
    +1

    Я везде использую консольный mplayer или fplay или mpv.
    Работают одинаково на всех платформах. :)