fixed by preloading samples. the audio callback is being called from the
authorJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 1 Feb 2019 05:17:13 +0000 (07:17 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 1 Feb 2019 05:17:13 +0000 (07:17 +0200)
context of the interrupt handler, and can't perform file I/O

src/au_sb.c
src/audio.c
src/audio.h
src/aufile.h
src/auwav.c
src/main.c

index a1ea830..0daa7c0 100644 (file)
@@ -86,13 +86,12 @@ static const char *sbname(int ver);
 static int base_port;\r
 static int irq, dma_chan, dma16_chan, dsp_ver;\r
 static int dsp4;\r
-static int xfer_mode;\r
 \r
 static void *buffer[2];\r
-static int wrbuf;\r
+static volatile int wrbuf;\r
 \r
-static int isplaying;\r
-static int cur_bits, cur_mode;\r
+static volatile int isplaying;\r
+static int cur_bits, cur_mode, auto_init;\r
 \r
 static void (INTERRUPT *prev_intr_handler)();\r
 \r
@@ -190,11 +189,11 @@ void *sb_buffer(int *size)
 void sb_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[0]) {\r
-               /* allocate a 64k-aligned buffer in low memory */\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
@@ -203,18 +202,34 @@ void sb_start(int rate, int bits, int nchan)
                printf("DBG allocated seg: %x, addr: %lx\n", (unsigned int)seg,\r
                                (unsigned long)seg << 4);\r
 \r
-               addr = ((uint32_t)(seg << 4) + 0xffff) & 0xffff0000;\r
-               printf("DBG aligned: %lx\n", (unsigned long)addr);\r
+               addr = (uint32_t)seg << 4;\r
+               next64k = (addr + 0x10000) & 0xffff0000;\r
+               if(next64k - addr < BUFSIZE) {\r
+                       addr = next64k;\r
+               }\r
+\r
+               printf("DBG aligned: %lx (mid: %lx)\n", (unsigned long)addr, (unsigned long)(addr + BUFSIZE / 2));\r
                buffer[0] = (void*)addr;\r
                buffer[1] = (void*)(addr + BUFSIZE / 2);\r
                wrbuf = 0;\r
        }\r
 \r
-       if(!(size = audio_callback(buffer[wrbuf], BUFSIZE / 2))) {\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
        addr = (uint32_t)buffer[wrbuf];\r
-       wrbuf ^= 1;\r
+       num_samples = bits == 8 ? size : size / 2;\r
 \r
        _disable();\r
        if(!prev_intr_handler) {\r
@@ -233,18 +248,16 @@ void sb_start(int rate, int bits, int nchan)
        }\r
 \r
        write_dsp(DSP_ENABLE_OUTPUT);\r
-       dma_out(bits == 8 ? dma_chan : dma16_chan, addr, size, DMA_SINGLE);\r
+       dma_out(cur_bits == 8 ? dma_chan : dma16_chan, addr, BUFSIZE, DMA_SINGLE | DMA_AUTO);\r
        sb_set_output_rate(rate);\r
-       start_dsp4(cur_bits, cur_mode, bits == 8 ? size : size / 2);\r
+       start_dsp4(cur_bits, cur_mode, num_samples / 2);\r
        isplaying = 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
@@ -280,10 +293,7 @@ void sb_stop(void)
        _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
+       write_dsp(cur_bits == 8 ? DSP_END_DMA8 : DSP_END_DMA16);\r
        isplaying = 0;\r
 }\r
 \r
@@ -308,21 +318,20 @@ static void INTERRUPT intr_handler()
        /* ask for more data */\r
        if(!(size = audio_callback(bptr, BUFSIZE / 2))) {\r
                sb_stop();\r
-       } else {\r
-               if(cur_bits == 8) {\r
-                       dma_out(dma_chan, addr, size, DMA_SINGLE);\r
-                       start_dsp4(cur_bits, cur_mode, size);\r
-               } else {\r
-                       dma_out(dma16_chan, addr, size, DMA_SINGLE);\r
-                       start_dsp4(cur_bits, cur_mode, size / 2);\r
-               }\r
        }\r
 \r
        /* acknowledge the interrupt */\r
        if(cur_bits == 8) {\r
                inp(REG_INTACK);\r
        } else {\r
-               inp(REG_INT16ACK);\r
+               /*\r
+               unsigned char istat;\r
+               outp(REG_MIXPORT, 0x82);\r
+               istat = inp(REG_MIXDATA);\r
+               if(istat & 2) {\r
+               */\r
+                       inp(REG_INT16ACK);\r
+               //}\r
        }\r
 \r
        if(irq > 7) {\r
index 88b8400..51a3521 100644 (file)
@@ -17,7 +17,7 @@ static struct audrv drv;
 static audio_callback_func cbfunc;\r
 static void *cbcls;\r
 \r
-void audio_init(void)\r
+int audio_init(void)\r
 {\r
        if(sb_detect()) {\r
                drv.get_buffer = sb_buffer;\r
@@ -27,10 +27,11 @@ void audio_init(void)
                drv.stop = sb_stop;\r
                drv.volume = sb_volume;\r
                drv.isplaying = sb_isplaying;\r
-               return;\r
+               return 0;\r
        }\r
 \r
-       printf("No supported audio device detected\n");\r
+       fprintf(stderr, "No supported audio device detected\n");\r
+       return -1;\r
 }\r
 \r
 void audio_set_callback(audio_callback_func func, void *cls)\r
@@ -49,6 +50,7 @@ int audio_callback(void *buf, int sz)
 \r
 void audio_play(int rate, int bits, int nchan)\r
 {\r
+       printf("play %d samples/s, %d bits, %s\n", rate, bits, nchan == 1 ? "mono" : "stereo");\r
        drv.start(rate, bits, nchan);\r
 }\r
 \r
index c522134..9268627 100644 (file)
@@ -3,7 +3,7 @@
 \r
 typedef int (*audio_callback_func)(void *buffer, int size, void *cls);\r
 \r
-void audio_init(void);\r
+int audio_init(void);\r
 \r
 void audio_set_callback(audio_callback_func func, void *cls);\r
 int audio_callback(void *buf, int sz);\r
index faca4c8..d5ef5e4 100644 (file)
@@ -4,6 +4,7 @@
 struct au_file {\r
        int rate, bits, chan;\r
        void *data;\r
+       unsigned long size;\r
 \r
        FILE *fp;\r
        void (*close)(struct au_file*);\r
index eca5a76..6e1afc1 100644 (file)
@@ -66,6 +66,7 @@ int au_open_wav(struct au_file *au)
        au->rate = fmt.rate;\r
        au->bits = fmt.sample_bytes * 8;\r
        au->chan = fmt.nchan;\r
+       au->size = pb->size;\r
        au->data = pb;\r
 \r
        au->close = close_wav;\r
index 9a1b262..4395af6 100644 (file)
@@ -1,16 +1,22 @@
 #include <stdio.h>\r
+#include <stdlib.h>\r
 #include <string.h>\r
 #include "aufile.h"\r
 #include "audio.h"\r
 \r
+#define DBG_PRELOAD\r
+\r
 static int play_file(const char *fname);\r
+static int cbfunc(void *buf, int size, void *cls);\r
 static void print_usage(const char *argv0);\r
 \r
 int main(int argc, char **argv)\r
 {\r
        int i;\r
 \r
-       play_file("namarie.wav");       /* TODO remove */\r
+       if(audio_init() == -1) {\r
+               return 1;\r
+       }\r
 \r
        for(i=1; i<argc; i++) {\r
                if(argv[i][0] == '-') {\r
@@ -28,6 +34,9 @@ int main(int argc, char **argv)
        return 0;\r
 }\r
 \r
+static unsigned char *dbg_samples;\r
+static int dbg_cur_offs;\r
+\r
 static int play_file(const char *fname)\r
 {\r
        struct au_file *au;\r
@@ -36,13 +45,56 @@ static int play_file(const char *fname)
                return -1;\r
        }\r
 \r
+       audio_set_callback(cbfunc, au);\r
+\r
+#ifdef DBG_PRELOAD\r
+       if(!(dbg_samples = malloc(au->size))) {\r
+               perror("failed to preload samples into memory");\r
+               return -1;\r
+       }\r
+       dbg_cur_offs = 0;\r
+       if(au_read(au, dbg_samples, au->size) < au->size) {\r
+               perror("failed to preload samples into memory");\r
+               return -1;\r
+       }\r
+#endif\r
+\r
        audio_play(au->rate, au->bits, au->chan);\r
        while(audio_isplaying());\r
 \r
        au_close(au);\r
+#ifdef DBG_PRELOAD\r
+       free(dbg_samples);\r
+#endif\r
        return 0;\r
 }\r
 \r
+#ifdef DBG_PRELOAD\r
+static int cbfunc(void *buf, int size, void *cls)\r
+{\r
+       struct au_file *au = cls;\r
+\r
+       if(dbg_cur_offs + size > au->size) {\r
+               size = au->size - dbg_cur_offs;\r
+       }\r
+       if(size <= 0) return 0;\r
+\r
+       memcpy(buf, dbg_samples + dbg_cur_offs, size);\r
+       dbg_cur_offs += size;\r
+       return size;\r
+}\r
+#else\r
+static int cbfunc(void *buf, int size, void *cls)\r
+{\r
+       int rd;\r
+\r
+       if((rd = au_read(cls, buf, size)) <= 0) {\r
+               return 0;\r
+       }\r
+       return rd;\r
+}\r
+#endif\r
+\r
 static void print_usage(const char *argv0)\r
 {\r
        printf("Usage: %s [options] <file1> <file2> ... <filen>\n", argv0);\r