/* GSI - general sound interface
 *
 * gsipcm.h
 *
 * ** GSI direct pcm (dsp) access **
 *
 *
 * 1996 - 2000 by W.H.Scholten
 *
 * This file is released to the public domain.
 */


#ifndef _GSI__gsipcm__gsipcm_h
#define _GSI__gsipcm__gsipcm_h

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

#include <gsi/pcm_data.h>
/* ------------------------------------------------------------------------- *
 *      Generalized pcm access.                                              *
 *                                                                           *
 *      Used by gsi_server but can also be used by programs that want to     *
 *      handle pcm stuff themselves (NOT a good idea in most cases, but      *
 *      better than directly accessing pcm devices as this library makes     *
 *      the program independent of the actual device & sound API used).      *
 * ------------------------------------------------------------------------- */
#define GSIPCM_BUSY               -2
#define GSIPCM_NOT_PRESENT        -1 /* no PCM hardware available */

/* Should we be able to set the mode for read/write separately? (can be
 * emulated if not supported by hardware). */
#define GSIPCM_MODE_BLOCKING         1
#define GSIPCM_MODE_NON_BLOCKING     2
#define GSIPCM_MODE_DMA              3

#include <gsi/device.h>


/* ------------------------------------------------------------------------- *
 *      pcm device abstraction.                                              *
 * ------------------------------------------------------------------------- */
#define GSIPCM_HW_S struct gsi_pcm_device *hw, struct gsi_pcm_devicedata *hw_status
typedef struct gsi_pcm_devicedata {
	int  dev; /* Remove ? */
	int  fd;
	int  fd_ct;

	int  modus; /* DMA, or blocking/non-blocking write. */
	int  samplerate;
	int  channels;
	int  type;
	int  byte_order; /* GSI_LITTLE_ENDIAN (lsb first) / GSI_BIG_ENDIAN. */
	int  log2_fragsize;
	int  fragsize;
	int  max_write_space; /* Kernel/API write buffer size. */
	//int  max_read_data; /* Kernel/API read buffer size. */

	int  buf_size;

	int  bps; /* Bytes per sample */
	int  samples; /* Number of samples that fit in the current fragment size. */
	unsigned char *fragment;
	/*   Pointer to the fragment (allocated by user program). */
} gsi_pcm_devicedata_t;

typedef struct gsi_pcm_device {
	char *device; /* The device to open, e.g. /dev/dsp1 */
	char id[32]; /* E.g. pcm/(OSS) 192kHz-24bit */

	int  (*init) (GSIPCM_HW_S, int modus);
	int  (*close) (GSIPCM_HW_S);
	int  (*set) (GSIPCM_HW_S, int samplerate, int channels, int type, int log2_fragsize);
	int  (*write_fragment) (GSIPCM_HW_S);
	int  (*write) (GSIPCM_HW_S, int bytes, void *data);
} gsi_pcm_device_t;


extern gsi_pcm_device_t *pcm_devices;


#define GSIPCM_HW gsi_pcm_device_t *hw, gsi_pcm_devicedata_t *hw_status

int  gsipcm_init (GSIPCM_HW, int modus);
/*   Grabs the pcm device and tries to set DMA mode if requested. returns 0 if
 *   init was ok, -1/-2 if unable to set requested write mode (NONE/BUSY;
 *   BUSY is returned when the PCM device is busy, NONE when neither write
 *   modes are available (e.g. when using NAS, but then we just get BUSY so??
 *   what did I mean here? check if NAS is running? Or if GSI then return
 *   GSIPCM_GSI?). */

int  gsipcm_close (GSIPCM_HW);
/*   Releases the PCM device and resources that have been allocated. */

int  gsipcm_set_pcm (GSIPCM_HW, int pcm_samplerate, int pcm_channels, int pcm_type, int pcm_log2_fragsize);
/*   Returns 0 if parameters set as requested, -1 if another choice had to be
 *   made. The best mode that could be set can be obtained from the hardware
 *   status structure shown above (if you don't like the results you can retry
 *   with other values).*/


/* The following functions are not recommended (gsipcm_set_pcm will probably
 * be better at handling choices) and simply call gsipcm_set_pcm using the old
 * paramaters for the other arguments: */
int  gsipcm_set_fragment (GSIPCM_HW, int pcm_log2_fragsize);
int  gsipcm_set_samplerate (GSIPCM_HW, int pcm_samplerate);
int  gsipcm_set_channels (GSIPCM_HW, int pcm_channels);
int  gsipcm_set_type (GSIPCM_HW, int pcm_type);

int  gsipcm_set_mode (GSIPCM_HW, int modus);
/*   Set blocking, non-blocking, dma (add blocking fragment mode?). */

// OLD:
int  gsipcm_write_fragment (GSIPCM_HW);
/*   Returns the number of bytes written (>= 0), or < 0 on error. */
/* === CHANGE TO:
 * int  gsipcm_write_fragment (GSIPCM_HW, char *your_buffer); ??
 * OR
 * int  gsipcm_write_fragment (GSIPCM_HW, char *your_buffer, int bytes); ??
 * OR
 * int  gsipcm_write (GSIPCM_HW, char *buf, int meta_samples); ??
 * === */

// NEW:
//int  gsipcm_convert_int32_to_output (GSIPCM_HW, int buf_in_byte_order, int buf_in_len, int32 *buf_in, int *buf_out_len, byte *buf_out);
int  gsipcm_write (GSIPCM_HW, char *buf, int meta_samples);
int  gsipcm_write_silence (GSIPCM_HW, int meta_samples);
int  gsipcm_read (GSIPCM_HW, char *buf, int meta_samples);


int  gsipcm_write_space (GSIPCM_HW);
/*   Returns available space in system output buffer. */

int  gsipcm_read_data (GSIPCM_HW);
/*   Returns available data (bytes recorded) in system input buffer. */

// => Or one call :
int gsipcm_buffer_status (GSIPCM_HW, int *writespace, int *readdata);
// Is this really useful? It usually takes two system calls anyway and it's
// better to get the read/write status when you need it instead of both before
// reading/writing.

#ifdef _cplusplus
}
#endif /* __cplusplus */

#endif /* _GSI__gsipcm__gsipcm_h */
