// KreateCD - CD recording software for the K desktop environment
//
// 1999 by Alexander Feigl <Alexander.Feigl@gmx.de>
//
// This file is subject to the terms and conditions of the GNU General      
// Public License.  See the file COPYING in the main directory of the       
// KreateCD distribution for more details.                                     

#include "AudioFile.h"
#include "Fork.h"
#include "ProgressDialog.h"

#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/stat.h>
#include <sys/ioctl.h>

#include <qtimer.h>
#include <qobject.h>
#include <klocale.h>

#if defined(__FreeBSD__)
# include <machine/soundcard.h>
#else
# include <linux/soundcard.h>
#endif


extern KLocale *locale;



int AudioFile::readChar(long int *mchar,int words)
 {
  int i;
  if (read(AudioFD,ReadBuffer,1*words)!=1*words) return(0);
  signed temp;
  for (i=0;i<words;++i)
   {
    temp=ReadBuffer[i];
    if (temp>127) temp=-(256-temp);
    *(mchar+i)=temp;
   }
  return(1);
 }

int AudioFile::readCharUS(long int *mchar,int words)
 {
  int i;
  if (read(AudioFD,ReadBuffer,1*words)!=1*words) return(0);
  signed temp;
  for (i=0;i<words;++i)
   {
    temp=ReadBuffer[i]-128;
    *(mchar+i)=temp;
   }
  return(1);
 }

int AudioFile::readLEShort(long int *mshort,int words)
 {
  int i;
  signed long temp;
  if (read(AudioFD,ReadBuffer,2*words)!=2*words) return(0);
  for (i=0;i<words;++i)
   {
#ifdef WORDS_BIGENDIAN
    temp=ReadBuffer[2*i]+(ReadBuffer[2*i+1]<<8);
#else
    temp=*((short int *) (ReadBuffer+2*i));
#endif
    if (temp>32767) temp=-(65536-temp);
    *(mshort+i)=temp;
   }
  return(1);
 }


int AudioFile::readBEShort(long int *mshort,int words)
 {
  int i;
  signed long temp;
  if (read(AudioFD,ReadBuffer,2*words)!=2*words) return(0);
  for (i=0;i<words;++i)
   {
#ifndef WORDS_BIGENDIAN
    temp=ReadBuffer[2*i+1]+(ReadBuffer[2*i]<<8);
#else
    temp=*((short int *) (ReadBuffer+2*i));
#endif
    if (temp>32767) temp=-(65536-temp);
    *(mshort+i)=temp;
   }
  return(1);
 }

int AudioFile::readShort(long int *mshort,int words)
 {
  if (SampleEndian==Endian_Big) return(readBEShort(mshort,words));
  return(readLEShort(mshort,words));
 }


int AudioFile::readLELong(long int *mlong,int words)
 {
#ifdef WORDS_BIGENDIAN
  int i;
  signed long long temp;
  if (read(AudioFD,ReadBuffer,4*words)!=4*words) return(0);
  for (i=0;i<words;++i)
   {
    temp=ReadBuffer[4*i]+(ReadBuffer[4*i+1]<<8)+(ReadBuffer[2*i+2]<<16)+(ReadBuffer[3]<<24);
    if (temp>2147483648LL) temp=temp-4294967296LL;
    *(mlong+i)=temp;
   }
  return(1);
#else
  if (read(AudioFD,mlong,4*words)!=4*words) return(0);
  return(1);
#endif
 }

int AudioFile::readBELong(long int *mlong,int words)
 {
#ifndef WORDS_BIGENDIAN
  int i;
  signed long long temp;
  if (read(AudioFD,ReadBuffer,4*words)!=4*words) return(0);
  for (i=0;i<words;++i)
   {
    temp=ReadBuffer[4*i+3]+(ReadBuffer[4*i+2]<<8)+(ReadBuffer[4*i+1]<<16)+(ReadBuffer[4*i]<<24);
    if (temp>2147483648LL) temp=temp-4294967296LL;
    *(mlong+i)=temp;
   }
  return(1);
#else
  if (read(AudioFD,mlong,4*words)!=4*words) return(0);
  return(1);
#endif
 }

int AudioFile::readLong(long int *mlong,int words)
 {
  if (SampleEndian==Endian_Big) return(readBELong(mlong,words));
  return(readLELong(mlong,words));
 }


int AudioFile::fillSampleBuffer(void)
 {
  long int restsamples,readburst;
  restsamples=SampleChannels*(DataSamples-AudioPos);
  readburst=(restsamples<=(Read_Cachesize-(Read_Cachesize%SampleChannels)))?restsamples:(Read_Cachesize-(Read_Cachesize%SampleChannels));
  if (readburst==0) return(0);
  switch (SampleSize)
   {
    case Samples_8Bit:
     {
      int i;
      if (!readCharUS(AudioBuffer,readburst)) return(0);
      AudioPointer=0;
      AudioPos+=readburst/SampleChannels;
      AudioCached=readburst;
      for (i=0;i<readburst;++i)
       {
        AudioBuffer[i]=AudioBuffer[i]<<8;
       }
      return(1);
     }
    case Samples_16Bit:
     {
      if (!readShort(AudioBuffer,readburst)) return(0);
      AudioPointer=0;
      AudioPos+=readburst/SampleChannels;
      AudioCached=readburst;
      return(1);
     }
    case Samples_32Bit:
     {
      if (!readLong(AudioBuffer,readburst)) return(0);
      AudioPointer=0;
      AudioPos+=readburst/SampleChannels;
      AudioCached=readburst;
      return(1);
     }
   }
  return(0);
 }

inline int AudioFile::readSample(long int *sample)
 {
  if (AudioCached<=AudioPointer)
   {
    if (!fillSampleBuffer()) return(0);
   }
  *sample=AudioBuffer[AudioPointer++];
  return(1);
 }

inline int AudioFile::readString(char *mstring,int length)
 {
  if (read(AudioFD,mstring,length)!=length) return(0);
  return(1);
 }

AudioFile::AudioFile(void)
 {
  AudioFD=-1;
  ImageFD=-1;
  AudioFork=0;
  AudioTimer=0;
 }

AudioFile::~AudioFile(void)
 {
  if (AudioFD!=-1) close(AudioFD);
  if (AudioFork) stopAudio();

 }

int AudioFile::identifyFile(const char *filename)
 {
  if (!openRead(filename)) return(0);
   
  if (identifyMSWave()) goto posID;
  closeRead();
  return(0);
  posID:
  closeRead();
  strcpy(FileName,filename);
  SampleBytes=SampleChannels;
  if (SampleSize==Samples_16Bit) SampleBytes*=2;
  if (SampleSize==Samples_32Bit) SampleBytes*=4;
  DataSamples=DataSize/SampleBytes;
  SelectedStart=0;
  SelectedEnd=DataSamples-1;
  BoostFactor=1;
  Balance=0;
  MaxBoost=-1;
  return(1);
 }


int AudioFile::needImage(void)
 {
  if (SampleChannels!=2) return(1);
  if (SampleRate!=44100) return(1);
  if (DataPos!=0) return(1);
  if (SampleEndian!=Endian_Big) return(1);
  if ( (SelectedStart!=0) || (SelectedEnd!=(DataSamples-1))) return(1);
  if ( (BoostFactor!=1) || (Balance!=0) ) return(1);
  return(0);
 }

int AudioFile::setupCDTrack(const char *filename)
 {
  struct stat mstat;
  if (!openRead(filename)) return(0);
  if (fstat(AudioFD,&mstat)!=0)
   {
    closeRead();
    return(0);
   } 
  closeRead();
  strcpy(FileName,filename);
  SampleChannels=2;
  SampleSize=Samples_16Bit;
  SampleRate=44100;
  DataSize=mstat.st_size;
  DataPos=0;
  SampleEndian=Endian_Big;
  strcpy(Format,"CD");

  SampleBytes=SampleChannels;
  SampleBytes*=2;
  DataSamples=DataSize/SampleBytes;
  SelectedStart=0;
  SelectedEnd=DataSamples-1;
  BoostFactor=1;
  Balance=0;
  MaxBoost=-1;

  return(1);
 }


enum AudioFile::Sample_Size AudioFile::getSampleSize(void)
 {
  return(SampleSize);
 }

long int AudioFile::getSampleRate(void)
 {
  return(SampleRate);
 }

enum AudioFile::Sample_Endian AudioFile::getEndian(void)
 {
  return(SampleEndian);
 }

int AudioFile::getChannels(void)
 {
  return(SampleChannels);
 }
int AudioFile::getAbsoluteDuration(void)
 {
  long long xdur;
  xdur=((long long) DataSamples)*75;
  return(xdur/SampleRate);
 }

int AudioFile::getSelectedDuration(void)
 {
  long long xdur;
  xdur=(((long long )SelectedEnd)-((long long )SelectedStart)+1)*75;
  return(xdur/SampleRate);
 } 

void AudioFile::getFormat(char *formatstring)
 {
  strcpy(formatstring,Format);
 }

int AudioFile::openRead(const char *mfilename)
 {
  if (AudioFD!=-1) close(AudioFD);
  AudioFD=open(mfilename,O_RDONLY);
  if (AudioFD==-1) return(0);
  AudioCached=0;
  AudioPos=0;
  AudioPointer=0;
  ReadBuffer=(unsigned char *) malloc(Read_Cachesize*4);
  if (ReadBuffer==0)
   {
    close(AudioFD);
    AudioFD=-1;
   }
  AudioBuffer=(long int *) malloc(Read_Cachesize*sizeof(long int));
  if (AudioBuffer==0)
   {
    close(AudioFD);
    AudioFD=-1;
    free(ReadBuffer);

   }
  return(1);
 }


void AudioFile::closeRead(void)
 {
  if (AudioFD!=-1)
   {
    close(AudioFD);
    free(ReadBuffer);
    free(AudioBuffer);
   }
  AudioFD=-1;
 }

int AudioFile::skipRead(int bytes)
 {
  if (lseek(AudioFD,bytes,SEEK_CUR)==-1) return(0);
  return(1);
 }


int AudioFile::positionSample(long int sample)
 {
  unsigned long position;
  if  ( (sample>=DataSamples) || (sample<0) ) return(0);
  position=DataPos+sample*SampleBytes;

  if (lseek(AudioFD,position,SEEK_SET)==-1) return(0);
  AudioPointer=0;
  AudioCached=0;
  AudioPos=sample;
  return(1);
 }





int AudioFile::flushCDDABuffer(void)
 {
  if (ImageContent==0) return(1);
  if (write(ImageFD,ImageBuffer,2*ImageContent)!=2*ImageContent) return(0);
  ImageContent=0;
  return(1);
 }


inline int AudioFile::writeCDDASample(float msample)
 {

  long int xsample;

  xsample=(long int )msample;

  if (ImageContent>=Write_Cachesize)
   {
    if (!flushCDDABuffer()) return(0);
   }

  if (xsample>32767) xsample=32767;
  if (xsample<-32768) xsample=-32768;

  if (DisableSwap)
   {
    *(short int *) (ImageBuffer+2*ImageContent)=xsample;
    ++ImageContent;
    return(1);
   }

#ifdef WORDS_BIGENDIAN
  *(short int *) (ImageBuffer+2*ImageContent)=xsample;
#else
  ImageBuffer[2*ImageContent]= (xsample>>8) & 0xff;
  ImageBuffer[2*ImageContent+1]= xsample & 0xff;
#endif
  ++ImageContent;
  return(1);
 }

int AudioFile::openWrite(const char *filename)
 {
  closeWrite();
  DisableSwap=0;
  ImageBuffer=(unsigned char *)malloc(Write_Cachesize*2);
  if (ImageBuffer==0)
   {
    return(0);
   }

  ImageFD=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0644);
  if (ImageFD==-1)
   {
    free(ImageBuffer);
    return(0);
   }
  ImageContent=0;
  return(1);
 }

void AudioFile::closeWrite(void)
 {
  if (ImageFD!=-1)
   {
    close(ImageFD);
    ImageFD=-1;
   }
 }

void AudioFile::setBoost(float boost)
 {
  BoostFactor=boost;
 }

float AudioFile::getBoost(void)
 {
  return(BoostFactor);
 }

float AudioFile::getBalance(void)
 {
  return(Balance);
 }

void AudioFile::setBalance(float balance)
 {
  Balance=balance;
 }

void AudioFile::setStartPos(long int pos)
 {
  if (pos>(DataSamples-1)) pos=DataSamples-1;
  if (pos<0) pos=0;
  SelectedStart=pos;
  if (SelectedEnd<SelectedStart) SelectedEnd=DataSamples-1;
 }

void AudioFile::setEndPos(long int pos)
 {
  if (pos>(DataSamples-1)) pos=DataSamples-1;
  if (pos<0) pos=0;
  SelectedEnd=pos;
  if (SelectedEnd<SelectedStart) SelectedStart=0;
 }


long int AudioFile::getStartPos(void)
 {
  return(SelectedStart);
 }

long int AudioFile::getEndPos(void)
 {
  return(SelectedEnd);
 }

void AudioFile::setMaxBoost(void)
 {
  if (MaxBoost==-1)
   {
    if (!scanImage()) return;
   }
  BoostFactor=MaxBoost;
 }


static int scanImageHFork(AudioFile *afi,const char *fn)
 {
  float rv;
  rv=afi->maximumBoost(fn);
  if (rv!=-1)
   {
    fprintf(stdout,"#OK# %f\n",rv);
   }
   else
   {
    fprintf(stdout,"#ERROR#\n");
   }
  return(0);
 }

int AudioFile::scanImage(void)
 {
  Fork *mfork;

  char xtext[128];
  int retval;

  mfork=new Fork(scanImageHFork,this,FileName);
  connect(mfork,SIGNAL(processExited(KProcess *)),this,
          SLOT(scanImageExit(KProcess *)));
  connect(mfork,SIGNAL(receivedStdout(KProcess *,char *,int)),this,
          SLOT(scanImageMsg(KProcess *,char *,int)));

  sprintf(xtext,locale->translate("Scanning audio file...\n"));
  ImagizeDialog=new ProgressDialog(0,0,xtext);
  connect(ImagizeDialog,SIGNAL(canceled()),ImagizeDialog,SLOT(abortCancel()));
  ImagizeFlag=0;
  linebuffer[0]=0;

  mfork->start(KProcess::NotifyOnExit,KProcess::Stdout);
  retval=ImagizeDialog->exec();
  delete(mfork);
  delete(ImagizeDialog);
  return(retval);
 }


void AudioFile::scanImageExit(KProcess *)
 {
  ImagizeDialog->abortDialog(ImagizeFlag);
 }

void AudioFile::scanImageMsg(KProcess *,char *buffer,int len)
 {
  int i;
  char *buf,*buf2;

  i=len;
  buf=buffer;
  buf2=linebuffer;
  while (*buf2!=0) ++buf2;
  while (i>0)
   {
    if ( (*buf=='\n') || (*buf=='\r') )
     {
      *buf2=0;
      buf2=linebuffer;
      if (strncmp("#OK#",linebuffer,4)==0)
       {
        char *xptr;
        xptr=linebuffer+4;
        sscanf(xptr,"%f",&MaxBoost);
        ImagizeFlag=1;
        goto ignLine;
       }
      if (strncmp("#ERROR#",linebuffer,7)==0)
       {
        ImagizeFlag=0;
        goto ignLine;
       }

      if (strncmp("#PROGRESS#",linebuffer,10)==0)
       {
        char *xptr,*yptr;
        unsigned long int cur,max;
        xptr=linebuffer+10;
        while (*xptr==' ') ++xptr;
        if (*xptr==0) goto ignLine;
        cur=strtoul(xptr,&yptr,10);
        xptr=yptr;
        while (*xptr==' ') ++xptr;
        if (*xptr==0) goto ignLine;
        max=strtoul(xptr,&yptr,10);
        ImagizeDialog->setProtectedProgress(cur,max);
        goto ignLine;
       }


      ignLine:
      *buf2=0;
     }
     else
     {
      *(buf2++)=*buf;
      *buf2=0;
     }
    ++buf;
    --i;
   }
 }




static int buildImageHFork(AudioFile *afi,const char *fn)
 {
  int rv;
  rv=afi->buildImageFork(fn,0);
  if (rv)
   {
    fprintf(stdout,"#OK#\n");
   }
   else
   {
    fprintf(stdout,"#ERROR#\n");
   }
  return(0);
 }

static int playImageHFork(AudioFile *afi,const char *fn)
 {
  afi->buildImageFork(fn,1);
  return(0);
 }


void AudioFile::stopAudio(void)
 {
  if (AudioFork) delete (AudioFork);
  AudioFork=0;
  if (AudioTimer) delete (AudioTimer);
  AudioTimer=0;
 }
void AudioFile::playAudio(long int position=-1)
 {
  if (AudioFork) return;

  AudioFork=new Fork(playImageHFork,this,"/dev/dsp");
  AudioTimer=new QTimer();
  AudioTimer->start(80,FALSE);
  connect(AudioTimer,SIGNAL(timeout()),this,SLOT(playAudioTrigger()));
  ImagizeFlag=0;
  AudioPosition=0;
  linebuffer[0]=0;
  startAudio=position;

  connect(AudioFork,SIGNAL(processExited(KProcess *)),this,
          SLOT(playAudioExit(KProcess *)));
  connect(AudioFork,SIGNAL(receivedStdout(KProcess *,char *,int)),this,
          SLOT(playAudioMsg(KProcess *,char *,int)));


  AudioFork->start(KProcess::NotifyOnExit,KProcess::All);
 }

void AudioFile::playAudioExit(KProcess *)
 {
  delete(AudioFork);
  AudioFork=0;
  delete(AudioTimer);
  AudioTimer=0;
 }

void AudioFile::playAudioTrigger(void)
 {
  AudioPosition+=6;
  emit(audioPlayPosition(AudioPosition));
 }

void AudioFile::setAudioPosition(int frame)
 {
  if (AudioFork==0) return;
  if (AudioPosition==frame) return;
  AudioPosition=frame;
  emit(audioPlayPosition(AudioPosition));
  sprintf(OutLine,"#SET# %ld\n",AudioPosition);
  AudioFork->writeStdin(OutLine,strlen(OutLine));
 }

void AudioFile::setAudioBoost(float boost)
 {
  if (AudioFork==0) return;
  sprintf(OutLine,"#BOOST# %f\n",boost);
  AudioFork->writeStdin(OutLine,strlen(OutLine));
 }

void AudioFile::setAudioBalance(float balance)
 {
  if (AudioFork==0) return;
  sprintf(OutLine,"#BALANCE# %f\n",balance);
  AudioFork->writeStdin(OutLine,strlen(OutLine));
 }

void AudioFile::playAudioMsg(KProcess *,char *buffer,int len)
 {
  int i;
  char *buf,*buf2;

  i=len;
  buf=buffer;
  buf2=linebuffer;
  while (*buf2!=0) ++buf2;
  while (i>0)
   {
    if ( (*buf=='\n') || (*buf=='\r') )
     {
      *buf2=0;
      buf2=linebuffer;
      if (strncmp("#PROGRESS#",linebuffer,10)==0)
       {
        char *xptr,*yptr;
        unsigned long int cur;
        xptr=linebuffer+10;
        while (*xptr==' ') ++xptr;
        if (*xptr==0) goto ignLine;
        cur=strtoul(xptr,&yptr,10);
        AudioPosition=cur;
        emit(audioPlayPosition(cur));
        goto ignLine;
       }


      ignLine:
      *buf2=0;
     }
     else
     {
      *(buf2++)=*buf;
      *buf2=0;
     }
    ++buf;
    --i;
   }
 }




int AudioFile::buildImage(const char *fn)
 {
  Fork *mfork;

  char xtext[128];
  int retval;

  mfork=new Fork(buildImageHFork,this,fn);
  connect(mfork,SIGNAL(processExited(KProcess *)),this,
          SLOT(buildImageExit(KProcess *)));
  connect(mfork,SIGNAL(receivedStdout(KProcess *,char *,int)),this,
          SLOT(buildImageMsg(KProcess *,char *,int)));

  sprintf(xtext,locale->translate("Converting audio track to CD-R format\n"));
  ImagizeDialog=new ProgressDialog(0,0,xtext);
  connect(ImagizeDialog,SIGNAL(canceled()),ImagizeDialog,SLOT(abortCancel()));
  ImagizeFlag=0;
  linebuffer[0]=0;

  mfork->start(KProcess::NotifyOnExit,KProcess::Stdout);
  retval=ImagizeDialog->exec();
  delete(mfork);
  delete(ImagizeDialog);
  if (retval==-1)
   {
    retval=0;
    remove(fn);
   }
  return(retval);
 }


void AudioFile::buildImageExit(KProcess *)
 {
  ImagizeDialog->abortDialog(ImagizeFlag);
 }

void AudioFile::buildImageMsg(KProcess *,char *buffer,int len)
 {
  int i;
  char *buf,*buf2;

  i=len;
  buf=buffer;
  buf2=linebuffer;
  while (*buf2!=0) ++buf2;
  while (i>0)
   {
    if ( (*buf=='\n') || (*buf=='\r') )
     {
      *buf2=0;
      buf2=linebuffer;
      if (strncmp("#OK#",linebuffer,4)==0)
       {
        ImagizeFlag=1;
        goto ignLine;
       }
      if (strncmp("#ERROR#",linebuffer,7)==0)
       {
        ImagizeFlag=0;
        goto ignLine;
       }

      if (strncmp("#PROGRESS#",linebuffer,10)==0)
       {
        char *xptr,*yptr;
        unsigned long int cur,max;
        xptr=linebuffer+10;
        while (*xptr==' ') ++xptr;
        if (*xptr==0) goto ignLine;
        cur=strtoul(xptr,&yptr,10);
        xptr=yptr;
        while (*xptr==' ') ++xptr;
        if (*xptr==0) goto ignLine;
        max=strtoul(xptr,&yptr,10);
        ImagizeDialog->setProtectedProgress(cur,max);
        goto ignLine;
       }


      ignLine:
      *buf2=0;
     }
     else
     {
      *(buf2++)=*buf;
      *buf2=0;
     }
    ++buf;
    --i;
   }
 }


int AudioFile::buildImageFork(const char *fn,int playmode)
 {
  int channel;
  long int samplesleft,maxsamples;
  float pos2;
  long int sample1[2],sample2[2];
  long int sampleswritten=0;
  float boost[2];
  long int start,end;


  if (Balance>0)
   {
    boost[0]=BoostFactor*(1-Balance);
   }
   else
   {
    boost[0]=BoostFactor;
   }

  if (Balance<0)
   {
    boost[1]=BoostFactor*(1+Balance);
   }
   else
   {
    boost[1]=BoostFactor;
   }


  if (!openWrite(fn))
   {
    return(0);
   }

  start=SelectedStart;
  end=SelectedEnd;

  if (playmode)
   {
    int sound_size=16,sound_stereo=1,sound_rate=44100;
    if (fcntl(STDIN_FILENO,F_SETFL,O_NONBLOCK)==-1)
     {
      closeWrite();
      return(0);
     }
    if (ioctl(ImageFD, SNDCTL_DSP_SAMPLESIZE, &sound_size)==-1)
     {
      closeWrite();
      return(0);
     }
    if (ioctl(ImageFD, SNDCTL_DSP_STEREO, &sound_stereo)==-1)
     {
      closeWrite();
      return(0);
     }

    if (ioctl(ImageFD, SNDCTL_DSP_SPEED, &sound_rate)==-1)
     {
      closeWrite();
      return(0);
     }
    DisableSwap=1;
    if (startAudio!=-1)
     {
      start=startAudio*588;
      if (start>=end)
       {
        end=DataSamples-1;
       }
     }
        fprintf(stdout,"#PROGRESS# %ld\n",(start)/588);
    fflush(stdout);

  }


  if (!openRead(FileName))
   {
    closeWrite();
    remove(fn);
    return(0);
   }
  if (!positionSample(start))
   {
    closeRead();
    closeWrite();
    remove(fn);
    return(0);
   }
  samplesleft= (end+1)-start;
  maxsamples=samplesleft;

  if (!readSample(&sample1[0]))
   {
    closeRead();
    closeWrite();
    remove(fn);
    return(0);
   }
  if (SampleChannels>1)
   {

    if (!readSample(&sample1[1]))
     {
      closeRead();
      closeWrite();
      remove(fn);
      return(0);
     }
   }
   else
   {
    sample1[1]=sample1[0];
   }


  for (channel=2;channel<SampleChannels;++channel)
   {
    long int dummy;
    if (!readSample(&dummy))
     {
      closeRead();
      closeWrite();
      remove(fn);
      return(0);
     }
   }
  samplesleft--;
  pos2=0;
  sample2[0]=sample1[0];
  sample2[1]=sample1[1];

  for (channel=0;channel<2;++channel)
   {
    if (!writeCDDASample(sample1[channel]))
     {
      closeRead();
      closeWrite();
      remove(fn);
      return(0);
     }
   }
  ++sampleswritten;

  while ( (samplesleft>0) || (pos2>=1) )
   {
    int xchannel;

    if (pos2>=1)
     {
      for (channel=0;channel<2;++channel)
       {
        if (SampleRate!=44100)
         {
          sample1[channel]= (long int) (sample1[channel]+
            ( (float) (sample2[channel]-sample1[channel]) *
            ((float) 1)/ pos2 ));
         }
         else
         {
          sample1[channel]=sample2[channel];
         }

        if (!writeCDDASample((float)sample1[channel]*boost[channel]))
         {
          closeRead();
          closeWrite();
          remove(fn);
          return(0);
         }
       }
      sampleswritten++;
      pos2-=1;
      continue;
     }
    if (!readSample(&sample2[0]))
     {
      closeRead();
      closeWrite();
      remove(fn);
      return(0);
     }
    if (SampleChannels>1)
     {

      if (!readSample(&sample2[1]))
       {
        closeRead();
        closeWrite();
        remove(fn);
        return(0);
       }
     }
     else
     {
      sample2[1]=sample2[0];
     }


    for (xchannel=2;xchannel<SampleChannels;++xchannel)
     {
      long int dummy;
      if (!readSample(&dummy))
       {
        closeRead();
        closeWrite();
        remove(fn);
        return(0);
       }
     }
    samplesleft--;
    if ((samplesleft%1000)==0)
     {
      if (!playmode)
       {
        fprintf(stdout,"#PROGRESS# %ld %ld\n",(maxsamples-samplesleft)/1000,(maxsamples+999)/1000);
       }
       else
       {
        char inbuf[80];
        while ( fgets(inbuf,80,stdin)!=0)
         {
          if (strncmp(inbuf,"#BOOST#",7)==0)
           {
            sscanf(inbuf+8,"%f",&BoostFactor);
            if (Balance>0)
             {
              boost[0]=BoostFactor*(1-Balance);
             }
             else
             {
              boost[0]=BoostFactor;
             }

            if (Balance<0)
             {
              boost[1]=BoostFactor*(1+Balance);
             }
             else
             {
              boost[1]=BoostFactor;
             }
           }


          if (strncmp(inbuf,"#BALANCE#",9)==0)
           {
            sscanf(inbuf+10,"%f",&Balance);
            if (Balance>0)
             {
              boost[0]=BoostFactor*(1-Balance);
             }
             else
             {
              boost[0]=BoostFactor;
             }

            if (Balance<0)
             {
              boost[1]=BoostFactor*(1+Balance);
             }
             else
             {
              boost[1]=BoostFactor;
             }
           }



          if (strncmp(inbuf,"#SET#",5)==0)
           {
            unsigned long newpos,xend;
            xend=SelectedEnd;
            newpos=strtoul(inbuf+5,0,10);
            newpos*=588;
            if ((signed long) newpos>=SelectedEnd) xend=DataSamples-1;
            samplesleft= (xend+1)-newpos;
            maxsamples=samplesleft;
            if (!positionSample(newpos))
             {
              closeRead();
              closeWrite();
              remove(fn);
              return(0);
              }
            ioctl(ImageFD,SNDCTL_DSP_SYNC,0);
            fprintf(stdout,"#PROGRESS# %ld\n",newpos/588);
            fflush(stdout);
           }
         }
       }
     }
    pos2+= (float) 44100/ (float) SampleRate;
   }

  samplesleft=0;
  if (sampleswritten%588!=0)
   {
    samplesleft=588-(sampleswritten%588);
   }

  if (sampleswritten<(44100*4))
   {
    samplesleft=(44100*4)-sampleswritten;
   }
  samplesleft*=2;
  while (samplesleft)
   {
    --samplesleft;
    if (!writeCDDASample(0))
     {
      closeRead();
      closeWrite();
      remove(fn);
      return(0);
     }
   }

  if (!flushCDDABuffer())
   {
    closeRead();
    closeWrite();
    remove(fn);
    return(0);
   }
  if (playmode)
   {
    ioctl(ImageFD,SNDCTL_DSP_SYNC,0);
   }
  closeRead();
  closeWrite();
  return(1);
 }

float AudioFile::maximumBoost(const char *fn)
 { 
  float negboost,posboost,maxval=128;
  long int MaxSamples,Samples,maxneg=0,maxpos=0,sample;
  if (!openRead(fn)) return(-1);
  if (positionSample(SelectedStart)==-1)
   {
    closeRead();
    return(-1);  
   }
  Samples=SelectedEnd-SelectedStart+1;

  Samples*=SampleChannels;
  MaxSamples=Samples;

  while (Samples)
   {
    if (!readSample(&sample)) return(-1);
    if (sample>0)
     {
      if (sample>maxpos) maxpos=sample;
     }
     else
     {
      if (sample<maxneg) maxneg=sample;
     }
    if ((Samples%1000)==0)
     {
      fprintf(stdout,"#PROGRESS# %ld %ld\n",(MaxSamples-Samples)/1000,
          (MaxSamples+999)/1000);

     }
    --Samples;
   }
  closeRead();
  switch (SampleSize)
   {
    case Samples_8Bit:
      maxval=128;
      break;
    case Samples_16Bit:
      maxval=32768;
      break;
    case Samples_32Bit:
      maxval=1<<31;
      break;
   }
  negboost=maxval/(-maxneg);
  posboost=(maxval-1)/(maxpos);
  if (negboost<posboost) return(negboost);
  return(posboost);
 }


// Microsoft Wave file : parse header 

int AudioFile::identifyMSWave(void)
 {
  char string[6];
  long int filesize,chunksize;
  int hasformat=0,hasdata=0;
  long int wavformat,wavchannels,wavalign,wavsamsize;
  long int wavaverage,wavrate;
  long int datapos=0,datasize=0;

  lseek(AudioFD,0,SEEK_SET);
  if (!readString(string,4)) return(0);
  string[5]=0;
  if (strncmp(string,"RIFF",4)!=0) return(0);
  if (!readLELong(&filesize)) return(0);
  if (!readString(string,4)) return(0);
  if (strncmp(string,"WAVE",4)!=0) return(0);
  filesize-=4;
  while (1)
   {
    if ( (hasformat) && (hasdata) ) break;
    if (filesize==0) break;
    if (filesize<8)
     {
      return(0);
     }
    if (!readString(string,4)) return(0);
    if (!readLELong(&chunksize)) return(0);
    filesize-=8;
    if (filesize<chunksize)
     {
      return(0);
     }
    filesize-=chunksize;
    if (strncmp(string,"fmt ",4)==0)
     {
      if (chunksize<16) return(0);
      hasformat=1;
      if (!readLEShort(&wavformat)) return(0);
      if (!readLEShort(&wavchannels)) return(0);
      if (!readLELong(&wavrate)) return(0);
      if (!readLELong(&wavaverage)) return(0);
      if (!readLEShort(&wavalign)) return(0);
      if (!readLEShort(&wavsamsize)) return(0);
      if (chunksize>16)
       {
        if (!skipRead(chunksize-16)) return(0);
       }
     } 
     else
     {
      if (strncmp(string,"data",4)==0)
       {
        datapos=lseek(AudioFD,0,SEEK_CUR);
        datasize=chunksize;
        hasdata=1;        
        if (!skipRead(chunksize)) return(0);
       }
       else
       {
        if (!skipRead(chunksize)) return(0);

       }
     }    
   }
  if ( (!hasformat) || (!hasdata)) return(0);
  switch (wavformat)
   {
    case 1: //WAVE_FORMAT_PCM
      SampleFormat=Samples_PCM;
      break;

    case 0: //WAVE_FORMAT_UNKNOWN
    case 0x2: //WAVE_FORMAT_ADPCM
    case 0x6: //WAVE_FORMAT ALAW
    case 0x7: //WAVE_FORMAT_MULAW
    case 0x10: //WAVE_FORMAT_OKI_ADPCM
    case 0x15: //WAVE_FORMAT_DIGISTD
    case 0x16: //WAVE_FORMAT_DIGIFIX
    case 0x101: //IBM_FORMAT_MULAW
    case 0x102: //IBM_FORMAT_ALAW
    default:
      return(0);
     
   }
  switch (wavsamsize)
   {
    case 8:
        SampleSize=Samples_8Bit;
      break;
    case 16:
        SampleSize=Samples_16Bit;
      break;
    case 32:
        SampleSize=Samples_32Bit;
      break;
    default:
      return(0);
   }
  SampleEndian=Endian_Little;
  SampleChannels=wavchannels;
  SampleRate=wavrate;
  DataPos=datapos;
  DataSize=datasize;
  if ( (SampleChannels<=0 ) || (SampleRate<=0) || (DataSize==0))
   {
    return(0);
   }
  strcpy(Format,"MS Windows WAV");
  return(1);  
 }
