mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-11 07:11:54 +00:00
- added loop tag reading to the new streaming music class.
This is somewhat brute-force thanks to the surprising lack of good documentation for the Ogg headers. The only other option would have been some rather bloated library for a function that should be 25-30 lines at most.
This commit is contained in:
parent
dfd3535e02
commit
3f552ea95f
1 changed files with 87 additions and 10 deletions
|
@ -42,6 +42,7 @@
|
||||||
#include "templates.h"
|
#include "templates.h"
|
||||||
#include "sndfile_decoder.h"
|
#include "sndfile_decoder.h"
|
||||||
#include "mpg123_decoder.h"
|
#include "mpg123_decoder.h"
|
||||||
|
#include "m_fixed.h"
|
||||||
|
|
||||||
// MACROS ------------------------------------------------------------------
|
// MACROS ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@
|
||||||
class SndFileSong : public StreamSong
|
class SndFileSong : public StreamSong
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SndFileSong(FileReader *reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end);
|
SndFileSong(FileReader *reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass);
|
||||||
~SndFileSong();
|
~SndFileSong();
|
||||||
bool SetSubsong(int subsong);
|
bool SetSubsong(int subsong);
|
||||||
void Play(bool looping, int subsong);
|
void Play(bool looping, int subsong);
|
||||||
|
@ -87,7 +88,83 @@ protected:
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// GME_OpenSong
|
// try to find the LOOP_START/LOOP_END tags
|
||||||
|
//
|
||||||
|
// This is a brute force implementation, thanks in no snall part
|
||||||
|
// that no decent documentation of Ogg headers seems to exist and
|
||||||
|
// all available tag libraries are horrendously bloated.
|
||||||
|
// So if we want to do this without any new third party dependencies,
|
||||||
|
// thanks to the lack of anything that would help to do this properly,
|
||||||
|
// this was the only solution.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FindLoopTags(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass)
|
||||||
|
{
|
||||||
|
unsigned char testbuf[256];
|
||||||
|
|
||||||
|
fr->Seek(0, SEEK_SET);
|
||||||
|
long got = fr->Read(testbuf, 256);
|
||||||
|
auto eqp = testbuf - 1;
|
||||||
|
int count;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
unsigned char *c = (unsigned char *)memchr(eqp + 1, '=', 256 - (eqp + 1 - testbuf));
|
||||||
|
if (c == nullptr) return; // If there is no '=' in the first 256 bytes there's also no metadata.
|
||||||
|
|
||||||
|
eqp = c;
|
||||||
|
while (*c >= 32 && *c < 127) c--;
|
||||||
|
if (*c != 0)
|
||||||
|
{
|
||||||
|
// doesn't look like a valid tag, so try again
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
c -= 3;
|
||||||
|
int len = LittleLong(*(int*)c);
|
||||||
|
if (len > 1000000 || len <= (eqp - c + 1))
|
||||||
|
{
|
||||||
|
// length looks fishy so retry with the next '='
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
c -= 4;
|
||||||
|
count = LittleLong(*(int*)c);
|
||||||
|
if (count <= 0 || count > 1000)
|
||||||
|
{
|
||||||
|
// very unlikely to have 1000 tags
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
c += 4;
|
||||||
|
fr->Seek(long(c - testbuf), SEEK_SET);
|
||||||
|
break; // looks like we found something.
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int length = 0;
|
||||||
|
fr->Read(&length, 4);
|
||||||
|
length = LittleLong(length);
|
||||||
|
if (length == 0 || length > 1000000) return; // looks like we lost it...
|
||||||
|
if (length > 25)
|
||||||
|
{
|
||||||
|
// This tag is too long to be a valid time stamp so don't even bother.
|
||||||
|
fr->Seek(length, SEEK_CUR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fr->Read(testbuf, length);
|
||||||
|
testbuf[length] = 0;
|
||||||
|
if (strnicmp((char*)testbuf, "LOOP_START=", 11) == 0)
|
||||||
|
{
|
||||||
|
S_ParseTimeTag((char*)testbuf + 11, startass, start);
|
||||||
|
}
|
||||||
|
else if (strnicmp((char*)testbuf, "LOOP_END=", 9) == 0)
|
||||||
|
{
|
||||||
|
S_ParseTimeTag((char*)testbuf + 9, endass, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// SndFile_OpenSong
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
@ -98,20 +175,17 @@ MusInfo *SndFile_OpenSong(FileReader &fr)
|
||||||
fr.Seek(0, SEEK_SET);
|
fr.Seek(0, SEEK_SET);
|
||||||
fr.Read(signature, 4);
|
fr.Read(signature, 4);
|
||||||
uint32_t loop_start = 0, loop_end = ~0u;
|
uint32_t loop_start = 0, loop_end = ~0u;
|
||||||
|
bool startass = false, endass = false;
|
||||||
|
|
||||||
if (!memcmp(signature, "OggS", 4) || !memcmp(signature, "fLaC", 4))
|
if (!memcmp(signature, "OggS", 4) || !memcmp(signature, "fLaC", 4))
|
||||||
{
|
{
|
||||||
// Todo: Read loop points from metadata
|
// Todo: Read loop points from metadata
|
||||||
|
FindLoopTags(&fr, &loop_start, &startass, &loop_end, &endass);
|
||||||
|
|
||||||
// ms to samples.
|
|
||||||
//size_t smp_offset = ms? (size_t)((double)ms_offset / 1000. * SndInfo.samplerate) : ms_offset;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
fr.Seek(0, SEEK_SET);
|
fr.Seek(0, SEEK_SET);
|
||||||
auto decoder = SoundRenderer::CreateDecoder(&fr);
|
auto decoder = SoundRenderer::CreateDecoder(&fr);
|
||||||
if (decoder == nullptr) return nullptr;
|
if (decoder == nullptr) return nullptr;
|
||||||
return new SndFileSong(&fr, decoder, loop_start, loop_end);
|
return new SndFileSong(&fr, decoder, loop_start, loop_end, startass, endass);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -120,13 +194,16 @@ MusInfo *SndFile_OpenSong(FileReader &fr)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
SndFileSong::SndFileSong(FileReader *reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end)
|
SndFileSong::SndFileSong(FileReader *reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass)
|
||||||
{
|
{
|
||||||
ChannelConfig iChannels;
|
ChannelConfig iChannels;
|
||||||
SampleType Type;
|
SampleType Type;
|
||||||
|
|
||||||
decoder->getInfo(&SampleRate, &iChannels, &Type);
|
decoder->getInfo(&SampleRate, &iChannels, &Type);
|
||||||
|
|
||||||
|
if (!startass) loop_start = Scale(loop_start, SampleRate, 1000);
|
||||||
|
if (!endass) loop_end = Scale(loop_end, SampleRate, 1000);
|
||||||
|
|
||||||
Loop_Start = loop_start;
|
Loop_Start = loop_start;
|
||||||
Loop_End = clamp<uint32_t>(loop_end, 0, (uint32_t)decoder->getSampleLength());
|
Loop_End = clamp<uint32_t>(loop_end, 0, (uint32_t)decoder->getSampleLength());
|
||||||
Reader = reader;
|
Reader = reader;
|
||||||
|
|
Loading…
Reference in a new issue