191 lines
4.7 KiB
C++
191 lines
4.7 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "audio_stream_ogg/audio_stream_ogg.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "platform.h"
|
|
#include "log/log.h"
|
|
#include "stb_vorbis.c"
|
|
|
|
using namespace GS;
|
|
using namespace GS::IO;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool AudioStreamOGG::Seek(int t_ms)
|
|
{
|
|
/// Rewind the stream and seek to the correct frame.
|
|
int target = (vf->sample_rate * t_ms) / 1000;
|
|
|
|
// Logic is broken when seeking below the first packet.
|
|
if (target < 1024)
|
|
{
|
|
h->Rewind();
|
|
byte_left = 0;
|
|
|
|
stb_vorbis_flush_pushdata(vf);
|
|
}
|
|
else
|
|
{
|
|
// TODO compute seek point and sample to skip.
|
|
int lo = 0, hi = h->GetSize();
|
|
|
|
forever
|
|
{
|
|
// Determine seek point.
|
|
int mid = (lo + hi) / 2;
|
|
|
|
// Seek and reload push buffer.
|
|
h->Seek(mid, Base::SeekStart);
|
|
RefillBuffer();
|
|
|
|
stb_vorbis_flush_pushdata(vf);
|
|
|
|
int ns = 0, ch = 0, cs = 0;
|
|
float **fo = NULL;
|
|
|
|
forever
|
|
{
|
|
ConsumeBuffer(stb_vorbis_decode_frame_pushdata(vf, (uchar *)buffer.c_ptr(), byte_left, &ch, &fo, &ns));
|
|
|
|
if (ns) // Samples are coming in.
|
|
{
|
|
cs = stb_vorbis_get_sample_offset(vf);
|
|
if (cs != -1) // Sample located.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if target sample is within reach.
|
|
if ((cs > (target - 24000)) && (cs <= target))
|
|
forever
|
|
{
|
|
RefillBuffer();
|
|
ConsumeBuffer(stb_vorbis_decode_frame_pushdata(vf, (uchar *)buffer.c_ptr(), byte_left, &ch, &fo, &ns));
|
|
|
|
int skip = target - cs;
|
|
if ((cs != -1) && (skip < ns))
|
|
{
|
|
seek_correction = skip;
|
|
return true;
|
|
}
|
|
cs = stb_vorbis_get_sample_offset(vf);
|
|
}
|
|
|
|
// Refine approximation.
|
|
if (cs > target)
|
|
hi = mid;
|
|
else
|
|
lo = mid;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
size_t AudioStreamOGG::GetPCMBufferSize() const
|
|
{ return vorbis_info.max_frame_size * format.channels * 2 * 2; }
|
|
size_t AudioStreamOGG::GetPCM(void *pcm)
|
|
{
|
|
int ns, ch;
|
|
float **fo = NULL;
|
|
|
|
RefillBuffer();
|
|
ConsumeBuffer(stb_vorbis_decode_frame_pushdata(vf, (uchar *)buffer.c_ptr(), byte_left, &ch, &fo, &ns));
|
|
|
|
if (vf->error != VORBIS__no_error)
|
|
return 0;
|
|
|
|
if (seek_correction)
|
|
{
|
|
for (int n = 0; n < ch; ++n)
|
|
fo[n] += seek_correction;
|
|
ns -= seek_correction;
|
|
seek_correction = 0;
|
|
}
|
|
if (ns < 0)
|
|
ns = 0;
|
|
|
|
convert_channels_short_interleaved(ch, (short *)pcm, ch, fo, 0, ns); // size = ns * 2 * format.channels
|
|
return ns * 2 * format.channels;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
size_t AudioStreamOGG::RefillBuffer()
|
|
{
|
|
size_t request_size = buffer.GetSize() - byte_left;
|
|
size_t read = h->Read(&buffer[(int)byte_left], request_size);
|
|
byte_left += read;
|
|
return read;
|
|
}
|
|
void AudioStreamOGG::ConsumeBuffer(size_t size)
|
|
{
|
|
memmove(buffer, &buffer[(int)size], byte_left - size);
|
|
byte_left -= size;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool AudioStreamOGG::IsEOF() const
|
|
{
|
|
if (byte_left > 0)
|
|
return false;
|
|
if (h->IsEOF())
|
|
return true;
|
|
return vf ? asbool(vf->eof) : false;
|
|
}
|
|
bool AudioStreamOGG::Open(const char *uri)
|
|
{
|
|
h = Platform::Get().io->Open(uri);
|
|
if (!h)
|
|
return false;
|
|
|
|
if (!buffer.Allocate(16384))
|
|
return false;
|
|
|
|
RefillBuffer();
|
|
vf = stb_vorbis_open_pushdata((uchar *)buffer.c_ptr(), byte_left, &consumed, &err, NULL);
|
|
if (!vf)
|
|
{
|
|
h = NULL;
|
|
return false;
|
|
}
|
|
ConsumeBuffer(consumed);
|
|
|
|
vorbis_info = stb_vorbis_get_info(vf);
|
|
seek_correction = 0;
|
|
|
|
format.channels = vorbis_info.channels;
|
|
format.resolution = 16;
|
|
format.frequency = vorbis_info.sample_rate;
|
|
|
|
__LOG_H__ << "Vorbis stream '" << uri << "' - " << vorbis_info.sample_rate << "hz 16bit " << vorbis_info.channels << " channel(s).\n";
|
|
__LOG__ << " Max frame size = " << vorbis_info.max_frame_size << "\n";
|
|
return true;
|
|
}
|
|
void AudioStreamOGG::Close()
|
|
{
|
|
if (vf)
|
|
stb_vorbis_close(vf);
|
|
vf = NULL;
|
|
|
|
__LOG_H__ << "Vorbis stream closed.\n";
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
AudioStreamOGG::AudioStreamOGG()
|
|
{
|
|
vf = NULL;
|
|
seek_correction = 0;
|
|
byte_left = 0;
|
|
}
|
|
AudioStreamOGG::~AudioStreamOGG()
|
|
{ Close(); }
|
|
//------------------------------------------------------------------------------
|