#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "mem.h"
#include "pci.h"
#include "sound.h"

#define N 16

#define ES1371_NCoef 91

static float low_fir_es1371_coef[ES1371_NCoef];

typedef struct es1371_t {
	uint8_t pci_command, pci_serr;

	uint32_t base_addr;

	uint8_t int_line;

	uint16_t pmcsr;

	uint32_t int_ctrl;
	uint32_t int_status;

	uint32_t legacy_ctrl;

	int mem_page;

	uint32_t si_cr;

	uint32_t sr_cir;
	uint16_t sr_ram[128];

	uint8_t uart_ctrl;
	uint8_t uart_status;

	uint16_t codec_regs[64];
	uint32_t codec_ctrl;

	struct {
		uint32_t addr, addr_latch;
		uint16_t count, size;

		uint16_t samp_ct;
		int curr_samp_ct;

		pc_timer_t timer;
		uint64_t latch;

		uint32_t vf, ac;

		int16_t buffer_l[64], buffer_r[64];
		int buffer_pos, buffer_pos_end;

		int filtered_l[32], filtered_r[32];
		int f_pos;

		int16_t out_l, out_r;

		int32_t vol_l, vol_r;
	} dac[2], adc;

	int dac_latch, dac_time;

	int master_vol_l, master_vol_r;

	int card;

	int pos;
	int16_t buffer[MAXSOUNDBUFLEN * 2];
} es1371_t;

#define LEGACY_SB_ADDR            (1 << 29)
#define LEGACY_SSCAPE_ADDR_SHIFT  27
#define LEGACY_CODEC_ADDR_SHIFT   25
#define LEGACY_FORCE_IRQ          (1 << 24)
#define LEGACY_CAPTURE_SLAVE_DMA  (1 << 23)
#define LEGACY_CAPTURE_SLAVE_PIC  (1 << 22)
#define LEGACY_CAPTURE_MASTER_DMA (1 << 21)
#define LEGACY_CAPTURE_MASTER_PIC (1 << 20)
#define LEGACY_CAPTURE_ADLIB      (1 << 19)
#define LEGACY_CAPTURE_SB         (1 << 18)
#define LEGACY_CAPTURE_CODEC      (1 << 17)
#define LEGACY_CAPTURE_SSCAPE     (1 << 16)
#define LEGACY_EVENT_SSCAPE     (0 << 8)
#define LEGACY_EVENT_CODEC      (1 << 8)
#define LEGACY_EVENT_SB         (2 << 8)
#define LEGACY_EVENT_ADLIB      (3 << 8)
#define LEGACY_EVENT_MASTER_PIC (4 << 8)
#define LEGACY_EVENT_MASTER_DMA (5 << 8)
#define LEGACY_EVENT_SLAVE_PIC  (6 << 8)
#define LEGACY_EVENT_SLAVE_DMA  (7 << 8)
#define LEGACY_EVENT_MASK       (7 << 8)
#define LEGACY_EVENT_ADDR_SHIFT 3
#define LEGACY_EVENT_ADDR_MASK (0x1f << 3)
#define LEGACY_EVENT_TYPE_RW  (1 << 2)
#define LEGACY_INT (1 << 0)

#define SRC_RAM_WE (1 << 24)

#define CODEC_READ  (1 << 23)
#define CODEC_READY (1 << 31)

#define INT_DAC1_EN (1 << 6)
#define INT_DAC2_EN (1 << 5)

#define SI_P2_INTR_EN (1 << 9)
#define SI_P1_INTR_EN (1 << 8)

#define INT_STATUS_INTR (1 << 31)
#define INT_STATUS_DAC1 (1 << 2)
#define INT_STATUS_DAC2 (1 << 1)

#define FORMAT_MONO_8    0
#define FORMAT_STEREO_8  1
#define FORMAT_MONO_16   2
#define FORMAT_STEREO_16 3

const int32_t codec_attn[] =
	{
		25, 32, 41, 51, 65, 82, 103, 130, 164, 206, 260, 327, 412, 519, 653,
		822, 1036, 1304, 1641, 2067, 2602, 3276, 4125, 5192, 6537, 8230, 10362, 13044,
		16422, 20674, 26027, 32767
	};

static void es1371_fetch(es1371_t *es1371, int dac_nr);
static void update_legacy(es1371_t *es1371, uint32_t old_legacy_ctrl);

static void es1371_update_irqs(es1371_t *es1371) {
	int irq = 0;

	if ((es1371->int_status & INT_STATUS_DAC1) && (es1371->si_cr & SI_P1_INTR_EN))
		irq = 1;
	if ((es1371->int_status & INT_STATUS_DAC2) && (es1371->si_cr & SI_P2_INTR_EN))
		irq = 1;

	if (irq)
		es1371->int_status |= INT_STATUS_INTR;
	else
		es1371->int_status &= ~INT_STATUS_INTR;

	if (es1371->legacy_ctrl & LEGACY_FORCE_IRQ)
		irq = 1;

	if (irq) {
		pci_set_irq(es1371->card, PCI_INTA);
//                pclog("Raise IRQ\n");
	} else {
		pci_clear_irq(es1371->card, PCI_INTA);
//                pclog("Drop IRQ\n");
	}
}

static uint8_t es1371_inb(uint16_t port, void *p) {
	es1371_t *es1371 = (es1371_t *)p;
	uint8_t ret = 0;

	switch (port & 0x3f) {
	case 0x00:ret = es1371->int_ctrl & 0xff;
		break;
	case 0x01:ret = (es1371->int_ctrl >> 8) & 0xff;
		break;
	case 0x02:ret = (es1371->int_ctrl >> 16) & 0xff;
		break;
	case 0x03:ret = (es1371->int_ctrl >> 24) & 0xff;
		break;

	case 0x04:ret = es1371->int_status & 0xff;
		break;
	case 0x05:ret = (es1371->int_status >> 8) & 0xff;
		break;
	case 0x06:ret = (es1371->int_status >> 16) & 0xff;
		break;
	case 0x07:ret = (es1371->int_status >> 24) & 0xff;
		break;

	case 0x09:ret = es1371->uart_status;
		break;

	case 0x0c:ret = es1371->mem_page;
		break;

	case 0x1a:ret = es1371->legacy_ctrl >> 16;
		break;
	case 0x1b:ret = es1371->legacy_ctrl >> 24;
		break;

	case 0x20:ret = es1371->si_cr & 0xff;
		break;
	case 0x21:ret = es1371->si_cr >> 8;
		break;
	case 0x22:ret = (es1371->si_cr >> 16) | 0x80;
		break;
	case 0x23:ret = 0xff;
		break;

	default:pclog("Bad es1371_inb: port=%04x\n", port);
	}

//        pclog("es1371_inb: port=%04x ret=%02x\n", port, ret);
//        output = 3;
	return ret;
}
static uint16_t es1371_inw(uint16_t port, void *p) {
	es1371_t *es1371 = (es1371_t *)p;
	uint16_t ret = 0;

	switch (port & 0x3e) {
	case 0x00:ret = es1371->int_ctrl & 0xffff;
		break;
	case 0x02:ret = (es1371->int_ctrl >> 16) & 0xffff;
		break;

	case 0x18:ret = es1371->legacy_ctrl & 0xffff;
//                pclog("Read legacy ctrl %04x\n", ret);
		break;

	case 0x26:ret = es1371->dac[0].curr_samp_ct;
		break;

	case 0x2a:ret = es1371->dac[1].curr_samp_ct;
		break;

	case 0x36:
		switch (es1371->mem_page) {
		case 0xc:ret = es1371->dac[0].count;
			break;

		default:pclog("Bad es1371_inw: mem_page=%x port=%04x\n", es1371->mem_page, port);
		}
		break;

	case 0x3e:
		switch (es1371->mem_page) {
		case 0xc:ret = es1371->dac[1].count;
			break;

		default:pclog("Bad es1371_inw: mem_page=%x port=%04x\n", es1371->mem_page, port);
		}
		break;

	default:pclog("Bad es1371_inw: port=%04x\n", port);
	}

//        pclog("es1371_inw: port=%04x ret=%04x %04x:%08x\n", port, ret, CS,cpu_state.pc);
	return ret;
}
static uint32_t es1371_inl(uint16_t port, void *p) {
	es1371_t *es1371 = (es1371_t *)p;
	uint32_t ret = 0;

	switch (port & 0x3c) {
	case 0x00:ret = es1371->int_ctrl;
		break;
	case 0x04:ret = es1371->int_status;
		break;

	case 0x10:ret = es1371->sr_cir & ~0xffff;
		ret |= es1371->sr_ram[es1371->sr_cir >> 25];
		break;

	case 0x14:ret = es1371->codec_ctrl & 0x00ff0000;
		ret |= es1371->codec_regs[(es1371->codec_ctrl >> 16) & 0x3f];
		ret |= CODEC_READY;
		break;

	case 0x34:
		switch (es1371->mem_page) {
		case 0xc:ret = es1371->dac[0].size | (es1371->dac[0].count << 16);
			break;

		case 0xd:ret = es1371->adc.size | (es1371->adc.count << 16);
			break;

		default:pclog("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
		}
		break;

	case 0x3c:
		switch (es1371->mem_page) {
		case 0xc:ret = es1371->dac[1].size | (es1371->dac[1].count << 16);
			break;

		default:pclog("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
		}
		break;

	default:pclog("Bad es1371_inl: port=%04x\n", port);
	}

//        pclog("es1371_inl: port=%04x ret=%08x  %08x\n", port, ret, cpu_state.pc);
	return ret;
}

static void es1371_outb(uint16_t port, uint8_t val, void *p) {
	es1371_t *es1371 = (es1371_t *)p;
	uint32_t old_legacy_ctrl;

//        pclog("es1371_outb: port=%04x val=%02x %04x:%08x\n", port, val, cs, cpu_state.pc);
	switch (port & 0x3f) {
	case 0x00:
		if (!(es1371->int_ctrl & INT_DAC1_EN) && (val & INT_DAC1_EN)) {
			es1371->dac[0].addr = es1371->dac[0].addr_latch;
			es1371->dac[0].buffer_pos = 0;
			es1371->dac[0].buffer_pos_end = 0;
			es1371_fetch(es1371, 0);
		}
		if (!(es1371->int_ctrl & INT_DAC2_EN) && (val & INT_DAC2_EN)) {
			es1371->dac[1].addr = es1371->dac[1].addr_latch;
			es1371->dac[1].buffer_pos = 0;
			es1371->dac[1].buffer_pos_end = 0;
			es1371_fetch(es1371, 1);
		}
		es1371->int_ctrl = (es1371->int_ctrl & 0xffffff00) | val;
		break;
	case 0x01:es1371->int_ctrl = (es1371->int_ctrl & 0xffff00ff) | (val << 8);
		break;
	case 0x02:es1371->int_ctrl = (es1371->int_ctrl & 0xff00ffff) | (val << 16);
		break;
	case 0x03:es1371->int_ctrl = (es1371->int_ctrl & 0x00ffffff) | (val << 24);
		break;

	case 0x09:es1371->uart_ctrl = val;
		break;

	case 0x0c:es1371->mem_page = val & 0xf;
		break;

	case 0x18:es1371->legacy_ctrl |= LEGACY_INT;
		nmi = 0;
		break;
	case 0x1a:old_legacy_ctrl = es1371->legacy_ctrl;
		es1371->legacy_ctrl = (es1371->legacy_ctrl & 0xff00ffff) | (val << 16);
		update_legacy(es1371, old_legacy_ctrl);
		break;
	case 0x1b:old_legacy_ctrl = es1371->legacy_ctrl;
		es1371->legacy_ctrl = (es1371->legacy_ctrl & 0x00ffffff) | (val << 24);
		es1371_update_irqs(es1371);
//                output = 3;
		update_legacy(es1371, old_legacy_ctrl);
		break;

	case 0x20:es1371->si_cr = (es1371->si_cr & 0xffff00) | val;
		break;
	case 0x21:es1371->si_cr = (es1371->si_cr & 0xff00ff) | (val << 8);
		if (!(es1371->si_cr & SI_P1_INTR_EN))
			es1371->int_status &= ~INT_STATUS_DAC1;
		if (!(es1371->si_cr & SI_P2_INTR_EN))
			es1371->int_status &= ~INT_STATUS_DAC2;
		es1371_update_irqs(es1371);
		break;
	case 0x22:es1371->si_cr = (es1371->si_cr & 0x00ffff) | (val << 16);
		break;

	default:pclog("Bad es1371_outb: port=%04x val=%02x\n", port, val);
	}
}
static void es1371_outw(uint16_t port, uint16_t val, void *p) {
	es1371_t *es1371 = (es1371_t *)p;

//        pclog("es1371_outw: port=%04x val=%04x\n", port, val);
	switch (port & 0x3f) {
	case 0x0c:es1371->mem_page = val & 0xf;
		break;

	case 0x24:es1371->dac[0].samp_ct = val;
		break;

	case 0x28:es1371->dac[1].samp_ct = val;
		break;

	default:pclog("Bad es1371_outw: port=%04x val=%04x\n", port, val);
	}
}
static void es1371_outl(uint16_t port, uint32_t val, void *p) {
	es1371_t *es1371 = (es1371_t *)p;

//        pclog("es1371_outl: port=%04x val=%08x %04x:%08x\n", port, val, CS, cpu_state.pc);
	switch (port & 0x3f) {
	case 0x04:break;

	case 0x0c:es1371->mem_page = val & 0xf;
		break;

	case 0x10:es1371->sr_cir = val;
		if (es1371->sr_cir & SRC_RAM_WE) {
//                        pclog("Write SR RAM %02x %04x\n", es1371->sr_cir >> 25, val & 0xffff);
			es1371->sr_ram[es1371->sr_cir >> 25] = val & 0xffff;
			switch (es1371->sr_cir >> 25) {
			case 0x71:es1371->dac[0].vf = (es1371->dac[0].vf & ~0x1f8000) | ((val & 0xfc00) << 5);
				es1371->dac[0].ac = (es1371->dac[0].ac & ~0x7f8000) | ((val & 0x00ff) << 15);
				es1371->dac[0].f_pos = 0;
				break;
			case 0x72:es1371->dac[0].ac = (es1371->dac[0].ac & ~0x7fff) | (val & 0x7fff);
				break;
			case 0x73:es1371->dac[0].vf = (es1371->dac[0].vf & ~0x7fff) | (val & 0x7fff);
				break;

			case 0x75:es1371->dac[1].vf = (es1371->dac[1].vf & ~0x1f8000) | ((val & 0xfc00) << 5);
				es1371->dac[1].ac = (es1371->dac[1].ac & ~0x7f8000) | ((val & 0x00ff) << 15);
				es1371->dac[1].f_pos = 0;
				break;
			case 0x76:es1371->dac[1].ac = (es1371->dac[1].ac & ~0x7fff) | (val & 0x7fff);
				break;
			case 0x77:es1371->dac[1].vf = (es1371->dac[1].vf & ~0x7fff) | (val & 0x7fff);
				break;

			case 0x7c:es1371->dac[0].vol_l = (int32_t)(int16_t)(val & 0xffff);
				break;
			case 0x7d:es1371->dac[0].vol_r = (int32_t)(int16_t)(val & 0xffff);
				break;
			case 0x7e:es1371->dac[1].vol_l = (int32_t)(int16_t)(val & 0xffff);
				break;
			case 0x7f:es1371->dac[1].vol_r = (int32_t)(int16_t)(val & 0xffff);
				break;
			}
		}
		break;

	case 0x14:es1371->codec_ctrl = val;
		if (!(val & CODEC_READ)) {
//                        pclog("Write codec %02x %04x\n", (val >> 16) & 0x3f, val & 0xffff);
			es1371->codec_regs[(val >> 16) & 0x3f] = val & 0xffff;
			switch ((val >> 16) & 0x3f) {
			case 0x02: /*Master volume*/
				if (val & 0x8000)
					es1371->master_vol_l = es1371->master_vol_r = 0;
				else {
					if (val & 0x2000)
						es1371->master_vol_l = codec_attn[0];
					else
						es1371->master_vol_l = codec_attn[0x1f - ((val >> 8) & 0x1f)];
					if (val & 0x20)
						es1371->master_vol_r = codec_attn[0];
					else
						es1371->master_vol_r = codec_attn[0x1f - (val & 0x1f)];
				}
				break;
			case 0x12: /*CD volume*/
				if (val & 0x8000)
					sound_set_cd_volume(0, 0);
				else
					sound_set_cd_volume(codec_attn[0x1f - ((val >> 8) & 0x1f)] * 2, codec_attn[0x1f - (val & 0x1f)] * 2);
				break;
			}
		}
		break;

	case 0x24:es1371->dac[0].samp_ct = val & 0xffff;
		break;

	case 0x28:es1371->dac[1].samp_ct = val & 0xffff;
		break;

	case 0x30:
		switch (es1371->mem_page) {
		case 0x0:
		case 0x1:
		case 0x2:
		case 0x3:
		case 0x4:
		case 0x5:
		case 0x6:
		case 0x7:
		case 0x8:
		case 0x9:
		case 0xa:
		case 0xb:break;

		case 0xc:es1371->dac[0].addr_latch = val;
//                        pclog("DAC1 addr %08x\n", val);
			break;

		default:pclog("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
		}
		break;
	case 0x34:
		switch (es1371->mem_page) {
		case 0x0:
		case 0x1:
		case 0x2:
		case 0x3:
		case 0x4:
		case 0x5:
		case 0x6:
		case 0x7:
		case 0x8:
		case 0x9:
		case 0xa:
		case 0xb:break;

		case 0xc:es1371->dac[0].size = val & 0xffff;
			es1371->dac[0].count = val >> 16;
			break;

		case 0xd:es1371->adc.size = val & 0xffff;
			es1371->adc.count = val >> 16;
			break;

		default:pclog("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
		}
		break;
	case 0x38:
		switch (es1371->mem_page) {
		case 0x0:
		case 0x1:
		case 0x2:
		case 0x3:
		case 0x4:
		case 0x5:
		case 0x6:
		case 0x7:
		case 0x8:
		case 0x9:
		case 0xa:
		case 0xb:break;

		case 0xc:es1371->dac[1].addr_latch = val;
			break;

		case 0xd:break;

		default:pclog("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
		}
		break;
	case 0x3c:
		switch (es1371->mem_page) {
		case 0x0:
		case 0x1:
		case 0x2:
		case 0x3:
		case 0x4:
		case 0x5:
		case 0x6:
		case 0x7:
		case 0x8:
		case 0x9:
		case 0xa:
		case 0xb:break;

		case 0xc:es1371->dac[1].size = val & 0xffff;
			es1371->dac[1].count = val >> 16;
			break;

		default:pclog("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
		}
		break;

	default:pclog("Bad es1371_outl: port=%04x val=%08x\n", port, val);
	}
}

static void capture_event(es1371_t *es1371, int type, int rw, uint16_t port) {
	es1371->legacy_ctrl &= ~(LEGACY_EVENT_MASK | LEGACY_EVENT_ADDR_MASK);
	es1371->legacy_ctrl |= type;
	if (rw)
		es1371->legacy_ctrl |= LEGACY_EVENT_TYPE_RW;
	else
		es1371->legacy_ctrl &= ~LEGACY_EVENT_TYPE_RW;
	es1371->legacy_ctrl |= ((port << LEGACY_EVENT_ADDR_SHIFT) & LEGACY_EVENT_ADDR_MASK);
	es1371->legacy_ctrl &= ~LEGACY_INT;
	nmi = 1;
//        pclog("Event! %s %04x\n", rw ? "write" : "read", port);
}

static void capture_write_sscape(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_SSCAPE, 1, port);
}
static void capture_write_codec(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_CODEC, 1, port);
}
static void capture_write_sb(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_SB, 1, port);
}
static void capture_write_adlib(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_ADLIB, 1, port);
}
static void capture_write_master_pic(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_MASTER_PIC, 1, port);
}
static void capture_write_master_dma(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_MASTER_DMA, 1, port);
}
static void capture_write_slave_pic(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_SLAVE_PIC, 1, port);
}
static void capture_write_slave_dma(uint16_t port, uint8_t val, void *p) {
	capture_event(p, LEGACY_EVENT_SLAVE_DMA, 1, port);
}

static uint8_t capture_read_sscape(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_SSCAPE, 0, port);
	return 0xff;
}
static uint8_t capture_read_codec(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_CODEC, 0, port);
	return 0xff;
}
static uint8_t capture_read_sb(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_SB, 0, port);
	return 0xff;
}
static uint8_t capture_read_adlib(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_ADLIB, 0, port);
	return 0xff;
}
static uint8_t capture_read_master_pic(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_MASTER_PIC, 0, port);
	return 0xff;
}
static uint8_t capture_read_master_dma(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_MASTER_DMA, 0, port);
	return 0xff;
}
static uint8_t capture_read_slave_pic(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_SLAVE_PIC, 0, port);
	return 0xff;
}
static uint8_t capture_read_slave_dma(uint16_t port, void *p) {
	capture_event(p, LEGACY_EVENT_SLAVE_DMA, 0, port);
	return 0xff;
}

static void update_legacy(es1371_t *es1371, uint32_t old_legacy_ctrl) {
	if (old_legacy_ctrl & LEGACY_CAPTURE_SSCAPE) {
		switch ((old_legacy_ctrl >> LEGACY_SSCAPE_ADDR_SHIFT) & 3) {
		case 0:io_removehandler(0x0320, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		case 1:io_removehandler(0x0330, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		case 2:io_removehandler(0x0340, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		case 3:io_removehandler(0x0350, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		}
	}
	if (old_legacy_ctrl & LEGACY_CAPTURE_CODEC) {
		switch ((old_legacy_ctrl >> LEGACY_CODEC_ADDR_SHIFT) & 3) {
		case 0:io_removehandler(0x5300, 0x0080, capture_read_codec, NULL, NULL, capture_write_codec, NULL, NULL, es1371);
			break;
		case 2:io_removehandler(0xe800, 0x0080, capture_read_codec, NULL, NULL, capture_write_codec, NULL, NULL, es1371);
			break;
		case 3:io_removehandler(0xf400, 0x0080, capture_read_codec, NULL, NULL, capture_write_codec, NULL, NULL, es1371);
			break;
		}
	}
	if (old_legacy_ctrl & LEGACY_CAPTURE_SB) {
		if (!(old_legacy_ctrl & LEGACY_SB_ADDR))
			io_removehandler(0x0220, 0x0010, capture_read_sb, NULL, NULL, capture_write_sb, NULL, NULL, es1371);
		else
			io_removehandler(0x0240, 0x0010, capture_read_sb, NULL, NULL, capture_write_sb, NULL, NULL, es1371);
	}
	if (old_legacy_ctrl & LEGACY_CAPTURE_ADLIB)
		io_removehandler(0x0388, 0x0004, capture_read_adlib, NULL, NULL, capture_write_adlib, NULL, NULL, es1371);
	if (old_legacy_ctrl & LEGACY_CAPTURE_MASTER_PIC)
		io_removehandler(0x0020, 0x0002, capture_read_master_pic, NULL, NULL, capture_write_master_pic, NULL, NULL, es1371);
	if (old_legacy_ctrl & LEGACY_CAPTURE_MASTER_DMA)
		io_removehandler(0x0000, 0x0010, capture_read_master_dma, NULL, NULL, capture_write_master_dma, NULL, NULL, es1371);
	if (old_legacy_ctrl & LEGACY_CAPTURE_SLAVE_PIC)
		io_removehandler(0x00a0, 0x0002, capture_read_slave_pic, NULL, NULL, capture_write_slave_pic, NULL, NULL, es1371);
	if (old_legacy_ctrl & LEGACY_CAPTURE_SLAVE_DMA)
		io_removehandler(0x00c0, 0x0020, capture_read_slave_dma, NULL, NULL, capture_write_slave_dma, NULL, NULL, es1371);

	if (es1371->legacy_ctrl & LEGACY_CAPTURE_SSCAPE) {
		switch ((es1371->legacy_ctrl >> LEGACY_SSCAPE_ADDR_SHIFT) & 3) {
		case 0:io_sethandler(0x0320, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		case 1:io_sethandler(0x0330, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		case 2:io_sethandler(0x0340, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		case 3:io_sethandler(0x0350, 0x0008, capture_read_sscape, NULL, NULL, capture_write_sscape, NULL, NULL, es1371);
			break;
		}
	}
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_CODEC) {
		switch ((es1371->legacy_ctrl >> LEGACY_CODEC_ADDR_SHIFT) & 3) {
		case 0:io_sethandler(0x5300, 0x0080, capture_read_codec, NULL, NULL, capture_write_codec, NULL, NULL, es1371);
			break;
		case 2:io_sethandler(0xe800, 0x0080, capture_read_codec, NULL, NULL, capture_write_codec, NULL, NULL, es1371);
			break;
		case 3:io_sethandler(0xf400, 0x0080, capture_read_codec, NULL, NULL, capture_write_codec, NULL, NULL, es1371);
			break;
		}
	}
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_SB) {
		if (!(es1371->legacy_ctrl & LEGACY_SB_ADDR))
			io_sethandler(0x0220, 0x0010, capture_read_sb, NULL, NULL, capture_write_sb, NULL, NULL, es1371);
		else
			io_sethandler(0x0240, 0x0010, capture_read_sb, NULL, NULL, capture_write_sb, NULL, NULL, es1371);
	}
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_ADLIB)
		io_sethandler(0x0388, 0x0004, capture_read_adlib, NULL, NULL, capture_write_adlib, NULL, NULL, es1371);
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_MASTER_PIC)
		io_sethandler(0x0020, 0x0002, capture_read_master_pic, NULL, NULL, capture_write_master_pic, NULL, NULL, es1371);
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_MASTER_DMA)
		io_sethandler(0x0000, 0x0010, capture_read_master_dma, NULL, NULL, capture_write_master_dma, NULL, NULL, es1371);
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_SLAVE_PIC)
		io_sethandler(0x00a0, 0x0002, capture_read_slave_pic, NULL, NULL, capture_write_slave_pic, NULL, NULL, es1371);
	if (es1371->legacy_ctrl & LEGACY_CAPTURE_SLAVE_DMA)
		io_sethandler(0x00c0, 0x0020, capture_read_slave_dma, NULL, NULL, capture_write_slave_dma, NULL, NULL, es1371);
}

static uint8_t es1371_pci_read(int func, int addr, void *p) {
	es1371_t *es1371 = (es1371_t *)p;

	if (func)
		return 0;

	//pclog("ES1371 PCI read %08X PC=%08x\n", addr, cpu_state.pc);

	switch (addr) {
	case 0x00:return 0x74; /*Ensoniq*/
	case 0x01:return 0x12;

	case 0x02:return 0x71; /*ES1371*/
	case 0x03:return 0x13;

	case 0x04:return es1371->pci_command;
	case 0x05:return es1371->pci_serr;

	case 0x06:return 0x10; /*Supports ACPI*/
	case 0x07:return 0;

	case 0x08:return 2; /*Revision ID*/
	case 0x09:return 0x00; /*Multimedia audio device*/
	case 0x0a:return 0x01;
	case 0x0b:return 0x04;

	case 0x10:return 0x01 | (es1371->base_addr & 0xc0); /*memBaseAddr*/
	case 0x11:return es1371->base_addr >> 8;
	case 0x12:return es1371->base_addr >> 16;
	case 0x13:return es1371->base_addr >> 24;

	case 0x2c:return 0x74; /*Subsystem vendor ID*/
	case 0x2d:return 0x12;
	case 0x2e:return 0x71;
	case 0x2f:return 0x13;

	case 0x34:return 0xdc; /*Capabilites pointer*/

	case 0x3c:return es1371->int_line;
	case 0x3d:return 0x01; /*INTA*/

	case 0x3e:return 0xc; /*Minimum grant*/
	case 0x3f:return 0x80; /*Maximum latency*/

	case 0xdc:return 0x01; /*Capabilities identifier*/
	case 0xdd:return 0x00; /*Next item pointer*/
	case 0xde:return 0x31; /*Power management capabilities*/
	case 0xdf:return 0x6c;

	case 0xe0:return es1371->pmcsr & 0xff;
	case 0xe1:return es1371->pmcsr >> 8;
	}
	return 0;
}

static void es1371_pci_write(int func, int addr, uint8_t val, void *p) {
	es1371_t *es1371 = (es1371_t *)p;

	if (func)
		return;

//        pclog("ES1371 PCI write %04X %02X PC=%08x\n", addr, val, cpu_state.pc);

	switch (addr) {
	case 0x04:
		if (es1371->pci_command & PCI_COMMAND_IO)
			io_removehandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
		es1371->pci_command = val & 0x05;
		if (es1371->pci_command & PCI_COMMAND_IO)
			io_sethandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
		break;
	case 0x05:es1371->pci_serr = val & 1;
		break;

	case 0x10:
		if (es1371->pci_command & PCI_COMMAND_IO)
			io_removehandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
		es1371->base_addr = (es1371->base_addr & 0xffffff00) | (val & 0xc0);
		if (es1371->pci_command & PCI_COMMAND_IO)
			io_sethandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
		break;
	case 0x11:
		if (es1371->pci_command & PCI_COMMAND_IO)
			io_removehandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
		es1371->base_addr = (es1371->base_addr & 0xffff00c0) | (val << 8);
		if (es1371->pci_command & PCI_COMMAND_IO)
			io_sethandler(es1371->base_addr, 0x0040, es1371_inb, es1371_inw, es1371_inl, es1371_outb, es1371_outw, es1371_outl, es1371);
		break;
	case 0x12:es1371->base_addr = (es1371->base_addr & 0xff00ffc0) | (val << 16);
		break;
	case 0x13:es1371->base_addr = (es1371->base_addr & 0x00ffffc0) | (val << 24);
		break;

	case 0x3c:es1371->int_line = val;
		break;

	case 0xe0:es1371->pmcsr = (es1371->pmcsr & 0xff00) | (val & 0x03);
		break;
	case 0xe1:es1371->pmcsr = (es1371->pmcsr & 0x00ff) | ((val & 0x01) << 8);
		break;
	}
//        pclog("es1371->base_addr %08x\n", es1371->base_addr);
}

static void es1371_fetch(es1371_t *es1371, int dac_nr) {
	int format = dac_nr ? ((es1371->si_cr >> 2) & 3) : (es1371->si_cr & 3);
	int pos = es1371->dac[dac_nr].buffer_pos & 63;
	int c;

//pclog("Fetch format=%i %08x %08x  %08x %08x  %08x\n", format, es1371->dac[dac_nr].count, es1371->dac[dac_nr].size,  es1371->dac[dac_nr].curr_samp_ct,es1371->dac[dac_nr].samp_ct, es1371->dac[dac_nr].addr);
	switch (format) {
	case FORMAT_MONO_8:
		for (c = 0; c < 32; c += 4) {
			es1371->dac[dac_nr].buffer_l[(pos + c) & 63] = es1371->dac[dac_nr].buffer_r[(pos + c) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr) ^ 0x80) << 8;
			es1371->dac[dac_nr].buffer_l[(pos + c + 1) & 63] = es1371->dac[dac_nr].buffer_r[(pos + c + 1) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 1) ^ 0x80) << 8;
			es1371->dac[dac_nr].buffer_l[(pos + c + 2) & 63] = es1371->dac[dac_nr].buffer_r[(pos + c + 2) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 2) ^ 0x80) << 8;
			es1371->dac[dac_nr].buffer_l[(pos + c + 3) & 63] = es1371->dac[dac_nr].buffer_r[(pos + c + 3) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 3) ^ 0x80) << 8;
			es1371->dac[dac_nr].addr += 4;

			es1371->dac[dac_nr].buffer_pos_end += 4;
			es1371->dac[dac_nr].count++;
			if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size) {
				es1371->dac[dac_nr].count = 0;
				es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
				break;
			}
		}
		break;
	case FORMAT_STEREO_8:
		for (c = 0; c < 16; c += 2) {
			es1371->dac[dac_nr].buffer_l[(pos + c) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr) ^ 0x80) << 8;
			es1371->dac[dac_nr].buffer_r[(pos + c) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 1) ^ 0x80) << 8;
			es1371->dac[dac_nr].buffer_l[(pos + c + 1) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 2) ^ 0x80) << 8;
			es1371->dac[dac_nr].buffer_r[(pos + c + 1) & 63] = (mem_readb_phys(es1371->dac[dac_nr].addr + 3) ^ 0x80) << 8;
			es1371->dac[dac_nr].addr += 4;

			es1371->dac[dac_nr].buffer_pos_end += 2;
			es1371->dac[dac_nr].count++;
			if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size) {
				es1371->dac[dac_nr].count = 0;
				es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
				break;
			}
		}
		break;
	case FORMAT_MONO_16:
		for (c = 0; c < 16; c += 2) {
			es1371->dac[dac_nr].buffer_l[(pos + c) & 63] = es1371->dac[dac_nr].buffer_r[(pos + c) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr);
			es1371->dac[dac_nr].buffer_l[(pos + c + 1) & 63] = es1371->dac[dac_nr].buffer_r[(pos + c + 1) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr + 2);
			es1371->dac[dac_nr].addr += 4;

			es1371->dac[dac_nr].buffer_pos_end += 2;
			es1371->dac[dac_nr].count++;
			if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size) {
				es1371->dac[dac_nr].count = 0;
				es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
				break;
			}
		}
		break;
	case FORMAT_STEREO_16:
		for (c = 0; c < 4; c++) {
			es1371->dac[dac_nr].buffer_l[(pos + c) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr);
			es1371->dac[dac_nr].buffer_r[(pos + c) & 63] = mem_readw_phys(es1371->dac[dac_nr].addr + 2);
//                        pclog("Fetch %02x %08x  %04x %04x\n", (pos+c) & 63, es1371->dac[dac_nr].addr, es1371->dac[dac_nr].buffer_l[(pos+c) & 63], es1371->dac[dac_nr].buffer_r[(pos+c) & 63]);
			es1371->dac[dac_nr].addr += 4;

			es1371->dac[dac_nr].buffer_pos_end++;
			es1371->dac[dac_nr].count++;
			if (es1371->dac[dac_nr].count > es1371->dac[dac_nr].size) {
				es1371->dac[dac_nr].count = 0;
				es1371->dac[dac_nr].addr = es1371->dac[dac_nr].addr_latch;
				break;
			}
		}
		break;
	}
}

static inline float low_fir_es1371(int dac_nr, int i, float NewSample) {
	static float x[2][2][128]; //input samples
	static int x_pos[2] = {0, 0};
	float out = 0.0;
	int read_pos;
	int n_coef;
	int pos = x_pos[dac_nr];

	x[dac_nr][i][pos] = NewSample;

	/*Since only 1/16th of input samples are non-zero, only filter those that
	  are valid.*/
	read_pos = (pos + 15) & (127 & ~15);
	n_coef = (16 - pos) & 15;

	while (n_coef < ES1371_NCoef) {
		out += low_fir_es1371_coef[n_coef] * x[dac_nr][i][read_pos];
		read_pos = (read_pos + 16) & (127 & ~15);
		n_coef += 16;
	}

	if (i == 1) {
		x_pos[dac_nr] = (x_pos[dac_nr] + 1) & 127;
		if (x_pos[dac_nr] > 127)
			x_pos[dac_nr] = 0;
	}

	return out;
}

static void es1371_next_sample_filtered(es1371_t *es1371, int dac_nr, int out_idx) {
	int out_l, out_r;
	int c;

	if ((es1371->dac[dac_nr].buffer_pos - es1371->dac[dac_nr].buffer_pos_end) >= 0) {
		es1371_fetch(es1371, dac_nr);
	}

	out_l = es1371->dac[dac_nr].buffer_l[es1371->dac[dac_nr].buffer_pos & 63];
	out_r = es1371->dac[dac_nr].buffer_r[es1371->dac[dac_nr].buffer_pos & 63];

	es1371->dac[dac_nr].filtered_l[out_idx] = (int)low_fir_es1371(dac_nr, 0, (float)out_l);
	es1371->dac[dac_nr].filtered_r[out_idx] = (int)low_fir_es1371(dac_nr, 1, (float)out_r);
	for (c = 1; c < 16; c++) {
		es1371->dac[dac_nr].filtered_l[out_idx + c] = (int)low_fir_es1371(dac_nr, 0, 0);
		es1371->dac[dac_nr].filtered_r[out_idx + c] = (int)low_fir_es1371(dac_nr, 1, 0);
	}

//        pclog("Use %02x %04x %04x\n", es1371->dac[dac_nr].buffer_pos & 63, es1371->dac[dac_nr].out_l, es1371->dac[dac_nr].out_r);

	es1371->dac[dac_nr].buffer_pos++;
//        pclog("Next sample %08x %08x  %08x\n", es1371->dac[dac_nr].buffer_pos, es1371->dac[dac_nr].buffer_pos_end, es1371->dac[dac_nr].curr_samp_ct);
}

//static FILE *es1371_f;//,*es1371_f2;

static void es1371_update(es1371_t *es1371) {
	int32_t l, r;

	l = (es1371->dac[0].out_l * es1371->dac[0].vol_l) >> 12;
	l += ((es1371->dac[1].out_l * es1371->dac[1].vol_l) >> 12);
	r = (es1371->dac[0].out_r * es1371->dac[0].vol_r) >> 12;
	r += ((es1371->dac[1].out_r * es1371->dac[1].vol_r) >> 12);

	l >>= 1;
	r >>= 1;

	l = (l * es1371->master_vol_l) >> 15;
	r = (r * es1371->master_vol_r) >> 15;

	if (l < -32768)
		l = -32768;
	else if (l > 32767)
		l = 32767;
	if (r < -32768)
		r = -32768;
	else if (r > 32767)
		r = 32767;

	for (; es1371->pos < sound_pos_global; es1371->pos++) {
		es1371->buffer[es1371->pos * 2] = l;
		es1371->buffer[es1371->pos * 2 + 1] = r;
	}
}

static void es1371_poll(void *p) {
	es1371_t *es1371 = (es1371_t *)p;

	timer_advance_u64(&es1371->dac[1].timer, es1371->dac[1].latch);

	es1371_update(es1371);

	if (es1371->int_ctrl & INT_DAC1_EN) {
		int frac = es1371->dac[0].ac & 0x7fff;
		int idx = es1371->dac[0].ac >> 15;
		int samp1_l = es1371->dac[0].filtered_l[idx];
		int samp1_r = es1371->dac[0].filtered_r[idx];
		int samp2_l = es1371->dac[0].filtered_l[(idx + 1) & 31];
		int samp2_r = es1371->dac[0].filtered_r[(idx + 1) & 31];

		es1371->dac[0].out_l = ((samp1_l * (0x8000 - frac)) + (samp2_l * frac)) >> 15;
		es1371->dac[0].out_r = ((samp1_r * (0x8000 - frac)) + (samp2_r * frac)) >> 15;
//                pclog("1Samp %i %i  %08x\n", es1371->dac[0].curr_samp_ct, es1371->dac[0].samp_ct, es1371->dac[0].ac);
		es1371->dac[0].ac += es1371->dac[0].vf;
		es1371->dac[0].ac &= ((32 << 15) - 1);
		if ((es1371->dac[0].ac >> (15 + 4)) != es1371->dac[0].f_pos) {
			es1371_next_sample_filtered(es1371, 0, es1371->dac[0].f_pos ? 16 : 0);
			es1371->dac[0].f_pos = (es1371->dac[0].f_pos + 1) & 1;

			es1371->dac[0].curr_samp_ct--;
			if (es1371->dac[0].curr_samp_ct < 0) {
//                                pclog("DAC1 IRQ\n");
				es1371->int_status |= INT_STATUS_DAC1;
				es1371_update_irqs(es1371);
				es1371->dac[0].curr_samp_ct = es1371->dac[0].samp_ct;
			}
		}
	}

	if (es1371->int_ctrl & INT_DAC2_EN) {
		int frac = es1371->dac[1].ac & 0x7fff;
		int idx = es1371->dac[1].ac >> 15;
		int samp1_l = es1371->dac[1].filtered_l[idx];
		int samp1_r = es1371->dac[1].filtered_r[idx];
		int samp2_l = es1371->dac[1].filtered_l[(idx + 1) & 31];
		int samp2_r = es1371->dac[1].filtered_r[(idx + 1) & 31];

		es1371->dac[1].out_l = ((samp1_l * (0x8000 - frac)) + (samp2_l * frac)) >> 15;
		es1371->dac[1].out_r = ((samp1_r * (0x8000 - frac)) + (samp2_r * frac)) >> 15;
//                pclog("2Samp %i %i  %08x\n", es1371->dac[1].curr_samp_ct, es1371->dac[1].samp_ct, es1371->dac[1].ac);
		es1371->dac[1].ac += es1371->dac[1].vf;
		es1371->dac[1].ac &= ((32 << 15) - 1);
		if ((es1371->dac[1].ac >> (15 + 4)) != es1371->dac[1].f_pos) {
			es1371_next_sample_filtered(es1371, 1, es1371->dac[1].f_pos ? 16 : 0);
			es1371->dac[1].f_pos = (es1371->dac[1].f_pos + 1) & 1;

			es1371->dac[1].curr_samp_ct--;
			if (es1371->dac[1].curr_samp_ct < 0) {
//                                pclog("DAC2 IRQ\n");
				es1371->int_status |= INT_STATUS_DAC2;
				es1371_update_irqs(es1371);
				es1371->dac[1].curr_samp_ct = es1371->dac[1].samp_ct;
			}
		}
	}
}

static void es1371_get_buffer(int32_t *buffer, int len, void *p) {
	es1371_t *es1371 = (es1371_t *)p;
	int c;

	es1371_update(es1371);

	for (c = 0; c < len * 2; c++)
		buffer[c] += (es1371->buffer[c] / 2);

	es1371->pos = 0;
}

static inline double sinc(double x) {
	return sin(M_PI * x) / (M_PI * x);
}

static void generate_es1371_filter() {
	/*Cutoff frequency = 1 / 32*/
	float fC = 1.0 / 32.0;
	float gain;
	int n;

	for (n = 0; n < ES1371_NCoef; n++) {
		/*Blackman window*/
		double w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double)(ES1371_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double)(ES1371_NCoef - 1)));
		/*Sinc filter*/
		double h = sinc(2.0 * fC * ((double)n - ((double)(ES1371_NCoef - 1) / 2.0)));

		/*Create windowed-sinc filter*/
		low_fir_es1371_coef[n] = w * h;
	}

	low_fir_es1371_coef[(ES1371_NCoef - 1) / 2] = 1.0;

	gain = 0.0;
	for (n = 0; n < ES1371_NCoef; n++)
		gain += low_fir_es1371_coef[n] / (float)N;

	gain /= 0.95;

	/*Normalise filter, to produce unity gain*/
	for (n = 0; n < ES1371_NCoef; n++)
		low_fir_es1371_coef[n] /= gain;
}

static void *es1371_init() {
	es1371_t *es1371 = malloc(sizeof(es1371_t));
	memset(es1371, 0, sizeof(es1371_t));

	sound_add_handler(es1371_get_buffer, es1371);

	es1371->card = pci_add(es1371_pci_read, es1371_pci_write, es1371);

	timer_add(&es1371->dac[1].timer, es1371_poll, es1371, 1);

	generate_es1371_filter();

	return es1371;
}

static void es1371_close(void *p) {
	es1371_t *es1371 = (es1371_t *)p;

	free(es1371);
}

static void es1371_speed_changed(void *p) {
	es1371_t *es1371 = (es1371_t *)p;

	es1371->dac[1].latch = (uint64_t)((double)TIMER_USEC * (1000000.0 / 48000.0));
}

void es1371_add_status_info_dac(es1371_t *es1371, char *s, int max_len, int dac_nr) {
	int ena = dac_nr ? INT_DAC2_EN : INT_DAC1_EN;
	char *dac_name = dac_nr ? "DAC2 (Wave)" : "DAC1 (MIDI)";
	char temps[128];

	if (es1371->int_ctrl & ena) {
		int format = dac_nr ? ((es1371->si_cr >> 2) & 3) : (es1371->si_cr & 3);
		double freq = 48000.0 * ((double)es1371->dac[dac_nr].vf / (32768.0 * 16.0));

		switch (format) {
		case FORMAT_MONO_8:snprintf(temps, 128, "%s format : 8-bit mono\n", dac_name);
			break;
		case FORMAT_STEREO_8:snprintf(temps, 128, "%s format : 8-bit stereo\n", dac_name);
			break;
		case FORMAT_MONO_16:snprintf(temps, 128, "%s format : 16-bit mono\n", dac_name);
			break;
		case FORMAT_STEREO_16:snprintf(temps, 128, "%s format : 16-bit stereo\n", dac_name);
			break;
		}

		strncat(s, temps, max_len);
		max_len -= strlen(temps);

		snprintf(temps, 128, "Playback frequency : %i Hz\n", (int)freq);
		strncat(s, temps, max_len);
	} else {
		snprintf(temps, max_len, "%s stopped\n", dac_name);
		strncat(s, temps, max_len);
	}
}

void es1371_add_status_info(char *s, int max_len, void *p) {
	es1371_t *es1371 = (es1371_t *)p;

	es1371_add_status_info_dac(es1371, s, max_len, 0);
	es1371_add_status_info_dac(es1371, s, max_len, 1);
}

device_t es1371_device =
	{
		"Ensoniq AudioPCI (ES1371)",
		0,
		es1371_init,
		es1371_close,
		NULL,
		es1371_speed_changed,
		NULL,
		es1371_add_status_info,
		NULL
	};
