在DOSBox的Sound Blaster设备上播放.wav文件

时间:2016-12-28 09:30:50

标签: audio assembly wav x86-16 microprocessors

我想在assembly / 8086 / masm / dosbox中制作一个程序,将键盘变成各种乐器,所以我需要能够播放一些.wav文件以产生所需的声音。我知道beep char和通过向pc扬声器(端口41h,42h和61h)发送频率来产生声音,但两种方式显然无法让我到那里。

我四处搜索,发现我需要使用int 21h来打开文件,了解.wav格式以及使用Sound Blaster进行声音编程的知识。

不幸的是我找不到任何有用的文档,说明如何在Dosbox(或一般)中使用Sound Blaster,如果你可以帮我解决我在dosbox上播放.wav文件的问题,或者你有任何问题变通方法我都是耳朵(更准确的眼睛)。

1 个答案:

答案 0 :(得分:5)

  

这是一个播放特定WAV文件的演示程序(以避免将RIFF解析器引入已经太长的SO代码。   该程序已经在DOSBox中进行了测试,但是在不同的配置上可能会出现很多问题。

     

最后,我被迫将代码分成两个答案   这是第2部分

<强> dsp.asm

.8086
.MODEL SMALL

 FORMAT_MONO     EQU 00h
 FORMAT_STEREO   EQU 20h
 FORMAT_SIGNED   EQU 10h
 FORMAT_UNSIGNED EQU 00h


_CODE SEGMENT PARA PUBLIC 'CODE' USE16
 ASSUME CS:_CODE


 ResetDSP:
  push ax
  push dx

  ;Set reset bit

  mov dx, REG_DSP_RESET
  mov al, 01h
  out dx, al

  ;Wait 3 us

  in al, 80h
  in al, 80h
  in al, 80h

  ;Clear reset bit

  xor al, al
  out dx, al

  ;Poll BS until bit 7 is set

  mov dx, REG_DSP_READ_BS

 _rd_poll_bs:
  in al, dx
  test al, 80h
 jz _rd_poll_bs

  ;Poll data until 0aah

  mov dx, REG_DSP_READ

 _rd_poll_data:
  in al, dx
  cmp al, 0aah
 jne _rd_poll_data

  pop dx
  pop ax
  ret



 ;AL = command/data
 WriteDSP:
  push dx
  push ax

  mov dx, REG_DSP_WRITE_BS

 _wd_poll:
  in al, dx
  test al, 80h
 jz _wd_poll

  pop ax

  mov dx, REG_DSP_WRITE_DATA
  out dx, al

  pop dx
  ret


 ;Return AL 
 ReadDSP:
  push dx

  mov dx, REG_DSP_READ_BS

 _rdd_poll:
  in al, dx
  test al, 80h
 jz _rdd_poll

  pop ax

  mov dx, REG_DSP_READ
  in al, dx

  pop dx
  ret


 ;AX = sampling
 SetSampling:
  push dx

  xchg al, ah

  push ax

  mov al, DSP_SET_SAMPLING_OUTPUT
  call WriteDSP

  pop ax

  call WriteDSP

  mov al, ah
  call WriteDSP

  pop dx
  ret


 ;Starts a playback

 ;AX = Sampling
 ;BL = Mode
 ;CX = Size
 StartPlayback:

  ;Set sampling

  call SetSampling

  ;Start playback command

  mov al, DSP_DMA_16_OUTPUT_AUTO
  call WriteDSP
  mov al, bl            
  call WriteDSP                            ;Format 
  mov al, cl
  call WriteDSP                            ;Size (Low)
  mov al, ch   
  call WriteDSP                            ;Size (High)

  ret

 ;Stops the playback

 StopPlayback:
   push ax

   mov al, DSP_STOP_DMA_16
   call WriteDSP

   pop ax
   ret

_CODE ENDS

<强> buffer.asm

.8086
.MODEL SMALL


 ;Block size is 1/100 of a second at 44100 samplings per seconds

 BLOCK_SIZE      EQU 44100 / 100 * 2

 ;Buffer size allocated, it is twice the BLOCK_SIZE because there are two blocks.
 ;Size is doubled again so that we are sure to find an area that doesn't cross a
 ;64KiB boundary
 ;Total buffer size is about 3.5 KiB

 BUFFER_SIZE     EQU  BLOCK_SIZE * 2 * 2

_DATI SEGMENT PARA PUBLIC 'DATA' USE16

 ;This is the buffer

 buffer            db BUFFER_SIZE DUP(0)


 bufferOffset      dw OFFSET buffer
 bufferSegment     dw _DATI

_DATI ENDS


_CODE SEGMENT PARA PUBLIC 'CODE' USE16
 ASSUME CS:_CODE, DS:_DATI


 ;Allocate a buffer of size BLOCK_SIZE * 2 that doesn't cross
 ;a physical 64KiB
 ;This is achieved by allocating TWICE as much space and than
 ;Aligning the segment on 64KiB if necessary


 AllocateBuffer:
  push bx
  push cx
  push ax
  push dx

  ;Compute linear address of the buffer

  mov bx, _DATI
  shr bx, 0ch
  mov cx, _DATI
  shl cx, 4
  add cx, OFFSET buffer
  adc bx, 0                                 ;BX:CX = Linear address



  ;Does it starts at 64KiB?

  test cx, cx
 jz _ab_End                                ;Yes, we are fine

  mov dx, cx
  mov ax, bx

  ;Find next start of 64KiB

  xor dx, dx
  inc ax

  push ax
  push dx

  ;Check if next boundary is after our buffer

  sub dx, cx
  sub ax, bx

  cmp dx, BUFFER_SIZE / 2

  pop dx
  pop ax

 jae _ab_end



  mov bx, dx
  and bx, 0fh
  mov WORD PTR [bufferOffset], bx

  mov bx, ax
  shl bx, 0ch
  shr dx, 04h
  or bx, dx
  mov WORD PTR [bufferSegment], bx

 _ab_end:
  clc

  pop dx
  pop ax
  pop cx
  pop bx

  ret


 ;Free the buffer

 FreeBufferIfAllocated:

  ;Nothing to do

  ret




_CODE ENDS