mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-06-26 23:20:16 +01:00
Add: Support sound effects in Ogg Opus format.
This commit is contained in:
parent
8b00661b22
commit
560ee2442d
3
.github/workflows/ci-linux.yml
vendored
3
.github/workflows/ci-linux.yml
vendored
@ -60,6 +60,9 @@ jobs:
|
|||||||
libicu-dev \
|
libicu-dev \
|
||||||
liblzma-dev \
|
liblzma-dev \
|
||||||
liblzo2-dev \
|
liblzo2-dev \
|
||||||
|
libogg-dev \
|
||||||
|
libopus-dev \
|
||||||
|
libopusfile-dev \
|
||||||
${{ inputs.libraries }} \
|
${{ inputs.libraries }} \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
# EOF
|
# EOF
|
||||||
|
3
.github/workflows/ci-mingw.yml
vendored
3
.github/workflows/ci-mingw.yml
vendored
@ -37,6 +37,9 @@ jobs:
|
|||||||
mingw-w64-${{ inputs.arch }}-libpng
|
mingw-w64-${{ inputs.arch }}-libpng
|
||||||
mingw-w64-${{ inputs.arch }}-lld
|
mingw-w64-${{ inputs.arch }}-lld
|
||||||
mingw-w64-${{ inputs.arch }}-ninja
|
mingw-w64-${{ inputs.arch }}-ninja
|
||||||
|
mingw-w64-${{ inputs.arch }}-ogg
|
||||||
|
mingw-w64-${{ inputs.arch }}-opus
|
||||||
|
mingw-w64-${{ inputs.arch }}-opusfile
|
||||||
|
|
||||||
- name: Install OpenGFX
|
- name: Install OpenGFX
|
||||||
shell: bash
|
shell: bash
|
||||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -54,6 +54,8 @@ jobs:
|
|||||||
libicu-dev \
|
libicu-dev \
|
||||||
liblzma-dev \
|
liblzma-dev \
|
||||||
liblzo2-dev \
|
liblzo2-dev \
|
||||||
|
libopus-dev \
|
||||||
|
libopusfile-dev \
|
||||||
libsdl2-dev \
|
libsdl2-dev \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
# EOF
|
# EOF
|
||||||
|
@ -155,6 +155,7 @@ if(NOT OPTION_DEDICATED)
|
|||||||
find_package(ICU OPTIONAL_COMPONENTS i18n uc)
|
find_package(ICU OPTIONAL_COMPONENTS i18n uc)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
find_package(OpusFile)
|
||||||
endif()
|
endif()
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
enable_language(OBJCXX)
|
enable_language(OBJCXX)
|
||||||
@ -332,6 +333,7 @@ if(NOT OPTION_DEDICATED)
|
|||||||
link_package(Harfbuzz TARGET harfbuzz::harfbuzz)
|
link_package(Harfbuzz TARGET harfbuzz::harfbuzz)
|
||||||
link_package(ICU_i18n)
|
link_package(ICU_i18n)
|
||||||
link_package(ICU_uc)
|
link_package(ICU_uc)
|
||||||
|
link_package(OpusFile TARGET OpusFile::opusfile)
|
||||||
|
|
||||||
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
|
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
|
||||||
# SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when
|
# SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when
|
||||||
|
37
cmake/FindOgg.cmake
Normal file
37
cmake/FindOgg.cmake
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_library(Ogg_LIBRARY
|
||||||
|
NAMES ogg
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Ogg_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of ogg")
|
||||||
|
|
||||||
|
set(Ogg_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of ogg")
|
||||||
|
|
||||||
|
set(Ogg_LINK_FLAGS "" CACHE STRING "Extra link flags of ogg")
|
||||||
|
|
||||||
|
find_path(Ogg_INCLUDE_PATH
|
||||||
|
NAMES ogg.h
|
||||||
|
PATH_SUFFIXES ogg
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(Ogg
|
||||||
|
REQUIRED_VARS Ogg_LIBRARY Ogg_INCLUDE_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Ogg_FOUND)
|
||||||
|
set(Ogg_dirs ${Ogg_INCLUDE_PATH})
|
||||||
|
if(EXISTS "${Ogg_INCLUDE_PATH}/ogg")
|
||||||
|
list(APPEND Ogg_dirs "${Ogg_INCLUDE_PATH}/ogg")
|
||||||
|
endif()
|
||||||
|
if (NOT TARGET Ogg::ogg)
|
||||||
|
add_library(Ogg::ogg UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(Ogg::ogg PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${Ogg_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${Ogg_dirs}"
|
||||||
|
INTERFACE_COMPILE_OPTIONS "${Ogg_COMPILE_OPTIONS}"
|
||||||
|
INTERFACE_LINK_LIBRARIES "${Ogg_LINK_LIBRARIES}"
|
||||||
|
INTERFACE_LINK_FLAGS "${Ogg_LINK_FLAGS}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
37
cmake/FindOpus.cmake
Normal file
37
cmake/FindOpus.cmake
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_library(Opus_LIBRARY
|
||||||
|
NAMES opus
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Opus_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of opus")
|
||||||
|
|
||||||
|
set(Opus_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of opus")
|
||||||
|
|
||||||
|
set(Opus_LINK_FLAGS "" CACHE STRING "Extra link flags of opus")
|
||||||
|
|
||||||
|
find_path(Opus_INCLUDE_PATH
|
||||||
|
NAMES opus.h
|
||||||
|
PATH_SUFFIXES opus
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(Opus
|
||||||
|
REQUIRED_VARS Opus_LIBRARY Opus_INCLUDE_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Opus_FOUND)
|
||||||
|
set(Opus_dirs ${Opus_INCLUDE_PATH})
|
||||||
|
if(EXISTS "${Opus_INCLUDE_PATH}/opus")
|
||||||
|
list(APPEND Opus_dirs "${Opus_INCLUDE_PATH}/opus")
|
||||||
|
endif()
|
||||||
|
if (NOT TARGET Opus::opus)
|
||||||
|
add_library(Opus::opus UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(Opus::opus PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${Opus_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${Opus_dirs}"
|
||||||
|
INTERFACE_COMPILE_OPTIONS "${Opus_COMPILE_OPTIONS}"
|
||||||
|
INTERFACE_LINK_LIBRARIES "${Opus_LINK_LIBRARIES}"
|
||||||
|
INTERFACE_LINK_FLAGS "${Opus_LINK_FLAGS}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
40
cmake/FindOpusFile.cmake
Normal file
40
cmake/FindOpusFile.cmake
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_library(OpusFile_LIBRARY
|
||||||
|
NAMES opusfile
|
||||||
|
)
|
||||||
|
|
||||||
|
set(OpusFile_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of opusfile")
|
||||||
|
|
||||||
|
set(OpusFile_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of opusfile")
|
||||||
|
|
||||||
|
set(OpusFile_LINK_FLAGS "" CACHE STRING "Extra link flags of opusfile")
|
||||||
|
|
||||||
|
find_path(OpusFile_INCLUDE_PATH
|
||||||
|
NAMES opusfile.h
|
||||||
|
PATH_SUFFIXES opus
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(OpusFile
|
||||||
|
REQUIRED_VARS OpusFile_LIBRARY OpusFile_INCLUDE_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package(Ogg)
|
||||||
|
find_package(Opus)
|
||||||
|
|
||||||
|
if (OpusFile_FOUND)
|
||||||
|
set(OpusFile_dirs ${OpusFile_INCLUDE_PATH})
|
||||||
|
if(EXISTS "${OpusFile_INCLUDE_PATH}/opus")
|
||||||
|
list(APPEND OpusFile_dirs "${OpusFile_INCLUDE_PATH}/opus")
|
||||||
|
endif()
|
||||||
|
if (NOT TARGET OpusFile::opusfile)
|
||||||
|
add_library(OpusFile::opusfile UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(OpusFile::opusfile PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${OpusFile_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${OpusFile_dirs}"
|
||||||
|
INTERFACE_COMPILE_OPTIONS "${OpusFile_COMPILE_OPTIONS}"
|
||||||
|
INTERFACE_LINK_LIBRARIES "Ogg::ogg;Opus::opus;${OpusFile_LINK_LIBRARIES}"
|
||||||
|
INTERFACE_LINK_FLAGS "${OpusFile_LINK_FLAGS}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
@ -39,6 +39,11 @@ add_files(
|
|||||||
CONDITION ICU_i18n_FOUND AND HARFBUZZ_FOUND
|
CONDITION ICU_i18n_FOUND AND HARFBUZZ_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_files(
|
||||||
|
soundloader_opus.cpp
|
||||||
|
CONDITION OpusFile_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
add_files(
|
add_files(
|
||||||
aircraft.h
|
aircraft.h
|
||||||
aircraft_cmd.cpp
|
aircraft_cmd.cpp
|
||||||
|
95
src/soundloader_opus.cpp
Normal file
95
src/soundloader_opus.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||||
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file sound_opus.cpp Loading of opus sounds. */
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "random_access_file_type.h"
|
||||||
|
#include "sound_type.h"
|
||||||
|
#include "soundloader_type.h"
|
||||||
|
|
||||||
|
#include <opusfile.h>
|
||||||
|
|
||||||
|
#include "safeguards.h"
|
||||||
|
|
||||||
|
/** Opus sound loader. */
|
||||||
|
class SoundLoader_Opus : public SoundLoader {
|
||||||
|
public:
|
||||||
|
SoundLoader_Opus() : SoundLoader("opus", "Opus sound loader", 10) {}
|
||||||
|
|
||||||
|
static constexpr uint16_t OPUS_SAMPLE_RATE = 48000; ///< OpusFile always decodes at 48kHz.
|
||||||
|
static constexpr uint8_t OPUS_SAMPLE_BITS = 16; ///< OpusFile op_read() uses 16 bits per sample.
|
||||||
|
|
||||||
|
/* For good results, you will need at least 57 bytes (for a pure Opus-only stream). */
|
||||||
|
static constexpr size_t MIN_OPUS_FILE_SIZE = 57U;
|
||||||
|
|
||||||
|
/* It is recommended that this be large enough for at least 120 ms of data at 48 kHz per channel (5760 values per channel).
|
||||||
|
* Smaller buffers will simply return less data, possibly consuming more memory to buffer the data internally. */
|
||||||
|
static constexpr size_t DECODE_BUFFER_SAMPLES = 5760 * 2;
|
||||||
|
static constexpr size_t DECODE_BUFFER_BYTES = DECODE_BUFFER_SAMPLES * sizeof(opus_int16);
|
||||||
|
|
||||||
|
bool Load(SoundEntry &sound, bool new_format, std::vector<uint8_t> &data) override
|
||||||
|
{
|
||||||
|
if (!new_format) return false;
|
||||||
|
|
||||||
|
/* At least 57 bytes are needed for an Opus-only file. */
|
||||||
|
if (sound.file_size < MIN_OPUS_FILE_SIZE) return false;
|
||||||
|
|
||||||
|
/* Test if data is an Ogg Opus stream, as identified by the initial file header. */
|
||||||
|
auto filepos = sound.file->GetPos();
|
||||||
|
std::vector<uint8_t> tmp(MIN_OPUS_FILE_SIZE);
|
||||||
|
sound.file->ReadBlock(tmp.data(), tmp.size());
|
||||||
|
if (op_test(nullptr, tmp.data(), tmp.size()) != 0) return false;
|
||||||
|
|
||||||
|
/* Read the whole file into memory. */
|
||||||
|
tmp.resize(sound.file_size);
|
||||||
|
sound.file->SeekTo(filepos, SEEK_SET);
|
||||||
|
sound.file->ReadBlock(tmp.data(), tmp.size());
|
||||||
|
|
||||||
|
int error = 0;
|
||||||
|
auto of = std::unique_ptr<OggOpusFile, OggOpusFileDeleter>(op_open_memory(tmp.data(), tmp.size(), &error));
|
||||||
|
if (error != 0) return false;
|
||||||
|
|
||||||
|
size_t datapos = 0;
|
||||||
|
for (;;) {
|
||||||
|
data.resize(datapos + DECODE_BUFFER_BYTES);
|
||||||
|
|
||||||
|
int link_index;
|
||||||
|
int read = op_read(of.get(), reinterpret_cast<opus_int16 *>(&data[datapos]), DECODE_BUFFER_BYTES, &link_index);
|
||||||
|
if (read == 0) break;
|
||||||
|
|
||||||
|
if (read < 0 || op_channel_count(of.get(), link_index) != 1) {
|
||||||
|
/* Error reading, or incorrect channel count. */
|
||||||
|
data.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
datapos += read * sizeof(opus_int16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OpusFile always decodes at 48kHz. */
|
||||||
|
sound.channels = 1;
|
||||||
|
sound.bits_per_sample = OPUS_SAMPLE_BITS;
|
||||||
|
sound.rate = OPUS_SAMPLE_RATE;
|
||||||
|
|
||||||
|
/* We resized by DECODE_BUFFER_BYTES just before finally reading zero bytes, undo this. */
|
||||||
|
data.resize(data.size() - DECODE_BUFFER_BYTES);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Helper class to RAII release an OggOpusFile. */
|
||||||
|
struct OggOpusFileDeleter {
|
||||||
|
void operator()(OggOpusFile *of)
|
||||||
|
{
|
||||||
|
if (of != nullptr) op_free(of);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static SoundLoader_Opus s_sound_loader_opus;
|
@ -42,6 +42,9 @@
|
|||||||
{
|
{
|
||||||
"name": "lzo"
|
"name": "lzo"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "opusfile"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "sdl2",
|
"name": "sdl2",
|
||||||
"platform": "linux"
|
"platform": "linux"
|
||||||
|
Loading…
Reference in New Issue
Block a user