Re: vorbis decoder stuffs

Ed Sweetman <[email protected]>
Newsgroups gmane.comp.audio.zinf.devel
Message-ID <[email protected]>
Just wanted to update the current progress of the decoder and clear up 
why i'm spending so much time on a new one while the current one "works".

1. I dont think the callback method of using libvorbisfile is able to be 
protected.  We'd have to force a workaround so the situation doesn't 
occur and that's very very ugly.

2. It doesn't follow the lmc design.  The callback method basically 
hands over control of the plugin to libvorbisfile.  The format of the 
plugin may look the same on the surface, but it doesn't function like 
the wav and xing plugins do.

These are the unreparable issues with using libvorbisfile to do our 
decoding.  There are fixable ones as well in all current plugins.

FILE handles have no place in the lmc's.  They will be removed and with 
it all depending code on them.

There are also global variables in each plugin that have the same name 
as other global variables and such.  This cannot be a good thing.  If 
these variables are used outside of the plugin we will need to make a 
standard way to retrieve their data via method calls to the plugin's 
class, otherwise they will just be private data to each plugin's lmc 
class.


Some of the issues mentioned above are of no fault to the code in 
question. There are architectural issues with zinf that create some of 
these problems and we cant do anything about that without major 
rewrites.  Other players dont have the same issues we're having because 
they dont have the same structural flaws. But the code written for zinf 
should not be written so that it simply "works" it should be written so 
that it works while complying with the as yet unwritten guidelines.

and for the status of my vorbis decoder.  It plays, seeks (somewhat), 
stops, pauses, track changes but does not fast track or automatically 
continue to the next track if you've seeked.  This has to do with some 
discrepency with how many frames are in the current file.  IF you left 
it play through, it has a good estimate but if you seek, it seems to be 
using a completely different number than you'd think would be used.


Seeking is still flakey. I need to fix the discrepency with the 
"position" number and the way the decoder works with m_frameCounter so 
that they're measuring the same thing.  Anyways, enough of my bs, here's 
the code.  And my work continues.  Finals weeks is approaching however, 
so I hope to have it completely functional before then.

Ed Sweetman wrote:
> One would think figuring out why your decoder is running way too fast 
> would be pretty straight forward...but that person would be wrong.
> 
> 
> I rewrote the  decoder to look much like the wavlmc and xinglmc does. In 
> the process i have new issues to deal with so yea...not very fun stuff.  
> I'll be working on it as much as i can.  It is so close to working that 
> it hurts and is extremely aggrivating.  In the end though the extra 
> responsiveness is worth it :)
> 
>
vorbislmc.cpp (text/x-c++src, 20.1 KB)
/*____________________________________________________________________________
  
  Zinf - Zinf Is Not FreeA*p (The Free MP3 Player)

  Portions Copyright (C) 2000 Monty

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, Write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  
  $Id: vorbislmc.cpp,v 1.16 2003/03/20 22:25:33 kgk Exp $
____________________________________________________________________________*/

/* system headers */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include <vector>
#include <math.h>

using namespace std;
#include "config.h"
#include "errors.h"
#include "pmoevent.h"
#include "eventbuffer.h"
#include "event.h"
#include "eventdata.h"
#include "mutex.h"
#include "semaphore.h"
#include "preferences.h"
#include "lmc.h"
#include "facontext.h"
#include "log.h"
#include "debug.h"

#include "vorbislmc.h"

#define DB printf("%s:%d\n",  __FILE__, __LINE__);

extern "C"
{
   LogicalMediaConverter *Initialize(FAContext *context) {
      return new VorbisLMC(context);
   }
}

/*
    These are global variables....WHY?!
*/
const int iDefaultBufferUpInterval = 3;
const int iDecodeBlockSize = 8192*2;
const int iFramesPerSecond = 10;
const int iBitrateLoopsPerUpdate = iFramesPerSecond * 3;
const int iInitialOutputBufferSize = 65536;
//const char *szFailRead = N_("Cannot read vorbis data from input plugin.");
const char *szFailWrite = N_("Cannot write audio data to output buffer.");
const char *szCannotDecode = N_("Skipped corrupted file.");
const char *szFailRead = _("Cannot read vorbis data from input plugin.");

VorbisLMC::VorbisLMC(FAContext *context) :
      LogicalMediaConverter(context), m_pPmi(NULL), m_pPmo(NULL),
      m_decoderThread(NULL), m_szUrl(NULL), m_szError(NULL), pBuffer(NULL),
      pOutBuffer(NULL)
{
   m_pContext = context;
   m_bInit = false;
   m_decodeInfo.sendInfo = true;
   iMaxFrameSize = 4096;
   iReadSize = 0;
}

VorbisLMC::~VorbisLMC()
{

   if (m_decoderThread) {
      m_bExit = true;
      m_pPauseSem->Signal();
      m_pSleepSem->Signal();
      m_decoderThread->Join();
      if(m_bInit) ClrDecode();
      delete m_decoderThread;
      // m_decoderThread=NULL;
   }

}

Error VorbisLMC::Prepare(PullBuffer *pInputBuffer, PullBuffer *&pOutBuffer)
{
   m_pInputBuffer = pInputBuffer;

   m_pOutputBuffer = new EventBuffer(iInitialOutputBufferSize, 0,
                                     m_pContext);
   if (!m_decoderThread) {
      m_decoderThread = Thread::CreateThread();
      if (!m_decoderThread) {
         return kError_CreateThreadFailed;
      }
      m_decoderThread->Create(VorbisLMC::DecodeWorkerThreadFunc, this);
   }

   pOutBuffer = m_pOutputBuffer;

   m_pInputBuffer->SetName("Input");
   m_pOutputBuffer->SetName("Output");

   return kError_NoErr;
}

vector<string> *VorbisLMC::GetExtensions(void)
{
   vector<string> *extList = new vector<string>;
   
   extList->push_back("OGG");
   return extList;
}


void VorbisLMC::ClrDecode()
{
   ogg_stream_clear(&os);
   vorbis_block_clear(&vb);
   vorbis_dsp_clear(&vd);
   vorbis_comment_clear(&vc);
   vorbis_info_clear(&vi);
   ogg_sync_clear(&oy);
}


// Only possible use for this method is external to the decoder.
Error VorbisLMC::CanDecode()
{
   Error err;

   if (!m_bInit) {
      err = InitDecoder();
      if (err != kError_NoErr)
         return err;
      ClrDecode();
      m_bInit = false;
   }
   return kError_NoErr;
}

Error VorbisLMC::VorbisInit()
{
   Error  Err = kError_PluginNotInitialized;
   int	  headers = 0, result;

   ogg_sync_init(&oy);
   (char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
   memcpy(pOutBuffer, pBuffer, iReadSize);
   ogg_sync_wrote(&oy, iReadSize);
   if (ogg_sync_pageout(&oy, &og) != 1) {
      return (kError_PluginNotInitialized);
   }
   serialno = ogg_page_serialno(&og);
   ogg_stream_init(&os, serialno);
   vorbis_info_init(&vi);
   vorbis_comment_init(&vc);
   if (ogg_stream_pagein(&os, &og) < 0) {
      return (kError_PluginNotInitialized);
   }
   if (ogg_stream_packetout(&os, &op) != 1) {
      return (kError_PluginNotInitialized);
   }
   if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0) {
      return (kError_PluginNotInitialized);
   }

   // Now read next two identification headers.
   headers++;
   while (headers < 3) {
      while (headers < 3) {
         if ((result = ogg_sync_pageout(&oy, &og)) == 0)
            break;
         if (result == 1) {
            ogg_stream_pagein(&os, &og);
            while (headers < 3) {
               if ((result = ogg_stream_packetout(&os, &op)) == 0)
                  break;
               if (result < 0) {
                  return (kError_PluginNotInitialized);
               }
               if (m_bExit) {
                  return kError_Interrupt;
               }
               vorbis_synthesis_headerin(&vi, &vc, &op);
               headers++;
            }
         }
      }
      if (headers < 3) {
         m_pInputBuffer->EndRead(iReadSize);
         (char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
         while ((Err = m_pInputBuffer->BeginRead(pBuffer, iReadSize)) == kError_NoDataAvail) {
            m_pPmi->Wake();
            if (Sleep()) {
               m_pInputBuffer->EndRead(0);
               return kError_Interrupt;
            }
         }
         if (Err == kError_EndOfStream) {
            m_pInputBuffer->EndRead(0);
            return kError_PluginNotInitialized;
         }
         if (Err != kError_NoErr) {
            m_pInputBuffer->EndRead(0);
            return (kError_PluginNotInitialized);
         }
         memcpy(pOutBuffer, pBuffer, iReadSize);
         ogg_sync_wrote(&oy, iReadSize);
      }
   }
   vorbis_synthesis_init(&vd, &vi);
   vorbis_block_init(&vd, &vb);
   return kError_NoErr;
}

Error VorbisLMC::InitDecoder()
{
   int          iNewSize, bytes;
   int	        result;
   OutputInfo   *info = NULL;
   Error        Err = kError_PluginNotInitialized;
   Error        Err2 = kError_PluginNotInitialized;

   if (!m_pTarget || !m_pPmi || !m_pPmo || !m_pInputBuffer || !m_pOutputBuffer) {
      return kError_PluginNotInitialized;
   }
   if (m_bExit)
      return kError_Interrupt;

   if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&
         m_pInputBuffer->GetNumBytesInBuffer() > 0 &&
         m_pInputBuffer->IsEndOfStream())
      iReadSize = m_pInputBuffer->GetNumBytesInBuffer();
   else
      iReadSize = iMaxFrameSize;
   while ((Err = m_pInputBuffer->BeginRead(pBuffer, iReadSize)) == kError_NoDataAvail) {
      m_pPmi->Wake();
      if (Sleep()) {
         m_pInputBuffer->EndRead(0);
         return kError_Interrupt;
      }
   }
   if (Err == kError_NoErr) {
      Err2 = VorbisInit();
      if (Err2 != kError_NoErr) {
         m_pInputBuffer->EndRead(0);
         return Err2;
      } else
         m_pInputBuffer->EndRead(iReadSize);
   } else {
      m_pInputBuffer->EndRead(0);
      return kError_PluginNotInitialized;
   }
   m_channels = vi.channels;
   m_rate = vi.rate;
   info = new OutputInfo;
   info->bits_per_sample = 16;
   info->number_of_channels = vi.channels;
   info->samples_per_second = vi.rate;
   iMaxWriteSize = info->number_of_channels * (info->bits_per_sample / 8) *
                   8192 * 2;
   info->samples_per_frame = 8192;
   info->max_buffer_size = iMaxWriteSize;
   m_pContext->prefs->GetPrefInt32(kOutputBufferSizePref, &iNewSize);
   iNewSize = max(iNewSize, iMinimumOutputBufferSize);
   iNewSize *= 2048;
   result = m_pOutputBuffer->Resize(iNewSize, iNewSize / 6);
   if (IsError((Error)result)) {
      ReportError(_("Internal buffer sizing error occurred."));
      m_pContext->log->Error("Resize output buffer failed.");
      return (Error)result;
   }
   ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOInitEvent(info));
   m_bInit = true;
   return kError_NoErr;
}

Error VorbisLMC::ExtractMediaInfo()
{
   Error           err;
   float           totalSeconds;
   int32_t	   filesize;
   MediaInfoEvent  *pMIE;

   // If we are here and m_bInit is false then we must not actually be
   // decoding the file.
   if (!m_bInit) {
      err = InitDecoder();
      if (err != kError_NoErr)
         return err;
      m_bInit = false;
   }

   if (m_bExit) {
      if (!m_bInit)
         ClrDecode();
      return kError_Interrupt;
   }

   // If we're streaming we should just return the total time to the
   // current position we're at. Seems reasonable.
   if (m_pPmi->IsStreaming())
      totalSeconds = (double)m_pPmi->Tell(filesize) /
                     ((double)vi.bitrate_nominal / 8.0);
   else {
      // Calculates a rough estimate of the total length of file.
      // To get an exact length we'd need to read the last page of the
      // file. Not sure it's worth the added cpu time. but it wouldn't be
      // too much more cpu time.
      err = m_pPmi->GetLength((size_t)filesize);
      if (err == kError_NoErr)
         totalSeconds = (double)filesize / ((double)vi.bitrate_nominal / 8.0);
      else {
         // something bad happened
         if (!m_bInit)
            ClrDecode();
         return (err);
      }
   }
   pMIE = new MediaInfoEvent(m_pPmi->Url().c_str(), totalSeconds);
   if (!pMIE) {
      // Something got screwed up
      if (!m_bInit)
         ClrDecode();
      return kError_OutOfMemory;
   }
   VorbisInfoEvent *mie = new VorbisInfoEvent(vi.bitrate_nominal,
                          vi.channels,
                          vi.rate,
                          1. / (float)iFramesPerSecond);
   if (mie) {
      pMIE->AddChildEvent((Event *) mie);
   } else {
      if (!m_bInit)
         ClrDecode();
      return kError_OutOfMemory;
   }
   if (m_pTarget)
      m_pTarget->AcceptEvent(pMIE);
   // Since we aren't decoding the file, clear our variables up.
   if (!m_bInit) {
      ClrDecode();
   }
   return kError_NoErr;
}


// This method is only used external from the decoder, not sure by what
uint32_t VorbisLMC::CalculateSongLength(const char *url)
{
   int	   totalSeconds;
   size_t  filesize;
   Error   err;
   
   if (!m_bInit) {
      err = InitDecoder();
      if (err != kError_NoErr)
         return err;
      m_bInit = false;
   }
   m_pPmi->GetLength(filesize);
   totalSeconds = (int)((float)filesize / ((float)vi.bitrate_nominal / 8.0));
   if (!m_bInit)
      ClrDecode();
   return (totalSeconds);
}

void VorbisLMC::DecodeWorkerThreadFunc(void *pxlmc)
{
   if (pxlmc) {
      VorbisLMC *xlmc = (VorbisLMC *) pxlmc;
      xlmc->DecodeWork();
   }
}

Error VorbisLMC::VDecode(int &vout, void *rOutBuffer)
{
   int	        ret, eos;
   ogg_int16_t  convbuffer[iMaxWriteSize + 4];
   
   while (1) {
      ret = ogg_sync_pageout(&oy, &og);
      if (ret <= 0) {
         // Need more data, breaking to read methods
         return kError_NoErr;
      } else {
         ret = ogg_stream_pagein(&os, &og);
         while (1) {
            ret = ogg_stream_packetout(&os, &op);
            if (ret <= 0) {
               // Need more data, try breaking to read methods
               return (kError_NoErr);
            } else {
               float  **pcm;
               int    samples;

               if ((samples = vorbis_synthesis(&vb, &op)) == 0)
                  vorbis_synthesis_blockin(&vd, &vb);
               if (samples) {
                  return kError_NoErr;
               }
               while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0
                      && !m_bExit) {
                  int j;
                  int clipflag = 0;
                  int bout = (samples < ((iMaxWriteSize - vout) / 4) ? samples : ((iMaxWriteSize - vout) / 4));
		  
                  if (bout == 0)
                     return kError_NoErr;
                  for (int i = 0;i < vi.channels;i++) {
                     ogg_int16_t  *ptr = convbuffer + i;
                     float        *mono = pcm[i];
		     
                     for (j = 0;j < bout;j++) {
                        int val = (int)((float)mono[j] * 32767.f);
			
                        if (val > 32767) {
                           val = 32767;
                           clipflag = 1;
                        }
                        if (val < -32768) {
                           val = -32768;
                           clipflag = 1;
                        }
                        *ptr = val;
                        ptr += vi.channels;
                     }
                  }
                  memcpy(((char*)rOutBuffer) + vout, convbuffer, bout*2*vi.channels);
                  vout += (bout * 2 * vi.channels);
                  vorbis_synthesis_read(&vd, bout);
               }
            }
         }
         if (ogg_page_eos(&og)) {
            return kError_EndOfStream;
         }
      }
   }
   if (m_bExit) {
      return kError_Interrupt;
   }
   return kError_NoErr;
}



Error VorbisLMC::BeginRead(void *&pBuf, unsigned int iBytesNeeded)
{
   Error eRet = kError_NoErr;
   
   for (;!m_bExit;) {
      eRet = m_pInputBuffer->BeginRead(pBuf, iBytesNeeded);
      if (eRet == kError_NoDataAvail) {
         m_pPmi->Wake();
         if (Sleep()) {
            m_pInputBuffer->EndRead(0);
            return kError_Interrupt;
         }
      }
      break;

   }
   if (m_bExit)
      return kError_Interrupt;
   return eRet;
}

Error VorbisLMC::EndRead(size_t iBytesUsed)
{
   return m_pInputBuffer->EndRead(iBytesUsed);
}


void VorbisLMC::DecodeWork()
{
   Error    Err, Err2;
   int32_t  iValue;
   int32_t  ret;
   bool     bRestart = false;
   void     *rOutBuffer = 0;

   assert(m_pPmi);
   assert(m_pPmo);

   m_pSleepSem->Wait();
   m_pPmi->Wake();

   // We have to init the decoder before actually decoding, duh
   if (!m_bInit) {
      Err = InitDecoder();
      if (Err == kError_Interrupt)
         return ;
      if (IsError(Err)) {
         if (m_decodeInfo.sendInfo) {
            ReportStatus(szCannotDecode);
            m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
         } else {
            ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
         }
         return ;
      }
      //cerr << "Initialization stuff here \n";
   }
   // We are an ogg vorbis file, so tell the player some info about it
   Err = ExtractMediaInfo();
   if (Err == kError_Interrupt)
      return ;
   if (IsError(Err)) {
      m_pContext->log->Error("ExtractMediaInfo failed: %d\n", Err);
      if (m_decodeInfo.sendInfo) {
         ReportStatus(szCannotDecode);
         m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
      } else
         ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
      return ;
   }
   // This is a non-functional feature.  Yay.  it looks good in theory
   m_pContext->prefs->GetPrefInt32(kDecoderThreadPriorityPref, &iValue);
   m_decoderThread->SetPriority(iValue);

   // start at frame 0 with 8192 bytes to fill in write buffer.
   vout = 0;
   for (m_frameCounter = 0; !m_bExit;) {
      ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(
         new PMOTimeInfoEvent(m_frameCounter));
      for (;!m_bExit;) {
         if (m_bPause) {
            m_pPauseSem->Wait();
            if (m_bExit)
               return ;
            continue;
         }
         if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&
               m_pInputBuffer->GetNumBytesInBuffer() > 0 &&
               m_pInputBuffer->IsEndOfStream())
            iReadSize = m_pInputBuffer->GetNumBytesInBuffer();
         else
            iReadSize = iMaxFrameSize;
         //cerr << "end of stream " << (int)m_pInputBuffer->IsEndOfStream() << endl;
         if (m_pInputBuffer->IsEndOfStream()) {
            ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOQuitEvent());
            return ;
         }
         Err = BeginRead(pBuffer, iReadSize);
         if (Err == kError_Interrupt) {
            return ;
         }
         /*if (Err == kError_EndOfStream) {
	    m_bExit = true;
            ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOQuitEvent());
            return ;
         }*/
         if (Err == kError_NoDataAvail) {
            if (Sleep())
               break;
            continue;
         }
         if (Err != kError_NoErr) {
            EndRead(0);
            ReportError(szFailRead);
            m_pContext->log->Error("LMC: Cannot read from pullbuffer: %s\n", m_szError);
            return ;
         }
         Err = m_pOutputBuffer->BeginWrite(rOutBuffer, iMaxWriteSize);
         if (Err == kError_Interrupt) {
            EndRead(0);
            break;
         }
         if (Err == kError_BufferTooSmall) {
            EndRead(0);
            m_pOutputBuffer->EndWrite(0);
            if (Sleep())
               break;
            continue;
         }
         if (Err != kError_NoErr) {
            EndRead(0);
            if (m_decodeInfo.sendInfo)
               ReportError(szFailWrite);
            else
               ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
            m_pContext->log->Error("LMC: Cannot write to eventbuffer: %s (%d)\n",
                                   m_szError, Err);
            return ;
         }
         (char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
         memcpy(pOutBuffer, pBuffer, iReadSize);
         ogg_sync_wrote(&oy, iReadSize);
         Err2 = VDecode(vout, rOutBuffer);
         if (Err2 != kError_NoErr) {
            vout = 0;
            m_pOutputBuffer->EndWrite(0);
            m_pInputBuffer->EndRead(0);
            return ;
         }
         break;
      }
      if (m_bExit || Err == kError_Interrupt || Err == kError_EndOfStream) {
         return ;
      }
      if (Err == kError_NoErr) {
         EndRead(iReadSize);
         m_frameCounter++;
      } else
         EndRead(0);
      m_pPmi->Wake();
      if (Err == kError_NoErr && m_pOutputBuffer && vout == iMaxWriteSize) {
         Err = m_pOutputBuffer->EndWrite(vout);
         vout = 0;
         if (Err == kError_Interrupt) {
            break;
         }
         if (IsError(Err)) {
            m_pContext->log->Error("lmc: EndWrite returned %d\n", Err);
            if (m_decodeInfo.sendInfo)
               ReportError(szFailWrite);
            else
               ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
            return ;
         }
      } else {
         m_pOutputBuffer->EndWrite(0);
      }
   }
   return ;
}

// This is the annoying bastard method that doesn't wanna work.
Error VorbisLMC::ChangePosition(int position)
{
   int32_t   dummy;
   uint32_t  ISeekTo;
   
   cerr << "seeking from pos :" << m_frameCounter << "   "
        << position << "    " << (m_frameCounter * iMaxFrameSize) / 2
        << " to " << (position * iMaxFrameSize) / 2 << endl;
   //m_frameCounter = (position/4 - vout/4);
   m_frameCounter = position;
   vout = 0;
   ISeekTo = ((m_frameCounter) * (iMaxFrameSize));
   m_pPmi->Seek(dummy, ISeekTo, SEEK_FROM_START);
   return kError_NoErr;
}


// Not sure if this is used anywhere
Error VorbisLMC::SetDecodeInfo(DecodeInfo &info)
{
   m_decodeInfo = info;
   return kError_NoErr;
}


// Not sure if this is used anywhere.
const string VorbisLMC::ConvertToISO(const char *utf8)
{
   unsigned char  *in, *buf;
   unsigned char  *out, *end;
   string         ret;

   in = (unsigned char *)utf8;
   buf = out = new unsigned char[strlen(utf8) + 1];
   end = in + strlen(utf8);
   for (;*in != 0x00 && in <= end; in++, out++) {
      if (*in < 0x80) {
         /* lower 7-bits unchanged */
         *out = *in;
      } else if (*in > 0xC3) {
         /* discard anything above 0xFF */
         *out = '?';
      } else if (*in & 0xC0) {
         /* parse upper 7-bits */
         if (in >= end)
            *out = 0;
         else {
            *out = (((*in) & 0x1F) << 6) | (0x3F & (*(++in)));
         }
      } else {
         *out = '?';  /* this should never happen */
      }
   }
   *out = 0x00; /* append null */
   ret = string((char *)buf);
   delete[] buf;
   return ret;
}
vorbislmc.h (text/x-chdr, 3.4 KB)
/*____________________________________________________________________________
  
  Zinf - Zinf Is Not FreeA*p (The Free MP3 Player)
  Portions Copyright (C) 1998-1999 EMusic.com

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  
  $Id: vorbislmc.h,v 1.4 2003/02/09 19:28:03 rottmann Exp $

____________________________________________________________________________*/

#ifndef INCLUDED_VORBISLMC_H_
#define INCLUDED_VORBISLMC_H_

/* system headers */
#include <stdlib.h>
#include <time.h>
#include <vorbis/vorbisfile.h>
#include <string>
#include <vector>

/* project headers */
#include "config.h"

#include "pmi.h"
#include "pmo.h"
#include "mutex.h"
#include "event.h"
#include "lmc.h"
#include "thread.h"
#include "mutex.h"
#include "queue.h"
#include "semaphore.h"

class VorbisLMC : public LogicalMediaConverter
{

   public:
      VorbisLMC(FAContext *context);
      virtual ~VorbisLMC();

      virtual uint32_t CalculateSongLength(const char *url);

      virtual Error ChangePosition(int position);

      virtual Error CanDecode();
      virtual Error ExtractMediaInfo();

      virtual void SetPMI(PhysicalMediaInput *pmi)
      {
         m_pPmi = pmi;
      };
      virtual void SetPMO(PhysicalMediaOutput *pmo)
      {
         m_pPmo = pmo;
      };
      virtual Error Prepare(PullBuffer *pInputBuffer, PullBuffer *&pOutBuffer);
      virtual Error InitDecoder();

      virtual std::vector<std::string> *GetExtensions(void);

      virtual Error SetEQData(float *f, float)
      {
         return kError_YouScrewedUp;
      };
      virtual Error SetEQData(bool b)
      {
         return kError_YouScrewedUp;
      };

      virtual Error SetDecodeInfo(DecodeInfo &info);

   private:

      const std::string	ConvertToISO(const char *utf8);

      Error BeginRead(void *&, unsigned int);
      Error EndRead(size_t);

      static void DecodeWorkerThreadFunc(void *);
      void ClrDecode();
      void DecodeWork();
      Error VorbisInit();
      Error VDecode(int&, void*);

      PhysicalMediaInput  *m_pPmi;
      PhysicalMediaOutput *m_pPmo;
      Thread              *m_decoderThread;

      char          *m_szUrl;
      const char    *m_szError;
      bool          m_bInit;
      int           m_channels, m_rate;
      long          m_frameCounter;
      void          *pBuffer;
      void          *pOutBuffer;
      int           iMaxFrameSize;
      int           iMaxWriteSize;
      int           vout;
      int           iReadSize;
            
      // Ogg vorbis related datastructures
      ogg_sync_state     oy;
      ogg_stream_state   os;
      ogg_page           og;
      ogg_packet         op;
      vorbis_dsp_state   vd;
      vorbis_block       vb;
      vorbis_info        vi;
      vorbis_comment     vc;
      long               serialno;

};

#endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.