From: John Tsiombikas Date: Thu, 8 Nov 2018 06:24:17 +0000 (+0200) Subject: initial commit X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dos_sbtest;a=commitdiff_plain;h=6d4de53a835a8258ba05c83a2af38f27a0e92d21 initial commit --- 6d4de53a835a8258ba05c83a2af38f27a0e92d21 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d81386e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.d +*.swp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cb6a3d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +src = $(wildcard src/*.c) +ssrc = $(wildcard src/*.s) +asmsrc = $(wildcard src/*.asm) +obj = $(src:.c=.o) $(ssrc:.s=.o) $(asmsrc:.asm=.o) +dep = $(src:.c=.d) +bin = sbtest.exe + +ifeq ($(findstring COMMAND.COM, $(SHELL)), COMMAND.COM) + hostsys = dos +else + hostsys = unix + TOOLPREFIX = i586-pc-msdosdjgpp- +endif + +inc = -Isrc +opt = -O3 -ffast-math -fno-strict-aliasing +dbg = -g +warn = -pedantic -Wall -Wno-unused-function -Wno-unused-variable + +CC = $(TOOLPREFIX)gcc +AR = $(TOOLPREFIX)ar +CFLAGS = $(warn) -march=pentium $(dbg) $(opt) $(inc) +LDFLAGS = + +$(bin): $(obj) + $(CC) -o $@ -Wl,-Map=ld.map $(obj) $(LDFLAGS) + +%.o: %.asm + nasm -f coff -o $@ $< + +-include $(dep) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +%.d: %.c + @echo "gen dep $< -> $@" && $(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +.PHONY: cleandep + +ifeq ($(hostsys), dos) +clean: + del src\*.o + del $(bin) + +cleandep: + del src\*.d +else +clean: + rm -f $(obj) $(bin) + +cleandep: + rm -f $(dep) +endif diff --git a/src/au_sb.c b/src/au_sb.c new file mode 100644 index 0000000..ab12b9e --- /dev/null +++ b/src/au_sb.c @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include +#include "audio.h" +#include "au_sb.h" +#include "dma.h" + +#define IRQ_TO_INTR(x) ((x) + 8) + +/* PIC command and data ports */ +#define PIC1_CMD 0x20 +#define PIC1_DATA 0x21 +#define PIC2_CMD 0xa0 +#define PIC2_DATA 0xa1 +/* PIC operation command word 2 bits */ +#define OCW2_EOI (1 << 5) + +#define REG_MIXPORT (base_port + 0x4) +#define REG_MIXDATA (base_port + 0x5) +#define REG_RESET (base_port + 0x6) +#define REG_RDATA (base_port + 0xa) +#define REG_WDATA (base_port + 0xc) +#define REG_WSTAT (base_port + 0xc) +#define REG_RSTAT (base_port + 0xe) +#define REG_INTACK (base_port + 0xe) +#define REG_INT16ACK (base_port + 0xf) + +#define WSTAT_BUSY 0x80 +#define RSTAT_RDY 0x80 + +#define CMD_RATE 0x40 +#define CMD_SB16_OUT_RATE 0x41 +#define CMD_SB16_IN_RATE 0x42 +#define CMD_GET_VER 0xe1 + +/* start DMA playback/recording. combine with fifo/auto/input flags */ +#define CMD_START_DMA8 0xc0 +#define CMD_START_DMA16 0xb0 +#define CMD_FIFO 0x02 +#define CMD_AUTO 0x04 +#define CMD_INPUT 0x08 + +/* immediately pause/continue */ +#define CMD_PAUSE_DMA8 0xd0 +#define CMD_ENABLE_OUTPUT 0xd1 +#define CMD_DISABLE_OUTPUT 0xd3 +#define CMD_CONT_DMA8 0xd4 +#define CMD_PAUSE_DMA16 0xd5 +#define CMD_CONT_DMA16 0xd6 + +/* end the playback at the end of the current buffer */ +#define CMD_END_DMA16 0xd9 +#define CMD_END_DMA8 0xda + +/* transfer mode commands */ +#define CMD_MODE_SIGNED 0x10 +#define CMD_MODE_STEREO 0x20 + +/* mixer registers */ +#define MIX_IRQ_SEL 0x80 +#define MIX_DMA_SEL 0x81 + +#define VER_MAJOR(x) ((x) >> 8) +#define VER_MINOR(x) ((x) & 0xff) + +/* delay for about 1us */ +#define iodelay() outportb(0x80, 0) + +static void intr_handler(); +static void start_dma_transfer(uint32_t addr, int size); +static void write_dsp(unsigned char val); +static unsigned char read_dsp(void); +static void write_mix(unsigned char val, int reg); +static unsigned char read_mix(int reg); +static int get_dsp_version(void); +static int sb16_detect_irq(void); +static int sb16_detect_dma(void); +static const char *sbname(int ver); + +extern unsigned char low_mem_buffer[]; + +static int base_port; +static int irq, dma_chan, dma16_chan; +static int sb16; +static void *buffer; +static int xfer_mode; + +int sb_detect(void) +{ + int i, ver; + + for(i=0; i<6; i++) { + base_port = 0x200 + ((i + 1) << 4); + if(sb_reset_dsp() == 0) { + ver = get_dsp_version(); + sb16 = VER_MAJOR(ver) >= 4; + + if(sb16) { + if(sb16_detect_irq() == -1) { + printf("sb_detect: failed to configure IRQ\n"); + return 0; + } + if(sb16_detect_dma() == -1) { + printf("sb_detect: failed to configure DMA\n"); + return 0; + } + + printf("sb_detect: found %s (DSP v%d.%02d) at port %xh, irq %d, dma %d/%d\n", + sbname(ver), VER_MAJOR(ver), VER_MINOR(ver), + base_port, irq, dma_chan, dma16_chan); + + } else { + /* XXX for old sound blasters, hard-code to IRQ 5 DMA 1 for now */ + irq = 5; + dma_chan = 1; + dma16_chan = -1; + + printf("sb_detect: found %s (DSP v%d.%02d) at port %xh\n", sbname(ver), + VER_MAJOR(ver), VER_MINOR(ver), base_port); + printf("sb_detect: old sound blaster dsp. assuming: irq 5, dma 1\n"); + } + + return 1; + } + } + + return 0; +} + +int sb_reset_dsp(void) +{ + int i; + + outportb(REG_RESET, 1); + for(i=0; i<3; i++) iodelay(); + outportb(REG_RESET, 0); + + for(i=0; i<128; i++) { + if(inportb(REG_RSTAT) & RSTAT_RDY) { + if(inportb(REG_RDATA) == 0xaa) { + return 0; + } + } + } + + return -1; +} + +void sb_set_output_rate(int rate) +{ + if(sb16) { + write_dsp(CMD_SB16_OUT_RATE); + write_dsp(rate >> 8); + write_dsp(rate & 0xff); + } else { + int tcon = 256 - 1000000 / rate; + write_dsp(CMD_RATE); + write_dsp(tcon); + } +} + +void *sb_buffer(int *size) +{ + *size = 65536; + return buffer; +} + +void sb_start(int rate, int nchan) +{ + uint32_t addr; + int size; + _go32_dpmi_seginfo intr; + + /* for now just use the are after boot2. it's only used by the second stage + * loader and the VBE init code, none of which should overlap with audio playback. + * It's not necessary to use low memory. We can use up to the first 16mb for this, + * so if this becomes an issue, I'll make a DMA buffer allocator over 1mb. + * start the buffer from the next 64k boundary. + */ + addr = ((uint32_t)low_mem_buffer + 0xffff) & 0xffff0000; + buffer = (void*)addr; + + xfer_mode = CMD_MODE_SIGNED; + if(nchan > 1) { + xfer_mode |= CMD_MODE_STEREO; + } + + if(!(size = audio_callback(buffer, 65536))) { + return; + } + + intr.pm_offset = (intptr_t)intr_handler; + intr.pm_selector = _go32_my_cs(); + _disable(); + _go32_dpmi_set_protected_mode_interrupt_vector(IRQ_TO_INTR(irq), &intr); + _enable(); + + sb_set_output_rate(rate); + start_dma_transfer(addr, size); + write_dsp(CMD_ENABLE_OUTPUT); +} + +void sb_pause(void) +{ + write_dsp(CMD_PAUSE_DMA8); +} + +void sb_continue(void) +{ + write_dsp(CMD_CONT_DMA8); +} + +void sb_stop(void) +{ + write_dsp(CMD_END_DMA8); + write_dsp(CMD_DISABLE_OUTPUT); +} + +void sb_volume(int vol) +{ + /* TODO */ +} + +static void intr_handler() +{ + int size; + + /* ask for more data */ + if(!(size = audio_callback(buffer, 65536))) { + sb_stop(); + return; + } + start_dma_transfer((uint32_t)buffer, size); + + /* acknowledge the interrupt */ + inportb(REG_INTACK); + + if(irq > 7) { + outportb(PIC2_CMD, OCW2_EOI); + } + outportb(PIC1_CMD, OCW2_EOI); +} + +static void start_dma_transfer(uint32_t addr, int size) +{ + /* set up the next DMA transfer */ + dma_out(dma_chan, addr, size, DMA_SINGLE); + + /* program the DSP to accept the DMA transfer */ + write_dsp(CMD_START_DMA8 | CMD_FIFO); + write_dsp(xfer_mode); + size--; + write_dsp(size & 0xff); + write_dsp((size >> 8) & 0xff); +} + +static void write_dsp(unsigned char val) +{ + while(inportb(REG_WSTAT) & WSTAT_BUSY); + outportb(REG_WDATA, val); +} + +static unsigned char read_dsp(void) +{ + while((inportb(REG_RSTAT) & RSTAT_RDY) == 0); + return inportb(REG_RDATA); +} + +static void write_mix(unsigned char val, int reg) +{ + outportb(REG_MIXPORT, reg); + outportb(REG_MIXDATA, val); +} + +static unsigned char read_mix(int reg) +{ + outportb(REG_MIXPORT, reg); + return inportb(REG_MIXDATA); +} + +static int get_dsp_version(void) +{ + int major, minor; + + write_dsp(CMD_GET_VER); + major = read_dsp(); + minor = read_dsp(); + + return (major << 8) | minor; +} + +static int sb16_detect_irq(void) +{ + int i, irqsel; + static int irqtab[] = {2, 5, 7, 10}; + + irq = 0; + irqsel = read_mix(MIX_IRQ_SEL); + for(i=0; i<4; i++) { + if(irqsel & (1 << i)) { + irq = irqtab[i]; + break; + } + } + if(!irq) { + /* try to force IRQ 5 */ + write_mix(2, MIX_IRQ_SEL); /* bit1 selects irq 5 */ + + /* re-read to verify */ + irqsel = read_mix(MIX_IRQ_SEL); + if(irqsel != 2) { + return -1; + } + irq = 5; + } + + return irq; +} + +static int sb16_detect_dma(void) +{ + int i, dmasel, tmp; + static int dmatab[] = {0, 1, -1, 3, -1, 5, 6, 7}; + + dma_chan = -1; + dma16_chan = -1; + dmasel = read_mix(MIX_DMA_SEL); + for(i=0; i<4; i++) { + if(dmasel & (1 << i)) { + dma_chan = dmatab[i]; + break; + } + } + for(i=5; i<8; i++) { + if(dmasel & (1 << i)) { + dma16_chan = dmatab[i]; + break; + } + } + if(dma_chan == -1) { + /* try to force DMA 1 */ + dmasel |= 2; + } + if(dma16_chan == -1) { + /* try to force 16bit DMA 5 */ + dmasel |= 0x20; + } + + if(dma_chan == -1 || dma16_chan == -1) { + write_mix(dmasel, MIX_DMA_SEL); + + /* re-read to verify */ + tmp = read_mix(MIX_DMA_SEL); + if(tmp != dmasel) { + return -1; + } + dma_chan = 1; + dma16_chan = 5; + } + + return dma_chan; +} + +#define V(maj, min) (((maj) << 8) | (min)) + +static const char *sbname(int ver) +{ + int major = VER_MAJOR(ver); + int minor = VER_MINOR(ver); + + switch(major) { + case 1: + if(minor == 5) { + return "Sound Blaster 1.5"; + } + return "Sound Blaster 1.0"; + + case 2: + if(minor == 1 || minor == 2) { + return "Sound Blaster 2.0"; + } + break; + + case 3: + switch(minor) { + case 0: + return "Sound Blaster Pro"; + case 1: + case 2: + return "Sound Blaster Pro 2"; + case 5: + return "Gallant SC-6000"; + default: + break; + } + break; + + case 4: + switch(minor) { + case 4: + case 5: + return "Sound Blaster 16"; + case 11: + return "Sound Blaster 16 SCSI-2"; + case 12: + return "Sound Blaster AWE 32"; + case 13: + return "Sound Blaster ViBRA16C"; + case 16: + return "Sound Blaster AWE 64"; + default: + break; + } + break; + } + + return "Unknown Sound Blaster"; +} diff --git a/src/au_sb.h b/src/au_sb.h new file mode 100644 index 0000000..d437ff7 --- /dev/null +++ b/src/au_sb.h @@ -0,0 +1,42 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef AU_SB_H_ +#define AU_SB_H_ + +/* returns true (nonzero) if a sound blaster DSP is detected in the ISA bus + * and sets the internal base_port so that subsequent calls can find it + */ +int sb_detect(void); + +/* returns 0 for success, non-zero if the DSP isn't responding at the currently + * selected base port + */ +int sb_reset_dsp(void); + +void *sb_buffer(int *size); + +void sb_set_output_rate(int rate); + +void sb_start(int rate, int nchan); +void sb_pause(void); +void sb_continue(void); +void sb_stop(void); + +void sb_volume(int vol); + +#endif /* AU_SB_H_ */ diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 0000000..49b8a8d --- /dev/null +++ b/src/audio.c @@ -0,0 +1,88 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include "audio.h" +#include "au_sb.h" + +struct audrv { + void *(*get_buffer)(int *size); + void (*start)(int rate, int nchan); + void (*pause)(void); + void (*cont)(void); + void (*stop)(void); + void (*volume)(int vol); +}; + +static struct audrv drv; + +static audio_callback_func cbfunc; +static void *cbcls; + +void audio_init(void) +{ + if(sb_detect()) { + drv.get_buffer = sb_buffer; + drv.start = sb_start; + drv.pause = sb_pause; + drv.cont = sb_continue; + drv.stop = sb_stop; + drv.volume = sb_volume; + return; + } + + printf("No supported audio device detected\n"); +} + +void audio_set_callback(audio_callback_func func, void *cls) +{ + cbfunc = func; + cbcls = cls; +} + +int audio_callback(void *buf, int sz) +{ + if(!cbfunc) { + return 0; + } + return cbfunc(buf, sz, cbcls); +} + +void audio_play(int rate, int nchan) +{ + drv.start(rate, nchan); +} + +void audio_pause(void) +{ + drv.pause(); +} + +void audio_resume(void) +{ + drv.cont(); +} + +void audio_stop(void) +{ + drv.stop(); +} + +void audio_volume(int vol) +{ + drv.volume(vol); +} diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 0000000..9b528c2 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,35 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef AUDIO_H_ +#define AUDIO_H_ + +typedef int (*audio_callback_func)(void *buffer, int size, void *cls); + +void audio_init(void); + +void audio_set_callback(audio_callback_func func, void *cls); +int audio_callback(void *buf, int sz); + +void audio_play(int rate, int nchan); +void audio_pause(void); +void audio_resume(void); +void audio_stop(void); + +void audio_volume(int vol); + +#endif /* AUDIO_H_ */ diff --git a/src/dma.c b/src/dma.c new file mode 100644 index 0000000..9c6ad67 --- /dev/null +++ b/src/dma.c @@ -0,0 +1,132 @@ +#include "dma.h" + +/* 8bit DMA ports */ +#define DMA_0_ADDR 0x00 +#define DMA_0_COUNT 0x01 +#define DMA_1_ADDR 0x02 +#define DMA_1_COUNT 0x03 +#define DMA_2_ADDR 0x04 +#define DMA_2_COUNT 0x05 +#define DMA_3_ADDR 0x06 +#define DMA_3_COUNT 0x07 +/* 16bit DMA ports */ +#define DMA_4_ADDR 0xc0 +#define DMA_4_COUNT 0xc2 +#define DMA_5_ADDR 0xc4 +#define DMA_5_COUNT 0xc6 +#define DMA_6_ADDR 0xc8 +#define DMA_6_COUNT 0xca +#define DMA_7_ADDR 0xcc +#define DMA_7_COUNT 0xce + +#define DMA_ADDR(c) \ + ((c < 4) ? DMA_0_ADDR + ((c) << 1) : (DMA_4_ADDR + ((c) << 2))) +#define DMA_COUNT(c) \ + ((c < 4) ? DMA_0_COUNT + ((c) << 1) : (DMA_4_COUNT + ((c) << 2))) + +#define DMA8_MASK 0x0a +#define DMA8_MODE 0x0b +#define DMA8_CLR_FLIPFLOP 0x0c +#define DMA8_RESET 0x0d +#define DMA8_MASK_RST 0x0e +#define DMA8_RMASK 0x0f +#define DMA16_MASK 0xd4 +#define DMA16_MODE 0xd6 +#define DMA16_CLR_FLIPFLOP 0xd8 +#define DMA16_RESET 0xda +#define DMA16_MASK_RST 0xdc +#define DMA16_RMASK 0xde + +#define DMA_MASK(c) ((c) < 4 ? DMA8_MASK : DMA16_MASK) +#define DMA_MODE(c) ((c) < 4 ? DMA8_MODE : DMA16_MODE) +#define DMA_CLR_FLIPFLOP(c) ((c) < 4 ? DMA8_CLR_FLIPFLOP : DMA16_CLR_FLIPFLOP) +#define DMA_RESET(c) ((c) < 4 ? DMA8_RESET : DMA16_RESET) +#define DMA_MASK_RST(c) ((c) < 4 ? DMA8_MASK_RST : DMA16_MASK_RST) +#define DMA_RMASK(c) ((c) < 4 ? DMA8_RMASK : DMA16_RMASK) + +#define DMA_0_PAGE 0x87 +#define DMA_1_PAGE 0x83 +#define DMA_2_PAGE 0x81 +#define DMA_3_PAGE 0x82 +#define DMA_4_PAGE 0x8f +#define DMA_5_PAGE 0x8b +#define DMA_6_PAGE 0x89 +#define DMA_7_PAGE 0x8a + +#define MODE_CHAN(x) ((x) & 3) +#define MODE_WRITE 0x04 +#define MODE_READ 0x08 +#define MODE_AUTO 0x10 +#define MODE_DECR 0x20 +#define MODE_SINGLE 0x40 +#define MODE_BLOCK 0x80 +#define MODE_CASCADE 0xc0 + +#define MASK_CHAN(x) ((x) & 3) +#define MASK_DISABLE 0x04 + +#define RMASK_CHAN(x) (1 << ((x) & 3)) + +#define IS_16BIT(c) ((c) >= 4) + +static void dma_io(int chan, uint32_t phyaddr, int size, unsigned int flags, unsigned int dir); +static inline void mask(int chan); +static inline void unmask(int chan); + +static int page_port[] = { + DMA_0_PAGE, DMA_1_PAGE, DMA_2_PAGE, DMA_3_PAGE, + DMA_4_PAGE, DMA_5_PAGE, DMA_6_PAGE, DMA_7_PAGE +}; + +void dma_out(int chan, uint32_t phyaddr, int size, unsigned int flags) +{ + dma_io(chan, phyaddr, size, flags, MODE_READ); +} + +void dma_in(int chan, uint32_t phyaddr, int size, unsigned int flags) +{ + dma_io(chan, phyaddr, size, flags, MODE_WRITE); +} + +static void dma_io(int chan, uint32_t phyaddr, int size, unsigned int flags, unsigned int dir) +{ + unsigned int mode; + int addr_port, count_port; + + addr_port = DMA_ADDR(chan); + count_port = DMA_COUNT(chan); + + mask(chan); + outportb(DMA_CLR_FLIPFLOP(chan), 0); + + /* single / block / cascade */ + mode = ((flags & 3) << 6) | MODE_CHAN(chan); + if(flags & DMA_DECR) mode |= MODE_DECR; + if(flags & DMA_AUTO) mode |= MODE_AUTO; + outportb(DMA_MODE(chan), mode); + + if(IS_16BIT(chan)) { + phyaddr >>= 1; + size >>= 1; + } + + outportb(addr_port, phyaddr & 0xff); + outportb(addr_port, (phyaddr >> 8) & 0xff); + outportb(page_port[chan], (phyaddr >> 16) & 0xff); + + size--; + outportb(count_port, size & 0xff); + outportb(count_port, (size >> 8) & 0xff); + + unmask(chan); +} + +static inline void mask(int chan) +{ + outportb(DMA_MASK(chan), MASK_CHAN(chan) | MASK_DISABLE); +} + +static inline void unmask(int chan) +{ + outportb(DMA_MASK(chan), MASK_CHAN(chan)); +} diff --git a/src/dma.h b/src/dma.h new file mode 100644 index 0000000..ca12ad1 --- /dev/null +++ b/src/dma.h @@ -0,0 +1,17 @@ +#ifndef DMA_H_ +#define DMA_H_ + +#include + +enum { + DMA_SINGLE = 0x01, + DMA_BLOCK = 0x02, + DMA_CASCADE = DMA_SINGLE | DMA_BLOCK, + DMA_DECR = 0x08, + DMA_AUTO = 0x10 +}; + +void dma_out(int chan, uint32_t phyaddr, int size, unsigned int flags); +void dma_in(int chan, uint32_t phyaddr, int size, unsigned int flags); + +#endif /* DMA_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..44f41b8 --- /dev/null +++ b/src/main.c @@ -0,0 +1,43 @@ +#include +#include +#include "audio.h" + +static int au_callback(void *buffer, int size, void *cls); + +/* defined in sndsamples.s */ +extern signed char snd_click[]; +extern int snd_click_size; + +int main(int argc, char **argv) +{ + audio_init(); + audio_set_callback(au_callback, 0); + + audio_play(22050, 1); + + return 0; +} + +/* snd_click_size is < 65536 so we can just throw it all at once in there */ +static int au_callback(void *buffer, int size, void *cls) +{ + int i; + signed char *ptr = buffer; + signed char *src = snd_click; + /* + for(i=0; i