/*-
 * Copyright (c) 2012-2022 Hans Petter Selasky
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef _VIRTUAL_INT_H_
#define	_VIRTUAL_INT_H_

#include <signal.h>
#include <pthread.h>

#include <cuse.h>
#include <samplerate.h>

extern pthread_mutex_t atomic_mtx;
extern pthread_cond_t atomic_cv;

#define atomic_lock()	pthread_mutex_lock(&atomic_mtx)
#define atomic_unlock()	pthread_mutex_unlock(&atomic_mtx)
#define atomic_wait()	pthread_cond_wait(&atomic_cv, &atomic_mtx)
#define atomic_wakeup()	do {			\
	pthread_cond_broadcast(&atomic_cv);	\
	cuse_poll_wakeup();			\
} while (0)

#define AFMT_32BIT \
	(AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE | \
	AFMT_F32_LE | AFMT_F32_BE)
#define AFMT_24BIT \
	(AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE)
#define AFMT_16BIT \
	(AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
#define AFMT_8BIT \
	(AFMT_U8 | AFMT_S8)

#define	VMAX_CHAN 64
/*
 * XXX 32 - strlen("/dev") to not exceed OSS_DEVNODE_SIZE in soundcard.h. Also
 * silences GCC warnings.
 */
#define	VMAX_STRING 27

#define	VTYPE_OSS_DAT 0
#define	VTYPE_WAV_HDR 1
#define	VTYPE_WAV_DAT 2

#define	VPREFERRED_SNE_AFMT \
  (AFMT_S8 | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
#define	VPREFERRED_UNE_AFMT \
  (AFMT_U8 | AFMT_U16_NE | AFMT_U24_NE | AFMT_U32_NE)
#define	VPREFERRED_SLE_AFMT \
  (AFMT_S8 | AFMT_S16_LE | AFMT_S24_LE | AFMT_S32_LE)
#define	VPREFERRED_SBE_AFMT \
  (AFMT_S8 | AFMT_S16_BE | AFMT_S24_BE | AFMT_S32_BE)
#define	VPREFERRED_ULE_AFMT \
  (AFMT_U8 | AFMT_U16_LE | AFMT_U24_LE | AFMT_U32_LE)
#define	VPREFERRED_UBE_AFMT \
  (AFMT_U8 | AFMT_U16_BE | AFMT_U24_BE | AFMT_U32_BE)

#define	VSUPPORTED_AFMT \
  (AFMT_S16_BE | AFMT_S16_LE | AFMT_U16_BE | AFMT_U16_LE | \
  AFMT_S24_BE | AFMT_S24_LE | AFMT_U24_BE | AFMT_U24_LE | \
  AFMT_S32_BE | AFMT_S32_LE | AFMT_U32_BE | AFMT_U32_LE | \
  AFMT_F32_BE | AFMT_F32_LE | \
  AFMT_U8 | AFMT_S8)

#define	VVOLUME_UNIT_SHIFT 7

struct virtual_profile;

typedef TAILQ_ENTRY(virtual_profile) vprofile_entry_t;
typedef TAILQ_HEAD(, virtual_profile) vprofile_head_t;
typedef struct virtual_profile vprofile_t;

struct virtual_client;

typedef TAILQ_ENTRY(virtual_client) vclient_entry_t;
typedef TAILQ_HEAD(, virtual_client) vclient_head_t;
typedef struct virtual_client vclient_t;

struct virtual_monitor;
typedef TAILQ_ENTRY(virtual_monitor) vmonitor_entry_t;
typedef TAILQ_HEAD(, virtual_monitor) vmonitor_head_t;
typedef struct virtual_monitor vmonitor_t;

struct virtual_resample;
typedef struct virtual_resample vresample_t;

struct cuse_methods;

struct virtual_compressor {
	uint8_t enabled;	/* 0..1 */
	uint8_t knee;		/* 0..255 */
	uint8_t	attack;		/* 0..62 */
	uint8_t	decay;		/* 0..62 */
};

struct virtual_profile {
	vprofile_entry_t entry;
	vclient_head_t head;
	char oss_name[VMAX_STRING];
	char wav_name[VMAX_STRING];
	uint32_t rx_filter_size;
	uint32_t tx_filter_size;
	double *rx_filter_data[VMAX_CHAN];
	double *tx_filter_data[VMAX_CHAN];
	int64_t	rx_peak_value[VMAX_CHAN];
	int64_t	tx_peak_value[VMAX_CHAN];
	int8_t	rx_shift[VMAX_CHAN];
	int8_t	tx_shift[VMAX_CHAN];
	uint8_t	rx_src[VMAX_CHAN];
	uint8_t	tx_dst[VMAX_CHAN];
	uint8_t	rx_mute[VMAX_CHAN];
	uint8_t	tx_mute[VMAX_CHAN];
	uint8_t	rx_pol[VMAX_CHAN];
	uint8_t	tx_pol[VMAX_CHAN];
	uint8_t	bits;
	uint8_t	channels;
	struct virtual_compressor rx_compressor_param;
	double rx_compressor_gain[VMAX_CHAN];
	uint8_t synchronized;
	uint32_t rec_delay;
	int fd_sta;
	struct {
		const char * host;
		const char * port;
		const char * rtp_ifname;
		const char * rtp_port;
		volatile struct http_state * state;
		size_t nstate;
		int rtp_fd;
		int rtp_vlanid;
		uint32_t rtp_ts;
		uint16_t rtp_seqnum;
	} http;
};

struct virtual_ring {
	uint8_t *buf_start;
	uint32_t pos_read;
	uint32_t total_size;
	uint32_t len_write;
};

struct virtual_resample {
	SRC_DATA data;
	SRC_STATE *state;
	float *data_in;
	float *data_out;
};

struct virtual_client {
	vclient_entry_t entry;
	uint32_t tx_filter_offset;
	uint32_t rx_filter_offset;
	int64_t *tx_filter_in[VMAX_CHAN];
	int64_t *rx_filter_in[VMAX_CHAN];
	double *tx_filter_out[VMAX_CHAN];
	double *rx_filter_out[VMAX_CHAN];
	struct virtual_ring rx_ring[2];
	struct virtual_ring tx_ring[2];
	vresample_t rx_resample;
	vresample_t tx_resample;
	struct virtual_profile *profile;
	uint64_t rx_samples;
	uint64_t rx_timestamp;
	uint64_t tx_samples;
	uint64_t tx_timestamp;
	uint32_t buffer_frags;
	uint32_t buffer_size;
	uint32_t low_water;
	uint32_t rec_delay;
	uint32_t rx_noise_rem;
	uint32_t tx_noise_rem;
	int	rx_busy;
	int	tx_busy;
	int	channels;
	int	format;
	int	rx_enabled;
	int	tx_enabled;
	int	rx_volume;
	int	tx_volume;
	int	type;		/* VTYPE_XXX */
	int	sample_rate;
	uint32_t buffer_size_set:1;
	uint32_t buffer_frags_set:1;
	uint32_t sync_busy:1;
	uint32_t sync_wakeup:1;
	int	padding:28;
};

struct virtual_monitor {
	vmonitor_entry_t entry;
	int64_t	peak_value;
	uint8_t	src_chan;
	uint8_t	dst_chan;
	uint8_t	pol;
	uint8_t	mute;
	int8_t	shift;
};

extern vprofile_head_t virtual_profile_client_head;
extern vprofile_head_t virtual_profile_loopback_head;

extern vmonitor_head_t virtual_monitor_input;
extern vmonitor_head_t virtual_monitor_local;
extern vmonitor_head_t virtual_monitor_output;

extern const struct cuse_methods vctl_methods;

extern struct virtual_compressor voss_output_compressor_param;
extern double voss_output_compressor_gain[VMAX_CHAN];
extern int64_t voss_output_peak[VMAX_CHAN];
extern int64_t voss_input_peak[VMAX_CHAN];
extern uint32_t voss_jitter_up;
extern uint32_t voss_jitter_down;
extern uint32_t voss_max_channels;
extern uint32_t voss_mix_channels;
extern uint32_t voss_dsp_samples;
extern uint32_t voss_dsp_max_channels;
extern uint32_t voss_dsp_sample_rate;
extern uint32_t voss_dsp_bits;
extern uint8_t voss_libsamplerate_enable;
extern uint8_t voss_libsamplerate_quality;
extern int voss_is_recording;
extern int voss_has_synchronization;
extern char voss_dsp_rx_device[VMAX_STRING];
extern char voss_dsp_tx_device[VMAX_STRING];
extern char voss_ctl_device[VMAX_STRING];
extern volatile sig_atomic_t voss_exit;

extern int vring_alloc(struct virtual_ring *, size_t);
extern void vring_free(struct virtual_ring *);
extern void vring_reset(struct virtual_ring *);
extern void vring_get_read(struct virtual_ring *, uint8_t **, size_t *);
extern void vring_get_write(struct virtual_ring *, uint8_t **, size_t *);
extern void vring_inc_read(struct virtual_ring *, size_t);
extern void vring_inc_write(struct virtual_ring *, size_t);
extern size_t vring_total_read_len(struct virtual_ring *);
extern size_t vring_total_write_len(struct virtual_ring *);
extern size_t vring_write_linear(struct virtual_ring *, const uint8_t *, size_t);
extern size_t vring_read_linear(struct virtual_ring *, uint8_t *, size_t);
extern size_t vring_write_zero(struct virtual_ring *, size_t);

extern vclient_t *vclient_alloc(void);
extern void vclient_free(vclient_t *);

extern int vclient_get_default_fmt(vprofile_t *, int type);
extern int vclient_setup_buffers(vclient_t *, int size, int frags,
    int channels, int format, int sample_rate);
extern int vclient_export_read_locked(vclient_t *);
extern void vclient_import_write_locked(vclient_t *);

extern uint32_t vclient_sample_bytes(vclient_t *);
extern uint32_t vclient_bufsize_internal(vclient_t *);
extern uint32_t vclient_bufsize_scaled(vclient_t *);

extern int64_t vclient_noise(uint32_t *, int64_t, int8_t);

extern vmonitor_t *vmonitor_alloc(int *, vmonitor_head_t *);

extern uint32_t format_best(uint32_t);
extern void format_import(uint32_t, const uint8_t *, uint32_t, int64_t *);
extern void format_export(uint32_t, const int64_t *, uint8_t *, uint32_t);
extern int64_t format_max(uint32_t);
extern void format_maximum(const int64_t *, int64_t *, uint32_t, uint32_t, int8_t);
extern void format_remix(int64_t *, uint32_t, uint32_t, uint32_t);
extern void format_silence(uint32_t, uint8_t *, uint32_t);

extern void *virtual_oss_process(void *);

/* Audio Delay prototypes */
extern uint32_t voss_ad_last_delay;
extern uint32_t voss_dsp_rx_refresh;
extern uint32_t voss_dsp_tx_refresh;
extern uint8_t voss_ad_enabled;
extern uint8_t voss_ad_output_signal;
extern uint8_t voss_ad_input_channel;
extern uint8_t voss_ad_output_channel;
extern void voss_ad_reset(void);
extern void voss_ad_init(uint32_t);
extern double voss_ad_getput_sample(double);

/* Add audio options prototype */
extern void voss_add_options(char *);

/* Get current timestamp */
extern uint64_t virtual_oss_delay_ns(void);
extern void virtual_oss_wait(void);
extern uint64_t virtual_oss_timestamp(void);

/* Fast array multiplication */
extern void voss_x3_multiply_double(const int64_t *, const double *, double *, const size_t);

/* Equalizer support */
extern void vclient_tx_equalizer(struct virtual_client *, int64_t *, size_t);
extern void vclient_rx_equalizer(struct virtual_client *, int64_t *, size_t);
extern int vclient_eq_alloc(struct virtual_client *);
extern void vclient_eq_free(struct virtual_client *);

/* Internal compressor */
extern void voss_compressor(int64_t *, double *, const struct virtual_compressor *,
   const unsigned, const unsigned, const int64_t);

/* HTTP daemon support */
extern const char *voss_httpd_start(vprofile_t *);

#endif					/* _VIRTUAL_INT_H_ */
