mirror of https://github.com/OpenTTD/OpenTTD
Add: Support sound effects in MP3 format.
parent
0435e29a06
commit
250681b543
|
@ -155,6 +155,7 @@ if(NOT OPTION_DEDICATED)
|
|||
find_package(ICU OPTIONAL_COMPONENTS i18n)
|
||||
endif()
|
||||
endif()
|
||||
find_package(LibMad)
|
||||
endif()
|
||||
if(APPLE)
|
||||
enable_language(OBJCXX)
|
||||
|
@ -326,6 +327,7 @@ if(NOT OPTION_DEDICATED)
|
|||
link_package(Fontconfig TARGET Fontconfig::Fontconfig)
|
||||
link_package(Harfbuzz TARGET harfbuzz::harfbuzz)
|
||||
link_package(ICU_i18n)
|
||||
link_package(LibMad)
|
||||
|
||||
if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
|
||||
# SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# LIBMAD_FOUND
|
||||
# LIBMAD_INCLUDE_DIR
|
||||
# LIBMAD_LIBRARY
|
||||
|
||||
find_path(LibMad_INCLUDE_DIR NAMES mad.h)
|
||||
|
||||
find_library(LibMad_LIBRARY NAMES mad)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibMad DEFAULT_MSG LibMad_LIBRARY LibMad_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(LibMad_INCLUDE_DIR LibMad_LIBRARY)
|
|
@ -16,6 +16,10 @@
|
|||
#include "window_gui.h"
|
||||
#include "vehicle_base.h"
|
||||
|
||||
#ifdef WITH_LIBMAD
|
||||
#include <mad.h>
|
||||
#endif /* WITH_LIBMAD */
|
||||
|
||||
/* The type of set we're replacing */
|
||||
#define SET_TYPE "sounds"
|
||||
#include "base_media_func.h"
|
||||
|
@ -138,10 +142,86 @@ static bool LoadSoundWav(SoundEntry &sound, bool new_format, std::vector<byte> &
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef WITH_LIBMAD
|
||||
|
||||
/* Struct to hold context during MP3 decoding. */
|
||||
struct MadContext {
|
||||
SoundEntry &sound;
|
||||
std::vector<byte> &data;
|
||||
bool read;
|
||||
std::vector<byte> in_data{};
|
||||
};
|
||||
|
||||
static mad_flow MadInputFunc(void *context, struct mad_stream *stream)
|
||||
{
|
||||
MadContext *ctx = static_cast<MadContext *>(context);
|
||||
if (ctx->read) return MAD_FLOW_STOP;
|
||||
|
||||
ctx->in_data.resize(ctx->sound.file_size);
|
||||
ctx->sound.file->ReadBlock(ctx->in_data.data(), ctx->in_data.size());
|
||||
ctx->read = true;
|
||||
|
||||
mad_stream_buffer(stream, ctx->in_data.data(), ctx->in_data.size());
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
static inline int16_t MadConvertToInt16(mad_fixed_t sample)
|
||||
{
|
||||
if (sample >= MAD_F_ONE) return INT16_MAX;
|
||||
if (sample <= -MAD_F_ONE) return INT16_MIN;
|
||||
return sample >> (MAD_F_FRACBITS - 15);
|
||||
}
|
||||
|
||||
static mad_flow MadOutputFunc(void *context, struct mad_header const *, struct mad_pcm *pcm)
|
||||
{
|
||||
MadContext *ctx = static_cast<MadContext *>(context);
|
||||
|
||||
ctx->sound.rate = pcm->samplerate;
|
||||
ctx->sound.bits_per_sample = 16;
|
||||
ctx->sound.channels = 1;
|
||||
|
||||
const mad_fixed_t *lin = pcm->samples[0];
|
||||
// const mad_fixed_t *rin = pcm->samples[1];
|
||||
int nsamples = pcm->length;
|
||||
|
||||
ctx->data.reserve(ctx->data.size() + nsamples * 2 + 2);
|
||||
while (nsamples--) {
|
||||
int16_t sample = MadConvertToInt16(*lin++);
|
||||
ctx->data.push_back(GB((uint16_t)sample, 0, 8));
|
||||
ctx->data.push_back(GB((uint16_t)sample, 8, 8));
|
||||
}
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
static bool LoadSoundMp3(SoundEntry &sound, bool, std::vector<byte> &data)
|
||||
{
|
||||
MadContext ctx{sound, data, false};
|
||||
|
||||
struct mad_decoder decoder;
|
||||
mad_decoder_init(&decoder, &ctx, MadInputFunc, nullptr, nullptr, MadOutputFunc, nullptr, nullptr);
|
||||
mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
|
||||
mad_decoder_finish(&decoder);
|
||||
|
||||
if (sound.rate == 0) return false;
|
||||
|
||||
/* Add padding sample for resampler. */
|
||||
uint padding = sound.channels * sound.bits_per_sample / 8;
|
||||
while (padding--) data.push_back(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* WITH_LIBMAD */
|
||||
|
||||
using SoundLoader = bool (*)(SoundEntry &sound, bool new_format, std::vector<byte> &data);
|
||||
|
||||
static std::initializer_list<SoundLoader> _sound_loaders = {
|
||||
LoadSoundWav,
|
||||
#ifdef WITH_LIBMAD
|
||||
LoadSoundMp3,
|
||||
#endif /* WITH_LIBMAD */
|
||||
LoadSoundRaw,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue