#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
};
