/*
    GDAM - Geoff & Dave's Audio Mixer
    Copyright (C) 1999    Dave Benson, Geoff Matters.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

    Contact:
        daveb@ffem.org <Dave Benson>
        geoff@ugcs.caltech.edu <Geoff Matters>
*/

/* gdamoutputdevice -- wrap the generic output device which
 *                     provides a file descriptor you can poll.
 */
#ifndef __GDAM_OUTPUT_DEVICE_H_
#define __GDAM_OUTPUT_DEVICE_H_


typedef struct _GdamOutputDevice GdamOutputDevice;
typedef struct _GdamOutputDeviceClass GdamOutputDeviceClass;
typedef struct _GdamOutputDeviceOptions GdamOutputDeviceOptions;

#include <gdam/common/gdamsampleconverter.h>
#include "gdamsink.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

GtkType gdam_output_device_get_type();
#define GDAM_TYPE_OUTPUT_DEVICE                 (gdam_output_device_get_type ())
#define GDAM_OUTPUT_DEVICE(obj)              (GTK_CHECK_CAST ((obj), GDAM_TYPE_OUTPUT_DEVICE, GdamOutputDevice))
#define GDAM_OUTPUT_DEVICE_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GDAM_TYPE_OUTPUT_DEVICE, GdamOutputDeviceClass))
#define GDAM_IS_OUTPUT_DEVICE(obj)           (GTK_CHECK_TYPE ((obj), GDAM_TYPE_OUTPUT_DEVICE))
#define GDAM_IS_OUTPUT_DEVICE_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GDAM_TYPE_OUTPUT_DEVICE))

struct _GdamOutputDeviceClass {
        GdamSinkClass           sink_class;

        gboolean              (*open_fd)       (GdamOutputDevice*);
        void                  (*close_fd)      (GdamOutputDevice*);
        int                   (*write_pcm)     (GdamOutputDevice  *device, 
                                                gconstpointer      buffer, 
                                                int                length);
        /* hack: autodetect if the device doesn't poll for writing
         *       for 3 seconds. */
        void                  (*on_write_block)(GdamOutputDevice*);
	/* a GSourceFunc used to fill the prebuffer with audio */
        gboolean              (*fill_buffer)   (GdamOutputDevice*);
	/* allocates buffers and sets up sample conversion */
        void                  (*options_changed)(GdamOutputDevice*);
};

struct _GdamOutputDeviceOptions {
        /* output sample rate / type */
        int             sampling_rate;
        int             num_channels;   /* must be 1,2,4 */
        int             bytes_per_sample;
        int             bits_per_sample;
        gboolean        is_signed;
        gboolean        is_wrong_endian;
        gboolean        is_wrong_stereo_order;

        /* the block size of the device in samples. */
        int             block_size;

	/* run the device writing in a background thread. */
	gboolean        use_pthreads;

        /* whether to fork the device into a separate process. */
        gboolean        do_forking;
        gboolean        do_forking_with_helper;

        /* whether to set the device file descriptor to nonblocking... */
        gboolean        make_non_blocking;

        /* used in subclass specific ways. */
        int             log_frag_size;
        int             num_fragments;

        gboolean        no_ioctls;
};

struct _GdamOutputDevice {
  GdamSink              sink;

  /* the device fd. (derived classes should set this in `open_fd'). */
  int                   device_fd;

  GdamSample           *conversion_buffer;
  GdamSample           *fragment_buffer;

  /* options which control the output mechanism. */
  GdamOutputDeviceOptions options;

  /* how to reduce sound so it can be played. */
  GdamSampleConverter   to_native_channels;
  GdamSampleConverter   to_native_samples;
  int                   sample_size;

  /* has the device written anything out? */
  gboolean              has_written;

  /* main loop stuff -- private. */
  GdamFdPoll           *fd_poll;

  /* "prebuffer" -- this means that as soon as we finish
   *                outputting the last frame, we begin
   *                to compute the new one.  This seems to
   *                result in better sound under worse conditions.  */
  void                 *pre_buffer;
  int                   pre_buffer_start;
  int                   pre_buffer_end;
  int                   pre_buffer_alloced;
  int                   total_sample_size;
  guint                 pre_buffer_busy_func;

  /* number of bytes we must temporarily allocate to do
   * the sample type conversion. */
  int                   conversion_sample_size;

  /* 0 == no limiting
     1 == hard limiting
     2 == soft limiting */
  int                   limit_mode;

  /* For derivers which don't use a fd, so we know not to complain */
  gboolean        	missing_device_fd;
  gboolean	        have_opened;
};

#define GDAM_DEVICE_PRIORITY G_PRIORITY_HIGH

void       gdam_output_device_options_changed(GdamOutputDevice* device);
gboolean   gdam_output_device_open           (GdamOutputDevice* device);
void       gdam_output_device_close          (GdamOutputDevice* device);

/* private */
void       gdam_output_device_set_fd         (GdamOutputDevice* device,
                                              int               fd);

#define GDAM_MAX_ATOMIC_SOURCE_READ		2048

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
