
/*
 *  Changes from version 1.1 to 1.2:
 *    - new LinuxObuffer class, which works with 16 bit soundcards
 *      like Gravis Ultrasound, SoundBlaster 16 or Pro Audio Spectrum 16,
 *      if attached to /dev/dsp
 *    - ShortObuffer renamed to FileObuffer
 *    - If ULAW is defined:
 *      - SparcObuffer feeds mono u-law output to an amd device (u-law format).
 *        The required downsampling to 8 kHz in done in the synthesis filter.
 *      - FileObuffer creates u-law output at 8 kHz instead of 16 bit PCM samples.
 *    - O_NDELAY flag is cleared now after a successful open syscall
 *      on /dev/audio (caused problems under Solaris)
 *    - options -us, -uh and -ul should work now
 *    - FileObuffer can handle incomplete write syscalls now
 */

// minor changes to LinuxObuffer Apr 2 1997 by Timo Jantunen (jeti@iki.fi)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
//#include <iostream.h>
#include "obuffer.h"
#include "header.h"





FileObuffer::FileObuffer (uint32 number_of_channels)
{
#ifdef DEBUG
  if (!number_of_channels || number_of_channels > MAXCHANNELS)
  {
  ; //  cerr << "FileObuffer: number of channels has to be in [1, " <<  MAXCHANNELS << "] !\n";
    exit (1);
  }
#endif
{ unsigned int i;
  channels = number_of_channels;
  for ( i = 0; i < number_of_channels; ++i)
    bufferp[i] = buffer + i;
}

}




#if 1

void FileObuffer::write_buffer (int fd)
{
  int length = (int)((char *)bufferp[0] - (char *)buffer), writelength;

  if ((writelength = write (fd, (char *)buffer, length)) != length)
  {
    // buffer has not been written completely
    if (writelength < 0)
    {
      perror ("write");
      exit (1);
    }
    length -= writelength;
    char *buffer_pos = (char *)buffer;
    do
    {
      buffer_pos += writelength;
      if ((writelength = write (fd, buffer_pos, length)) < 0)
      {
	perror ("write");
	exit (1);
      }
    }
    while (length -= writelength);
  }

{ unsigned int i;
  for ( i = 0; i < channels; ++i)
    bufferp[i] = buffer + i;
}

}

#endif



#ifdef LINUX
int LinuxObuffer::audio_fd = -1;

int LinuxObuffer::open_audio_device (void)
{
  int fd;

  if ((fd = open ("/dev/dsp", O_WRONLY | O_NDELAY, 0)) < 0)
    if (errno == EBUSY)
    {
 //     cerr << "Sorry, the audio device is busy!\n";
      exit (1);
    }
    else
    {
      perror ("can't open /dev/dsp for writing");
      exit (1);
    }

  // turn NDELAY mode off:
#if 1
{
  int flags;
  if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
  {
    perror ("fcntl F_GETFL on /dev/dsp failed");
    exit (1);
  }
  flags &= ~O_NDELAY;
  if (fcntl (fd, F_SETFL, flags) < 0)
  {
    perror ("fcntl F_SETFL on /dev/dsp failed");
    exit (1);
  }
}
#endif
  return fd;
}


LinuxObuffer::LinuxObuffer (uint32 number_of_channels, Header *header)
{
#ifdef DEBUG
  if (!number_of_channels || number_of_channels > MAXCHANNELS)
  {
 ///   cerr << "LinuxObuffer: 0 < number of channels < " << MAXCHANNELS << "!\n";
    exit (1);
  }
#endif
{ unsigned int i;
  channels = number_of_channels;
  for ( i = 0; i < number_of_channels; ++i)
    bufferp[i] = buffer + i;

}
  if (audio_fd < 0)
  {
 //   cerr << "Internal error, LinuxObuffer::audio_fd has to be initialized\n"
//	    "by LinuxObuffer::class_suitable()!\n";
    exit (1);
  }

  // configure the device:
  int play_precision = 16;
  int play_stereo = channels-1;
  int play_sample_rate = header->frequency ();
//fprintf(stderr,"opening audio   %i, %i, %i, \n",sizeof(uint),play_sample_rate, play_precision);

//fprintf(stderr,"opening audio   %i, %i, %i, \n",SNDCTL_DSP_SAMPLESIZE,
//SNDCTL_DSP_STEREO,SNDCTL_DSP_SPEED);

  if(
      ioctl(audio_fd, SNDCTL_DSP_SAMPLESIZE, &play_precision) == -1 ||
      ioctl(audio_fd, SNDCTL_DSP_STEREO, &play_stereo) == -1 ||
      ioctl(audio_fd, SNDCTL_DSP_SPEED, &play_sample_rate) == -1
    )
  {
    perror ("configuration of /dev/dsp failed");
    exit (1);
  }
}


LinuxObuffer::~LinuxObuffer (void)
{
  //sleep (2);
  if (ioctl (audio_fd, SNDCTL_DSP_SYNC, NULL) == -1) {
    perror ("ioctl SNDCTL_DSP_SYNC on /dev/dsp failed");
    exit (1);
  }

  close (audio_fd);
}

#if 0
int16 bitrev(int16 aa)
{
//uint16 aa=23455;
uint16 ff=(aa),dd=0;
uint16 boo[17]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
//fprintf(stderr,"\n%x    %x\n",aa,ff);
dd=32768;
for(int i=0 ; i<=15; i++){
//ff=aa;
//dd=1 ;
//fprintf(stderr,"%x-%x\n",dd,boo[i]);
if(ff&boo[i]){    (aa)= (aa)|dd; }
else{ /*fprintf(stderr,"0");*/ (aa)= (aa)& ~dd; }
dd = dd >> 1;
}
return(aa);
//fprintf(stderr,"\n%x    %x\n",aa,ff);

}
#endif


void LinuxObuffer::write_buffer (int fd)
{
#if 1
  int length = (int)(   (char *)bufferp[0]  - (char *)buffer );
char * bufr=(char *)buffer;  //+(length/2);
//length=length/2;

//for(int i=0 ; i<length/2; i++){  buffer[i]=buffer[i-(length/2)]; } //bitrev(buffer[i]);  }

  if (write (audio_fd, bufr, length) != length)
  {
    perror ("write to /dev/dsp failed");
    exit (1);

  }
{ unsigned int i;

  for (i = 0; i < channels; ++i)
    bufferp[i] = (buffer + i);
}
#else

  int length = (int)((char *)bufferp[0] - (char *)buffer), writelength;

  if ((writelength = write (fd, (char *)buffer, length)) != length)
  {
    // buffer has not been written completely
    if (writelength < 0)
    {
      perror ("write");
      exit (1);
    }
    length -= writelength;
    char *buffer_pos = (char *)buffer;
    do
    {
      buffer_pos += writelength;
      if ((writelength = write (fd, buffer_pos, length)) < 0)
      {
	perror ("write");
	exit (1);
      }
    }
    while (length -= writelength);
  }

{ unsigned int i;
  for ( i = 0; i < channels; ++i)
    bufferp[i] = buffer + i;
}

#endif
}

bool LinuxObuffer::class_suitable (uint32 number_of_channels)
{
  // open the dsp audio device:
  audio_fd = open_audio_device ();
  return TRUE;
}

#endif	/* LINUX */
