mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Audio source decoding improvements.
- Streaming sources now also stream the file contents instead of loading it into memory, by default. Added a new stream type enum parameter to newSource ("file" or "memory").
- Audio file decoding now chooses the most appropriate decoder based on the file contents instead of the file extension.
- Videos now stream audio from the file instead of loading all of the video file into memory for use with audio decoding.
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "wrap_Audio.h"
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
|
||||
#include "openal/Audio.h"
|
||||
#include "null/Audio.h"
|
||||
@@ -57,8 +58,23 @@ int w_newSource(lua_State *L)
|
||||
return luaL_error(L, "Cannot create queueable sources using newSource. Use newQueueableSource instead.");
|
||||
}
|
||||
|
||||
if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type))
|
||||
luax_convobj(L, 1, "sound", "newDecoder");
|
||||
if (love::filesystem::luax_cangetdata(L, 1))
|
||||
{
|
||||
// stream type
|
||||
if (stype == Source::TYPE_STATIC)
|
||||
lua_pushstring(L, "memory");
|
||||
else if (!lua_isnone(L, 3))
|
||||
lua_pushvalue(L, 3);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
// buffer size
|
||||
lua_pushnil(L);
|
||||
|
||||
// (file, buffer size, stream type)
|
||||
int idxs[] = { 1, lua_gettop(L), lua_gettop(L) - 1 };
|
||||
luax_convobj(L, idxs, 3, "sound", "newDecoder");
|
||||
}
|
||||
|
||||
if (stype == Source::TYPE_STATIC && luax_istype(L, 1, love::sound::Decoder::type))
|
||||
luax_convobj(L, 1, "sound", "newSoundData");
|
||||
@@ -84,20 +100,17 @@ int w_newSource(lua_State *L)
|
||||
|
||||
int w_newQueueableSource(lua_State *L)
|
||||
{
|
||||
int samplerate = (int) luaL_checkinteger(L, 1);
|
||||
int bitdepth = (int) luaL_checkinteger(L, 2);
|
||||
int channels = (int) luaL_checkinteger(L, 3);
|
||||
int buffers = (int) luaL_optinteger(L, 4, 0);
|
||||
|
||||
Source *t = nullptr;
|
||||
luax_catchexcept(L, [&]() { t = instance()->newSource(samplerate, bitdepth, channels, buffers); });
|
||||
|
||||
luax_catchexcept(L, [&]() {
|
||||
t = instance()->newSource((int)luaL_checkinteger(L, 1), (int)luaL_checkinteger(L, 2), (int)luaL_checkinteger(L, 3), (int)luaL_optinteger(L, 4, 0));
|
||||
});
|
||||
|
||||
if (t != nullptr)
|
||||
{
|
||||
luax_pushtype(L, t);
|
||||
t->release();
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0; //all argument type errors are checked in above constructor
|
||||
luax_pushtype(L, t);
|
||||
t->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::vector<Source*> readSourceList(lua_State *L, int n)
|
||||
|
||||
@@ -33,7 +33,7 @@ function love.graphics.newVideo(file, settings)
|
||||
local source, success
|
||||
|
||||
if settings.audio ~= false and love.audio then
|
||||
success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream")
|
||||
success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream", "file")
|
||||
end
|
||||
if success then
|
||||
video:setSource(source)
|
||||
|
||||
@@ -29,13 +29,16 @@ namespace sound
|
||||
|
||||
love::Type Decoder::type("Decoder", &Object::type);
|
||||
|
||||
Decoder::Decoder(Data *data, int bufferSize)
|
||||
: data(data)
|
||||
Decoder::Decoder(Stream *stream, int bufferSize)
|
||||
: stream(stream)
|
||||
, bufferSize(bufferSize)
|
||||
, sampleRate(DEFAULT_SAMPLE_RATE)
|
||||
, buffer(0)
|
||||
, eof(false)
|
||||
{
|
||||
if (!stream->isReadable() || !stream->isSeekable())
|
||||
throw love::Exception("Decoder input stream must be readable and seekable.");
|
||||
|
||||
buffer = new char[bufferSize];
|
||||
}
|
||||
|
||||
@@ -65,5 +68,12 @@ bool Decoder::isFinished()
|
||||
return eof;
|
||||
}
|
||||
|
||||
STRINGMAP_CLASS_BEGIN(Decoder, Decoder::StreamSource, Decoder::STREAM_MAX_ENUM, streamSource)
|
||||
{
|
||||
{ "memory", Decoder::STREAM_MEMORY },
|
||||
{ "file", Decoder::STREAM_FILE },
|
||||
}
|
||||
STRINGMAP_CLASS_END(Decoder, Decoder::StreamSource, Decoder::STREAM_MAX_ENUM, streamSource)
|
||||
|
||||
} // sound
|
||||
} // love
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/Object.h"
|
||||
#include "filesystem/File.h"
|
||||
#include "common/Stream.h"
|
||||
#include "common/StringMap.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -39,9 +40,16 @@ class Decoder : public Object
|
||||
{
|
||||
public:
|
||||
|
||||
enum StreamSource
|
||||
{
|
||||
STREAM_MEMORY,
|
||||
STREAM_FILE,
|
||||
STREAM_MAX_ENUM
|
||||
};
|
||||
|
||||
static love::Type type;
|
||||
|
||||
Decoder(Data *data, int bufferSize);
|
||||
Decoder(Stream *stream, int bufferSize);
|
||||
virtual ~Decoder();
|
||||
|
||||
/**
|
||||
@@ -143,11 +151,12 @@ public:
|
||||
**/
|
||||
virtual double getDuration() = 0;
|
||||
|
||||
STRINGMAP_CLASS_DECLARE(StreamSource);
|
||||
|
||||
protected:
|
||||
|
||||
// The encoded data. This should be replaced with buffered file
|
||||
// reads in the future.
|
||||
StrongRef<Data> data;
|
||||
// A readable stream containing the encoded data.
|
||||
StrongRef<Stream> stream;
|
||||
|
||||
// When the decoder decodes data incrementally, it writes
|
||||
// this many bytes at a time (at most).
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/Module.h"
|
||||
#include "filesystem/File.h"
|
||||
#include "common/Stream.h"
|
||||
|
||||
#include "SoundData.h"
|
||||
#include "Decoder.h"
|
||||
@@ -83,11 +83,11 @@ public:
|
||||
/**
|
||||
* Attempts to find a decoder for the encoded sound data in the
|
||||
* specified file.
|
||||
* @param file The file with encoded sound data.
|
||||
* @param stream The readable Stream with encoded sound data.
|
||||
* @param bufferSize The size of each decoded chunk.
|
||||
* @return A Decoder object on success, or zero if no decoder could be found.
|
||||
**/
|
||||
virtual Decoder *newDecoder(filesystem::FileData *file, int bufferSize) = 0;
|
||||
virtual Decoder *newDecoder(Stream *stream, int bufferSize) = 0;
|
||||
|
||||
}; // Sound
|
||||
|
||||
|
||||
@@ -34,37 +34,23 @@ namespace lullaby
|
||||
{
|
||||
|
||||
// Callbacks
|
||||
namespace
|
||||
static OSStatus readFunc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount)
|
||||
{
|
||||
OSStatus readFunc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount)
|
||||
{
|
||||
Data *data = (Data *) inClientData;
|
||||
SInt64 bytesLeft = data->getSize() - inPosition;
|
||||
auto stream = (Stream *) inClientData;
|
||||
int64 readbytes = stream->read(buffer, requestCount);
|
||||
|
||||
if (bytesLeft > 0)
|
||||
{
|
||||
UInt32 actualSize = bytesLeft >= requestCount ? requestCount : (UInt32) bytesLeft;
|
||||
memcpy(buffer, (char *) data->getData() + inPosition, actualSize);
|
||||
*actualCount = actualSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
*actualCount = 0;
|
||||
return kAudioFilePositionError;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
*actualCount = (UInt32) readbytes;
|
||||
return readbytes > 0 ? noErr : kAudioFilePositionError;
|
||||
}
|
||||
|
||||
SInt64 getSizeFunc(void *inClientData)
|
||||
static SInt64 getSizeFunc(void *inClientData)
|
||||
{
|
||||
Data *data = (Data *) inClientData;
|
||||
return data->getSize();
|
||||
auto stream = (Stream *) inClientData;
|
||||
return stream->getSize();
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize)
|
||||
: Decoder(data, bufferSize)
|
||||
CoreAudioDecoder::CoreAudioDecoder(Stream *stream, int bufferSize)
|
||||
: Decoder(stream, bufferSize)
|
||||
, audioFile(nullptr)
|
||||
, extAudioFile(nullptr)
|
||||
, inputInfo()
|
||||
@@ -75,23 +61,23 @@ CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize)
|
||||
{
|
||||
OSStatus err = noErr;
|
||||
|
||||
// Open the file represented by the Data.
|
||||
err = AudioFileOpenWithCallbacks(data, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile);
|
||||
// Open the file represented by the Stream.
|
||||
err = AudioFileOpenWithCallbacks(stream, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile);
|
||||
if (err != noErr)
|
||||
throw love::Exception("Could open audio file for decoding.");
|
||||
throw love::Exception("Could open audio file for decoding with CoreAudio.");
|
||||
|
||||
// We want to use the Extended AudioFile API.
|
||||
err = ExtAudioFileWrapAudioFileID(audioFile, false, &extAudioFile);
|
||||
|
||||
if (err != noErr)
|
||||
throw love::Exception("Could open audio file for decoding.");
|
||||
throw love::Exception("Could open audio file for decoding with CoreAudio.");
|
||||
|
||||
// Get the format of the audio data.
|
||||
UInt32 propertySize = sizeof(inputInfo);
|
||||
err = ExtAudioFileGetProperty(extAudioFile, kExtAudioFileProperty_FileDataFormat, &propertySize, &inputInfo);
|
||||
|
||||
if (err != noErr)
|
||||
throw love::Exception("Could not determine file format.");
|
||||
throw love::Exception("Could not determine CoreAudio file format.");
|
||||
|
||||
// Set the output format to 16 bit signed integer (native-endian) data.
|
||||
// Keep the channel count and sample rate of the source format.
|
||||
@@ -116,7 +102,7 @@ CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize)
|
||||
err = ExtAudioFileSetProperty(extAudioFile, kExtAudioFileProperty_ClientDataFormat, propertySize, &outputInfo);
|
||||
|
||||
if (err != noErr)
|
||||
throw love::Exception("Could not set decoder properties.");
|
||||
throw love::Exception("Could not set CoreAudio decoder properties.");
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
@@ -143,59 +129,10 @@ void CoreAudioDecoder::closeAudioFile()
|
||||
audioFile = nullptr;
|
||||
}
|
||||
|
||||
bool CoreAudioDecoder::accepts(const std::string &ext)
|
||||
{
|
||||
UInt32 size = 0;
|
||||
std::vector<UInt32> types;
|
||||
|
||||
// Get the size in bytes of the type array we're about to get.
|
||||
OSStatus err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_ReadableTypes, sizeof(UInt32), nullptr, &size);
|
||||
if (err != noErr)
|
||||
return false;
|
||||
|
||||
types.resize(size / sizeof(UInt32));
|
||||
|
||||
// Get an array of supported types.
|
||||
err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ReadableTypes, 0, nullptr, &size, &types[0]);
|
||||
if (err != noErr)
|
||||
return false;
|
||||
|
||||
// Turn the extension string into a CFStringRef.
|
||||
CFStringRef extstr = CFStringCreateWithCString(nullptr, ext.c_str(), kCFStringEncodingUTF8);
|
||||
|
||||
CFArrayRef exts = nullptr;
|
||||
size = sizeof(CFArrayRef);
|
||||
|
||||
for (UInt32 type : types)
|
||||
{
|
||||
// Get the extension strings for the type.
|
||||
err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, sizeof(UInt32), &type, &size, &exts);
|
||||
if (err != noErr)
|
||||
continue;
|
||||
|
||||
// A type can have more than one extension string.
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(exts); i++)
|
||||
{
|
||||
CFStringRef value = (CFStringRef) CFArrayGetValueAtIndex(exts, i);
|
||||
|
||||
if (CFStringCompare(extstr, value, 0) == kCFCompareEqualTo)
|
||||
{
|
||||
CFRelease(extstr);
|
||||
CFRelease(exts);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(exts);
|
||||
}
|
||||
|
||||
CFRelease(extstr);
|
||||
return false;
|
||||
}
|
||||
|
||||
love::sound::Decoder *CoreAudioDecoder::clone()
|
||||
{
|
||||
return new CoreAudioDecoder(data.get(), bufferSize);
|
||||
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
|
||||
return new CoreAudioDecoder(s, bufferSize);
|
||||
}
|
||||
|
||||
int CoreAudioDecoder::decode()
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#ifdef LOVE_SUPPORT_COREAUDIO
|
||||
|
||||
// LOVE
|
||||
#include "common/Data.h"
|
||||
#include "common/Stream.h"
|
||||
#include "sound/Decoder.h"
|
||||
|
||||
// Core Audio
|
||||
@@ -47,19 +47,17 @@ class CoreAudioDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
CoreAudioDecoder(Data *data, int bufferSize);
|
||||
CoreAudioDecoder(Stream *stream, int bufferSize);
|
||||
virtual ~CoreAudioDecoder();
|
||||
|
||||
static bool accepts(const std::string &ext);
|
||||
|
||||
love::sound::Decoder *clone();
|
||||
int decode();
|
||||
bool seek(double s);
|
||||
bool rewind();
|
||||
bool isSeekable();
|
||||
int getChannelCount() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
love::sound::Decoder *clone() override;
|
||||
int decode() override;
|
||||
bool seek(double s) override;
|
||||
bool rewind() override;
|
||||
bool isSeekable() override;
|
||||
int getChannelCount() const override;
|
||||
int getBitDepth() const override;
|
||||
double getDuration() override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "FLACDecoder.h"
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "common/Exception.h"
|
||||
|
||||
namespace love
|
||||
@@ -31,10 +32,24 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
FLACDecoder::FLACDecoder(Data *data, int nbufferSize)
|
||||
: Decoder(data, nbufferSize)
|
||||
static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead)
|
||||
{
|
||||
flac = drflac_open_memory(data->getData(), data->getSize(), nullptr);
|
||||
auto stream = (Stream *) pUserData;
|
||||
int64 read = stream->read(pBufferOut, bytesToRead);
|
||||
return std::max<int64>(0, read);
|
||||
}
|
||||
|
||||
static drflac_bool32 onSeek(void* pUserData, int offset, drflac_seek_origin origin)
|
||||
{
|
||||
auto stream = (Stream *) pUserData;
|
||||
auto seekorigin = origin == drflac_seek_origin_current ? Stream::SEEKORIGIN_CURRENT : Stream::SEEKORIGIN_BEGIN;
|
||||
return stream->seek(offset, seekorigin) ? DRFLAC_TRUE : DRFLAC_FALSE;
|
||||
}
|
||||
|
||||
FLACDecoder::FLACDecoder(Stream *stream, int nbufferSize)
|
||||
: Decoder(stream, nbufferSize)
|
||||
{
|
||||
flac = drflac_open(onRead, onSeek, stream, nullptr);
|
||||
if (flac == nullptr)
|
||||
throw love::Exception("Could not load FLAC file");
|
||||
}
|
||||
@@ -44,29 +59,10 @@ FLACDecoder::~FLACDecoder()
|
||||
drflac_close(flac);
|
||||
}
|
||||
|
||||
bool FLACDecoder::accepts(const std::string &ext)
|
||||
{
|
||||
// dr_flac supports FLAC encapsulated in Ogg, but unfortunately
|
||||
// LOVE detects .ogg extension as Vorbis. It would be a good idea
|
||||
// to always probe in the future (see #1487 and commit ccf9e63).
|
||||
// Please remove once it's no longer the case.
|
||||
static const std::string supported[] =
|
||||
{
|
||||
"flac", "ogg"
|
||||
};
|
||||
|
||||
for (const auto& s : supported)
|
||||
{
|
||||
if (s.compare(ext) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
love::sound::Decoder *FLACDecoder::clone()
|
||||
{
|
||||
return new FLACDecoder(data.get(), bufferSize);
|
||||
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
|
||||
return new FLACDecoder(s, bufferSize);
|
||||
}
|
||||
|
||||
int FLACDecoder::decode()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define LOVE_SOUND_LULLABY_FLAC_DECODER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/Data.h"
|
||||
#include "common/Stream.h"
|
||||
#include "sound/Decoder.h"
|
||||
|
||||
#include "dr/dr_flac.h"
|
||||
@@ -38,23 +38,22 @@ namespace lullaby
|
||||
class FLACDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
FLACDecoder(Data *data, int bufferSize);
|
||||
FLACDecoder(Stream *stream, int bufferSize);
|
||||
~FLACDecoder();
|
||||
|
||||
static bool accepts(const std::string &ext);
|
||||
love::sound::Decoder *clone();
|
||||
int decode();
|
||||
bool seek(double s);
|
||||
bool rewind();
|
||||
bool isSeekable();
|
||||
int getChannelCount() const;
|
||||
int getBitDepth() const;
|
||||
int getSampleRate() const;
|
||||
double getDuration();
|
||||
love::sound::Decoder *clone() override;
|
||||
int decode() override;
|
||||
bool seek(double s) override;
|
||||
bool rewind() override;
|
||||
bool isSeekable() override;
|
||||
int getChannelCount() const override;
|
||||
int getBitDepth() const override;
|
||||
int getSampleRate() const override;
|
||||
double getDuration() override;
|
||||
|
||||
private:
|
||||
drflac *flac;
|
||||
}; // Decoder
|
||||
}; // FLACDecoder
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define DR_MP3_IMPLEMENTATION
|
||||
#define DR_MP3_NO_STDIO
|
||||
#include "MP3Decoder.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -29,11 +30,25 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
MP3Decoder::MP3Decoder(Data *data, int bufferSize)
|
||||
: Decoder(data, bufferSize)
|
||||
static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead)
|
||||
{
|
||||
auto stream = (Stream *) pUserData;
|
||||
int64 read = stream->read(pBufferOut, bytesToRead);
|
||||
return std::max<int64>(0, read);
|
||||
}
|
||||
|
||||
static drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin)
|
||||
{
|
||||
auto stream = (Stream *) pUserData;
|
||||
auto seekorigin = origin == drmp3_seek_origin_current ? Stream::SEEKORIGIN_CURRENT : Stream::SEEKORIGIN_BEGIN;
|
||||
return stream->seek(offset, seekorigin) ? DRMP3_TRUE : DRMP3_FALSE;
|
||||
}
|
||||
|
||||
MP3Decoder::MP3Decoder(Stream *stream, int bufferSize)
|
||||
: Decoder(stream, bufferSize)
|
||||
{
|
||||
// initialize mp3 handle
|
||||
if(drmp3_init_memory(&mp3, data->getData(), data->getSize(), nullptr, nullptr) == 0)
|
||||
if (!drmp3_init(&mp3, onRead, onSeek, stream, nullptr, nullptr))
|
||||
throw love::Exception("Could not read mp3 data.");
|
||||
|
||||
sampleRate = mp3.sampleRate;
|
||||
@@ -43,7 +58,7 @@ MP3Decoder::MP3Decoder(Data *data, int bufferSize)
|
||||
if (!drmp3_get_mp3_and_pcm_frame_count(&mp3, &mp3FrameCount, &pcmCount))
|
||||
{
|
||||
drmp3_uninit(&mp3);
|
||||
throw love::Exception("Could not calculate duration.");
|
||||
throw love::Exception("Could not calculate mp3 duration.");
|
||||
}
|
||||
duration = ((double) pcmCount) / ((double) mp3.sampleRate);
|
||||
|
||||
@@ -53,7 +68,7 @@ MP3Decoder::MP3Decoder(Data *data, int bufferSize)
|
||||
if (!drmp3_calculate_seek_points(&mp3, &mp3FrameInt, seekTable.data()))
|
||||
{
|
||||
drmp3_uninit(&mp3);
|
||||
throw love::Exception("Could not calculate seek table");
|
||||
throw love::Exception("Could not calculate mp3 seek table");
|
||||
}
|
||||
mp3FrameInt = mp3FrameInt > mp3FrameCount ? mp3FrameCount : mp3FrameInt;
|
||||
|
||||
@@ -61,7 +76,7 @@ MP3Decoder::MP3Decoder(Data *data, int bufferSize)
|
||||
if (!drmp3_bind_seek_table(&mp3, mp3FrameInt, seekTable.data()))
|
||||
{
|
||||
drmp3_uninit(&mp3);
|
||||
throw love::Exception("Could not bind seek table");
|
||||
throw love::Exception("Could not bind mp3 seek table");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,25 +85,10 @@ MP3Decoder::~MP3Decoder()
|
||||
drmp3_uninit(&mp3);
|
||||
}
|
||||
|
||||
bool MP3Decoder::accepts(const std::string &ext)
|
||||
{
|
||||
static const std::string supported[] =
|
||||
{
|
||||
"mp3"
|
||||
};
|
||||
|
||||
for (const auto& s : supported)
|
||||
{
|
||||
if (s.compare(ext) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
love::sound::Decoder *MP3Decoder::clone()
|
||||
{
|
||||
return new MP3Decoder(data, bufferSize);
|
||||
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
|
||||
return new MP3Decoder(s, bufferSize);
|
||||
}
|
||||
|
||||
int MP3Decoder::decode()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define LOVE_SOUND_LULLABY_MP3_DECODER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/Data.h"
|
||||
#include "common/Stream.h"
|
||||
#include "sound/Decoder.h"
|
||||
|
||||
// dr_mp3
|
||||
@@ -41,18 +41,17 @@ class MP3Decoder: public love::sound::Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
MP3Decoder(Data *data, int bufsize);
|
||||
MP3Decoder(Stream *stream, int bufsize);
|
||||
virtual ~MP3Decoder();
|
||||
|
||||
static bool accepts(const std::string &ext);
|
||||
love::sound::Decoder *clone();
|
||||
int decode();
|
||||
bool seek(double s);
|
||||
bool rewind();
|
||||
bool isSeekable();
|
||||
int getChannelCount() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
love::sound::Decoder *clone() override;
|
||||
int decode() override;
|
||||
bool seek(double s) override;
|
||||
bool rewind() override;
|
||||
bool isSeekable() override;
|
||||
int getChannelCount() const override;
|
||||
int getBitDepth() const override;
|
||||
double getDuration() override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifndef LOVE_NO_MODPLUG
|
||||
|
||||
#include "common/Exception.h"
|
||||
#include "common/Data.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -31,12 +32,11 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
ModPlugDecoder::ModPlugDecoder(Data *data, int bufferSize)
|
||||
: Decoder(data, bufferSize)
|
||||
ModPlugDecoder::ModPlugDecoder(Stream *stream, int bufferSize)
|
||||
: Decoder(stream, bufferSize)
|
||||
, plug(0)
|
||||
, duration(-2.0)
|
||||
{
|
||||
|
||||
// Set some ModPlug settings.
|
||||
settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
|
||||
settings.mChannels = 2;
|
||||
@@ -60,10 +60,29 @@ ModPlugDecoder::ModPlugDecoder(Data *data, int bufferSize)
|
||||
|
||||
ModPlug_SetSettings(&settings);
|
||||
|
||||
// Load the module.
|
||||
plug = ModPlug_Load(data->getData(), (int) data->getSize());
|
||||
// ModPlug has no streaming API. Miserable.
|
||||
// We don't want to load the entire stream immediately if it's big, because
|
||||
// it might not be compatible with ModPlug. So we just try to load 4MB and
|
||||
// see if that works, and then load the whole thing if it does.
|
||||
if (stream->getSize() > 1024 * 1024 * 4)
|
||||
{
|
||||
data.set(stream->read(1024 * 1024 * 4), Acquire::NORETAIN);
|
||||
|
||||
if (plug == 0)
|
||||
plug = ModPlug_Load(data->getData(), (int)data->getSize());
|
||||
|
||||
if (plug == nullptr)
|
||||
throw love::Exception("Could not load file with ModPlug.");
|
||||
|
||||
stream->seek(0);
|
||||
ModPlug_Unload(plug);
|
||||
}
|
||||
|
||||
data.set(stream->read(stream->getSize()), Acquire::NORETAIN);
|
||||
|
||||
// Load the module.
|
||||
plug = ModPlug_Load(data->getData(), (int)data->getSize());
|
||||
|
||||
if (plug == nullptr)
|
||||
throw love::Exception("Could not load file with ModPlug.");
|
||||
|
||||
// set master volume for delicate ears
|
||||
@@ -72,32 +91,14 @@ ModPlugDecoder::ModPlugDecoder(Data *data, int bufferSize)
|
||||
|
||||
ModPlugDecoder::~ModPlugDecoder()
|
||||
{
|
||||
if (plug != 0)
|
||||
if (plug != nullptr)
|
||||
ModPlug_Unload(plug);
|
||||
}
|
||||
|
||||
bool ModPlugDecoder::accepts(const std::string &ext)
|
||||
{
|
||||
static const std::string supported[] =
|
||||
{
|
||||
"699", "abc", "amf", "ams", "dbm", "dmf",
|
||||
"dsm", "far", "it", "j2b", "mdl", "med",
|
||||
"mid", "mod", "mt2", "mtm", "okt", "pat",
|
||||
"psm", "s3m", "stm", "ult", "umx", "xm",
|
||||
};
|
||||
|
||||
for (const auto &s : supported)
|
||||
{
|
||||
if (s.compare(ext) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
love::sound::Decoder *ModPlugDecoder::clone()
|
||||
{
|
||||
return new ModPlugDecoder(data.get(), bufferSize);
|
||||
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
|
||||
return new ModPlugDecoder(s, bufferSize);
|
||||
}
|
||||
|
||||
int ModPlugDecoder::decode()
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#ifndef LOVE_NO_MODPLUG
|
||||
|
||||
// LOVE
|
||||
#include "common/Data.h"
|
||||
#include "common/Stream.h"
|
||||
#include "sound/Decoder.h"
|
||||
|
||||
// libmodplug
|
||||
@@ -47,28 +47,28 @@ class ModPlugDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
ModPlugDecoder(Data *data, int bufferSize);
|
||||
ModPlugDecoder(Stream *stream, int bufferSize);
|
||||
virtual ~ModPlugDecoder();
|
||||
|
||||
static bool accepts(const std::string &ext);
|
||||
|
||||
love::sound::Decoder *clone();
|
||||
int decode();
|
||||
bool seek(double s);
|
||||
bool rewind();
|
||||
bool isSeekable();
|
||||
int getChannelCount() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
love::sound::Decoder *clone() override;
|
||||
int decode() override;
|
||||
bool seek(double s) override;
|
||||
bool rewind() override;
|
||||
bool isSeekable() override;
|
||||
int getChannelCount() const override;
|
||||
int getBitDepth() const override;
|
||||
double getDuration() override;
|
||||
|
||||
private:
|
||||
|
||||
StrongRef<Data> data;
|
||||
|
||||
ModPlugFile *plug;
|
||||
ModPlug_Settings settings;
|
||||
|
||||
double duration;
|
||||
|
||||
}; // Decoder
|
||||
}; // ModPlugDecoder
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
|
||||
@@ -37,21 +37,16 @@
|
||||
|
||||
struct DecoderImpl
|
||||
{
|
||||
love::sound::Decoder *(*create)(love::filesystem::FileData *data, int bufferSize);
|
||||
bool (*accepts)(const std::string& ext);
|
||||
love::sound::Decoder *(*create)(love::Stream *stream, int bufferSize);
|
||||
};
|
||||
|
||||
template<typename DecoderType>
|
||||
DecoderImpl DecoderImplFor()
|
||||
{
|
||||
DecoderImpl decoderImpl;
|
||||
decoderImpl.create = [](love::filesystem::FileData *data, int bufferSize) -> love::sound::Decoder*
|
||||
decoderImpl.create = [](love::Stream *stream, int bufferSize) -> love::sound::Decoder*
|
||||
{
|
||||
return new DecoderType(data, bufferSize);
|
||||
};
|
||||
decoderImpl.accepts = [](const std::string& ext) -> bool
|
||||
{
|
||||
return DecoderType::accepts(ext);
|
||||
return new DecoderType(stream, bufferSize);
|
||||
};
|
||||
return decoderImpl;
|
||||
}
|
||||
@@ -76,15 +71,9 @@ const char *Sound::getName() const
|
||||
return "love.sound.lullaby";
|
||||
}
|
||||
|
||||
sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSize)
|
||||
sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize)
|
||||
{
|
||||
std::string ext = data->getExtension();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
|
||||
|
||||
std::vector<DecoderImpl> possibleDecoders = {
|
||||
#ifndef LOVE_NO_MODPLUG
|
||||
DecoderImplFor<ModPlugDecoder>(),
|
||||
#endif // LOVE_NO_MODPLUG
|
||||
DecoderImplFor<MP3Decoder>(),
|
||||
DecoderImplFor<VorbisDecoder>(),
|
||||
#ifdef LOVE_SUPPORT_COREAUDIO
|
||||
@@ -92,24 +81,19 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi
|
||||
#endif
|
||||
DecoderImplFor<WaveDecoder>(),
|
||||
DecoderImplFor<FLACDecoder>(),
|
||||
// DecoderImplFor<OtherDecoder>(),
|
||||
#ifndef LOVE_NO_MODPLUG
|
||||
DecoderImplFor<ModPlugDecoder>(), // Last because it doesn't work well with Streams.
|
||||
#endif
|
||||
};
|
||||
|
||||
// First find a matching decoder based on extension
|
||||
for (DecoderImpl &possibleDecoder : possibleDecoders)
|
||||
{
|
||||
if (possibleDecoder.accepts(ext))
|
||||
return possibleDecoder.create(data, bufferSize);
|
||||
}
|
||||
|
||||
// If that fails, start probing instead
|
||||
std::stringstream decodingErrors;
|
||||
decodingErrors << "Failed to determine file type:\n";
|
||||
for (DecoderImpl &possibleDecoder : possibleDecoders)
|
||||
{
|
||||
try
|
||||
{
|
||||
sound::Decoder *decoder = possibleDecoder.create(data, bufferSize);
|
||||
stream->seek(0);
|
||||
sound::Decoder *decoder = possibleDecoder.create(stream, bufferSize);
|
||||
return decoder;
|
||||
}
|
||||
catch (love::Exception &e)
|
||||
@@ -118,8 +102,8 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi
|
||||
}
|
||||
}
|
||||
|
||||
// Probing failed too, bail with the accumulated errors
|
||||
throw love::Exception(decodingErrors.str().c_str());
|
||||
std::string errors = decodingErrors.str();
|
||||
throw love::Exception("No suitable audio decoders found.\n%s", errors.c_str());
|
||||
|
||||
// Unreachable, but here to prevent (possible) warnings
|
||||
return nullptr;
|
||||
|
||||
@@ -45,21 +45,14 @@ class Sound : public love::sound::Sound
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor. Initializes relevant libraries.
|
||||
**/
|
||||
Sound();
|
||||
|
||||
/**
|
||||
* Destructor. Deinitializes relevant libraries.
|
||||
**/
|
||||
virtual ~Sound();
|
||||
|
||||
/// @copydoc love::Module::getName
|
||||
const char *getName() const;
|
||||
const char *getName() const override;
|
||||
|
||||
/// @copydoc love::sound::Sound::newDecoder
|
||||
sound::Decoder *newDecoder(love::filesystem::FileData *file, int bufferSize);
|
||||
sound::Decoder *newDecoder(Stream *stream, int bufferSize) override;
|
||||
|
||||
}; // Sound
|
||||
|
||||
|
||||
@@ -31,132 +31,65 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
/**
|
||||
* CALLBACK FUNCTIONS
|
||||
**/
|
||||
static int vorbisClose(void * /* ptr to the data that the vorbis files need*/)
|
||||
static int vorbisClose(void *)
|
||||
{
|
||||
// Does nothing (handled elsewhere)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t vorbisRead(void *ptr /* ptr to the data that the vorbis files need*/,
|
||||
size_t byteSize /* how big a byte is*/,
|
||||
size_t sizeToRead /* How much we can read*/,
|
||||
void *datasource /* this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct*/)
|
||||
static size_t vorbisRead(void *ptr, size_t byteSize, size_t sizeToRead, void *datasource)
|
||||
{
|
||||
size_t spaceToEOF; // How much more we can read till we hit the EOF marker
|
||||
size_t actualSizeToRead; // How much data we are actually going to read from memory
|
||||
SOggFile *vorbisData; // Our vorbis data, for the typecast
|
||||
|
||||
// Get the data in the right format
|
||||
vorbisData = (SOggFile *)datasource;
|
||||
|
||||
// Calculate how much we need to read. This can be sizeToRead*byteSize or less depending on how near the EOF marker we are
|
||||
spaceToEOF = vorbisData->dataSize - vorbisData->dataRead;
|
||||
if ((sizeToRead*byteSize) < spaceToEOF)
|
||||
actualSizeToRead = (sizeToRead*byteSize);
|
||||
else
|
||||
actualSizeToRead = spaceToEOF;
|
||||
|
||||
// A simple copy of the data from memory to the datastruct that the vorbis libs will use
|
||||
if (actualSizeToRead)
|
||||
{
|
||||
// Copy the data from the start of the file PLUS how much we have already read in
|
||||
memcpy(ptr, (const char *)vorbisData->dataPtr + vorbisData->dataRead, actualSizeToRead);
|
||||
// Increase by how much we have read by
|
||||
vorbisData->dataRead += (actualSizeToRead);
|
||||
}
|
||||
|
||||
// Return how much we read (in the same way fread would)
|
||||
return actualSizeToRead;
|
||||
auto stream = (Stream *) datasource;
|
||||
return stream->read(ptr, byteSize * sizeToRead);
|
||||
}
|
||||
|
||||
static int vorbisSeek(void *datasource /* ptr to the data that the vorbis files need*/,
|
||||
ogg_int64_t offset /*offset from the point we wish to seek to*/,
|
||||
int whence /*where we want to seek to*/)
|
||||
static int vorbisSeek(void *datasource, ogg_int64_t offset, int whence)
|
||||
{
|
||||
int64 spaceToEOF; // How much more we can read till we hit the EOF marker
|
||||
int64 actualOffset; // How much we can actually offset it by
|
||||
SOggFile *vorbisData; // The data we passed in (for the typecast)
|
||||
auto stream = (Stream *) datasource;
|
||||
auto origin = Stream::SEEKORIGIN_BEGIN;
|
||||
|
||||
// Get the data in the right format
|
||||
vorbisData = (SOggFile *) datasource;
|
||||
|
||||
// Goto where we wish to seek to
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET: // Seek to the start of the data file
|
||||
// Make sure we are not going to the end of the file
|
||||
if (vorbisData->dataSize >= offset)
|
||||
actualOffset = offset;
|
||||
else
|
||||
actualOffset = vorbisData->dataSize;
|
||||
// Set where we now are
|
||||
vorbisData->dataRead = (int)actualOffset;
|
||||
case SEEK_SET:
|
||||
origin = Stream::SEEKORIGIN_BEGIN;
|
||||
break;
|
||||
case SEEK_CUR: // Seek from where we are
|
||||
// Make sure we dont go past the end
|
||||
spaceToEOF = vorbisData->dataSize - vorbisData->dataRead;
|
||||
if (offset < spaceToEOF)
|
||||
actualOffset = (offset);
|
||||
else
|
||||
actualOffset = spaceToEOF;
|
||||
// Seek from our currrent location
|
||||
vorbisData->dataRead += actualOffset;
|
||||
case SEEK_CUR:
|
||||
origin = Stream::SEEKORIGIN_CURRENT;
|
||||
break;
|
||||
case SEEK_END: // Seek from the end of the file
|
||||
if (offset < 0)
|
||||
vorbisData->dataRead = vorbisData->dataSize + offset;
|
||||
else
|
||||
vorbisData->dataRead = vorbisData->dataSize;
|
||||
case SEEK_END:
|
||||
origin = Stream::SEEKORIGIN_END;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
return stream->seek(offset, origin) ? 0 : -1;
|
||||
}
|
||||
|
||||
static long vorbisTell(void *datasource /* ptr to the data that the vorbis files need*/)
|
||||
static long vorbisTell(void *datasource)
|
||||
{
|
||||
SOggFile *vorbisData;
|
||||
vorbisData = (SOggFile *) datasource;
|
||||
return vorbisData->dataRead;
|
||||
auto stream = (Stream *) datasource;
|
||||
return (long) stream->tell();
|
||||
}
|
||||
/**
|
||||
* END CALLBACK FUNCTIONS
|
||||
**/
|
||||
|
||||
VorbisDecoder::VorbisDecoder(Data *data, int bufferSize)
|
||||
: Decoder(data, bufferSize)
|
||||
VorbisDecoder::VorbisDecoder(Stream *stream, int bufferSize)
|
||||
: Decoder(stream, bufferSize)
|
||||
, duration(-2.0)
|
||||
{
|
||||
// Initialize callbacks
|
||||
vorbisCallbacks.close_func = vorbisClose;
|
||||
vorbisCallbacks.seek_func = vorbisSeek;
|
||||
vorbisCallbacks.read_func = vorbisRead;
|
||||
vorbisCallbacks.tell_func = vorbisTell;
|
||||
|
||||
// Check endianness
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
endian = 1;
|
||||
#else
|
||||
endian = 0;
|
||||
#endif
|
||||
|
||||
// Initialize OGG file
|
||||
oggFile.dataPtr = (const char *) data->getData();
|
||||
oggFile.dataSize = data->getSize();
|
||||
oggFile.dataRead = 0;
|
||||
ov_callbacks callbacks = {};
|
||||
callbacks.close_func = vorbisClose;
|
||||
callbacks.seek_func = vorbisSeek;
|
||||
callbacks.read_func = vorbisRead;
|
||||
callbacks.tell_func = vorbisTell;
|
||||
|
||||
// Open Vorbis handle
|
||||
if (ov_open_callbacks(&oggFile, &handle, NULL, 0, vorbisCallbacks) < 0)
|
||||
if (ov_open_callbacks(stream, &handle, nullptr, 0, callbacks) < 0)
|
||||
throw love::Exception("Could not read Ogg bitstream");
|
||||
|
||||
// Get info and comments
|
||||
vorbisInfo = ov_info(&handle, -1);
|
||||
vorbisComment = ov_comment(&handle, -1);
|
||||
}
|
||||
|
||||
VorbisDecoder::~VorbisDecoder()
|
||||
@@ -164,31 +97,22 @@ VorbisDecoder::~VorbisDecoder()
|
||||
ov_clear(&handle);
|
||||
}
|
||||
|
||||
bool VorbisDecoder::accepts(const std::string &ext)
|
||||
{
|
||||
static const std::string supported[] =
|
||||
{
|
||||
"ogg", "oga", "ogv"
|
||||
};
|
||||
|
||||
for (const auto& s : supported)
|
||||
{
|
||||
if (s.compare(ext) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
love::sound::Decoder *VorbisDecoder::clone()
|
||||
{
|
||||
return new VorbisDecoder(data.get(), bufferSize);
|
||||
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
|
||||
return new VorbisDecoder(s, bufferSize);
|
||||
}
|
||||
|
||||
int VorbisDecoder::decode()
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
int endian = 1;
|
||||
#else
|
||||
int endian = 0;
|
||||
#endif
|
||||
|
||||
while (size < bufferSize)
|
||||
{
|
||||
long result = ov_read(&handle, (char *) buffer + size, bufferSize - size, endian, (getBitDepth() == 16 ? 2 : 1), 1, 0);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define LOVE_SOUND_LULLABY_VORBIS_DECODER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/Data.h"
|
||||
#include "common/Stream.h"
|
||||
#include "common/int.h"
|
||||
#include "sound/Decoder.h"
|
||||
|
||||
@@ -38,41 +38,29 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
// Struct for handling data
|
||||
struct SOggFile
|
||||
{
|
||||
const char *dataPtr; // Pointer to the data in memory
|
||||
int64 dataSize; // Size of the data
|
||||
int64 dataRead; // How much we've read so far
|
||||
};
|
||||
|
||||
class VorbisDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
VorbisDecoder(Data *data, int bufferSize);
|
||||
VorbisDecoder(Stream *stream, int bufferSize);
|
||||
virtual ~VorbisDecoder();
|
||||
|
||||
static bool accepts(const std::string &ext);
|
||||
|
||||
love::sound::Decoder *clone();
|
||||
int decode();
|
||||
bool seek(double s);
|
||||
bool rewind();
|
||||
bool isSeekable();
|
||||
int getChannelCount() const;
|
||||
int getBitDepth() const;
|
||||
int getSampleRate() const;
|
||||
double getDuration();
|
||||
love::sound::Decoder *clone() override;
|
||||
int decode() override;
|
||||
bool seek(double s) override;
|
||||
bool rewind() override;
|
||||
bool isSeekable() override;
|
||||
int getChannelCount() const override;
|
||||
int getBitDepth() const override;
|
||||
int getSampleRate() const override;
|
||||
double getDuration() override;
|
||||
|
||||
private:
|
||||
SOggFile oggFile; // (see struct)
|
||||
ov_callbacks vorbisCallbacks; // Callbacks used to read the file from mem
|
||||
OggVorbis_File handle; // Handle to the file
|
||||
vorbis_info *vorbisInfo; // Info
|
||||
vorbis_comment *vorbisComment; // Comments
|
||||
int endian; // Endianness
|
||||
|
||||
OggVorbis_File handle;
|
||||
vorbis_info *vorbisInfo;
|
||||
double duration;
|
||||
|
||||
}; // VorbisDecoder
|
||||
|
||||
} // lullaby
|
||||
|
||||
@@ -34,40 +34,32 @@ namespace lullaby
|
||||
// Callbacks
|
||||
static wuff_sint32 read_callback(void *userdata, wuff_uint8 *buffer, size_t *size)
|
||||
{
|
||||
WaveFile *input = (WaveFile *) userdata;
|
||||
size_t bytes_left = input->size - input->offset;
|
||||
size_t target_size = *size < bytes_left ? *size : bytes_left;
|
||||
memcpy(buffer, input->data + input->offset, target_size);
|
||||
input->offset += target_size;
|
||||
*size = target_size;
|
||||
auto stream = (Stream *) userdata;
|
||||
size_t readsize = stream->read(buffer, *size);
|
||||
*size = readsize;
|
||||
return WUFF_SUCCESS;
|
||||
}
|
||||
|
||||
static wuff_sint32 seek_callback(void *userdata, wuff_uint64 offset)
|
||||
{
|
||||
WaveFile *input = (WaveFile *)userdata;
|
||||
input->offset = (size_t) (offset < input->size ? offset : input->size);
|
||||
auto stream = (Stream *) userdata;
|
||||
stream->seek(offset, Stream::SEEKORIGIN_BEGIN);
|
||||
return WUFF_SUCCESS;
|
||||
}
|
||||
|
||||
static wuff_sint32 tell_callback(void *userdata, wuff_uint64 *offset)
|
||||
{
|
||||
WaveFile *input = (WaveFile *)userdata;
|
||||
*offset = input->offset;
|
||||
auto stream = (Stream *) userdata;
|
||||
*offset = stream->tell();
|
||||
return WUFF_SUCCESS;
|
||||
}
|
||||
|
||||
wuff_callback WaveDecoderCallbacks = {read_callback, seek_callback, tell_callback};
|
||||
static wuff_callback WaveDecoderCallbacks = {read_callback, seek_callback, tell_callback};
|
||||
|
||||
|
||||
WaveDecoder::WaveDecoder(Data *data, int bufferSize)
|
||||
: Decoder(data, bufferSize)
|
||||
WaveDecoder::WaveDecoder(Stream *stream, int bufferSize)
|
||||
: Decoder(stream, bufferSize)
|
||||
{
|
||||
dataFile.data = (char *) data->getData();
|
||||
dataFile.size = data->getSize();
|
||||
dataFile.offset = 0;
|
||||
|
||||
int wuff_status = wuff_open(&handle, &WaveDecoderCallbacks, &dataFile);
|
||||
int wuff_status = wuff_open(&handle, &WaveDecoderCallbacks, stream);
|
||||
if (wuff_status < 0)
|
||||
throw love::Exception("Could not open WAVE");
|
||||
|
||||
@@ -78,13 +70,13 @@ WaveDecoder::WaveDecoder(Data *data, int bufferSize)
|
||||
throw love::Exception("Could not retrieve WAVE stream info");
|
||||
|
||||
if (info.channels > 2)
|
||||
throw love::Exception("Multichannel audio not supported");
|
||||
throw love::Exception("WAVE Multichannel audio not supported");
|
||||
|
||||
if (info.format != WUFF_FORMAT_PCM_U8 && info.format != WUFF_FORMAT_PCM_S16)
|
||||
{
|
||||
wuff_status = wuff_format(handle, WUFF_FORMAT_PCM_S16);
|
||||
if (wuff_status < 0)
|
||||
throw love::Exception("Could not set output format");
|
||||
throw love::Exception("Could not set WAVE output format");
|
||||
}
|
||||
}
|
||||
catch (love::Exception &)
|
||||
@@ -99,25 +91,10 @@ WaveDecoder::~WaveDecoder()
|
||||
wuff_close(handle);
|
||||
}
|
||||
|
||||
bool WaveDecoder::accepts(const std::string &ext)
|
||||
{
|
||||
static const std::string supported[] =
|
||||
{
|
||||
"wav"
|
||||
};
|
||||
|
||||
for (const auto& s : supported)
|
||||
{
|
||||
if (s.compare(ext) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
love::sound::Decoder *WaveDecoder::clone()
|
||||
{
|
||||
return new WaveDecoder(data.get(), bufferSize);
|
||||
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
|
||||
return new WaveDecoder(s, bufferSize);
|
||||
}
|
||||
|
||||
int WaveDecoder::decode()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define LOVE_SOUND_LULLABY_WAVE_DECODER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/Data.h"
|
||||
#include "common/Stream.h"
|
||||
#include "sound/Decoder.h"
|
||||
|
||||
#include "libraries/Wuff/wuff.h"
|
||||
@@ -34,36 +34,25 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
// Struct for handling data
|
||||
struct WaveFile
|
||||
{
|
||||
char *data;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
class WaveDecoder : public Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
WaveDecoder(Data *data, int bufferSize);
|
||||
WaveDecoder(Stream *stream, int bufferSize);
|
||||
virtual ~WaveDecoder();
|
||||
|
||||
static bool accepts(const std::string &ext);
|
||||
|
||||
love::sound::Decoder *clone();
|
||||
int decode();
|
||||
bool seek(double s);
|
||||
bool rewind();
|
||||
bool isSeekable();
|
||||
int getChannelCount() const;
|
||||
int getBitDepth() const;
|
||||
int getSampleRate() const;
|
||||
double getDuration();
|
||||
love::sound::Decoder *clone() override;
|
||||
int decode() override;
|
||||
bool seek(double s) override;
|
||||
bool rewind() override;
|
||||
bool isSeekable() override;
|
||||
int getChannelCount() const override;
|
||||
int getBitDepth() const override;
|
||||
int getSampleRate() const override;
|
||||
double getDuration() override;
|
||||
|
||||
private:
|
||||
|
||||
WaveFile dataFile;
|
||||
wuff_handle *handle;
|
||||
wuff_info info;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "wrap_Sound.h"
|
||||
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
#include "data/DataStream.h"
|
||||
|
||||
// Implementations.
|
||||
#include "lullaby/Sound.h"
|
||||
@@ -34,18 +35,50 @@ namespace sound
|
||||
|
||||
int w_newDecoder(lua_State *L)
|
||||
{
|
||||
love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1);
|
||||
int bufferSize = (int) luaL_optinteger(L, 2, Decoder::DEFAULT_BUFFER_SIZE);
|
||||
int bufferSize = (int)luaL_optinteger(L, 2, Decoder::DEFAULT_BUFFER_SIZE);
|
||||
love::Stream *stream = nullptr;
|
||||
|
||||
if (love::filesystem::luax_cangetfile(L, 1))
|
||||
{
|
||||
Decoder::StreamSource source = Decoder::STREAM_FILE;
|
||||
|
||||
const char* sourcestr = lua_isnoneornil(L, 3) ? nullptr : luaL_checkstring(L, 3);
|
||||
if (sourcestr != nullptr && !Decoder::getConstant(sourcestr, source))
|
||||
return luax_enumerror(L, "stream type", Decoder::getConstants(source), sourcestr);
|
||||
|
||||
if (source == Decoder::STREAM_FILE)
|
||||
{
|
||||
auto file = love::filesystem::luax_getfile(L, 1);
|
||||
luax_catchexcept(L, [&]() { file->open(love::filesystem::File::MODE_READ); });
|
||||
stream = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
luax_catchexcept(L, [&]()
|
||||
{
|
||||
StrongRef<love::filesystem::FileData> data(love::filesystem::luax_getfiledata(L, 1), Acquire::NORETAIN);
|
||||
stream = new data::DataStream(data);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else if (luax_istype(L, 1, Data::type))
|
||||
{
|
||||
Data *data = luax_checktype<Data>(L, 1);
|
||||
luax_catchexcept(L, [&]() { stream = new data::DataStream(data); });
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = luax_checktype<Stream>(L, 1);
|
||||
stream->retain();
|
||||
}
|
||||
|
||||
Decoder *t = nullptr;
|
||||
luax_catchexcept(L,
|
||||
[&]() { t = instance()->newDecoder(data, bufferSize); },
|
||||
[&](bool) { data->release(); }
|
||||
[&]() { t = instance()->newDecoder(stream, bufferSize); },
|
||||
[&](bool) { stream->release(); }
|
||||
);
|
||||
|
||||
if (t == nullptr)
|
||||
return luaL_error(L, "Extension \"%s\" not supported.", data->getExtension().c_str());
|
||||
|
||||
luax_pushtype(L, t);
|
||||
t->release();
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user