Add MP3 decoding support using 'minimp3'

Co-authored-by: Lukas Dürrenberger <eXpl0it3r@my-gate.net>
Co-authored-by: Vittorio Romeo <vittorio.romeo@outlook.com>
This commit is contained in:
lieff 2021-12-06 11:32:36 +00:00 committed by Lukas Dürrenberger
parent d0ebdc8607
commit e458f4651e
12 changed files with 3575 additions and 5 deletions

View File

@ -175,14 +175,14 @@ if(SFML_USE_SYSTEM_DEPS)
foreach(DEP_FILE ${DEP_LIBS} ${DEP_BINS} ${DEP_HEADERS}) foreach(DEP_FILE ${DEP_LIBS} ${DEP_BINS} ${DEP_HEADERS})
get_filename_component(DEP_DIR ${DEP_FILE} PATH) get_filename_component(DEP_DIR ${DEP_FILE} PATH)
if(NOT DEP_DIR MATCHES "/stb_image(/|$)") if(NOT DEP_DIR MATCHES "/(stb_image|minimp3)(/|$)")
set(CMAKE_IGNORE_PATH ${CMAKE_IGNORE_PATH} ${DEP_DIR}) list(APPEND CMAKE_IGNORE_PATH "${DEP_DIR}")
endif() endif()
get_filename_component(DEP_PARENT_DIR ${DEP_DIR} PATH) get_filename_component(DEP_PARENT_DIR ${DEP_DIR} PATH)
while(NOT DEP_PARENT_DIR STREQUAL "${CMAKE_SOURCE_DIR}/extlibs") while(NOT DEP_PARENT_DIR STREQUAL "${CMAKE_SOURCE_DIR}/extlibs")
if(NOT DEP_DIR MATCHES "/stb_image(/|$)") if(NOT DEP_DIR MATCHES "/(stb_image|minimp3)(/|$)")
set(CMAKE_IGNORE_PATH ${CMAKE_IGNORE_PATH} ${DEP_PARENT_DIR}) list(APPEND CMAKE_IGNORE_PATH "${DEP_PARENT_DIR}")
endif() endif()
get_filename_component(DEP_PARENT_DIR ${DEP_PARENT_DIR} PATH) get_filename_component(DEP_PARENT_DIR ${DEP_PARENT_DIR} PATH)

View File

@ -11,6 +11,7 @@ All assets are under public domain (CC0):
| shader/resources/background.jpg | Arcana Dea | [Public Domain Images][4] | | shader/resources/background.jpg | Arcana Dea | [Public Domain Images][4] |
| shader/resources/devices.png | Kenny.nl | [Game Icons Pack][5] | | shader/resources/devices.png | Kenny.nl | [Game Icons Pack][5] |
| sound/resources/ding.flac | Kenny.nl | [Interface Sounds Pack][6] | | sound/resources/ding.flac | Kenny.nl | [Interface Sounds Pack][6] |
| sound/resources/ding.mp3 | Kenny.nl | [Interface Sounds Pack][6] |
| win32/resources/image1.jpg | Kenny.nl | [Toon Character Pack][7] | | win32/resources/image1.jpg | Kenny.nl | [Toon Character Pack][7] |
| win32/resources/image2.jpg | Kenny.nl | [Toon Character Pack][7] | | win32/resources/image2.jpg | Kenny.nl | [Toon Character Pack][7] |
| sound/resources/killdeer.wav | US National Park Services | [Bird sounds][8] | | sound/resources/killdeer.wav | US National Park Services | [Bird sounds][8] |

View File

@ -93,6 +93,9 @@ int main()
// Play music from a flac file // Play music from a flac file
playMusic("ding.flac"); playMusic("ding.flac");
// Play music from a mp3 file
playMusic("ding.mp3");
// Wait until the user presses 'enter' key // Wait until the user presses 'enter' key
std::cout << "Press enter to exit..." << std::endl; std::cout << "Press enter to exit..." << std::endl;
std::cin.ignore(10000, '\n'); std::cin.ignore(10000, '\n');

Binary file not shown.

1855
extlibs/headers/minimp3/minimp3.h vendored Normal file

File diff suppressed because it is too large Load Diff

1397
extlibs/headers/minimp3/minimp3_ex.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -63,9 +63,14 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Open a sound file from the disk for reading /// \brief Open a sound file from the disk for reading
/// ///
/// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. /// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC, MP3.
/// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. /// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
/// ///
/// Because of minimp3_ex limitation, for MP3 files with big (>16kb) APEv2 tag,
/// it may not be properly removed, tag data will be treated as MP3 data
/// and there is a low chance of garbage decoded at the end of file.
/// See also: https://github.com/lieff/minimp3
///
/// \param filename Path of the sound file to load /// \param filename Path of the sound file to load
/// ///
/// \return True if the file was successfully opened /// \return True if the file was successfully opened

View File

@ -18,3 +18,4 @@ Permission is granted to anyone to use this software for any purpose, including
* _libogg_ is under the BSD license * _libogg_ is under the BSD license
* _libvorbis_ is under the BSD license * _libvorbis_ is under the BSD license
* _libflac_ is under the BSD license * _libflac_ is under the BSD license
* _minimp3_ is under the CC0 license

View File

@ -41,6 +41,8 @@ set(CODECS_SRC
${INCROOT}/SoundFileReader.hpp ${INCROOT}/SoundFileReader.hpp
${SRCROOT}/SoundFileReaderFlac.hpp ${SRCROOT}/SoundFileReaderFlac.hpp
${SRCROOT}/SoundFileReaderFlac.cpp ${SRCROOT}/SoundFileReaderFlac.cpp
${SRCROOT}/SoundFileReaderMp3.hpp
${SRCROOT}/SoundFileReaderMp3.cpp
${SRCROOT}/SoundFileReaderOgg.hpp ${SRCROOT}/SoundFileReaderOgg.hpp
${SRCROOT}/SoundFileReaderOgg.cpp ${SRCROOT}/SoundFileReaderOgg.cpp
${SRCROOT}/SoundFileReaderWav.hpp ${SRCROOT}/SoundFileReaderWav.hpp
@ -81,6 +83,9 @@ sfml_add_library(sfml-audio
# setup dependencies # setup dependencies
target_link_libraries(sfml-audio PRIVATE OpenAL) target_link_libraries(sfml-audio PRIVATE OpenAL)
# minimp3 sources
target_include_directories(sfml-audio SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/extlibs/headers/minimp3")
if(SFML_OS_ANDROID) if(SFML_OS_ANDROID)
target_link_libraries(sfml-audio PRIVATE android OpenSLES) target_link_libraries(sfml-audio PRIVATE android OpenSLES)
endif() endif()

View File

@ -28,6 +28,7 @@
#include <SFML/Audio/SoundFileFactory.hpp> #include <SFML/Audio/SoundFileFactory.hpp>
#include <SFML/Audio/SoundFileReaderFlac.hpp> #include <SFML/Audio/SoundFileReaderFlac.hpp>
#include <SFML/Audio/SoundFileWriterFlac.hpp> #include <SFML/Audio/SoundFileWriterFlac.hpp>
#include <SFML/Audio/SoundFileReaderMp3.hpp>
#include <SFML/Audio/SoundFileReaderOgg.hpp> #include <SFML/Audio/SoundFileReaderOgg.hpp>
#include <SFML/Audio/SoundFileWriterOgg.hpp> #include <SFML/Audio/SoundFileWriterOgg.hpp>
#include <SFML/Audio/SoundFileReaderWav.hpp> #include <SFML/Audio/SoundFileReaderWav.hpp>
@ -47,6 +48,7 @@ namespace
{ {
sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderFlac>(); sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderFlac>();
sf::SoundFileFactory::registerWriter<sf::priv::SoundFileWriterFlac>(); sf::SoundFileFactory::registerWriter<sf::priv::SoundFileWriterFlac>();
sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderMp3>();
sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderOgg>(); sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderOgg>();
sf::SoundFileFactory::registerWriter<sf::priv::SoundFileWriterOgg>(); sf::SoundFileFactory::registerWriter<sf::priv::SoundFileWriterOgg>();
sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderWav>(); sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderWav>();

View File

@ -0,0 +1,158 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#define MINIMP3_IMPLEMENTATION // Minimp3 control define, places implementation in this file.
#ifndef NOMINMAX
#define NOMINMAX // To avoid windows.h and std::min issue
#endif
#define MINIMP3_NO_STDIO // Minimp3 control define, eliminate file manipulation code which is useless here
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4242 4244 4267 4456 4706)
#endif
#include <minimp3_ex.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#undef NOMINMAX
#undef MINIMP3_NO_STDIO
#include <SFML/Audio/SoundFileReaderMp3.hpp>
#include <SFML/System/MemoryInputStream.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <cstring>
namespace
{
std::size_t readCallback(void* ptr, std::size_t size, void* data)
{
sf::InputStream* stream = static_cast<sf::InputStream*>(data);
return static_cast<std::size_t>(stream->read(ptr, static_cast<sf::Int64>(size)));
}
int seekCallback(std::uint64_t offset, void* data)
{
sf::InputStream* stream = static_cast<sf::InputStream*>(data);
sf::Int64 position = stream->seek(static_cast<sf::Int64>(offset));
return position < 0 ? -1 : 0;
}
bool hasValidId3Tag(const sf::Uint8* header)
{
return std::memcmp(header, "ID3", 3) == 0 && !((header[5] & 15) || (header[6] & 0x80) || (header[7] & 0x80) || (header[8] & 0x80) || (header[9] & 0x80));
}
}
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
bool SoundFileReaderMp3::check(InputStream& stream)
{
Uint8 header[10];
if (static_cast<std::size_t>(stream.read(header, static_cast<Int64>(sizeof(header)))) < sizeof(header))
return false;
if (hasValidId3Tag(header))
return true;
if (hdr_valid(header))
return true;
return false;
}
////////////////////////////////////////////////////////////
SoundFileReaderMp3::SoundFileReaderMp3() :
m_numSamples(0),
m_position(0)
{
std::memset(&m_io, 0, sizeof(m_io));
std::memset(&m_decoder, 0, sizeof(m_decoder));
m_io.read = readCallback;
m_io.seek = seekCallback;
}
////////////////////////////////////////////////////////////
SoundFileReaderMp3::~SoundFileReaderMp3()
{
mp3dec_ex_close(&m_decoder);
}
////////////////////////////////////////////////////////////
bool SoundFileReaderMp3::open(InputStream& stream, Info& info)
{
// Init IO callbacks
m_io.read_data = &stream;
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 = static_cast<unsigned int>(m_decoder.info.channels);
info.sampleRate = static_cast<unsigned int>(m_decoder.info.hz);
info.sampleCount = m_decoder.samples;
m_numSamples = info.sampleCount;
return true;
}
////////////////////////////////////////////////////////////
void SoundFileReaderMp3::seek(Uint64 sampleOffset)
{
m_position = std::min(sampleOffset, m_numSamples);
mp3dec_ex_seek(&m_decoder, m_position);
}
////////////////////////////////////////////////////////////
Uint64 SoundFileReaderMp3::read(Int16* samples, Uint64 maxCount)
{
Uint64 toRead = std::min(maxCount, m_numSamples - m_position);
toRead = static_cast<Uint64>(mp3dec_ex_read(&m_decoder, samples, static_cast<std::size_t>(toRead)));
m_position += toRead;
return toRead;
}
} // namespace priv
} // namespace sf

View File

@ -0,0 +1,143 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef SFML_SOUNDFILEREADERMP3_HPP
#define SFML_SOUNDFILEREADERMP3_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#ifndef NOMINMAX
#define NOMINMAX // To avoid windows.h and std::min issue
#endif
#define MINIMP3_NO_STDIO // Minimp3 control define, eliminate file manipulation code which is useless here
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4242 4244 4267 4456 4706)
#endif
#include <minimp3_ex.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#undef NOMINMAX
#undef MINIMP3_NO_STDIO
#include <SFML/Audio/SoundFileReader.hpp>
#include <vector>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Implementation of sound file reader that handles MP3 files
///
////////////////////////////////////////////////////////////
class SoundFileReaderMp3 : public SoundFileReader
{
public:
////////////////////////////////////////////////////////////
/// \brief Check if this reader can handle a file given by an input stream
///
/// \param stream Source stream to check
///
/// \return True if the file is supported by this reader
///
////////////////////////////////////////////////////////////
static bool check(InputStream& stream);
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
SoundFileReaderMp3();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~SoundFileReaderMp3();
////////////////////////////////////////////////////////////
/// \brief Open a sound file for reading
///
/// \param stream Source stream to read from
/// \param info Structure to fill with the properties of the loaded sound
///
/// \return True if the file was successfully opened
///
////////////////////////////////////////////////////////////
virtual bool open(InputStream& stream, Info& info);
////////////////////////////////////////////////////////////
/// \brief Change the current read position to the given sample offset
///
/// The sample offset takes the channels into account.
/// If you have a time offset instead, you can easily find
/// the corresponding sample offset with the following formula:
/// `timeInSeconds * sampleRate * channelCount`
/// If the given offset exceeds to total number of samples,
/// this function must jump to the end of the file.
///
/// \param sampleOffset Index of the sample to jump to, relative to the beginning
///
////////////////////////////////////////////////////////////
virtual void seek(Uint64 sampleOffset);
////////////////////////////////////////////////////////////
/// \brief Read audio samples from the open file
///
/// \param samples Pointer to the sample array to fill
/// \param maxCount Maximum number of samples to read
///
/// \return Number of samples actually read (may be less than \a maxCount)
///
////////////////////////////////////////////////////////////
virtual Uint64 read(Int16* samples, Uint64 maxCount);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
mp3dec_io_t m_io;
mp3dec_ex_t m_decoder;
Uint64 m_numSamples; // Decompressed audio storage size
Uint64 m_position; // Position in decompressed audio buffer
};
} // namespace priv
} // namespace sf
#endif // SFML_SOUNDFILEREADERMP3_HPP