📄 tl_sb.c
字号:
}/*** Probe for an SB*/static int sb_probe(void){ int retval; retval = parse_blaster_env(); /* if environment parse failed, try brute force autodetection */ if (-1 == retval) retval = sb_detect(); /* no blaster found */ if (-1 == retval) { thin_printf("thin@sb: no sound blaster found\n"); return -1; } if (dsp_reset()) { thin_printf("thin@sb: could not reset SB DSP: check BLASTER= variable\n"); return -1; } sb.dsp_version = dsp_getversion(); return 0;}/*** Interrupt handler for 8/16-bit audio */static void sb_isr(void){ uint32 address, offset; dma.count++; /* NOTE: this only works with 8-bit, as one-shot mode ** does not seem to work with 16-bit transfers */ if (false == dma.autoinit) { dsp_write(DSP_DMA_DAC_8BIT); dsp_write(LOW_BYTE(sb.buf_size - 1)); dsp_write(HIGH_BYTE(sb.buf_size - 1)); } /* indicate we got the interrupt */ inportb(dma.ackport); /* determine the current playback position */ address = inportb(dma.addrport); address |= (inportb(dma.addrport) << 8); address -= dos.offset; if (address < sb.buf_size) offset = sb.buf_chunk; else offset = 0; sb.callback(sb.buffer + offset, sb.buf_size); if (0 == thinlib_nearptr) dosmemput(sb.buffer + offset, sb.buf_chunk, dos.bufaddr + offset); /* acknowledge interrupt was taken */ if (sb.irq > 7) outportb(0xA0, 0x20); outportb(0x20, 0x20);}THIN_LOCKED_STATIC_FUNC(sb_isr)/* install the SB ISR */static void sb_setisr(void){ /* lock variables, routines */ THIN_LOCK_VAR(dma); THIN_LOCK_VAR(dos); THIN_LOCK_VAR(sb); THIN_LOCK_FUNC(sb_isr); if (sb.format & SB_FORMAT_16BIT) { dma.ackport = sb.baseio + DSP_DMA_ACK_16BIT; dma.addrport = DMA_ADDRBASE_16BIT + (4 * (sb.dma16 - 4)); } else { dma.ackport = sb.baseio + DSP_DMA_ACK_8BIT; dma.addrport = DMA_ADDRBASE_8BIT + (2 * sb.dma); } if (sb.irq < 8) { /* PIC 1 */ intr.irq_vector = 0x08 + sb.irq; intr.pic_rotateport = 0x20; intr.pic_maskport = 0x21; } else { /* PIC 2 */ intr.irq_vector = 0x70 + (sb.irq - 8); intr.pic_rotateport = 0xA0; intr.pic_maskport = 0xA1; } intr.irq_stopmask = 1 << (sb.irq & 7); intr.irq_startmask = ~intr.irq_stopmask; /* reset DMA count */ dma.count = 0; THIN_DISABLE_INTS(); outportb(intr.pic_maskport, inportb(intr.pic_maskport) | intr.irq_stopmask); _go32_dpmi_get_protected_mode_interrupt_vector(intr.irq_vector, &intr.old_interrupt); intr.new_interrupt.pm_offset = (int) sb_isr; intr.new_interrupt.pm_selector = _go32_my_cs(); _go32_dpmi_allocate_iret_wrapper(&intr.new_interrupt); _go32_dpmi_set_protected_mode_interrupt_vector(intr.irq_vector, &intr.new_interrupt); /* unmask the PIC, get things ready to roll */ outportb(intr.pic_maskport, inportb(intr.pic_maskport) & intr.irq_startmask); THIN_ENABLE_INTS();}/* remove SB ISR, restore old */static void sb_resetisr(void){ THIN_DISABLE_INTS(); outportb(intr.pic_maskport, inportb(intr.pic_maskport) | intr.irq_stopmask); _go32_dpmi_set_protected_mode_interrupt_vector(intr.irq_vector, &intr.old_interrupt); _go32_dpmi_free_iret_wrapper(&intr.new_interrupt); THIN_ENABLE_INTS();}/* allocate sound buffers */static int sb_allocate_buffers(int buf_size){ int double_bufsize; sb.buf_size = buf_size; if (sb.format & SB_FORMAT_16BIT) sb.buf_chunk = sb.buf_size * sizeof(uint16); else sb.buf_chunk = sb.buf_size * sizeof(uint8); double_bufsize = 2 * sb.buf_chunk; dos.buffer.size = (double_bufsize + 15) >> 4; if (_go32_dpmi_allocate_dos_memory(&dos.buffer)) return -1; /* calc linear address */ dos.bufaddr = dos.buffer.rm_segment << 4; if (sb.format & SB_FORMAT_16BIT) { dos.page = (dos.bufaddr >> 16) & 0xFF; dos.offset = (dos.bufaddr >> 1) & 0xFFFF; } else { dos.page = (dos.bufaddr >> 16) & 0xFF; dos.offset = dos.bufaddr & 0xFFFF; } if (thinlib_nearptr) { sb.buffer = (uint8 *) THIN_PHYS_ADDR(dos.bufaddr); } else { sb.buffer = malloc(double_bufsize); if (NULL == sb.buffer) return -1; } /* clear out the buffers */ if (sb.format & SB_FORMAT_16BIT) memset(sb.buffer, SILENCE_SIGNED, double_bufsize); else memset(sb.buffer, SILENCE_UNSIGNED, double_bufsize); if (0 == thinlib_nearptr) dosmemput(sb.buffer, double_bufsize, dos.bufaddr); return 0;}/* free them buffers */static void sb_free_buffers(void){ sb.callback = NULL; _go32_dpmi_free_dos_memory(&dos.buffer); if (0 == thinlib_nearptr) { free(sb.buffer); sb.buffer = NULL; } sb.buffer = NULL;}/* get rid of all things SB */void thin_sb_shutdown(void){ if (true == sb.initialized) { sb.initialized = false; dsp_reset(); sb_resetisr(); sb_free_buffers(); }}/* initialize sound bastard */int thin_sb_init(int *sample_rate, int *buf_size, int *format){#define CLAMP_RATE(in_rate, min_rate, max_rate) \ (in_rate < min_rate ? min_rate : \ (in_rate > max_rate ? max_rate : in_rate)) /* don't init twice! */ if (true == sb.initialized) return 0; memset(&sb, 0, sizeof(sb)); if (sb_probe()) return -1; /* try autoinit DMA first */ dma.autoinit = true; sb.format = (uint8) *format; /* determine which SB model we have, and act accordingly */ if (sb.dsp_version < DSP_VERSION_SB_15) { /* SB 1.0 */ sb.sample_rate = CLAMP_RATE(*sample_rate, 4000, 22050); sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO); dma.autoinit = false; } else if (sb.dsp_version < DSP_VERSION_SB_20) { /* SB 1.5 */ sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 22050); sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO); } else if (sb.dsp_version < DSP_VERSION_SB_PRO) { /* SB 2.0 */ sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100); sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO); } else if (sb.dsp_version < DSP_VERSION_SB16) { /* SB Pro */ if (sb.format & SB_FORMAT_STEREO) sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 22050); else sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100); sb.format &= ~SB_FORMAT_16BIT; } else { /* SB 16 */ sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100); } /* sanity check for 16-bit */ if ((sb.format & SB_FORMAT_16BIT) && ((uint8) INVALID == sb.dma16)) { sb.format &= ~SB_FORMAT_16BIT; thin_printf("thin@sb: 16-bit DMA channel not available, dropping to 8-bit\n"); } /* clamp buffer size to something sane */ if ((uint16) *buf_size > sb.sample_rate) { *buf_size = sb.sample_rate; thin_printf("thin@sb: buffer size too big, dropping to %d bytes\n", *buf_size); } /* allocate buffer / DOS memory */ if (sb_allocate_buffers(*buf_size)) { thin_printf("thin@sb: failed allocating sound buffers\n"); return -1; } /* set the new IRQ vector! */ sb_setisr(); sb.initialized = true; /* return the actual values */ *sample_rate = sb.sample_rate; *buf_size = sb.buf_size; *format = sb.format; return 0;}void thin_sb_stop(void){ if (true == sb.initialized) { if (sb.format & SB_FORMAT_16BIT) { dsp_write(DSP_DMA_PAUSE_16BIT); /* pause 16-bit DMA */ dsp_write(DSP_DMA_STOP_8BIT); dsp_write(DSP_DMA_PAUSE_16BIT); } else { dsp_write(DSP_DMA_PAUSE_8BIT); /* pause 8-bit DMA */ dsp_write(DSP_SPEAKER_OFF); } }}/* return time constant for older sound bastards */static uint8 get_time_constant(int rate){ return ((65536 - (256000000L / rate)) / 256);}static void init_samplerate(int rate){ if ((sb.format & SB_FORMAT_16BIT) || sb.dsp_version >= DSP_VERSION_SB16) { dsp_write(DSP_DMA_DAC_RATE); dsp_write(HIGH_BYTE(rate)); dsp_write(LOW_BYTE(rate)); } else { dsp_write(DSP_DMA_TIME_CONST); dsp_write(get_time_constant(rate)); }}/* set the sample rate */void thin_sb_setrate(int rate){ if (sb.format & SB_FORMAT_16BIT) { dsp_write(DSP_DMA_PAUSE_16BIT); /* pause 16-bit DMA */ init_samplerate(rate); dsp_write(DSP_DMA_CONT_16BIT); /* continue 16-bit DMA */ } else { dsp_write(DSP_DMA_PAUSE_8BIT); /* pause 8-bit DMA */ init_samplerate(rate); dsp_write(DSP_DMA_CONT_8BIT); /* continue 8-bit DMA */ } sb.sample_rate = rate;}/* start SB DMA transfer */static void start_transfer(void){ uint8 dma_mode, start_command, mode_command; int dma_length; dma.count = 0; dma_length = sb.buf_size * 2; if (true == dma.autoinit) { start_command = DSP_DMA_DAC_MODE; /* autoinit DMA */ dma_mode = DMA_AUTOINIT_MODE; } else { start_command = 0; dma_mode = DMA_ONESHOT_MODE; } /* things get a little bit nasty here, look out */ if (sb.format & SB_FORMAT_16BIT) { uint8 dma_base = sb.dma16 - 4; dma_mode |= dma_base; start_command |= DSP_DMA_START_16BIT; mode_command = DSP_DMA_SIGNED; outportb(DMA_MASKPORT_16BIT, DMA_STOPMASK_BASE | dma_base); outportb(DMA_MODEPORT_16BIT, dma_mode); outportb(DMA_CLRPTRPORT_16BIT, 0x00); outportb(DMA_ADDRBASE_16BIT + (4 * dma_base), LOW_BYTE(dos.offset)); outportb(DMA_ADDRBASE_16BIT + (4 * dma_base), HIGH_BYTE(dos.offset)); outportb(DMA_COUNTBASE_16BIT + (4 * dma_base), LOW_BYTE(dma_length - 1)); outportb(DMA_COUNTBASE_16BIT + (4 * dma_base), HIGH_BYTE(dma_length - 1)); outportb(dma16_ports[dma_base], dos.page); outportb(DMA_MASKPORT_16BIT, DMA_STARTMASK_BASE | dma_base); } else { dma_mode |= sb.dma; start_command |= DSP_DMA_START_8BIT; mode_command = DSP_DMA_UNSIGNED; outportb(DMA_MASKPORT_8BIT, DMA_STOPMASK_BASE + sb.dma); outportb(DMA_MODEPORT_8BIT, dma_mode); outportb(DMA_CLRPTRPORT_8BIT, 0x00); outportb(DMA_ADDRBASE_8BIT + (2 * sb.dma), LOW_BYTE(dos.offset)); outportb(DMA_ADDRBASE_8BIT + (2 * sb.dma), HIGH_BYTE(dos.offset)); outportb(DMA_COUNTBASE_8BIT + (2 * sb.dma), LOW_BYTE(dma_length - 1)); outportb(DMA_COUNTBASE_8BIT + (2 * sb.dma), HIGH_BYTE(dma_length - 1)); outportb(dma8_ports[sb.dma], dos.page); outportb(DMA_MASKPORT_8BIT, DMA_STARTMASK_BASE + sb.dma); } /* check stereo */ if (sb.format & SB_FORMAT_STEREO) mode_command |= DSP_DMA_STEREO; else mode_command |= DSP_DMA_MONO; init_samplerate(sb.sample_rate); /* start things going */ if ((sb.format & SB_FORMAT_16BIT) || sb.dsp_version >= DSP_VERSION_SB16) { dsp_write(start_command); dsp_write(mode_command); dsp_write(LOW_BYTE(sb.buf_size - 1)); dsp_write(HIGH_BYTE(sb.buf_size - 1)); } else { /* turn on speaker */ dsp_write(DSP_SPEAKER_ON); if (true == dma.autoinit) { dsp_write(DSP_DMA_BLOCK_SIZE); /* set buffer size */ dsp_write(LOW_BYTE(sb.buf_size - 1)); dsp_write(HIGH_BYTE(sb.buf_size - 1)); if (sb.dsp_version < DSP_VERSION_SB_20) dsp_write(DSP_DMA_DAC_AI_8BIT); /* low speed autoinit */ else dsp_write(DSP_DMA_DAC_HS_8BIT); } else { dsp_write(DSP_DMA_DAC_8BIT); dsp_write(LOW_BYTE(sb.buf_size - 1)); dsp_write(HIGH_BYTE(sb.buf_size - 1)); } }}/* start playing the output buffer */int thin_sb_start(sbmix_t fillbuf){ clock_t count; int projected_dmacount; /* make sure we really should be here... */ if (false == sb.initialized || NULL == fillbuf) return -1; /* stop any current processing */ thin_sb_stop(); /* set the callback routine */ sb.callback = fillbuf; /* calculate how many DMAs we should have in one second ** and scale it down just a tad */ projected_dmacount = (int) ((0.8 * sb.sample_rate) / sb.buf_size); if (projected_dmacount < 1) projected_dmacount = 1; /* get the transfer going, so we can ensure interrupts are firing */ start_transfer(); count = clock(); while ((clock() - count) < CLOCKS_PER_SEC && dma.count < projected_dmacount) ; /* spin */ if (dma.count < projected_dmacount) { if (true == dma.autoinit) { thin_printf("thin@sb: Autoinit DMA failed, trying one-shot mode.\n"); dsp_reset(); dma.autoinit = false; dma.count = 0; return (thin_sb_start(fillbuf)); } else { thin_printf("thin@sb: One-shot DMA mode failed, sound will not be heard.\n"); thin_printf("thin@sb: DSP version: %d.%d baseio: %X IRQ: %d DMA: %d High: %d\n", sb.dsp_version >> 8, sb.dsp_version & 0xFF, sb.baseio, sb.irq, sb.dma, sb.dma16); return -1; } } return 0;}/*** $Log: tl_sb.c,v $** Revision 1.11 2001/03/12 06:06:55 matt** better keyboard driver, support for bit depths other than 8bpp**** Revision 1.10 2001/02/19 03:38:32 matt** stereo buffer overrun**** Revision 1.9 2001/02/01 06:28:26 matt** thinlib now works under NT/2000**** Revision 1.8 2001/01/15 05:25:52 matt** i hate near pointers**** Revision 1.7 2000/12/17 21:49:24 matt** whose idea was it to have functions returning bool all over the place?**** Revision 1.6 2000/12/16 21:18:11 matt** thinlib cleanups**** Revision 1.5 2000/12/13 14:14:27 matt** DJGPP_USE_NEARPTR -> THINLIB_NEARPTR**** Revision 1.4 2000/12/11 12:32:25 matt** buffer allocation size miscalculation**** Revision 1.3 2000/11/25 20:27:48 matt** typo**** Revision 1.2 2000/11/05 16:32:36 matt** thinlib round 2**** Revision 1.1 2000/11/05 06:29:03 matt** initial revision***/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -