diff --git a/src/modules/audio/Source.h b/src/modules/audio/Source.h index 4c0fef64e..01a06c5f2 100644 --- a/src/modules/audio/Source.h +++ b/src/modules/audio/Source.h @@ -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; diff --git a/src/modules/audio/null/Source.cpp b/src/modules/audio/null/Source.cpp index aa682a627..8b70bf8b6 100644 --- a/src/modules/audio/null/Source.cpp +++ b/src/modules/audio/null/Source.cpp @@ -112,6 +112,11 @@ float Source::tell(Source::Unit) return 0.0f; } +double Source::getDuration(Unit) +{ + return -1.0f; +} + void Source::setPosition(float *) { } diff --git a/src/modules/audio/null/Source.h b/src/modules/audio/null/Source.h index bbab20a40..5b1265813 100644 --- a/src/modules/audio/null/Source.h +++ b/src/modules/audio/null/Source.h @@ -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); diff --git a/src/modules/audio/openal/Pool.cpp b/src/modules/audio/openal/Pool.cpp index a1018f550..0f9e9a5ad 100644 --- a/src/modules/audio/openal/Pool.cpp +++ b/src/modules/audio/openal/Pool.cpp @@ -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::const_iterator i = playing.find((Source *)source); diff --git a/src/modules/audio/openal/Pool.h b/src/modules/audio/openal/Pool.h index e875b4f55..d054315cf 100644 --- a/src/modules/audio/openal/Pool.h +++ b/src/modules/audio/openal/Pool.h @@ -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: diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 376f7fb4a..04f42bd85 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -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) diff --git a/src/modules/audio/openal/Source.h b/src/modules/audio/openal/Source.h index 28937243b..e94b28e1a 100644 --- a/src/modules/audio/openal/Source.h +++ b/src/modules/audio/openal/Source.h @@ -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 decoder; diff --git a/src/modules/audio/wrap_Source.cpp b/src/modules/audio/wrap_Source.cpp index c4fe17007..77cb76595 100644 --- a/src/modules/audio/wrap_Source.cpp +++ b/src/modules/audio/wrap_Source.cpp @@ -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 }, diff --git a/src/modules/audio/wrap_Source.h b/src/modules/audio/wrap_Source.h index 93dc52e2c..2ae79c953 100644 --- a/src/modules/audio/wrap_Source.h +++ b/src/modules/audio/wrap_Source.h @@ -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); diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 80522cc05..b722d923f 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -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); diff --git a/src/modules/sound/Decoder.h b/src/modules/sound/Decoder.h index 0c5de3155..a4db3bf52 100644 --- a/src/modules/sound/Decoder.h +++ b/src/modules/sound/Decoder.h @@ -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 diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp index a32847457..11c36f561 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.cpp +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -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 diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.h b/src/modules/sound/lullaby/CoreAudioDecoder.h index 61dd0d934..953ba56ef 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.h +++ b/src/modules/sound/lullaby/CoreAudioDecoder.h @@ -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 diff --git a/src/modules/sound/lullaby/FLACDecoder.cpp b/src/modules/sound/lullaby/FLACDecoder.cpp index 6504c9b45..0e18da014 100644 --- a/src/modules/sound/lullaby/FLACDecoder.cpp +++ b/src/modules/sound/lullaby/FLACDecoder.cpp @@ -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(); diff --git a/src/modules/sound/lullaby/FLACDecoder.h b/src/modules/sound/lullaby/FLACDecoder.h index eaf61b605..f1c3a7178 100644 --- a/src/modules/sound/lullaby/FLACDecoder.h +++ b/src/modules/sound/lullaby/FLACDecoder.h @@ -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); diff --git a/src/modules/sound/lullaby/GmeDecoder.cpp b/src/modules/sound/lullaby/GmeDecoder.cpp index d7bd8b2de..701d6f5fb 100644 --- a/src/modules/sound/lullaby/GmeDecoder.cpp +++ b/src/modules/sound/lullaby/GmeDecoder.cpp @@ -142,6 +142,11 @@ int GmeDecoder::getBitDepth() const return 16; } +double GmeDecoder::getDuration() +{ + return -1; +} + } // lullaby } // sound } // love diff --git a/src/modules/sound/lullaby/GmeDecoder.h b/src/modules/sound/lullaby/GmeDecoder.h index 564c3ab23..704a2350a 100644 --- a/src/modules/sound/lullaby/GmeDecoder.h +++ b/src/modules/sound/lullaby/GmeDecoder.h @@ -56,6 +56,7 @@ public: bool isSeekable(); int getChannels() const; int getBitDepth() const; + double getDuration(); private: Music_Emu *emu; diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index 1a2f97f99..c291f7014 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/src/modules/sound/lullaby/ModPlugDecoder.cpp @@ -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 diff --git a/src/modules/sound/lullaby/ModPlugDecoder.h b/src/modules/sound/lullaby/ModPlugDecoder.h index 58dd37427..9b7fb853c 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.h +++ b/src/modules/sound/lullaby/ModPlugDecoder.h @@ -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 diff --git a/src/modules/sound/lullaby/Mpg123Decoder.cpp b/src/modules/sound/lullaby/Mpg123Decoder.cpp index a0913efcf..dfd780dbf 100644 --- a/src/modules/sound/lullaby/Mpg123Decoder.cpp +++ b/src/modules/sound/lullaby/Mpg123Decoder.cpp @@ -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(s * static_cast(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 diff --git a/src/modules/sound/lullaby/Mpg123Decoder.h b/src/modules/sound/lullaby/Mpg123Decoder.h index 47706bc03..d04ac19cb 100644 --- a/src/modules/sound/lullaby/Mpg123Decoder.h +++ b/src/modules/sound/lullaby/Mpg123Decoder.h @@ -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 diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 683660cfe..d0efdf10b 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -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) diff --git a/src/modules/sound/lullaby/VorbisDecoder.cpp b/src/modules/sound/lullaby/VorbisDecoder.cpp index 76f053681..b7be7be77 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -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 diff --git a/src/modules/sound/lullaby/VorbisDecoder.h b/src/modules/sound/lullaby/VorbisDecoder.h index ebd815c5b..511798929 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.h +++ b/src/modules/sound/lullaby/VorbisDecoder.h @@ -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 diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp index 9dfc3ec23..da27b76fb 100644 --- a/src/modules/sound/lullaby/WaveDecoder.cpp +++ b/src/modules/sound/lullaby/WaveDecoder.cpp @@ -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 diff --git a/src/modules/sound/lullaby/WaveDecoder.h b/src/modules/sound/lullaby/WaveDecoder.h index 1f0d9d56f..844b1cc53 100644 --- a/src/modules/sound/lullaby/WaveDecoder.h +++ b/src/modules/sound/lullaby/WaveDecoder.h @@ -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 diff --git a/src/modules/sound/wrap_Decoder.cpp b/src/modules/sound/wrap_Decoder.cpp index 3d9c03a01..af8398fb4 100644 --- a/src/modules/sound/wrap_Decoder.cpp +++ b/src/modules/sound/wrap_Decoder.cpp @@ -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 } }; diff --git a/src/modules/sound/wrap_Decoder.h b/src/modules/sound/wrap_Decoder.h index 7ae77e2b7..d0db3fc09 100644 --- a/src/modules/sound/wrap_Decoder.h +++ b/src/modules/sound/wrap_Decoder.h @@ -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