Skip to content

Source: Mp3 Soundfile Reader minimp3

Lukas Dürrenberger edited this page May 8, 2021 · 2 revisions

Using minimp3 and based on the example implementation by lieff as well.

Minimalistic, single-header library for decoding MP3. minimp3 is designed to be small, fast (with SSE and NEON support), and accurate (ISO conformant). Licensed under CC0

Note: There might be some issue if your MP3 contains APEv2 tags

Download the minimp3_ex.h and minimp3.h and copy them next to follow source and/or adjust the include path.

SoundFileReaderMp3.hpp

#pragma once

#include "minimp3_ex.h"
#include <SFML/Audio/SoundFileReader.hpp>

class SoundFileReaderMp3 final : public sf::SoundFileReader
{
public:
    static bool check(sf::InputStream& stream);

public:
    SoundFileReaderMp3();
    ~SoundFileReaderMp3() override;

    SoundFileReaderMp3(const SoundFileReaderMp3& other) = delete;
    SoundFileReaderMp3(SoundFileReaderMp3&& other) noexcept = delete;
    SoundFileReaderMp3& operator=(const SoundFileReaderMp3& other) = delete;
    SoundFileReaderMp3& operator=(SoundFileReaderMp3&& other) noexcept = delete;

    bool open(sf::InputStream& stream, Info& info) override;
    void seek(sf::Uint64 sampleOffset) override;
    sf::Uint64 read(sf::Int16* samples, sf::Uint64 maxCount) override;

private:
    void close();

    mp3dec_io_t m_io;
    mp3dec_ex_t m_decoder;
    sf::Uint64  m_numSamples;
    sf::Uint64  m_position;
};

SoundFileReaderMp3.cpp

#define MINIMP3_IMPLEMENTATION
#include "SoundFileReaderMp3.hpp"
#include <SFML/System/MemoryInputStream.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>

namespace
{
size_t read_cb(void* ptr, const size_t size, void* data)
{
    auto stream = static_cast<sf::InputStream*>(data);
    return static_cast<std::size_t>(stream->read(ptr, size));
}

int seek_cb(const uint64_t offset, void* data)
{
    auto stream = static_cast<sf::InputStream*>(data);
    const auto position = stream->seek(offset);
    return position < 0 ? -1 : 0;
}
}

bool SoundFileReaderMp3::check(sf::InputStream& stream)
{
    char hdr[10];
    if (stream.read(hdr, sizeof(hdr)) < sizeof(hdr))
    {
        return false;
    }
    if (!memcmp(hdr, "ID3", 3) && !((hdr[5] & 15) || (hdr[6] & 0x80) || (hdr[7] & 0x80) || (hdr[8] & 0x80) || (hdr[9] & 0x80)))
    {
        return true;
    }
    if (hdr_valid(reinterpret_cast<const uint8_t*>(hdr)))
    {
        return true;
    }

    return false;
}

SoundFileReaderMp3::SoundFileReaderMp3() : m_io{}
                                         , m_decoder{}
                                         , m_numSamples{ 0 }
                                         , m_position{ 0 }
{
    m_io.read = read_cb;
    m_io.seek = seek_cb;
}

SoundFileReaderMp3::~SoundFileReaderMp3()
{
    close();
}

bool SoundFileReaderMp3::open(sf::InputStream& stream, Info& info)
{
    // Init IO callbacks
    m_io.read_data = m_io.seek_data = &stream;

    // Init mp3 decoder
    mp3dec_ex_open_cb(&m_decoder, &m_io, MP3D_SEEK_TO_SAMPLE);
    if (!m_decoder.samples)
    {
        return false;
    }

    // Retrieve the music attributes
    info.channelCount = m_decoder.info.channels;
    info.sampleRate = m_decoder.info.hz;
    info.sampleCount = m_decoder.samples;

    m_numSamples = info.sampleCount;
    return true;
}

void SoundFileReaderMp3::seek(sf::Uint64 sampleOffset)
{
    m_position = std::min<sf::Uint64>(sampleOffset, m_numSamples);
    mp3dec_ex_seek(&m_decoder, m_position);
}

sf::Uint64 SoundFileReaderMp3::read(sf::Int16* samples, sf::Uint64 maxCount)
{
    auto toRead = std::min<sf::Uint64>(maxCount, m_numSamples - m_position);
    toRead = mp3dec_ex_read(&m_decoder, samples, toRead);
    m_position += toRead;
    return toRead;
}

void SoundFileReaderMp3::close()
{
    mp3dec_ex_close(&m_decoder);
}

Example main.cpp

#include "SoundFileReaderMp3.hpp"
#include <SFML/Audio.hpp>
#include <ostream>

int main()
{
	sf::SoundFileFactory::registerReader<SoundFileReaderMp3>();

	auto music = sf::Music{};
	if (!music.openFromFile("example.mp3"))
	{
		sf::err() << "Failed to load MP3 file" << std::endl;
		return 1;
	}

	music.play();

	while (music.getStatus() == sf::Music::Playing)
	{
		sf::sleep(sf::seconds(1.f));
	}
}

Example CMakeLists.txt

cmake_minimum_required(VERSION 3.2)

project(MP3)

set(CMAKE_CXX_STANDARD 17)

find_package(SFML 2.5 COMPONENTS audio REQUIRED)

add_executable(MP3 main.cpp SoundFileReaderMp3.cpp)

target_link_libraries(MP3 sfml-audio)

install(TARGETS MP3 DESTINATION ".")
install(FILES "example.mp3" DESTINATION ".")
Clone this wiki locally