Multiplatform audio player in C ++ and OpenAL

    It so happened that most of my life I used Windows and was used to playing audio files using Winamp. It integrates very conveniently with the command line - launched any audio file and you're done. After switching to Linux and OS X (mostly for work, but I use Macs at home with Windows), there was an urgent need to find an alternative. I tried a large number of craft players. Their main problem is the lack of normal integration with the command line and often support only one of the platforms: either Linux or OS X. The situation is better with console players: mpg123 and mpg321 do exactly what they need almost perfectly. Just one big “but” appeared. They do not know how to play .ogg and tracker music ( .it, .mod , .xm , .s3m and others), which also accumulated enough and did not want to part with it at all.

    The fact is that during my programming career I had to write a couple of multi-platform audio systems for game engines: for the Linderdaum Engine and for Blippar, and I still have one small one for this book . Why not apply the experience gained to write the player yourself? The requirements for the player are as follows:

    • run on Windows, Linux, and OS X;
    • play MP3, Vorbis, WAV and the maximum reasonable number of modular audio formats;
    • convenient integration with the command line;
    • use a maximum of third-party libraries so as not to turn the project into a long-term construction;

    In fact, the first version that replaced mpg123 was written in 3 days. And the version that could play the entire music collection of ~ 12 thousand files took exactly a month. It was decided to use OpenAL (OpenAL Soft on Linux and official support on OS X) as a back-end for audio output. For decoding audio formats, libogg, libvorbis, minimp3, libmodplug and id3v2lib are used. Writing a player is “slightly” different from writing an audio system for a game (except that you need only one single sound source without any 3D positioning and effects). In fact, the main difference is that free audio formats are not at all the same as sound assets for a game project. There may be broken files, strange tags, non-standard additives at the end of the file, unusual .mp3in which the sampling rate changes from frame to frame, there may be .wav containers with a .mp3 stream inside .

    The player was conceived as a modular rapid expansion option for playing other formats and for using different back-ends. At the heart of everything is the sound source interface, the abstract class iAudioSource , and the interface the iAudioSubsystem audio system .

    class iAudioSource
    {
    public:
    	iAudioSource()
    	: m_Looping( false )
    	{}
    	virtual void BindDataProvider( const std::shared_ptr& 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 CreateAudioSource() = 0;
    	virtual void SetListenerGain( float Gain ) = 0;
    };
    

    The only implementations of these interfaces use OpenAL to output sound, since support for this API on all three platforms is quite decent.

    To decode various formats in PCM, we create the iWaveDataProvider interface .

    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; }
    };
    

    And for convenience, here is such a factory:

    std::shared_ptr CreateWaveDataProvider( const char* FileName, const std::shared_ptr& Data );
    

    Various implementations of iWaveDataProvider use third-party libraries to decode audio formats. It turned out to be very compact and suitable for further expansion of functionality.

    The source project is available here: github.com/corporateshark/PortAMP

    Maybe I will add FLAC support one day, but so far there is absolutely no incentive - there is no such format in the home collection of files.

    Support for FLAC is now also there.

    Also popular now: