Мультиплатформовий аудіо плеєр на 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, Aac, 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;
віртуальний bool IsPlaying() const = 0;
віртуальний 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;

віртуальний bool IsStreaming() const { return false; }
віртуальний 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, але поки зовсім немає стимулу — у домашній колекції файлів такого формату немає.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.