mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Added Source:getDuration (thanks Boolsheet!)
For streaming sources, the duration may not always be sample-accurate and may return -1 if it cannot be determined at all.
This commit is contained in:
@@ -39,7 +39,7 @@ public:
|
||||
TYPE_STATIC,
|
||||
TYPE_STREAM,
|
||||
TYPE_MAX_ENUM
|
||||
}; // Type
|
||||
};
|
||||
|
||||
enum Unit
|
||||
{
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
|
||||
virtual void seek(float offset, Unit unit) = 0;
|
||||
virtual float tell(Unit unit) = 0;
|
||||
virtual double getDuration(Unit unit) = 0;
|
||||
|
||||
// all float * v must be of size 3
|
||||
virtual void setPosition(float *v) = 0;
|
||||
|
||||
@@ -112,6 +112,11 @@ float Source::tell(Source::Unit)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
double Source::getDuration(Unit)
|
||||
{
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
void Source::setPosition(float *)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
virtual float getVolume() const;
|
||||
virtual void seek(float offset, Unit unit);
|
||||
virtual float tell(Unit unit);
|
||||
virtual double getDuration(Unit unit);
|
||||
virtual void setPosition(float *v);
|
||||
virtual void getPosition(float *v) const;
|
||||
virtual void setVelocity(float *v);
|
||||
|
||||
@@ -263,6 +263,12 @@ float Pool::tell(Source *source, void *unit)
|
||||
return source->tellAtomic(unit);
|
||||
}
|
||||
|
||||
double Pool::getDuration(Source *source, void *unit)
|
||||
{
|
||||
thread::Lock lock(mutex);
|
||||
return source->getDurationAtomic(unit);
|
||||
}
|
||||
|
||||
ALuint Pool::findi(const Source *source) const
|
||||
{
|
||||
std::map<Source *, ALuint>::const_iterator i = playing.find((Source *)source);
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
void softRewind(Source *source);
|
||||
void seek(Source *source, float offset, void *unit);
|
||||
float tell(Source *source, void *unit);
|
||||
double getDuration(Source *source, void *unit);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ Ensure the Source is not multi-channel before calling this function.")
|
||||
};
|
||||
|
||||
StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
|
||||
: size(size)
|
||||
{
|
||||
alGenBuffers(1, &buffer);
|
||||
alBufferData(buffer, format, data, size, freq);
|
||||
@@ -95,6 +96,7 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
|
||||
, offsetSeconds(0)
|
||||
, sampleRate(soundData->getSampleRate())
|
||||
, channels(soundData->getChannels())
|
||||
, bitDepth(soundData->getBitDepth())
|
||||
, decoder(nullptr)
|
||||
, toLoop(0)
|
||||
{
|
||||
@@ -134,6 +136,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
|
||||
, offsetSeconds(0)
|
||||
, sampleRate(decoder->getSampleRate())
|
||||
, channels(decoder->getChannels())
|
||||
, bitDepth(decoder->getBitDepth())
|
||||
, decoder(decoder)
|
||||
, toLoop(0)
|
||||
{
|
||||
@@ -169,6 +172,7 @@ Source::Source(const Source &s)
|
||||
, offsetSeconds(0)
|
||||
, sampleRate(s.sampleRate)
|
||||
, channels(s.channels)
|
||||
, bitDepth(s.bitDepth)
|
||||
, decoder(nullptr)
|
||||
, toLoop(0)
|
||||
{
|
||||
@@ -441,6 +445,36 @@ float Source::tell(Source::Unit unit)
|
||||
return pool->tell(this, &unit);
|
||||
}
|
||||
|
||||
double Source::getDurationAtomic(void *vunit)
|
||||
{
|
||||
Unit unit = *(Unit *) vunit;
|
||||
|
||||
if (type == TYPE_STREAM)
|
||||
{
|
||||
double seconds = decoder->getDuration();
|
||||
|
||||
if (unit == UNIT_SECONDS)
|
||||
return seconds;
|
||||
else
|
||||
return seconds * decoder->getSampleRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
ALsizei size = staticBuffer->getSize();
|
||||
ALsizei samples = (size / channels) / (bitDepth / 8);
|
||||
|
||||
if (unit == UNIT_SAMPLES)
|
||||
return (double) samples;
|
||||
else
|
||||
return (double) samples / (double) sampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
double Source::getDuration(Unit unit)
|
||||
{
|
||||
return pool->getDuration(this, &unit);
|
||||
}
|
||||
|
||||
void Source::setPosition(float *v)
|
||||
{
|
||||
if (channels > 1)
|
||||
|
||||
@@ -65,9 +65,15 @@ public:
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline ALsizei getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ALuint buffer;
|
||||
ALsizei size;
|
||||
|
||||
}; // StaticDataBuffer
|
||||
|
||||
@@ -98,6 +104,8 @@ public:
|
||||
virtual void seek(float offset, Unit unit);
|
||||
virtual float tellAtomic(void *unit) const;
|
||||
virtual float tell(Unit unit);
|
||||
virtual double getDurationAtomic(void *unit);
|
||||
virtual double getDuration(Unit unit);
|
||||
virtual void setPosition(float *v);
|
||||
virtual void getPosition(float *v) const;
|
||||
virtual void setVelocity(float *v);
|
||||
@@ -180,6 +188,7 @@ private:
|
||||
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitDepth;
|
||||
|
||||
StrongRef<love::sound::Decoder> decoder;
|
||||
|
||||
|
||||
@@ -140,6 +140,19 @@ int w_Source_tell(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_getDuration(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
|
||||
Source::Unit u = Source::UNIT_SECONDS;
|
||||
const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
|
||||
if (unit && !t->getConstant(unit, u))
|
||||
return luaL_error(L, "Invalid Source time unit: %s", unit);
|
||||
|
||||
lua_pushnumber(L, t->getDuration(u));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_setPosition(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
@@ -373,6 +386,7 @@ static const luaL_Reg functions[] =
|
||||
{ "getVolume", w_Source_getVolume },
|
||||
{ "seek", w_Source_seek },
|
||||
{ "tell", w_Source_tell },
|
||||
{ "getDuration", w_Source_getDuration },
|
||||
{ "setPosition", w_Source_setPosition },
|
||||
{ "getPosition", w_Source_getPosition },
|
||||
{ "setVelocity", w_Source_setVelocity },
|
||||
|
||||
@@ -42,6 +42,7 @@ int w_Source_setVolume(lua_State *L);
|
||||
int w_Source_getVolume(lua_State *L);
|
||||
int w_Source_seek(lua_State *L);
|
||||
int w_Source_tell(lua_State *L);
|
||||
int w_Source_getDuration(lua_State *L);
|
||||
int w_Source_setPosition(lua_State *L);
|
||||
int w_Source_getPosition(lua_State *L);
|
||||
int w_Source_setVelocity(lua_State *L);
|
||||
|
||||
@@ -581,11 +581,9 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
||||
enabledattribs |= 1 << uint32(attriblocation);
|
||||
}
|
||||
|
||||
// Not supported on all platforms or GL versions, I believe.
|
||||
if (!(enabledattribs & ATTRIBFLAG_POS))
|
||||
{
|
||||
// Not supported on all platforms or GL versions at least, I believe.
|
||||
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
||||
}
|
||||
|
||||
gl.useVertexAttribArrays(enabledattribs);
|
||||
|
||||
|
||||
@@ -134,6 +134,12 @@ public:
|
||||
**/
|
||||
virtual int getSampleRate() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the estimated total duration of the stream. in seconds. May return
|
||||
* -1 if the duration cannot be determined.
|
||||
**/
|
||||
virtual double getDuration() = 0;
|
||||
|
||||
}; // Decoder
|
||||
|
||||
} // sound
|
||||
|
||||
@@ -69,6 +69,7 @@ CoreAudioDecoder::CoreAudioDecoder(Data *data, const std::string &ext, int buffe
|
||||
, extAudioFile(nullptr)
|
||||
, inputInfo()
|
||||
, outputInfo()
|
||||
, duration(-2.0)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -267,6 +268,25 @@ int CoreAudioDecoder::getBitDepth() const
|
||||
return outputInfo.mBitsPerChannel;
|
||||
}
|
||||
|
||||
double CoreAudioDecoder::getDuration()
|
||||
{
|
||||
// Only calculate the duration if we haven't done so already.
|
||||
if (duration == -2.0)
|
||||
{
|
||||
SInt64 samples = 0;
|
||||
UInt32 psize = (UInt32) sizeof(samples);
|
||||
|
||||
OSStatus err = ExtAudioFileGetProperty(extAudioFile, kExtAudioFileProperty_FileLengthFrames, &psize, &samples);
|
||||
|
||||
if (err == noErr)
|
||||
duration = (double) samples / (double) sampleRate;
|
||||
else
|
||||
duration = -1.0;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
} // love
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
bool isSeekable();
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
|
||||
private:
|
||||
|
||||
@@ -70,6 +71,8 @@ private:
|
||||
AudioStreamBasicDescription inputInfo;
|
||||
AudioStreamBasicDescription outputInfo;
|
||||
|
||||
double duration;
|
||||
|
||||
}; // CoreAudioDecoder
|
||||
|
||||
} // lullaby
|
||||
|
||||
@@ -108,6 +108,11 @@ int FLACDecoder::getSampleRate() const
|
||||
return get_sample_rate();
|
||||
}
|
||||
|
||||
double FLACDecoder::getDuration()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
FLAC__StreamDecoderReadStatus FLACDecoder::read_callback(FLAC__byte buffer[], size_t *bytes)
|
||||
{
|
||||
int size = data->getSize();
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
int getSampleRate() const;
|
||||
double getDuration();
|
||||
|
||||
//needed for FLAC
|
||||
FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes);
|
||||
|
||||
@@ -142,6 +142,11 @@ int GmeDecoder::getBitDepth() const
|
||||
return 16;
|
||||
}
|
||||
|
||||
double GmeDecoder::getDuration()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
} // love
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
bool isSeekable();
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
|
||||
private:
|
||||
Music_Emu *emu;
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace lullaby
|
||||
ModPlugDecoder::ModPlugDecoder(Data *data, const std::string &ext, int bufferSize)
|
||||
: Decoder(data, ext, bufferSize)
|
||||
, plug(0)
|
||||
, duration(-2.0)
|
||||
{
|
||||
|
||||
// Set some ModPlug settings.
|
||||
@@ -141,6 +142,22 @@ int ModPlugDecoder::getBitDepth() const
|
||||
return 16;
|
||||
}
|
||||
|
||||
double ModPlugDecoder::getDuration()
|
||||
{
|
||||
// Only calculate the duration if we haven't done so already.
|
||||
if (duration == -2.0)
|
||||
{
|
||||
int lengthms = ModPlug_GetLength(plug);
|
||||
|
||||
if (lengthms < 0)
|
||||
duration = -1.0;
|
||||
else
|
||||
duration = (double) lengthms / 1000.0;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
} // love
|
||||
|
||||
@@ -59,11 +59,15 @@ public:
|
||||
bool isSeekable();
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
|
||||
private:
|
||||
|
||||
ModPlugFile *plug;
|
||||
ModPlug_Settings settings;
|
||||
|
||||
double duration;
|
||||
|
||||
}; // Decoder
|
||||
|
||||
} // lullaby
|
||||
|
||||
@@ -31,18 +31,88 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
/**
|
||||
* mpg123 callbacks for seekable streams.
|
||||
**/
|
||||
|
||||
static ssize_t read_callback(void *udata, void *buffer, size_t count)
|
||||
{
|
||||
DecoderFile *file = (DecoderFile *) udata;
|
||||
|
||||
// Calculates how much data is still left and takes that value or
|
||||
// the buffer size, whichever is lower, as the number of bytes to write.
|
||||
size_t countLeft = file->size - file->offset;
|
||||
size_t countWrite = countLeft < count ? countLeft : count;
|
||||
|
||||
if (countWrite > 0)
|
||||
{
|
||||
memcpy(buffer, file->data + file->offset, countWrite);
|
||||
file->offset += countWrite;
|
||||
}
|
||||
|
||||
// Returns the number of written bytes. 0 means EOF.
|
||||
return countWrite;
|
||||
}
|
||||
|
||||
static off_t seek_callback(void *udata, off_t offset, int whence)
|
||||
{
|
||||
DecoderFile *file = (DecoderFile *) udata;
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
// Negative values are invalid at this point.
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
// Prevents the offset from going over EOF.
|
||||
if (file->size > (size_t) offset)
|
||||
file->offset = offset;
|
||||
else
|
||||
file->offset = file->size;
|
||||
break;
|
||||
case SEEK_END:
|
||||
// Offset is set to EOF. Offset calculation is just like SEEK_CUR.
|
||||
file->offset = file->size;
|
||||
case SEEK_CUR:
|
||||
// Prevents the offset from going over EOF or below 0.
|
||||
if (offset > 0)
|
||||
{
|
||||
if (file->size > file->offset + (size_t) offset)
|
||||
file->offset = file->offset + offset;
|
||||
else
|
||||
file->offset = file->size;
|
||||
}
|
||||
else if (offset < 0)
|
||||
{
|
||||
if (file->offset >= (size_t) (-offset))
|
||||
file->offset = file->offset - (size_t) (-offset);
|
||||
else
|
||||
file->offset = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
};
|
||||
|
||||
return file->offset;
|
||||
}
|
||||
|
||||
static void cleanup_callback(void *)
|
||||
{
|
||||
// Cleanup is done by the Decoder class.
|
||||
}
|
||||
|
||||
bool Mpg123Decoder::inited = false;
|
||||
|
||||
Mpg123Decoder::Mpg123Decoder(Data *data, const std::string &ext, int bufferSize)
|
||||
: Decoder(data, ext, bufferSize)
|
||||
, decoder_file(data)
|
||||
, handle(0)
|
||||
, channels(MPG123_STEREO)
|
||||
, duration(-2.0)
|
||||
{
|
||||
|
||||
data_size = data->getSize();
|
||||
data_offset = 0;
|
||||
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!inited)
|
||||
{
|
||||
@@ -52,18 +122,46 @@ Mpg123Decoder::Mpg123Decoder(Data *data, const std::string &ext, int bufferSize)
|
||||
inited = (ret == MPG123_OK);
|
||||
}
|
||||
|
||||
//Intialize the handle
|
||||
handle = mpg123_new(NULL, &ret);
|
||||
if (handle == NULL)
|
||||
throw love::Exception("Could not create handle.");
|
||||
ret = mpg123_open_feed(handle);
|
||||
if (ret != MPG123_OK)
|
||||
throw love::Exception("Could not open feed.");
|
||||
// Intialize the handle.
|
||||
handle = mpg123_new(nullptr, nullptr);
|
||||
if (handle == nullptr)
|
||||
throw love::Exception("Could not create decoder.");
|
||||
|
||||
ret = feed(16384);
|
||||
// Suppressing all mpg123 messages.
|
||||
mpg123_param(handle, MPG123_ADD_FLAGS, MPG123_QUIET, 0);
|
||||
|
||||
if (ret != MPG123_OK && ret != MPG123_DONE)
|
||||
throw love::Exception("Could not feed!");
|
||||
try
|
||||
{
|
||||
ret = mpg123_replace_reader_handle(handle, &read_callback, &seek_callback, &cleanup_callback);
|
||||
if (ret != MPG123_OK)
|
||||
throw love::Exception("Could not set decoder callbacks.");
|
||||
|
||||
ret = mpg123_open_handle(handle, &decoder_file);
|
||||
if (ret != MPG123_OK)
|
||||
throw love::Exception("Could not open decoder.");
|
||||
|
||||
// mpg123_getformat should be able to tell us the properties of the stream's first frame.
|
||||
long rate = 0;
|
||||
ret = mpg123_getformat(handle, &rate, &channels, nullptr);
|
||||
if (ret == MPG123_ERR)
|
||||
throw love::Exception("Could not get stream information.");
|
||||
|
||||
// I forgot what this was about.
|
||||
if (channels == 0)
|
||||
channels = 2;
|
||||
|
||||
// Force signed 16-bit output.
|
||||
mpg123_param(handle, MPG123_FLAGS, (channels == 2 ? MPG123_FORCE_STEREO : MPG123_MONO_MIX), 0);
|
||||
mpg123_format_none(handle);
|
||||
mpg123_format(handle, rate, channels, MPG123_ENC_SIGNED_16);
|
||||
|
||||
sampleRate = rate;
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
mpg123_delete(handle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Mpg123Decoder::~Mpg123Decoder()
|
||||
@@ -102,64 +200,24 @@ love::sound::Decoder *Mpg123Decoder::clone()
|
||||
int Mpg123Decoder::decode()
|
||||
{
|
||||
int size = 0;
|
||||
bool done = false;
|
||||
|
||||
while (size < bufferSize && !done && !eof)
|
||||
while (size < bufferSize && !eof)
|
||||
{
|
||||
size_t numbytes = 0;
|
||||
int res = mpg123_read(handle, (unsigned char *) buffer + size, bufferSize - size, &numbytes);
|
||||
|
||||
int r = mpg123_read(handle, (unsigned char *) buffer + size, bufferSize - size, &numbytes);
|
||||
|
||||
switch (r)
|
||||
switch (res)
|
||||
{
|
||||
case MPG123_NEW_FORMAT:
|
||||
{
|
||||
size += numbytes;
|
||||
long rate = 0;
|
||||
int encoding = 0;
|
||||
mpg123_getformat(handle, &rate, &channels, &encoding);
|
||||
if (rate == 0)
|
||||
rate = sampleRate;
|
||||
else
|
||||
sampleRate = rate;
|
||||
if (channels == 0)
|
||||
channels = MPG123_STEREO;
|
||||
if (encoding == 0)
|
||||
encoding = MPG123_ENC_SIGNED_16;
|
||||
int ret = mpg123_format(handle, rate, channels, encoding);
|
||||
if (ret != MPG123_OK)
|
||||
throw love::Exception("Could not set output format.");
|
||||
}
|
||||
continue;
|
||||
case MPG123_NEED_MORE:
|
||||
{
|
||||
size += numbytes;
|
||||
int v = feed(8192);
|
||||
|
||||
switch (v)
|
||||
{
|
||||
case MPG123_OK:
|
||||
continue;
|
||||
case MPG123_DONE:
|
||||
if (numbytes == 0)
|
||||
eof = true;
|
||||
break;
|
||||
default:
|
||||
done = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
case MPG123_NEW_FORMAT:
|
||||
case MPG123_OK:
|
||||
size += numbytes;
|
||||
continue;
|
||||
case MPG123_DONE:
|
||||
// Apparently, mpg123_read does not return MPG123_DONE, but
|
||||
// let's keep it here anyway.
|
||||
size += numbytes;
|
||||
eof = true;
|
||||
default:
|
||||
done = true;
|
||||
break;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,14 +226,13 @@ int Mpg123Decoder::decode()
|
||||
|
||||
bool Mpg123Decoder::seek(float s)
|
||||
{
|
||||
off_t offset = static_cast<off_t>(s * static_cast<float>(sampleRate));
|
||||
off_t offset = (off_t) (s * (double) sampleRate);
|
||||
|
||||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
if (mpg123_feedseek(handle, offset, SEEK_SET, &offset) >= 0)
|
||||
if (mpg123_seek(handle, offset, SEEK_SET) >= 0)
|
||||
{
|
||||
data_offset = offset;
|
||||
eof = false;
|
||||
return true;
|
||||
}
|
||||
@@ -186,13 +243,9 @@ bool Mpg123Decoder::seek(float s)
|
||||
bool Mpg123Decoder::rewind()
|
||||
{
|
||||
eof = false;
|
||||
off_t offset;
|
||||
|
||||
if (mpg123_feedseek(handle, 0, SEEK_SET, &offset) >= 0)
|
||||
{
|
||||
data_offset = offset;
|
||||
if (mpg123_seek(handle, 0, SEEK_SET) >= 0)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -212,21 +265,22 @@ int Mpg123Decoder::getBitDepth() const
|
||||
return 16;
|
||||
}
|
||||
|
||||
int Mpg123Decoder::feed(int bytes)
|
||||
double Mpg123Decoder::getDuration()
|
||||
{
|
||||
int remaining = data_size - data_offset;
|
||||
// Only calculate the duration if we haven't done so already.
|
||||
if (duration == -2.0)
|
||||
{
|
||||
mpg123_scan(handle);
|
||||
|
||||
if (remaining <= 0)
|
||||
return MPG123_DONE;
|
||||
off_t length = mpg123_length(handle);
|
||||
|
||||
int feed_bytes = remaining < bytes ? remaining : bytes;
|
||||
if (length == MPG123_ERR || length < 0)
|
||||
duration = -1.0;
|
||||
else
|
||||
duration = (double) length / (double) sampleRate;
|
||||
}
|
||||
|
||||
int r = mpg123_feed(handle, (unsigned char *)data->getData() + data_offset, feed_bytes);
|
||||
|
||||
if (r == MPG123_OK || r == MPG123_DONE)
|
||||
data_offset += feed_bytes;
|
||||
|
||||
return r;
|
||||
return duration;
|
||||
}
|
||||
|
||||
} // lullaby
|
||||
|
||||
@@ -39,6 +39,19 @@ namespace sound
|
||||
namespace lullaby
|
||||
{
|
||||
|
||||
struct DecoderFile
|
||||
{
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
size_t offset;
|
||||
|
||||
DecoderFile(Data *d)
|
||||
: data((unsigned char *) d->getData())
|
||||
, size(d->getSize())
|
||||
, offset(0)
|
||||
{}
|
||||
};
|
||||
|
||||
class Mpg123Decoder : public Decoder
|
||||
{
|
||||
public:
|
||||
@@ -56,19 +69,19 @@ public:
|
||||
bool isSeekable();
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
double getDuration();
|
||||
|
||||
private:
|
||||
|
||||
int feed(int bytes);
|
||||
DecoderFile decoder_file;
|
||||
|
||||
mpg123_handle *handle;
|
||||
static bool inited;
|
||||
|
||||
int data_offset;
|
||||
int data_size;
|
||||
|
||||
int channels;
|
||||
|
||||
double duration;
|
||||
|
||||
}; // Decoder
|
||||
|
||||
} // lullaby
|
||||
|
||||
@@ -66,7 +66,7 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi
|
||||
std::string ext = data->getExtension();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
|
||||
|
||||
sound::Decoder *decoder = 0;
|
||||
sound::Decoder *decoder = nullptr;
|
||||
|
||||
// Find a suitable decoder here, and return it.
|
||||
if (false)
|
||||
|
||||
@@ -130,6 +130,7 @@ static long vorbisTell(void *datasource /* ptr to the data that the vorbis files
|
||||
|
||||
VorbisDecoder::VorbisDecoder(Data *data, const std::string &ext, int bufferSize)
|
||||
: Decoder(data, ext, bufferSize)
|
||||
, duration(-2.0)
|
||||
{
|
||||
// Initialize callbacks
|
||||
vorbisCallbacks.close_func = vorbisClose;
|
||||
@@ -264,6 +265,20 @@ int VorbisDecoder::getSampleRate() const
|
||||
return (int) vorbisInfo->rate;
|
||||
}
|
||||
|
||||
double VorbisDecoder::getDuration()
|
||||
{
|
||||
// Only calculate the duration if we haven't done so already.
|
||||
if (duration == -2.0)
|
||||
{
|
||||
duration = ov_time_total(&handle, -1);
|
||||
|
||||
if (duration == OV_EINVAL || duration < 0.0)
|
||||
duration = -1.0;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
} // love
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
int getSampleRate() const;
|
||||
double getDuration();
|
||||
|
||||
private:
|
||||
SOggFile oggFile; // (see struct)
|
||||
@@ -70,6 +71,7 @@ private:
|
||||
vorbis_info *vorbisInfo; // Info
|
||||
vorbis_comment *vorbisComment; // Comments
|
||||
int endian; // Endianness
|
||||
double duration;
|
||||
}; // VorbisDecoder
|
||||
|
||||
} // lullaby
|
||||
|
||||
@@ -189,6 +189,11 @@ int WaveDecoder::getSampleRate() const
|
||||
return info.sample_rate;
|
||||
}
|
||||
|
||||
double WaveDecoder::getDuration()
|
||||
{
|
||||
return (double) info.length / (double) info.sample_rate;
|
||||
}
|
||||
|
||||
} // lullaby
|
||||
} // sound
|
||||
} // love
|
||||
|
||||
@@ -59,11 +59,12 @@ public:
|
||||
int getChannels() const;
|
||||
int getBitDepth() const;
|
||||
int getSampleRate() const;
|
||||
double getDuration();
|
||||
|
||||
private:
|
||||
|
||||
WaveFile dataFile;
|
||||
wuff_handle * handle;
|
||||
wuff_handle *handle;
|
||||
wuff_info info;
|
||||
|
||||
}; // WaveDecoder
|
||||
|
||||
@@ -51,11 +51,19 @@ int w_Decoder_getSampleRate(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Decoder_getDuration(lua_State *L)
|
||||
{
|
||||
Decoder *t = luax_checkdecoder(L, 1);
|
||||
lua_pushnumber(L, t->getDuration());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "getChannels", w_Decoder_getChannels },
|
||||
{ "getBitDepth", w_Decoder_getBitDepth },
|
||||
{ "getSampleRate", w_Decoder_getSampleRate },
|
||||
{ "getDuration", w_Decoder_getDuration },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ Decoder *luax_checkdecoder(lua_State *L, int idx);
|
||||
int w_Decoder_getChannels(lua_State *L);
|
||||
int w_Decoder_getBitDepth(lua_State *L);
|
||||
int w_Decoder_getSampleRate(lua_State *L);
|
||||
int w_Decoder_getDuration(lua_State *L);
|
||||
extern "C" int luaopen_decoder(lua_State *L);
|
||||
|
||||
} // sound
|
||||
|
||||
Reference in New Issue
Block a user