/* ----------------------------------------------------------------------------- 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(); } //------------------------------------------------------------------------------