- Implemented native Song playback in OpenAL and XAudio2 - Some tweaks in the MediaPlayer and MediaQueue - Added a testmusic.ogg file to the media folder
290 lines
6.5 KiB
C#
290 lines
6.5 KiB
C#
/* csogg
|
|
* Copyright (C) 2000 ymnk, JCraft,Inc.
|
|
*
|
|
* Written by: 2000 ymnk<ymnk@jcraft.com>
|
|
* Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org>
|
|
*
|
|
* Thanks go to the JOrbis team, for licencing the code under the
|
|
* LGPL, making my job a lot easier.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library 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 Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
using System;
|
|
|
|
namespace csogg
|
|
{
|
|
/// <summary>
|
|
/// Summary description for SyncState.
|
|
/// </summary>
|
|
public class SyncState
|
|
{
|
|
public byte[] data;
|
|
int storage;
|
|
int fill;
|
|
int returned;
|
|
|
|
int unsynced;
|
|
int headerbytes;
|
|
int bodybytes;
|
|
|
|
public int clear()
|
|
{
|
|
data=null;
|
|
return(0);
|
|
}
|
|
|
|
// !!!!!!!!!!!!
|
|
// byte[] buffer(int size){
|
|
public int buffer(int size)
|
|
{
|
|
// first, clear out any space that has been previously returned
|
|
if(returned!=0)
|
|
{
|
|
fill-=returned;
|
|
if(fill>0)
|
|
{
|
|
Array.Copy(data, returned, data, 0, fill);
|
|
}
|
|
returned=0;
|
|
}
|
|
|
|
if(size>storage-fill)
|
|
{
|
|
// We need to extend the internal buffer
|
|
int newsize=size+fill+4096; // an extra page to be nice
|
|
if(data!=null)
|
|
{
|
|
byte[] foo=new byte[newsize];
|
|
Array.Copy(data, 0, foo, 0, data.Length);
|
|
data=foo;
|
|
}
|
|
else
|
|
{
|
|
data=new byte[newsize];
|
|
}
|
|
storage=newsize;
|
|
}
|
|
|
|
// expose a segment at least as large as requested at the fill mark
|
|
// return((char *)oy->data+oy->fill);
|
|
// return(data);
|
|
return(fill);
|
|
}
|
|
|
|
public int wrote(int bytes)
|
|
{
|
|
if(fill+bytes>storage)return(-1);
|
|
fill+=bytes;
|
|
return(0);
|
|
}
|
|
|
|
// sync the stream. This is meant to be useful for finding page
|
|
// boundaries.
|
|
//
|
|
// return values for this:
|
|
// -n) skipped n bytes
|
|
// 0) page not ready; more data (no bytes skipped)
|
|
// n) page synced at current location; page length n bytes
|
|
private Page pageseek_p=new Page();
|
|
private byte[] chksum=new byte[4];
|
|
public int pageseek(Page og)
|
|
{
|
|
int page=returned;
|
|
int next;
|
|
int bytes=fill-returned;
|
|
|
|
if(headerbytes==0)
|
|
{
|
|
int _headerbytes,i;
|
|
if(bytes<27)return(0); // not enough for a header
|
|
|
|
/* verify capture pattern */
|
|
//!!!!!!!!!!!
|
|
if(data[page]!='O' ||
|
|
data[page+1]!='g' ||
|
|
data[page+2]!='g' ||
|
|
data[page+3]!='S')
|
|
{
|
|
headerbytes=0;
|
|
bodybytes=0;
|
|
|
|
// search for possible capture
|
|
next=0;
|
|
for(int ii=0; ii<bytes-1; ii++)
|
|
{
|
|
if(data[page+1+ii]=='O'){next=page+1+ii; break;}
|
|
}
|
|
//next=memchr(page+1,'O',bytes-1);
|
|
if(next==0) next=fill;
|
|
|
|
returned=next;
|
|
return(-(next-page));
|
|
}
|
|
_headerbytes=(data[page+26]&0xff)+27;
|
|
if(bytes<_headerbytes)return(0); // not enough for header + seg table
|
|
|
|
// count up body length in the segment table
|
|
|
|
for(i=0;i<(data[page+26]&0xff);i++)
|
|
{
|
|
bodybytes+=(data[page+27+i]&0xff);
|
|
}
|
|
headerbytes=_headerbytes;
|
|
}
|
|
|
|
if(bodybytes+headerbytes>bytes)return(0);
|
|
|
|
// The whole test page is buffered. Verify the checksum
|
|
lock(chksum)
|
|
{
|
|
// Grab the checksum bytes, set the header field to zero
|
|
|
|
Array.Copy(data, page+22, chksum, 0, 4);
|
|
data[page+22]=0;
|
|
data[page+23]=0;
|
|
data[page+24]=0;
|
|
data[page+25]=0;
|
|
|
|
// set up a temp page struct and recompute the checksum
|
|
Page log=pageseek_p;
|
|
log.header_base=data;
|
|
log.header=page;
|
|
log.header_len=headerbytes;
|
|
|
|
log.body_base=data;
|
|
log.body=page+headerbytes;
|
|
log.body_len=bodybytes;
|
|
log.checksum();
|
|
|
|
// Compare
|
|
if(chksum[0]!=data[page+22] ||
|
|
chksum[1]!=data[page+23] ||
|
|
chksum[2]!=data[page+24] ||
|
|
chksum[3]!=data[page+25])
|
|
{
|
|
// D'oh. Mismatch! Corrupt page (or miscapture and not a page at all)
|
|
// replace the computed checksum with the one actually read in
|
|
Array.Copy(chksum, 0, data, page+22, 4);
|
|
// Bad checksum. Lose sync */
|
|
|
|
headerbytes=0;
|
|
bodybytes=0;
|
|
// search for possible capture
|
|
next=0;
|
|
for(int ii=0; ii<bytes-1; ii++)
|
|
{
|
|
if(data[page+1+ii]=='O'){next=page+1+ii; break;}
|
|
}
|
|
//next=memchr(page+1,'O',bytes-1);
|
|
if(next==0) next=fill;
|
|
returned=next;
|
|
return(-(next-page));
|
|
}
|
|
}
|
|
|
|
// yes, have a whole page all ready to go
|
|
{
|
|
page=returned;
|
|
|
|
if(og!=null)
|
|
{
|
|
og.header_base=data;
|
|
og.header=page;
|
|
og.header_len=headerbytes;
|
|
og.body_base=data;
|
|
og.body=page+headerbytes;
|
|
og.body_len=bodybytes;
|
|
}
|
|
|
|
unsynced=0;
|
|
returned+=(bytes=headerbytes+bodybytes);
|
|
headerbytes=0;
|
|
bodybytes=0;
|
|
return(bytes);
|
|
}
|
|
// headerbytes=0;
|
|
// bodybytes=0;
|
|
// next=0;
|
|
// for(int ii=0; ii<bytes-1; ii++){
|
|
// if(data[page+1+ii]=='O'){next=page+1+ii;}
|
|
// }
|
|
// //next=memchr(page+1,'O',bytes-1);
|
|
// if(next==0) next=fill;
|
|
// returned=next;
|
|
// return(-(next-page));
|
|
}
|
|
|
|
|
|
// sync the stream and get a page. Keep trying until we find a page.
|
|
// Supress 'sync errors' after reporting the first.
|
|
//
|
|
// return values:
|
|
// -1) recapture (hole in data)
|
|
// 0) need more data
|
|
// 1) page returned
|
|
//
|
|
// Returns pointers into buffered data; invalidated by next call to
|
|
// _stream, _clear, _init, or _buffer
|
|
|
|
public int pageout(Page og)
|
|
{
|
|
// all we need to do is verify a page at the head of the stream
|
|
// buffer. If it doesn't verify, we look for the next potential
|
|
// frame
|
|
|
|
while(true)
|
|
{
|
|
int ret=pageseek(og);
|
|
if(ret>0)
|
|
{
|
|
// have a page
|
|
return(1);
|
|
}
|
|
if(ret==0)
|
|
{
|
|
// need more data
|
|
return(0);
|
|
}
|
|
|
|
// head did not start a synced page... skipped some bytes
|
|
if(unsynced==0)
|
|
{
|
|
unsynced=1;
|
|
return(-1);
|
|
}
|
|
// loop. keep looking
|
|
}
|
|
}
|
|
|
|
// clear things to an initial state. Good to call, eg, before seeking
|
|
public int reset()
|
|
{
|
|
fill=0;
|
|
returned=0;
|
|
unsynced=0;
|
|
headerbytes=0;
|
|
bodybytes=0;
|
|
return(0);
|
|
}
|
|
public void init(){}
|
|
|
|
public SyncState()
|
|
{
|
|
// No constructor needed
|
|
}
|
|
}
|
|
}
|