// Copyright (C) 1999 Jean-Marc Valin & Dominic Letourneau

#include "Node.h"
#include "Vector.h"
#include "ObjectParser.h"
#include <iostream>

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

#include "stream_wrap.h"
#include "Stream.h"

#ifdef HAVE_MACHINE_SOUNDCARD_H
#define HAVE_OSS_AUDIO
#include <machine/soundcard.h>
#endif

#ifdef HAVE_SYS_SOUNDCARD_H
#define HAVE_OSS_AUDIO
#include <sys/soundcard.h>
#endif

#ifdef HAVE_SYS_AUDIO_H
#include <sys/audio.h>
#endif

#include <unistd.h>

using namespace std;

namespace FD {

class Sound;

DECLARE_NODE(Sound)
/*Node
 *
 * @name Sound
 * @category DSP:Audio
 * @description Opens a sound device
 *
 * @output_name OUTPUT
 * @output_description A file descriptor to the sound device
 *
 * @parameter_name DEVICE
 * @parameter_type string
 * @parameter_value /dev/dsp
 * @parameter_description Path to the sound device
 *
 * @parameter_name RATE
 * @parameter_type int
 * @parameter_description Sampling rate
 *
 * @parameter_name STEREO
 * @parameter_type int
 * @parameter_description 1 for stereo, 0 for mono
 *
 * @parameter_name MODE
 * @parameter_type string
 * @parameter_value W
 * @parameter_description R for sound input, W for sound output, RW for full-duplex mode
 *
 * @parameter_name BUFFER
 * @parameter_type int
 * @parameter_description Length of the audio buffer to allocate (not reliable)
 *
 * @parameter_name DUMMY
 * @parameter_type any
 * @parameter_description Put something here to output to a file
 *
END*/


/** A constant node contains a value that will never changes. */
class Sound : public Node
{

protected:

   /**The value of the constant*/
   ObjectRef value;

   /*the file descriptor*/
   int audio_fd;

   /**The ID of the 'value' output*/
   int outputID;

public:

   /**Constructor, takes the name of the node and a set of parameters*/
   Sound(string nodeName, ParameterSet params)
      : Node(nodeName, params)
   {
      outputID = addOutput("OUTPUT");
      
   }

   void initialize()
   {
      Node::initialize();

      int speed=44100;
      int stereo=0;
      //int audio_fd;
      String device = object_cast <String> (parameters.get("DEVICE"));
      if (parameters.exist("RATE"))
	 speed = dereference_cast<int> (parameters.get("RATE"));
      if (parameters.exist("STEREO"))
	 stereo = dereference_cast<int> (parameters.get("STEREO")); 
      int mode=O_WRONLY;
      if (parameters.exist("DUMMY"))
	 mode |= O_CREAT;
      if (parameters.exist("MODE"))
      {
	 String modeStr = object_cast <String> (parameters.get("MODE"));
	 if (modeStr == "R")
	    mode=O_RDONLY;
	 if (modeStr == "RW")
	    mode=O_RDWR;
      }
      
      if (parameters.exist("DUMMY"))
      {
	 rt_assert((audio_fd=open(device.c_str(),mode, 0644)) != -1, "Can't open sound file\n", __FILE__, __LINE__);
      } else {
	 rt_assert((audio_fd=open(device.c_str(),mode)) != -1, "Can't open sound device\n", __FILE__, __LINE__);
      }

#ifdef HAVE_OSS_AUDIO

      
      if (!parameters.exist("DUMMY"))
      {
	 //int arg=0x7fff0004;
	 int arg=0x7fff000a;
	 if (parameters.exist("BUFFER"))
	 {
	    unsigned int buffLen = dereference_cast<int> (parameters.get("BUFFER"));
	    buffLen--;
	    arg=-1;
	    while (buffLen)
	    {
	       arg++;
	       buffLen >>= 1;
	    }
	    if (arg < 4)
	       arg=4;
	    arg |= 0x7fff0000;
	    //cerr << "arg = " << arg << endl;
	 }      
	 ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &arg);
	 
	 
	 int format=AFMT_S16_LE;
	 if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
	 {
	    perror("SNDCTL_DSP_SETFMT");
	    close(audio_fd);
	    throw_error (true, "Can't set the sample format\n", __FILE__, __LINE__);
	 }
	 
	 if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
	 {
	    perror("SNDCTL_DSP_STEREO");
	    close(audio_fd);
	    throw_error (true, "Can't set/reset stereo mode\n", __FILE__, __LINE__);
	 }
	 
	 if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1)
	 {
	    perror("SNDCTL_DSP_SPEED");
	    close(audio_fd);
	    throw_error (true, "Can't set sound device speed\n", __FILE__, __LINE__);
	 }
      }

#endif

      //FIXME: Theoretically, we should choose the type of stream according to "MODE"
      value = ObjectRef(new IOStream(new fd_iostream(audio_fd, true)));
   }

   virtual ~Sound()
   {
      //cerr << "Sound destructor\n";
      //close(audio_fd);
   }
   /**Ask for the node's output which ID (number) is output_id 
      and for the 'count' iteration */
   virtual ObjectRef getOutput(int output_id, int count)
   {
      if (output_id==outputID) return value;
      else throw new NodeException (this, "Sound: Unknown output id", __FILE__, __LINE__);
   }

protected:
   /**Default constructor, should not be used*/
   Sound() {throw new GeneralException("Sound copy constructor should not be called",__FILE__,__LINE__);}

};

}//namespace FD


syntax highlighted by Code2HTML, v. 0.9.1