Re: vorbis decoder stuffs

Ed Sweetman <[email protected]>
Newsgroups gmane.comp.audio.zinf.devel
Message-ID <[email protected]>
Ok, here's the latest and greatest.  Commented too.

Seeking code works rarely, I kind of know what's going on but i dont 
know how to fix it.  I cant lock around my the writes to the 
outputbuffer.  That and the fact that the seeking code seems to always 
take effect after my read from the input buffer.  Something is not 
synced up between the accesses to the two threads and i've tried a ton 
of crap.  Multiple locks  etc.  Nothing seems to work really.  I get the 
seek to work randomnly, and always the slider is at the correct 
position, then reverts back to the old one, then back to the one i set 
it at.  Something weird is going on.



anyway this plugin is now solid except soley for seeking. I can 
fast-track like a mother and it doesn't crash and i can stop and start 
and pause and all that works exactly like it should and the time is only 
slightly off.  In my opinion, not crashing when you track select is more 
important than not crashing when you seek in the file.  This is not 
ready for commit yet obviously though. It wont be until seeking is fixed 
and i do more cleanups and get rid of unecessary shit.

The locks seem like a little too much but it's seriously necessary.  At 
least for some of the methods, perhaps after seeking works i'll go 
through and test each method to see which ones are succeptible to 
memmove/malloc issues etc etc.


Try these out, tell me what you think.   All the other decoders are 
going to go into similar rewrites since they all violate interfaces. I 
noticed a bunch of win32 commits but no activity on this mailing list or 
the irc channel, communication anyone?  Still a day or two ...and a 
slight chance of even a few more days until I have this stupid seeking 
problem licked and then i'm going to have a number of patches backed up 
that need to be committed, win32 issues or not.
vorbislmc.h (text/x-chdr, 3.1 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(int32_t 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);

   static void          DecodeWorkerThreadFunc(void *);
   void			ClrDecode();
   void                 DecodeWork();

   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;
   // Ogg vorbis related datastructures
    ogg_sync_state oy;
    ogg_stream_state os;
    ogg_page og;
    vorbis_dsp_state vd;
    vorbis_block vb;
    vorbis_info vi;
    vorbis_comment vc;
    long serialno;
    // end of ogg vorbis related datastructures
    
    //  input/output buffers
    void *pBuffer;
    void *pOutBuffer;
    void *tbuffer;
    
    int iMaxFrameSize;
    int iReadSize;    
    int left;
    Mutex seekLock;
    bool seeked;
};

#endif
vorbislmc.cpp (text/x-c++src, 19.7 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 iDecodeBlockSize = 8192;
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.");


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),tbuffer(NULL)
{
   m_pContext = context;
   m_bInit = false;
   m_decodeInfo.sendInfo = true;
   iMaxFrameSize = 8192;
   iReadSize = 0;
   seeked = false;
}

VorbisLMC::~VorbisLMC()
{
    
   if (m_decoderThread)
   {
      m_pPauseSem->Signal();
      m_pSleepSem->Signal();
      seekLock.Acquire();
      m_pMutex->Acquire();
      if(m_bInit) ClrDecode();
      m_bExit = true;
      m_pMutex->Release();
      seekLock.Release();
      m_decoderThread->Join();
      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()
{
    m_pMutex->Acquire();
    ogg_stream_clear(&os);
    vorbis_block_clear(&vb);
    vorbis_dsp_clear(&vd);
    vorbis_comment_clear(&vc);
    vorbis_info_clear(&vi);
    ogg_sync_clear(&oy);
    m_pMutex->Release();
}    


// 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::InitDecoder()
{
    int            result;
    int            iNewSize,bytes;
    int		   m_iMaxWriteSize;
    OutputInfo	   *info = NULL;
    int		   headers = 0;
    Error 	   Err = kError_PluginNotInitialized;
    ogg_packet 	   op;
    
    if (!m_pTarget || !m_pPmi || !m_pPmo || !m_pInputBuffer || !m_pOutputBuffer){
	return kError_PluginNotInitialized;
    }
    if (m_bExit) 
	return kError_Interrupt;
	
    m_pMutex->Acquire();
    ogg_sync_init(&oy);
    if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&     
			m_pInputBuffer->GetNumBytesInBuffer() > 0 &&                      
			m_pInputBuffer->IsEndOfStream())                                  
	iReadSize = m_pInputBuffer->GetNumBytesInBuffer();                
    else                                                                  
	iReadSize = iMaxFrameSize;

    // Allocate input buffer space in libvorbis
    (char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
    
    // Read in data to different pointer, so we dont lose pointer to libvorbis
    // memory
    while((Err = m_pInputBuffer->BeginRead(pBuffer,iReadSize)) == kError_NoDataAvail){
	m_pPmi->Wake();
	if(Sleep()){
	    m_pMutex->Release();
	    return kError_Interrupt;
	}
    }
    // Copy data we read in BeginRead to memory in libvorbis
    if(Err == kError_NoErr){
	memcpy(pOutBuffer,pBuffer,iReadSize);
	ogg_sync_wrote(&oy,iReadSize);
	m_pInputBuffer->EndRead(iReadSize);
    }
    
    // Initialized some vorbis data structures and read in beginning header
    if(ogg_sync_pageout(&oy,&og) != 1){
	m_pMutex->Release();
	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){
	m_pMutex->Release();
	return (kError_PluginNotInitialized);
    }
    if(ogg_stream_packetout(&os,&op) != 1){
	m_pMutex->Release();
	return (kError_PluginNotInitialized);
    }
    if(vorbis_synthesis_headerin(&vi,&vc,&op) < 0){
	m_pMutex->Release();
	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){
			m_pMutex->Release();
			return (kError_PluginNotInitialized);
		    }
		    vorbis_synthesis_headerin(&vi,&vc,&op);
		    headers++;
		}
	    }
	}
	// read in more data if needed to read next header
	if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&     
			m_pInputBuffer->GetNumBytesInBuffer() > 0 &&                      
			m_pInputBuffer->IsEndOfStream())                                  
	    iReadSize = m_pInputBuffer->GetNumBytesInBuffer();                
	else                                                                  
	    iReadSize = iMaxFrameSize; 
        // Allocate input buffer space in libvorbis
	(char*)pOutBuffer = ogg_sync_buffer(&oy,iReadSize);
	while((Err = m_pInputBuffer->BeginRead(pBuffer,iReadSize)) == kError_NoDataAvail){
	    m_pPmi->Wake();
	    if(Sleep()){
		m_pMutex->Release();
		return kError_Interrupt;
	    }
	}
	if(Err == kError_EndOfStream){
	    m_pMutex->Release();
	    return kError_PluginNotInitialized;
	}
	if(Err==kError_NoErr){
	    memcpy(pOutBuffer,pBuffer,iReadSize);
	    ogg_sync_wrote(&oy,iReadSize);
	    m_pInputBuffer->EndRead(iReadSize);
	}
	if(iReadSize == 0 && headers < 3 || Err != kError_NoErr){
	    m_pMutex->Release();
	    return (kError_PluginNotInitialized);
	}
    }
    // All headers have successfully been read.
    
    m_channels = vi.channels;
    m_rate = vi.rate;
        
    vorbis_synthesis_init(&vd,&vi);
    vorbis_block_init(&vd,&vb);
    
    // values here probably shouldn't be hardcoded.
    info = new OutputInfo;
    info->bits_per_sample = 16;
    info->number_of_channels = vi.channels;
    info->samples_per_second = vi.rate;
    m_iMaxWriteSize = info->number_of_channels * (info->bits_per_sample/8) *
			4096;
    // This determines how fast time is "counted"
    info->samples_per_frame = 2048;
    info->max_buffer_size = m_iMaxWriteSize;
    m_pContext->prefs->GetPrefInt32(kOutputBufferSizePref, &iNewSize);
    iNewSize = max(iNewSize, iMinimumOutputBufferSize);
    iNewSize *= 1024;
    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.");
	m_pMutex->Release();
	return (Error)result;
    }   
    ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOInitEvent(info));
    m_bInit = true;
    m_pMutex->Release();
    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();
   }
}

void VorbisLMC::DecodeWork()
{
    Error	Err;
    int32_t	iValue;
    int32_t	ret;
    int		eos = 0;

    assert(m_pPmi);
    assert(m_pPmo);

    m_pSleepSem->Wait();
    m_pPmi->Wake();
    
    // intermediate buffer
    char convbuffer[4096];
    
    // write buffer
    char writeBuffer[iMaxFrameSize];
    
    // using void* for ease of use
    tbuffer = writeBuffer;
    
    // 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;
	}
    }
    // 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.
    m_frameCounter = 0;
    left = iMaxFrameSize;
    
    while(!eos){
	while(!eos && !seeked){
	
	    // Lock all all ogg vorbis method calls so we dont delete
	    // or change them via another thread while in execution
	    // they'll flip out if you do.
	    m_pMutex->Acquire();
	    ogg_packet op;
	    m_pMutex->Release();
	    if(m_bPause){
		m_pPauseSem->Wait();
		if(m_bExit)
		    break;
	    }
	    m_pMutex->Acquire();
	    ret = ogg_sync_pageout(&oy,&og);
	    m_pMutex->Release();
	    if(!ret){
		// Need more data, breaking to read methods
		break;
	    } else {
		m_pMutex->Acquire();
		ogg_stream_pagein(&os,&og);
		m_pMutex->Release();
		// Theoretically this should cause us to break to read methods
		while(!seeked){
		    m_pMutex->Acquire();
		    ret = ogg_stream_packetout(&os,&op);
		    m_pMutex->Release();
		    if(!ret){
			// Need more data, try breaking to read methods
			break;
		    } else {
			float **pcm;
			int samples;

			m_pMutex->Acquire();
			if((samples = vorbis_synthesis(&vb,&op)) == 0)
			    vorbis_synthesis_blockin(&vd,&vb);
			m_pMutex->Release();
			
			// samples is the error return here, if not 0 
			// try breaking to read methods. 
			if(samples)break;
			
			while((samples=vorbis_synthesis_pcmout(&vd,&pcm))>0 && 
				!seeked && !m_bExit){
			    int j;
			    int clipflag=0;
			    int bout=(samples<(left/4)?samples:(left/4));
			    if(m_bPause){
				m_pPauseSem->Wait();
				if(m_bExit)
				    break;
			    }

			    for(int i=0;i<vi.channels;i++){
				ogg_int16_t *ptr=((ogg_int16_t*)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;
				}
			    }
			    m_pMutex->Acquire();
			    // Here we fill tbuffer, this should always fill to
			    // exactly 8192 bytes. 
			    memcpy(((char*)tbuffer+(iMaxFrameSize-left)), convbuffer,bout*2*vi.channels);				
			    left = left - (bout * 2 * vi.channels);
			    
			    // Theoretically this is obsolete data if we seeked
			    if(!seeked)
				vorbis_synthesis_read(&vd,bout);			    
			    m_pMutex->Release();

			    // We only want to write to the output buffer
			    // when we have a full tbuffer and haven't seeked
			    if((left == 0 || iReadSize < iMaxFrameSize)&& !seeked){
				while(!seeked && !m_bExit && 
					(Err = m_pOutputBuffer->BeginWrite(tbuffer,iMaxFrameSize))==kError_BufferTooSmall){
				    // Since we're not locked no telling when
				    // the seek can happen. This is the root
				    // of the problem with seeking
				    if(Sleep())
					break;
				    continue;
				}
				if(m_pOutputBuffer){
				    if(Err == kError_NoErr){
					m_pMutex->Acquire();
					/* we increment our counter here since
					   we could loop any number of times
					   to reach 8192 bytes so this is only
					   sure way to know we have done a 
					   "frame"
					*/
					if(!seeked)
					m_frameCounter++;
					m_pMutex->Release();
					Err = m_pOutputBuffer->EndWrite(iMaxFrameSize);
				    }
				}
				// Since we output when left == 0, reset
				left = iMaxFrameSize;
			    }
			    if(m_bExit || Err == kError_Interrupt || Err == kError_EndOfStream){
				return;
			    }
			    if(seeked)break;
			}
		    }
		}
		if(m_bExit || Err == kError_Interrupt || Err == kError_EndOfStream)
		    return;
		m_pMutex->Acquire();
		// We have obsolete data here if we seeked
		if(!seeked)
		if(ogg_page_eos(&og))
		    eos=1;
		m_pMutex->Release();
	    }
	}
	if(!eos){
	    if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&     
				m_pInputBuffer->GetNumBytesInBuffer() > 0 &&                      
				m_pInputBuffer->IsEndOfStream())                                  
		iReadSize = m_pInputBuffer->GetNumBytesInBuffer();                
	    else                                                                  
		iReadSize = iMaxFrameSize;

	    // seekLock theoretically allows us to lock just the vorbis
	    // lmc so we can lock across BeginRead etc methods but this 
	    // doesn't work the way i wanted it to.
	    seekLock.Acquire();
	    m_pMutex->Acquire();

	    // pOutBuffer is a pointer to memory allocated in libvorbis
	    (char*)pOutBuffer = ogg_sync_buffer(&oy,left);
	    m_pMutex->Release();
	    // Since BeginRead allocates it's own buffer and sets the pointer
	    // we sent to it to it's memory, we have to use a different pointer
	    // We read "left" since we might be here with partially filled 
	    // tbuffer. 
	    
	    while((Err = m_pInputBuffer->BeginRead(pBuffer,left)) == kError_NoDataAvail){
		m_pPmi->Wake();
		if(Sleep())
		    return;
	    }
	    if(Err == kError_EndOfStream){
		((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOQuitEvent());
		eos = 1;
	    }
	    // Since we now have two pointers to the same amount of memory
	    // we have to copy the data from BeginRead to the ogg pointer
	    // If we seeked we've theoretically resynced given new position
	    seeked = false;
	    if(Err == kError_NoErr){
		((EventBuffer *)m_pOutputBuffer)->AcceptEvent(
			new PMOTimeInfoEvent(m_frameCounter));
		m_pMutex->Acquire();
		memcpy(pOutBuffer,pBuffer,left);
		ogg_sync_wrote(&oy,left);
		m_pMutex->Release();
		m_pInputBuffer->EndRead(left);
	    }
	    seekLock.Release();
	    
	    if(m_bExit || Err == kError_Interrupt || Err == kError_EndOfStream)
		return;
	    if(!iReadSize)
		eos=1;
	}	
    }
}

// This is the annoying bastard method that doesn't wanna work.
Error VorbisLMC::ChangePosition(int32_t position)
{
    int32_t dummy;
    uint32_t ISeekTo;
    size_t length;
    Error Err; 
    m_bPause = true;
    m_pPmi->GetLength(length);
    ISeekTo = (position * 2048);
    m_pMutex->Acquire();
    m_frameCounter = position*2;
    seeked = true;
    left = iMaxFrameSize;
    m_pMutex->Release();
    m_pPmi->Seek(dummy,ISeekTo, SEEK_FROM_START);
    m_bPause = false;
    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;
}
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.