#include <dos.h>\r
#include <conio.h>\r
#include "audio.h"\r
-#include "au_sb.h"\r
+#include "audrv.h"\r
#include "dpmi.h"\r
#include "dma.h"\r
#include "intr.h"\r
\r
+#define BUFSIZE 16384\r
+\r
#define REG_MIXPORT (base_port + 0x4)\r
#define REG_MIXDATA (base_port + 0x5)\r
#define REG_RESET (base_port + 0x6)\r
#define MIX_SB16_VOICE_R 0x33\r
#define MIX_SB16_IRQ_SEL 0x80\r
#define MIX_SB16_DMA_SEL 0x81\r
+#define MIX_SB16_INTSTAT 0x82\r
+\r
+#define INTSTAT_DMA8 0x01\r
+#define INTSTAT_DMA16 0x02\r
+#define INTSTAT_MPU401 0x04\r
\r
#define VER_MAJOR(x) ((x) >> 8)\r
#define VER_MINOR(x) ((x) & 0xff)\r
/* delay for about 1us */\r
#define iodelay() outp(0x80, 0)\r
\r
+\r
+static int reset_dsp(void);\r
+static void *sb_buffer(int *size);\r
+static void set_output_rate(int rate);\r
+static void start(int rate, int bits, int nchan);\r
+static void pause(void);\r
+static void cont(void);\r
+static void stop(void);\r
+static int isplaying(void);\r
+static void setvolume(int ctl, int vol);\r
+static int getvolume(int ctl);\r
+\r
static void INTERRUPT intr_handler();\r
static void start_dsp4(int bits, unsigned int mode, int num_samples);\r
static void start_dsp(int nchan, int num_samples);\r
static void write_dsp(unsigned char val);\r
static unsigned char read_dsp(void);\r
-static void write_mix(unsigned char val, int reg);\r
+static void write_mix(int reg, unsigned char val);\r
static unsigned char read_mix(int reg);\r
static int get_dsp_version(void);\r
static int dsp4_detect_irq(void);\r
static void print_dsp_info(void);\r
static const char *sbname(int ver);\r
\r
+\r
+static const struct audrv sbdrv = {\r
+ start,\r
+ pause,\r
+ cont,\r
+ stop,\r
+ setvolume,\r
+ getvolume,\r
+ isplaying\r
+};\r
+\r
+\r
static int base_port;\r
static int irq, dma_chan, dma16_chan, dsp_ver;\r
static int dsp4;\r
-static void *buffer;\r
-static int xfer_mode;\r
\r
-static int isplaying;\r
-static int cur_bits, cur_mode;\r
-static int curblk;\r
+static void *buffer[2];\r
+static volatile int wrbuf;\r
+\r
+static volatile int playing;\r
+static int cur_bits, cur_mode, auto_init;\r
\r
static void (INTERRUPT *prev_intr_handler)();\r
\r
\r
-int sb_detect(void)\r
+int sb_detect(struct audrv *drv)\r
{\r
int i;\r
char *env;\r
if((env = getenv("BLASTER"))) {\r
dma16_chan = -1;\r
if(sscanf(env, "A%x I%d D%d H%d", &base_port, &irq, &dma_chan, &dma16_chan) >= 3) {\r
- if(sb_reset_dsp() == 0) {\r
+ if(reset_dsp() == 0) {\r
dsp_ver = get_dsp_version();\r
dsp4 = VER_MAJOR(dsp_ver) >= 4 ? 1 : 0;\r
print_dsp_info();\r
+ *drv = sbdrv;\r
return 1;\r
}\r
} else {\r
\r
for(i=0; i<6; i++) {\r
base_port = 0x200 + ((i + 1) << 4);\r
- if(sb_reset_dsp() == 0) {\r
+ if(reset_dsp() == 0) {\r
dsp_ver = get_dsp_version();\r
dsp4 = VER_MAJOR(dsp_ver) >= 4 ? 1 : 0;\r
\r
printf("sb_detect: old sound blaster dsp. assuming: irq 5, dma 1\n");\r
}\r
print_dsp_info();\r
+ *drv = sbdrv;\r
\r
return 1;\r
}\r
return 0;\r
}\r
\r
-int sb_reset_dsp(void)\r
+static int reset_dsp(void)\r
{\r
int i;\r
\r
return -1;\r
}\r
\r
-void sb_set_output_rate(int rate)\r
+static void set_output_rate(int rate)\r
{\r
if(dsp4) {\r
write_dsp(DSP4_OUT_RATE);\r
}\r
}\r
\r
-void *sb_buffer(int *size)\r
-{\r
- *size = 32768;\r
- return (char*)buffer + curblk * 32768;\r
-}\r
-\r
-void sb_start(int rate, int bits, int nchan)\r
+static void start(int rate, int bits, int nchan)\r
{\r
uint16_t seg, pmsel;\r
- uint32_t addr;\r
- int size;\r
+ uint32_t addr, next64k;\r
+ int size, num_samples;\r
\r
- if(!buffer) {\r
- /* allocate a 64k-aligned 64k buffer in low memory */\r
- if(!(seg = dpmi_alloc(65536 * 2 / 16, &pmsel))) {\r
- fprintf(stderr, "sb_start: failed to allocate DMA buffer\n");\r
+ if(!buffer[1]) {\r
+ /* allocate a buffer in low memory that doesn't cross 64k boundaries */\r
+ if(!(seg = dpmi_alloc(BUFSIZE * 2 / 16, &pmsel))) {\r
+ fprintf(stderr, "SB start: failed to allocate DMA buffer\n");\r
return;\r
}\r
\r
- printf("DBG allocated seg: %x, addr: %lx\n", (unsigned int)seg,\r
- (unsigned long)seg << 4);\r
+ addr = (uint32_t)seg << 4;\r
+ next64k = (addr + 0x10000) & 0xffff0000;\r
+ if(next64k - addr < BUFSIZE) {\r
+ addr = next64k;\r
+ }\r
\r
- addr = ((uint32_t)(seg << 4) + 0xffff) & 0xffff0000;\r
- printf("DBG aligned: %lx\n", (unsigned long)addr);\r
- buffer = (void*)addr;\r
- } else {\r
- addr = (uint32_t)buffer;\r
+ buffer[0] = (void*)addr;\r
+ buffer[1] = (void*)(addr + BUFSIZE / 2);\r
+ wrbuf = 0;\r
}\r
\r
- if(!(size = audio_callback(buffer, 32768))) {\r
+ /* Auto-init playback mode:\r
+ * fill the whole buffer with data, program the DMA for BUFSIZE transfer,\r
+ * and program the DSP for BUFSIZE/2 transfer. We'll get an interrupt in the\r
+ * middle, while the DSP uses the upper half, and we'll refill the bottom half.\r
+ * Then continue ping-ponging the two halves of the buffer until we run out of\r
+ * data.\r
+ */\r
+ auto_init = 1;\r
+\r
+ /* TODO: if size <= BUFSIZE, do a single transfer instead */\r
+ wrbuf = 0;\r
+ if(!(size = audio_callback(buffer[wrbuf], BUFSIZE))) {\r
return;\r
}\r
- curblk = 1;\r
+ addr = (uint32_t)buffer[wrbuf];\r
+ num_samples = bits == 8 ? size : size / 2;\r
\r
_disable();\r
if(!prev_intr_handler) {\r
prev_intr_handler = _dos_getvect(IRQ_TO_INTR(irq));\r
}\r
- printf("setting interrupt vector: %d\n", IRQ_TO_INTR(irq));\r
_dos_setvect(IRQ_TO_INTR(irq), intr_handler);\r
_enable();\r
\r
unmask_irq(irq);\r
\r
cur_bits = bits;\r
- cur_mode = 0; /* TODO */\r
+ cur_mode = bits == 8 ? 0 : DSP4_MODE_SIGNED;\r
if(nchan > 1) {\r
cur_mode |= DSP4_MODE_STEREO;\r
}\r
\r
write_dsp(DSP_ENABLE_OUTPUT);\r
- dma_out(dma_chan, addr, size, DMA_SINGLE);\r
- sb_set_output_rate(rate);\r
- start_dsp4(cur_bits, cur_mode, size);\r
- isplaying = 1;\r
+ dma_out(cur_bits == 8 ? dma_chan : dma16_chan, addr, BUFSIZE, DMA_SINGLE | DMA_AUTO);\r
+ set_output_rate(rate);\r
+ start_dsp4(cur_bits, cur_mode, num_samples / 2);\r
+ playing = 1;\r
}\r
\r
static void start_dsp4(int bits, unsigned int mode, int num_samples)\r
{\r
unsigned char cmd = bits == 8 ? DSP4_START_DMA8 : DSP4_START_DMA16;\r
- assert(bits == 8 || bits == 16);\r
-\r
- /*cmd |= DSP4_AUTO | DSP4_FIFO;*/\r
+ cmd |= DSP4_AUTO | DSP4_FIFO;\r
\r
/* program the DSP to start the DMA transfer */\r
write_dsp(cmd);\r
/* TODO */\r
}\r
\r
-void sb_pause(void)\r
+static void pause(void)\r
{\r
write_dsp(cur_bits == 8 ? DSP_PAUSE_DMA8 : DSP_PAUSE_DMA16);\r
}\r
\r
-void sb_continue(void)\r
+static void cont(void)\r
{\r
write_dsp(cur_bits == 8 ? DSP_CONT_DMA8 : DSP_CONT_DMA16);\r
}\r
\r
-void sb_stop(void)\r
+static void stop(void)\r
{\r
write_dsp(DSP_DISABLE_OUTPUT);\r
\r
_dos_setvect(IRQ_TO_INTR(irq), prev_intr_handler);\r
_enable();\r
\r
- /* no need to stop anything if we're in single-cycle mode */\r
- if(cur_mode & DSP4_AUTO) {\r
- write_dsp(cur_bits == 8 ? DSP_END_DMA8 : DSP_END_DMA16);\r
- }\r
- isplaying = 0;\r
+ write_dsp(cur_bits == 8 ? DSP_END_DMA8 : DSP_END_DMA16);\r
+ playing = 0;\r
}\r
\r
-int sb_isplaying(void)\r
+static int isplaying(void)\r
{\r
- return isplaying;\r
+ return playing;\r
}\r
\r
-void sb_volume(int vol)\r
+static void setvolume(int ctl, int vol)\r
{\r
- /* TODO */\r
+ unsigned char val;\r
+\r
+ if(VER_MAJOR(dsp_ver) >= 4) {\r
+ /* DSP 4.x - SB16 */\r
+ val = vol & 0xf8;\r
+ if(ctl == AUDIO_PCM) {\r
+ write_mix(MIX_SB16_VOICE_L, val);\r
+ write_mix(MIX_SB16_VOICE_R, val);\r
+ } else {\r
+ write_mix(MIX_SB16_MASTER_L, val);\r
+ write_mix(MIX_SB16_MASTER_R, val);\r
+ }\r
+\r
+ } else if(VER_MAJOR(dsp_ver) >= 3) {\r
+ /* DSP 3.x - SBPro */\r
+ val = vol & 0xe0;\r
+ val |= val >> 4;\r
+\r
+ if(ctl == AUDIO_PCM) {\r
+ write_mix(MIX_SBPRO_VOICE, val);\r
+ } else {\r
+ write_mix(MIX_SBPRO_MASTER, val);\r
+ }\r
+\r
+ } else {\r
+ /* DSP 2.x - SB 2.0 */\r
+ if(ctl == AUDIO_PCM) {\r
+ val = (vol >> 5) & 6;\r
+ write_mix(MIX_VOICE, val);\r
+ } else {\r
+ val = (vol >> 4) & 0xe;\r
+ write_mix(MIX_MASTER, val);\r
+ }\r
+ }\r
+}\r
+\r
+static int getvolume(int ctl)\r
+{\r
+ int left, right;\r
+ unsigned char val;\r
+\r
+ if(VER_MAJOR(dsp_ver) >= 4) {\r
+ /* DSP 4.x - SB16 */\r
+ int lreg, rreg;\r
+ if(ctl == AUDIO_PCM) {\r
+ lreg = MIX_SB16_VOICE_L;\r
+ rreg = MIX_SB16_VOICE_R;\r
+ } else { /* MASTER or DEFAULT */\r
+ lreg = MIX_SB16_MASTER_L;\r
+ rreg = MIX_SB16_MASTER_R;\r
+ }\r
+\r
+ val = read_mix(lreg);\r
+ left = (val & 0xf8) | (val >> 5);\r
+ val = read_mix(rreg);\r
+ right = (val & 0xf8) | (val >> 5);\r
+\r
+ } else if(VER_MAJOR(dsp_ver) >= 3) {\r
+ /* DSP 3.x - SBPro */\r
+ val = read_mix(ctl == AUDIO_PCM ? MIX_SBPRO_VOICE : MIX_SBPRO_MASTER);\r
+\r
+ /* left is top 3 bits, duplicate twice(-ish) */\r
+ left = (val & 0xe0) | ((val >> 3) & 0x1c) | (val >> 6);\r
+ /* right is top 3 bits of the lower nibble */\r
+ right = (val << 4) | ((val << 1) & 0x1c) | ((val >> 2) & 3);\r
+\r
+ } else {\r
+ int volume;\r
+\r
+ /* DSP 2.x - SB 2.0 */\r
+ if(ctl == AUDIO_PCM) {\r
+ /* voice is in bits 1 and 2 */\r
+ val = read_mix(MIX_VOICE);\r
+ volume = (val << 5) | ((val << 3) & 0x30) | ((val << 1) & 0xc) | ((val >> 1) & 3);\r
+ } else {\r
+ /* master is in the 3 top bits of the lower nibble */\r
+ val = read_mix(MIX_MASTER);\r
+ volume = (val << 4) | ((val << 1) & 0x1c) | ((val >> 2) & 3);\r
+ }\r
+ return volume;\r
+ }\r
+\r
+ return (left + right) >> 1;\r
}\r
\r
static void INTERRUPT intr_handler()\r
{\r
int size;\r
- void *bptr = (unsigned char*)buffer + curblk * 32768;\r
+ void *bptr = buffer[wrbuf];\r
+ uint32_t addr = (uint32_t)bptr;\r
\r
- curblk = (curblk + 1) & 1;\r
+ wrbuf ^= 1;\r
\r
/* ask for more data */\r
- if(!(size = audio_callback(bptr, 32768))) {\r
- sb_stop();\r
- } else {\r
- dma_out(dma_chan, (uint32_t)bptr, size, DMA_SINGLE);\r
- start_dsp4(cur_bits, cur_mode, size);\r
+ if(!(size = audio_callback(bptr, BUFSIZE / 2))) {\r
+ stop();\r
}\r
\r
/* acknowledge the interrupt */\r
- inp(REG_INTACK);\r
+ if(cur_bits == 8) {\r
+ inp(REG_INTACK);\r
+ } else {\r
+ unsigned char istat = read_mix(MIX_SB16_INTSTAT);\r
+ if(istat & INTSTAT_DMA16) {\r
+ inp(REG_INT16ACK);\r
+ }\r
+ }\r
\r
if(irq > 7) {\r
outp(PIC2_CMD, OCW2_EOI);\r
return inp(REG_RDATA);\r
}\r
\r
-static void write_mix(unsigned char val, int reg)\r
+static void write_mix(int reg, unsigned char val)\r
{\r
outp(REG_MIXPORT, reg);\r
outp(REG_MIXDATA, val);\r
}\r
if(!irq) {\r
/* try to force IRQ 5 */\r
- write_mix(2, MIX_SB16_IRQ_SEL); /* bit1 selects irq 5 */\r
+ write_mix(MIX_SB16_IRQ_SEL, 2); /* bit1 selects irq 5 */\r
\r
/* re-read to verify */\r
irqsel = read_mix(MIX_SB16_IRQ_SEL);\r
}\r
\r
if(dma_chan == -1 || dma16_chan == -1) {\r
- write_mix(dmasel, MIX_SB16_DMA_SEL);\r
+ write_mix(MIX_SB16_DMA_SEL, dmasel);\r
\r
/* re-read to verify */\r
tmp = read_mix(MIX_SB16_DMA_SEL);\r
\r
static void print_dsp_info(void)\r
{\r
- int master[2], voice[2];\r
- int val;\r
-\r
printf("sb_detect: %s (DSP v%d.%02d) port %xh, irq %d", sbname(dsp_ver),\r
VER_MAJOR(dsp_ver), VER_MINOR(dsp_ver), base_port, irq);\r
if(VER_MAJOR(dsp_ver) >= 4) {\r
} else {\r
printf(" dma %d\n", dma_chan);\r
}\r
-\r
- if(VER_MAJOR(dsp_ver) >= 4) {\r
- master[0] = 100 * (int)(read_mix(MIX_SB16_MASTER_L) >> 3) / 31;\r
- master[1] = 100 * (int)(read_mix(MIX_SB16_MASTER_R) >> 3) / 31;\r
- voice[0] = 100 * (int)(read_mix(MIX_SB16_VOICE_L) >> 3) / 31;\r
- voice[1] = 100 * (int)(read_mix(MIX_SB16_VOICE_R) >> 3) / 31;\r
- } else if(VER_MAJOR(dsp_ver) >= 3) {\r
- val = read_mix(MIX_SBPRO_MASTER);\r
- master[0] = 100 * (val >> 5) / 7;\r
- master[1] = 100 * ((val >> 1) & 7) / 7;\r
- val = read_mix(MIX_SBPRO_VOICE);\r
- voice[0] = 100 * (val >> 5) / 7;\r
- voice[1] = 100 * ((val >> 1) & 7) / 7;\r
- } else {\r
- val = read_mix(MIX_MASTER);\r
- master[0] = master[1] = 100 * ((val >> 1) & 7) / 7;\r
- val = read_mix(MIX_VOICE);\r
- voice[0] = voice[1] = 100 * ((val >> 1) & 3) / 3;\r
- }\r
- printf(" mixer: master(%d|%d) voice(%d|%d)\n", master[0], master[1],\r
- voice[0], voice[1]);\r
}\r
\r
#define V(maj, min) (((maj) << 8) | (min))\r