Added Opus sound file support, added SoundFileReaderOpus and FindOpus cmake Module

This commit is contained in:
Ferdinand Thiessen 2016-02-21 16:08:44 +01:00 committed by Lukas Dürrenberger
parent 37ac80dbe5
commit 15bb2ef28e
5 changed files with 362 additions and 0 deletions

View File

@ -0,0 +1,43 @@
# This file was taken from Unvanquished,
# Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
# It's licensed under the terms of the 3-clause OpenBSD license.
# Modifications Copyright 2014-2015 the openage authors.
# See copying.md for further legal info.
# - Find opus library
# Find the native Opus headers and libraries.
# This module defines
# OPUS_INCLUDE_DIRS - where to find opus/opus.h, opus/opusfile.h, etc
# OPUS_LIBRARIES - List of libraries when using libopus
# OPUS_FOUND - True if opus is found.
# find the opusfile header, defines our api.
find_path(OPUS_INCLUDE_DIR
NAMES opus/opusfile.h
DOC "Opus include directory"
)
mark_as_advanced(OPUS_INCLUDE_DIR)
# look for libopusfile, the highlevel container-aware api.
find_library(OPUSFILE_LIBRARY
NAMES opusfile
DOC "Path to OpusFile library"
)
mark_as_advanced(OPUSFILE_LIBRARY)
# find libopus, the core codec component.
find_library(OPUS_LIBRARY
NAMES opus
DOC "Path to Opus library"
)
mark_as_advanced(OPUS_LIBRARY)
# handle the QUIETLY and REQUIRED arguments and set OPUSFILE_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Opus DEFAULT_MSG OPUSFILE_LIBRARY OPUS_LIBRARY OPUS_INCLUDE_DIR)
# export the variables
set(OPUS_LIBRARIES "${OPUSFILE_LIBRARY}" "${OPUS_LIBRARY}")
set(OPUS_INCLUDE_DIRS "${OPUS_INCLUDE_DIR}" "${OPUS_INCLUDE_DIR}/opus")

View File

@ -48,6 +48,8 @@ set(CODECS_SRC
${SRCROOT}/SoundFileReaderMp3.cpp ${SRCROOT}/SoundFileReaderMp3.cpp
${SRCROOT}/SoundFileReaderOgg.hpp ${SRCROOT}/SoundFileReaderOgg.hpp
${SRCROOT}/SoundFileReaderOgg.cpp ${SRCROOT}/SoundFileReaderOgg.cpp
${SRCROOT}/SoundFileReaderOpus.hpp
${SRCROOT}/SoundFileReaderOpus.cpp
${SRCROOT}/SoundFileReaderWav.hpp ${SRCROOT}/SoundFileReaderWav.hpp
${SRCROOT}/SoundFileReaderWav.cpp ${SRCROOT}/SoundFileReaderWav.cpp
${INCROOT}/SoundFileWriter.hpp ${INCROOT}/SoundFileWriter.hpp
@ -77,6 +79,7 @@ endif()
# find external libraries # find external libraries
find_package(Vorbis REQUIRED) find_package(Vorbis REQUIRED)
find_package(FLAC REQUIRED) find_package(FLAC REQUIRED)
find_package(Opus REQUIRED)
# define the sfml-audio target # define the sfml-audio target
sfml_add_library(Audio sfml_add_library(Audio

View File

@ -29,9 +29,11 @@
#include <SFML/Audio/SoundFileReaderFlac.hpp> #include <SFML/Audio/SoundFileReaderFlac.hpp>
#include <SFML/Audio/SoundFileReaderMp3.hpp> #include <SFML/Audio/SoundFileReaderMp3.hpp>
#include <SFML/Audio/SoundFileReaderOgg.hpp> #include <SFML/Audio/SoundFileReaderOgg.hpp>
#include <SFML/Audio/SoundFileReaderOpus.hpp>
#include <SFML/Audio/SoundFileReaderWav.hpp> #include <SFML/Audio/SoundFileReaderWav.hpp>
#include <SFML/Audio/SoundFileWriterFlac.hpp> #include <SFML/Audio/SoundFileWriterFlac.hpp>
#include <SFML/Audio/SoundFileWriterOgg.hpp> #include <SFML/Audio/SoundFileWriterOgg.hpp>
//#include <SFML/Audio/SoundFileWriterOpus.hpp>
#include <SFML/Audio/SoundFileWriterWav.hpp> #include <SFML/Audio/SoundFileWriterWav.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
@ -144,6 +146,7 @@ SoundFileFactory::ReaderFactoryMap& SoundFileFactory::getReaderFactoryMap()
static ReaderFactoryMap result{{&priv::createReader<priv::SoundFileReaderFlac>, &priv::SoundFileReaderFlac::check}, static ReaderFactoryMap result{{&priv::createReader<priv::SoundFileReaderFlac>, &priv::SoundFileReaderFlac::check},
{&priv::createReader<priv::SoundFileReaderMp3>, &priv::SoundFileReaderMp3::check}, {&priv::createReader<priv::SoundFileReaderMp3>, &priv::SoundFileReaderMp3::check},
{&priv::createReader<priv::SoundFileReaderOgg>, &priv::SoundFileReaderOgg::check}, {&priv::createReader<priv::SoundFileReaderOgg>, &priv::SoundFileReaderOgg::check},
{&priv::createReader<priv::SoundFileReaderOpus>, &priv::SoundFileReaderOpus::check},
{&priv::createReader<priv::SoundFileReaderWav>, &priv::SoundFileReaderWav::check}}; {&priv::createReader<priv::SoundFileReaderWav>, &priv::SoundFileReaderWav::check}};
return result; return result;

View File

@ -0,0 +1,189 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2016 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
////////////////////////////////////////////////////////////
#include <SFML/Audio/SoundFileReaderOpus.hpp>
#include <SFML/System/MemoryInputStream.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <cctype>
#include <cassert>
#include <climits>
namespace
{
int read(void* data, unsigned char* ptr, int bytes)
{
sf::InputStream* stream = static_cast<sf::InputStream*>(data);
return static_cast<int>(stream->read(ptr, bytes));
}
int seek(void* data, opus_int64 offset, int whence)
{
sf::InputStream* stream = static_cast<sf::InputStream*>(data);
switch (whence)
{
case SEEK_SET:
break;
case SEEK_CUR:
offset += stream->tell();
break;
case SEEK_END:
offset = stream->getSize() - offset;
}
// Return value expected from libopusfile: 0 - Success and -1 - Failure
return static_cast<int>(stream->seek(offset)) >= 0 ? 0 : -1;
}
opus_int64 tell(void* data)
{
sf::InputStream* stream = static_cast<sf::InputStream*>(data);
return static_cast<opus_int64>(stream->tell());
}
static OpusFileCallbacks callbacks = {&read, &seek, &tell, NULL};
}
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
bool SoundFileReaderOpus::check(InputStream& stream)
{
int error = 0;
OggOpusFile* file = op_test_callbacks(&stream, &callbacks, NULL, 0, &error);
if (error == 0)
{
op_free(file);
return true;
}
else
{
return false;
}
}
////////////////////////////////////////////////////////////
SoundFileReaderOpus::SoundFileReaderOpus() :
m_opus (NULL),
m_channelCount(0)
{
}
////////////////////////////////////////////////////////////
SoundFileReaderOpus::~SoundFileReaderOpus()
{
close();
}
////////////////////////////////////////////////////////////
bool SoundFileReaderOpus::open(InputStream& stream, Info& info)
{
// Open the Opus stream
int status = 0;
m_opus = op_open_callbacks(&stream, &callbacks, NULL, 0, &status);
if (status != 0)
{
err() << "Failed to open Opus file for reading" << std::endl;
return false;
}
// Retrieve the music attributes
const OpusHead* opusHead = op_head(m_opus, -1);
info.channelCount = opusHead->channel_count;
info.sampleRate = opusHead->input_sample_rate;
info.sampleCount = static_cast<std::size_t>(op_pcm_total(m_opus, -1) * opusHead->channel_count);
// We must keep the channel count for the seek function
m_channelCount = info.channelCount;
return true;
}
////////////////////////////////////////////////////////////
void SoundFileReaderOpus::seek(Uint64 sampleOffset)
{
assert(m_opus != NULL);
op_pcm_seek(m_opus, sampleOffset / m_channelCount);
}
////////////////////////////////////////////////////////////
Uint64 SoundFileReaderOpus::read(Int16* samples, Uint64 maxCount)
{
assert(m_opus != NULL);
int samplesToRead;
// Try to read the requested number of samples, stop only on error or end of file
Uint64 count = 0;
while (maxCount > 0)
{
// since maxCount is uint64 we have to ensure that samplesToRead is <= INT_MAX (int overflow)
if (maxCount > INT_MAX)
{
samplesToRead = INT_MAX;
}
else
{
samplesToRead = maxCount;
}
// op_read returns number of SAMPLES read PER CHANNEL
int samplesRead = op_read(m_opus, samples, samplesToRead, NULL) * m_channelCount;
if (samplesRead > 0)
{
maxCount -= samplesRead;
count += samplesRead;
samples += samplesRead;
}
else
{
// error or end of file
break;
}
}
return count;
}
////////////////////////////////////////////////////////////
void SoundFileReaderOpus::close()
{
if (m_opus != NULL)
{
op_free(m_opus);
m_channelCount = 0;
}
}
} // namespace priv
} // namespace sf

View File

@ -0,0 +1,124 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2016 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_SOUNDFILEREADEROPUS_HPP
#define SFML_SOUNDFILEREADEROPUS_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Audio/SoundFileReader.hpp>
#include <opus/opusfile.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Implementation of sound file reader that handles Opus files
///
////////////////////////////////////////////////////////////
class SoundFileReaderOpus : 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
///
////////////////////////////////////////////////////////////
SoundFileReaderOpus();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~SoundFileReaderOpus();
////////////////////////////////////////////////////////////
/// \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
///
/// 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:
////////////////////////////////////////////////////////////
/// \brief Close the open Opus file
///
////////////////////////////////////////////////////////////
void close();
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
OggOpusFile* m_opus; // opus file handle
unsigned int m_channelCount; // number of channels of the open sound file
};
} // namespace priv
} // namespace sf
#endif // SFML_SOUNDFILEREADEROPUS_HPP