📄 tl_sb.c
字号:
sb.dma = detect_dma(false); if ((uint8) INVALID == sb.dma) return -1; /* may or may not exist */ sb.dma16 = detect_dma(true); return 0;}/*** 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("thinlib.sb: no sound blaster found\n"); return -1; } if (dsp_reset()) { thin_printf("thinlib.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 int 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.user_data, sb.buffer + offset, sb.buf_size); /* if we haven't enabled near pointers, we've written to a double ** buffer, so transfer it to low DOS memory area */ 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); return 0;}THIN_LOCKED_STATIC_FUNC(sb_isr)/* install the SB ISR */static void sb_setisr(void){ THIN_DISABLE_INTS(); thin_int_install(SB_IRQVEC(sb.irq), sb_isr); /* enable IRQ */ thin_irq_enable(sb.irq); THIN_ENABLE_INTS();}static void sb_restoreisr(void){ THIN_DISABLE_INTS(); /* restore IRQ to previous state */ thin_irq_restore(sb.irq); thin_int_remove(SB_IRQVEC(sb.irq)); 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_STEREO)// sb.buf_size *= 2; 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_PHYSICAL_ADDR(dos.bufaddr); } else { sb.buffer = malloc(double_bufsize); if (NULL == sb.buffer) return -1; } /* clear out the buffers */ if (sb.format & SB_FORMAT_SIGNED) 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 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_restoreisr(); 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; /* lock variables, routines */ THIN_LOCK_VAR(dma); THIN_LOCK_VAR(dos); THIN_LOCK_VAR(sb); THIN_LOCK_FUNC(sb_isr); 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("thinlib.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("thinlib.sb: buffer size too big, dropping to %d bytes\n", *buf_size); } /* allocate buffer / DOS memory */ if (sb_allocate_buffers(*buf_size)) { thin_printf("thinlib.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; /* reset DMA count */ 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; 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); dma.ackport = sb.baseio + DSP_DMA_ACK_16BIT; dma.addrport = DMA_ADDRBASE_16BIT + (4 * (sb.dma16 - 4)); } else { dma_mode |= sb.dma; start_command |= DSP_DMA_START_8BIT; 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); dma.ackport = sb.baseio + DSP_DMA_ACK_8BIT; dma.addrport = DMA_ADDRBASE_8BIT + (2 * sb.dma); } /* check signed/unsigned */ if (sb.format & SB_FORMAT_SIGNED) mode_command = DSP_DMA_SIGNED; else mode_command = DSP_DMA_UNSIGNED; /* 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)); } }}/* TODO: this gets totally wacked when we change the timer rate!!! *//* start playing the output buffer */int thin_sb_start(sbmix_t fillbuf, void *user_data){ 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; sb.user_data = user_data; /* 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("thinlib.sb: Autoinit DMA failed, trying one-shot mode.\n"); dsp_reset(); dma.autoinit = false; dma.count = 0; return (thin_sb_start(fillbuf, user_data)); } else { thin_printf("thinlib.sb: One-shot DMA mode failed, sound will not be heard.\n"); thin_printf("thinlib.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: $*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -