Merge pull request #1884 from SFML/feature/backmerge
Back-merge changes from 2.6.x to master
This commit is contained in:
commit
40cac9a403
@ -25,11 +25,6 @@ if (NOT CMAKE_VERSION VERSION_LESS 3.9)
|
|||||||
cmake_policy(SET CMP0068 NEW)
|
cmake_policy(SET CMP0068 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# add some default value for some additional macOS variable
|
|
||||||
# note that those variables are ignored on other systems
|
|
||||||
if(NOT CMAKE_OSX_ARCHITECTURES)
|
|
||||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "macOS architecture to build; 64-bit is expected" FORCE)
|
|
||||||
endif()
|
|
||||||
if(NOT CMAKE_OSX_SYSROOT)
|
if(NOT CMAKE_OSX_SYSROOT)
|
||||||
# query the path to the default SDK, will fail on non-macOS, but it's okay.
|
# query the path to the default SDK, will fail on non-macOS, but it's okay.
|
||||||
execute_process(COMMAND xcodebuild -sdk macosx -version Path
|
execute_process(COMMAND xcodebuild -sdk macosx -version Path
|
||||||
@ -181,14 +176,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)
|
||||||
|
@ -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] |
|
||||||
|
@ -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');
|
||||||
|
BIN
examples/sound/resources/ding.mp3
Normal file
BIN
examples/sound/resources/ding.mp3
Normal file
Binary file not shown.
1855
extlibs/headers/minimp3/minimp3.h
vendored
Normal file
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
1397
extlibs/headers/minimp3/minimp3_ex.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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>();
|
||||||
|
158
src/SFML/Audio/SoundFileReaderMp3.cpp
Normal file
158
src/SFML/Audio/SoundFileReaderMp3.cpp
Normal 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
|
143
src/SFML/Audio/SoundFileReaderMp3.hpp
Normal file
143
src/SFML/Audio/SoundFileReaderMp3.hpp
Normal 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() override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \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
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool open(InputStream& stream, Info& info) override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \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
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void seek(Uint64 sampleOffset) override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \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)
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
Uint64 read(Int16* samples, Uint64 maxCount) override;
|
||||||
|
|
||||||
|
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
|
@ -372,14 +372,14 @@ Image Texture::copyToImage() const
|
|||||||
// Then we copy the useful pixels from the temporary array to the final one
|
// Then we copy the useful pixels from the temporary array to the final one
|
||||||
const Uint8* src = allPixels.data();
|
const Uint8* src = allPixels.data();
|
||||||
Uint8* dst = pixels.data();
|
Uint8* dst = pixels.data();
|
||||||
unsigned int srcPitch = m_actualSize.x * 4;
|
int srcPitch = static_cast<int>(m_actualSize.x * 4);
|
||||||
unsigned int dstPitch = m_size.x * 4;
|
unsigned int dstPitch = m_size.x * 4;
|
||||||
|
|
||||||
// Handle the case where source pixels are flipped vertically
|
// Handle the case where source pixels are flipped vertically
|
||||||
if (m_pixelsFlipped)
|
if (m_pixelsFlipped)
|
||||||
{
|
{
|
||||||
src += srcPitch * (m_size.y - 1);
|
src += static_cast<unsigned int>(srcPitch * static_cast<int>((m_size.y - 1)));
|
||||||
srcPitch = UINT_MAX - srcPitch + 1;
|
srcPitch = -srcPitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < m_size.y; ++i)
|
for (unsigned int i = 0; i < m_size.y; ++i)
|
||||||
|
@ -178,7 +178,7 @@ Packet& Packet::operator >>(Int32& data)
|
|||||||
if (checkSize(sizeof(data)))
|
if (checkSize(sizeof(data)))
|
||||||
{
|
{
|
||||||
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
|
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
|
||||||
data = static_cast<Int16>(ntohl(static_cast<uint32_t>(data)));
|
data = static_cast<Int32>(ntohl(static_cast<uint32_t>(data)));
|
||||||
m_readPos += sizeof(data);
|
m_readPos += sizeof(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +430,7 @@ Packet& Packet::operator <<(Uint16 data)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Packet& Packet::operator <<(Int32 data)
|
Packet& Packet::operator <<(Int32 data)
|
||||||
{
|
{
|
||||||
Int32 toWrite = static_cast<Int16>(htonl(static_cast<uint32_t>(data)));
|
Int32 toWrite = static_cast<Int32>(htonl(static_cast<uint32_t>(data)));
|
||||||
append(&toWrite, sizeof(toWrite));
|
append(&toWrite, sizeof(toWrite));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ namespace
|
|||||||
// The shared display and its reference counter
|
// The shared display and its reference counter
|
||||||
Display* sharedDisplay = nullptr;
|
Display* sharedDisplay = nullptr;
|
||||||
unsigned int referenceCount = 0;
|
unsigned int referenceCount = 0;
|
||||||
|
XIM sharedXIM = NULL;
|
||||||
|
unsigned int referenceCountXIM = 0;
|
||||||
sf::Mutex mutex;
|
sf::Mutex mutex;
|
||||||
|
|
||||||
using AtomMap = std::unordered_map<std::string, Atom>;
|
using AtomMap = std::unordered_map<std::string, Atom>;
|
||||||
@ -85,6 +87,56 @@ void CloseDisplay(Display* display)
|
|||||||
XCloseDisplay(display);
|
XCloseDisplay(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
XIM OpenXIM()
|
||||||
|
{
|
||||||
|
Lock lock(mutex);
|
||||||
|
|
||||||
|
assert(sharedDisplay != nullptr);
|
||||||
|
|
||||||
|
if (referenceCountXIM == 0)
|
||||||
|
{
|
||||||
|
// Create a new XIM instance
|
||||||
|
|
||||||
|
// We need the default (environment) locale and X locale for opening
|
||||||
|
// the IM and properly receiving text
|
||||||
|
// First save the previous ones (this might be able to be written more elegantly?)
|
||||||
|
const char* p;
|
||||||
|
std::string prevLoc((p = setlocale(LC_ALL, nullptr)) ? p : "");
|
||||||
|
std::string prevXLoc((p = XSetLocaleModifiers(nullptr)) ? p : "");
|
||||||
|
|
||||||
|
// Set the locales from environment
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
XSetLocaleModifiers("");
|
||||||
|
|
||||||
|
// Create the input context
|
||||||
|
sharedXIM = XOpenIM(sharedDisplay, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Restore the previous locale
|
||||||
|
if (prevLoc.length() != 0)
|
||||||
|
setlocale(LC_ALL, prevLoc.c_str());
|
||||||
|
|
||||||
|
if (prevXLoc.length() != 0)
|
||||||
|
XSetLocaleModifiers(prevXLoc.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
referenceCountXIM++;
|
||||||
|
|
||||||
|
return sharedXIM;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void CloseXIM(XIM xim)
|
||||||
|
{
|
||||||
|
Lock lock(mutex);
|
||||||
|
|
||||||
|
assert(xim == sharedXIM);
|
||||||
|
|
||||||
|
referenceCountXIM--;
|
||||||
|
|
||||||
|
if ((referenceCountXIM == 0) && (xim != nullptr))
|
||||||
|
XCloseIM(xim);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Atom getAtom(const std::string& name, bool onlyIfExists)
|
Atom getAtom(const std::string& name, bool onlyIfExists)
|
||||||
|
@ -55,6 +55,27 @@ Display* OpenDisplay();
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void CloseDisplay(Display* display);
|
void CloseDisplay(Display* display);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Get the shared XIM context for the Display
|
||||||
|
///
|
||||||
|
/// This function increments the reference count of the XIM context,
|
||||||
|
/// it must be matched with a call to CloseXIM.
|
||||||
|
///
|
||||||
|
/// It must be called with a display already opened.
|
||||||
|
///
|
||||||
|
/// \return XIM handle (a pointer) of the context
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
XIM OpenXIM();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Release a reference to the shared XIM context
|
||||||
|
///
|
||||||
|
/// \param xim XIM context to release
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void CloseXIM(XIM xim);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Get the atom with the specified name
|
/// \brief Get the atom with the specified name
|
||||||
///
|
///
|
||||||
|
@ -101,7 +101,9 @@ namespace
|
|||||||
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
||||||
{
|
{
|
||||||
// Just check if the event matches the window
|
// Just check if the event matches the window
|
||||||
return event->xany.window == reinterpret_cast< ::Window >(userData);
|
// The input method sometimes sends ClientMessages with a different window ID,
|
||||||
|
// our event loop has to process them for the IM to work
|
||||||
|
return (event->xany.window == reinterpret_cast< ::Window >(userData)) || (event->type == ClientMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the name of the current executable
|
// Find the name of the current executable
|
||||||
@ -803,7 +805,7 @@ WindowImplX11::~WindowImplX11()
|
|||||||
|
|
||||||
// Close the input method
|
// Close the input method
|
||||||
if (m_inputMethod)
|
if (m_inputMethod)
|
||||||
XCloseIM(m_inputMethod);
|
CloseXIM(m_inputMethod);
|
||||||
|
|
||||||
// Close the connection with the X server
|
// Close the connection with the X server
|
||||||
CloseDisplay(m_display);
|
CloseDisplay(m_display);
|
||||||
@ -1607,7 +1609,7 @@ void WindowImplX11::initialize()
|
|||||||
using namespace WindowsImplX11Impl;
|
using namespace WindowsImplX11Impl;
|
||||||
|
|
||||||
// Create the input context
|
// Create the input context
|
||||||
m_inputMethod = XOpenIM(m_display, nullptr, nullptr, nullptr);
|
m_inputMethod = OpenXIM();
|
||||||
|
|
||||||
if (m_inputMethod)
|
if (m_inputMethod)
|
||||||
{
|
{
|
||||||
@ -1844,6 +1846,9 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
|
|
||||||
// Close event
|
// Close event
|
||||||
case ClientMessage:
|
case ClientMessage:
|
||||||
|
{
|
||||||
|
// Input methods might want random ClientMessage events
|
||||||
|
if (!XFilterEvent(&windowEvent, None))
|
||||||
{
|
{
|
||||||
static Atom wmProtocols = getAtom("WM_PROTOCOLS");
|
static Atom wmProtocols = getAtom("WM_PROTOCOLS");
|
||||||
|
|
||||||
@ -1868,6 +1873,7 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
XSendEvent(m_display, DefaultRootWindow(m_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &windowEvent);
|
XSendEvent(m_display, DefaultRootWindow(m_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &windowEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1904,7 +1910,7 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
if (m_inputContext)
|
if (m_inputContext)
|
||||||
{
|
{
|
||||||
Status status;
|
Status status;
|
||||||
Uint8 keyBuffer[16];
|
Uint8 keyBuffer[64];
|
||||||
|
|
||||||
int length = Xutf8LookupString(
|
int length = Xutf8LookupString(
|
||||||
m_inputContext,
|
m_inputContext,
|
||||||
@ -1915,10 +1921,19 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
&status
|
&status
|
||||||
);
|
);
|
||||||
|
|
||||||
if (length > 0)
|
if (status == XBufferOverflow)
|
||||||
|
err() << "A TextEntered event has more than 64 bytes of UTF-8 input, and "
|
||||||
|
"has been discarded\nThis means either you have typed a very long string "
|
||||||
|
"(more than 20 chars), or your input method is broken in obscure ways." << std::endl;
|
||||||
|
else if (status == XLookupChars)
|
||||||
{
|
{
|
||||||
|
// There might be more than 1 characters in this event,
|
||||||
|
// so we must iterate it
|
||||||
Uint32 unicode = 0;
|
Uint32 unicode = 0;
|
||||||
Utf8::decode(keyBuffer, keyBuffer + length, unicode, 0);
|
Uint8* iter = keyBuffer;
|
||||||
|
while (iter < keyBuffer + length)
|
||||||
|
{
|
||||||
|
iter = Utf8::decode(iter, keyBuffer + length, unicode, 0);
|
||||||
if (unicode != 0)
|
if (unicode != 0)
|
||||||
{
|
{
|
||||||
Event textEvent;
|
Event textEvent;
|
||||||
@ -1928,6 +1943,7 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
@ -33,9 +33,17 @@ if(SFML_BUILD_GRAPHICS)
|
|||||||
sfml_add_test(test-sfml-graphics "${GRAPHICS_SRC}" sfml-graphics)
|
sfml_add_test(test-sfml-graphics "${GRAPHICS_SRC}" sfml-graphics)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(SFML_BUILD_NETWORK)
|
||||||
|
SET(NETWORK_SRC
|
||||||
|
"${SRCROOT}/CatchMain.cpp"
|
||||||
|
"${SRCROOT}/Network/Packet.cpp"
|
||||||
|
)
|
||||||
|
sfml_add_test(test-sfml-network "${NETWORK_SRC}" sfml-network)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Automatically run the tests at the end of the build
|
# Automatically run the tests at the end of the build
|
||||||
add_custom_target(runtests ALL
|
add_custom_target(runtests ALL
|
||||||
DEPENDS test-sfml-system test-sfml-window test-sfml-graphics
|
DEPENDS test-sfml-system test-sfml-window test-sfml-graphics test-sfml-network
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(TARGET runtests
|
add_custom_command(TARGET runtests
|
||||||
|
52
test/src/Network/Packet.cpp
Normal file
52
test/src/Network/Packet.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <SFML/Network.hpp>
|
||||||
|
|
||||||
|
#include <catch.hpp>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
template <typename IntegerType>
|
||||||
|
static void testPacketStreamOperators(IntegerType expected)
|
||||||
|
{
|
||||||
|
sf::Packet packet;
|
||||||
|
packet << expected;
|
||||||
|
IntegerType received;
|
||||||
|
packet >> received;
|
||||||
|
CHECK(expected == received);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("sf::Packet class", "[network]")
|
||||||
|
{
|
||||||
|
SECTION("Stream operators")
|
||||||
|
{
|
||||||
|
SECTION("Int8")
|
||||||
|
{
|
||||||
|
testPacketStreamOperators(sf::Int8(0));
|
||||||
|
testPacketStreamOperators(sf::Int8(1));
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int8>::min());
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int8>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Int16")
|
||||||
|
{
|
||||||
|
testPacketStreamOperators(sf::Int16(0));
|
||||||
|
testPacketStreamOperators(sf::Int16(1));
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int16>::min());
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int16>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Int32")
|
||||||
|
{
|
||||||
|
testPacketStreamOperators(sf::Int32(0));
|
||||||
|
testPacketStreamOperators(sf::Int32(1));
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int32>::min());
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int32>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Int64")
|
||||||
|
{
|
||||||
|
testPacketStreamOperators(sf::Int64(0));
|
||||||
|
testPacketStreamOperators(sf::Int64(1));
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int64>::min());
|
||||||
|
testPacketStreamOperators(std::numeric_limits<sf::Int64>::max());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user