/* JEsd
 * Copyright (C) 1999 JCraft Inc.
 *  
 * Written by: 1999 ymnk
 *   
 * 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.
 */

package com.jcraft.jesd.vorbis;

import com.jcraft.jesd.SamplePlugin;
import com.jcraft.jesd.PluginException;
import com.jcraft.jorbis.*;

import java.util.*;
import java.net.*;
import java.io.*;

public class Sample implements SamplePlugin{
  InputStream bitStream;
  static int convsize=4096*2;
  byte[] convbuffer=null;

  SyncState oy;
  StreamState os;
  Page og;
  Packet op;
  Info vi;
  Comment vc;
  DspState vd;
  Block vb;
  
  byte[] buffer=null;
  int bytes=0;

  int format;
  int rate=0;
  int left_vol_scale=100;
  int right_vol_scale=100;
  java.io.OutputStream outputLine=null;

  int frameSizeInBytes;
  int bufferLengthInBytes;

  public void play(java.io.OutputStream outputLine,
		   java.io.InputStream in) throws PluginException{

    this.outputLine=outputLine;
    bitStream=in;

    int eos=0;

    oy=new SyncState();
    og=new Page();

    int index=oy.buffer(4096);
    buffer=oy.data;
    try{ bytes=bitStream.read(buffer, index, 4096); }
    catch(Exception e){
      bye(buffer, bytes, 4096);
    }
    oy.wrote(bytes);
    if(oy.pageout(og)!=1){
      //if(bytes<4096)break;
      //System.err.println("Input does not appear to be an Ogg bitstream.");
      bye(buffer, bytes, 4096);
    }
  
    init_jorbis();

    os.init(og.serialno());
    vi.init();
    vc.init();
    if(os.pagein(og)<0){ 
      // error; stream version mismatch perhaps
      //System.err.println("Error reading first page of Ogg bitstream data.");
      //bye(buffer, bytes, 4096);
      return;
    }

    if(os.packetout(op)!=1){ 
      // no page? must not be vorbis
      //System.err.println("Error reading initial header packet.");
      //bye(buffer, bytes, 4096);
      return;
    }
    if(op.synthesis_headerin(vi,vc)<0){ 
      // error case; not a vorbis header
      //System.err.println("This Ogg bitstream does not contain Vorbis audio data.");
      //bye(buffer, bytes, 4096);
      return;
    }

    int i=0;
    while(i<2){
      while(i<2){
        int result=oy.pageout(og);
        if(result==0) break; // Need more data
	if(result==1){
          os.pagein(og);
          while(i<2){
	    result=os.packetout(op);
	    if(result==0)break;
	    if(result==-1){
    	      //System.err.println("Corrupt secondary header.  Exiting.");
              return;
	    }
	    op.synthesis_headerin(vi,vc);
	    i++;
	  }
        }
      }

      index=oy.buffer(4096);
      buffer=oy.data; 
      try{ bytes=bitStream.read(buffer, index, 4096); }
      catch(Exception e){
        //System.err.println(e);
        return;
      }
      if(bytes==0 && i<2){
        //System.err.println("End of file before finding all Vorbis headers!");
        return;
      }
      oy.wrote(bytes);
    }

    convsize=4096/vi.channels;

    vd.synthesis_init(vi);
    vb.init(vd);

    double[][][] _pcm=new double[1][][];
    int[] _index=new int[vi.channels];

    //getOutputLine(vi.rate);

    while(eos==0){
      while(eos==0){
        int result=oy.pageout(og);
        if(result==0){
	  break; // need more data
	}
        if(result==-1){ // missing or corrupt data at this page position
//System.err.println("Corrupt or missing data in bitstream; continuing...");
        }
        else{
          os.pagein(og);
	  while(true){
	    result=os.packetout(op);
	    if(result==0){
              break; // need more data
	    }
	    if(result==-1){ // missing or corrupt data at this page position
	                   // no reason to complain; already complained above
//System.err.println("Corrupt or missing data in bitstream; continuing...2");
            }
            else{
              // we have a packet.  Decode it
	      int samples;
	      if(vb.synthesis(op)==0){ // test for success!
	        vd.synthesis_blockin(vb);
	      }
              while((samples=vd.synthesis_pcmout(_pcm, _index))>0){
//System.out.println("samples: "+samples+", "+_pcm[0]);
  	        double[][] pcm=_pcm[0];
                boolean clipflag=false;
	        int bout=(samples<convsize?samples:convsize);

                // convert doubles to 16 bit signed ints (host order) and
		// interleave
		  int ptr=0;

		for(i=0;i<vi.channels;i++){
		  ptr=i*2;
		  //int ptr=i;
		  int mono=_index[i];
		  for(int j=0;j<bout;j++){
		    int val=(int)(pcm[i][mono+j]*32767.);
		    if(val>32767){
		      val=32767;
		      clipflag=true;
		    }
		    if(val<-32768){
		      val=-32768;
		      clipflag=true;
		    }
                    if(val<0) val=val|0x8000;
		    convbuffer[ptr]=(byte)(val);
		    convbuffer[ptr+1]=(byte)(val>>>8);
		    ptr+=2*(vi.channels);
		  }
	        }
		try{ outputLine.write(convbuffer, 0, 2*vi.channels*bout); }
		catch(Exception e){};
		vd.synthesis_read(bout);
	      }	  
	    }
          }
	  if(og.eos()!=0)eos=1;
	}
      }

      if(eos==0){
	index=oy.buffer(4096);
	buffer=oy.data;
	try{  bytes=bitStream.read(buffer,index,4096); }
	catch(Exception e){
	  System.err.println(e);
          return;
	}

        if(bytes==-1) break;
	oy.wrote(bytes);
	if(bytes==0)eos=1;
      }
    }
    os.clear();
    vb.clear();
    vd.clear();
    vi.clear();
    oy.clear();

  }

  void init_jorbis(){
    //convbuffer=new byte[convsize]; 
    convbuffer=new byte[4096*2]; 
    //oy=new SyncState();
    //og=new Page();
    os=new StreamState();
    op=new Packet();
  
    vi=new Info();
    vc=new Comment();
    vd=new DspState();
    vb=new Block(vd);
  
    buffer=null;
    bytes=0;

    oy.init();
  }

  private void bye(byte[] foo, int len, int max) throws PluginException{
    if(len<max){
      byte[] bar = new byte[len];
      System.arraycopy(foo, 0, bar, 0, len);
      foo=bar;
    }
    throw new PluginException(foo);
  }

}
