6#include "common/config.h"
11#include "medialoader/Exception.h"
12#include "medialoader/sound/WaveDecoder.h"
13#include "medialoader/sound/VorbisDecoder.h"
14#include "medialoader/sound/Mpg123Decoder.h"
15#include "medialoader/sound/FLACDecoder.h"
16#include "medialoader/sound/ModPlugDecoder.h"
18#include <simplesquirrel/simplesquirrel.hpp>
38#if defined(EVENGINE_MACOSX) || defined(EVENGINE_LINUX) || defined(EVENGINE_WINDOWS)
40bool fileExists(
const std::string &path) {
41 std::ifstream in(path, std::ios::binary);
45std::string parentPath(std::string path) {
46 while (!path.empty() && (path.back() ==
'/' || path.back() ==
'\\'))
48 auto slash = path.find_last_of(
"/\\");
49 if (slash == std::string::npos)
52 return path.substr(0, 1);
53 return path.substr(0, slash);
56std::string joinPath(
const std::string &
a,
const std::string &
b) {
59 if (
a.back() ==
'/' ||
a.back() ==
'\\')
66void ensureDesktopTimidityShare() {
67 if (
const char *existing = std::getenv(
"MMPAT_PATH_TO_CFG")) {
68 if (existing[0] !=
'\0')
72 auto *fs = filesystem::Filesystem::create();
73 std::string exe = fs->getExecutablePath();
77 std::string
dir = parentPath(exe);
78 const char *relatives[] = {
80 "../share/eve/timidity",
81 "../../share/eve/timidity",
82 "../../../share/eve/timidity",
86 for (
const char *rel : relatives) {
87 std::string cand = joinPath(
dir, rel);
88 if (fileExists(joinPath(cand,
"timidity.cfg"))) {
97 _putenv_s(
"MMPAT_PATH_TO_CFG", found.c_str());
99 setenv(
"MMPAT_PATH_TO_CFG", found.c_str(), 0);
108#if defined(EVENGINE_MACOSX) || defined(EVENGINE_LINUX) || defined(EVENGINE_WINDOWS)
109 ensureDesktopTimidityShare();
117std::string lowerExt(std::string ext) {
119 c = static_cast<char>(std::tolower(static_cast<unsigned char>(
c)));
124std::unique_ptr<medialoader::Decoder> tryMake(
const char *data,
size_t size,
int bufferSize) {
126 return std::unique_ptr<medialoader::Decoder>(
new T(data, size, bufferSize));
127 }
catch (
const medialoader::Exception &) {
137 if (
data ==
nullptr ||
data->getData() ==
nullptr ||
data->getSize() == 0)
140 std::vector<char> owned(
static_cast<const char *
>(
data->getData()),
141 static_cast<const char *
>(
data->getData()) +
data->getSize());
145 ext = lowerExt(fd->getExtension());
147 const char *
ptr = owned.data();
148 size_t size = owned.size();
150 auto tryByExt = [&](
const std::string &e) -> std::unique_ptr<medialoader::Decoder> {
153 if (medialoader::WaveDecoder::accepts(e))
154 return tryMake<medialoader::WaveDecoder>(
ptr, size, bufferSize);
155 if (medialoader::VorbisDecoder::accepts(e))
156 return tryMake<medialoader::VorbisDecoder>(
ptr, size, bufferSize);
157 if (medialoader::Mpg123Decoder::accepts(e))
158 return tryMake<medialoader::Mpg123Decoder>(
ptr, size, bufferSize);
159 if (medialoader::FLACDecoder::accepts(e))
160 return tryMake<medialoader::FLACDecoder>(
ptr, size, bufferSize);
161 if (medialoader::ModPlugDecoder::accepts(e))
162 return tryMake<medialoader::ModPlugDecoder>(
ptr, size, bufferSize);
166 auto impl = tryByExt(ext);
168 if (!
impl)
impl = tryMake<medialoader::WaveDecoder>(
ptr, size, bufferSize);
169 if (!
impl)
impl = tryMake<medialoader::VorbisDecoder>(
ptr, size, bufferSize);
170 if (!
impl)
impl = tryMake<medialoader::Mpg123Decoder>(
ptr, size, bufferSize);
171 if (!
impl)
impl = tryMake<medialoader::FLACDecoder>(
ptr, size, bufferSize);
172 if (!
impl)
impl = tryMake<medialoader::ModPlugDecoder>(
ptr, size, bufferSize);
176 throw eve::Exception(
"Could not decode sound data: unsupported format");
178 return new Decoder(std::move(
impl), std::move(owned));
182 if (decoder ==
nullptr)
185 std::vector<uint8_t> pcm;
190 auto *buf =
static_cast<const uint8_t *
>(decoder->
getBuffer());
191 pcm.insert(pcm.end(), buf, buf +
n);
216 throw eve::Exception(
"Could not load sound file: %s", path.c_str());
217 return static_cast<SoundData *
>(resource);
223 size_t bytes =
static_cast<size_t>(samples) *
static_cast<size_t>(bitDepth / 8) *
static_cast<size_t>(channels);
224 std::vector<uint8_t> pcm(bytes, 0);
225 return new SoundData(std::move(pcm), rate, bitDepth, channels);
228void Sound::expose(ssq::Table& table) {
229 auto cls = table.addClass(
name, Sound::create,
false);
232 auto dec = table.addClass<
Decoder>(
233 "Decoder", std::function<Decoder *()>([]() ->
Decoder * {
return nullptr; }),
true);
245 auto sd = table.addClass<SoundData>(
246 "SoundData", std::function<SoundData *()>([]() -> SoundData * {
return nullptr; }),
true);
255void Sound::expose(ssq::Class&
cls) {
#define Module_IMPL(ModuleName, newExpr)
virtual std::string getName() const =0
Resource * get(std::string key)
Get the cached resource for key; on a miss, load it through the registered IAssetReloader providers a...
static ResourceManager & getInstance()
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Data buffer paired with a filename (used for type identification).
Streaming audio decoder (medialoader-backed). Owns the raw encoded bytes and decodes incrementally vi...
void * getBuffer() const
Internal decoded buffer.
double getDuration()
Total duration in seconds.
bool isFinished()
True when the stream is fully decoded.
int getSampleRate() const
bool isSeekable()
True when seeking is supported.
int getChannelCount() const
Format metadata.
bool rewind()
Rewinds to the start; false when unsupported.
bool seek(double seconds)
Seeks to a time in seconds; false when unsupported.
int getSize() const
Bytes decoded so far into the internal buffer.
int decode()
Decodes the next chunk; returns bytes produced (0 = end/error).
Raw PCM audio buffer with format metadata (samples/rate/bit depth/channels).
double getDuration() const
Duration in seconds.
int getChannelCount() const
int getSampleRate() const
int getSampleCount() const
Format metadata.
Sound module: decodes compressed audio and produces SoundData buffers. Script: sound <- eve....
SoundData * newSoundDataFromDecoder(Decoder *decoder)
Fully decodes a Decoder into a SoundData buffer.
SoundData * newSoundDataFromFile(std::string path)
Decodes a sound file from a VFS path through the unified resource cache. Repeated loads of one path s...
SoundData * newSoundData(Data *data)
Fully decodes data into a SoundData buffer.
SoundData * newSoundDataEmpty(int samples, int rate, int bitDepth, int channels)
Creates an empty PCM buffer (e.g. for synthesis).
Decoder * newDecoder(Data *data, int bufferSize=16384)
Creates a streaming decoder over raw encoded data.