Welcome to TFM's WAVE SB Page.




TFM's Home Page  TFM's Sound Blaster PAGE   


PIO
DMA
DOUBLE BUFFERED DMA

DIGITAL SAMPLING

To create a sound by it's wave you must supply the amplitude setting on every part of the wave. The samples (specific amplitude values) must be taken frequently enough to allow an accurate picture of the wave.

These samples of the position of the wave allow the wave to be re-created. To get an idear of the rates, CD quality is about 44kHz (44,000 samples/sec) and telephone quality is about 4kHz.

The SB uses two different methods for creating sound.

(i) DMA (Dirrect memory access)
(ii) PIO (Programable I/O)


PIO

Writting to the DSP
Playing digital samples uses the Sound Blasters DSP (Digital Signal Processor.) The method to do this is:-

1) Read the DSP's WRITE BUFFER STATUS port (2xCh) until bit 7 = 0
2) Write the value to the WRITE COMMAND/DATA port (2xCh)

The x in port addresses means that it depends on the base port (set by jumpers)
e.g. if the SB is at 220h then 2xA = 22A


Dirrect access to the DSP's DAC is achieved by using command 10h this means that to output a byte to the DAC you first send 10 to the DSP, and then send the value of the sample to the DSP.

It is the controling program's job to ensure that the samples are sent at regular intervals. The timing can be achieved by using the PIT (Programable Interrupt Timer)

DMA



The DMA chip of the PC works in parrallel with the CPU, it's jobs are:-
(i) To move data from memory to hardware
(ii) To move data from hardware to memory
(iii) To move data from hardware to other hardware
(iv) To move data from memory to other memory areas (Not supported by many systems)

The sound blaster can use a DMA channel to move the waveform data from memory into it's DAC (playback through DMA), or from it's ADC to memory (recording through the DMA.)

Setting up to play through the DMA is not very difficult. You simply have to load the correct values into the ports on the DMA chip, and on the SB. The method to do this is:-

(i) Load the wave data into memory.
(ii) Set up the interupt handler.
(iii) Set up the DMA chip.
(iv) Turn on the speaker (command D1).
(v) Setting up the sampling rate.
(vi) Set up the Transfer to the SB.

Load the wave data into memory

The block must not cross a page boundary, becasue the DMA chip is unable to cross a page boundary (this means that the segment for the start and end blocks must be the same) The block must also be located in the first megabyte of memory. This also limmits transfers to 64K, if you need more use multiple transfers.

Set up the interrupt handler

When the block has all been played the sound blaster causes an interrupt. The interrupt generated depends upon the jumper settings on the card (IRQ)
IRQ   Interrupt number
 2            0Ah
 3            0Bh
 5            0Dh
 7            0Fh
The interrupt must be hooked (either by dirrect manipulation of the interrupt vector table) or by using DOS to change them (most High levels languages have functions to do this (except BASIC!!))

When an interrupt vector points to your code, your code will be called every time the interrupt is called, you MUST PRESERVE ALL REGISTERS and return with an IRET instruction (high level languages normaly do this if you define the function as an interrupt handling function.)

The interrupt must
1) Acknowledge the DSP interrupt by reading the DATA AVAILABLE port (2xEh) once.
2) If there are more blocks to transfer then set them up
3) Output value 20h (EOI) to the interrupt controller port 20h

Don't forget to restore the interrupt vector when you quit the program

Set up the DMA chip

1) Calculate the 20 bit address of the memory buffer you are using
where Base Address = Segment * 16 + Offset
eg 1234h:5678h = 179B8h
2) Send the value 05h to port 0Ah (mask off channel 1)
3) Send the value 00h to port 0Ch (clear the internal DMA flip/flop)
4) Send the value 49h to port 0Bh (for playback) or 45h to port 0Bh (for recording)
5) Write the LSB (bits 0 -> 7) of the 20 bit memory address to port 02h
6) Write the MSB (bits 8 -> 15) of the 20 bit memory address to ort 02h
7) Write the Page (bits 16 -> 19) of the 20 bit memory address to port 83h
8) Send the LSB of DATA_LENGTH to port 03h
9) Send the MSB of DATA_LENGTH to port 03h
10) Send the value 01h to port 0Ah (enable channel 1)

Setting up the sampling rate

First send 040 to the DSP then the LSB of time constant, then MSB of the time constant
where the time constant = 256 - 1000000 / frequency

Set up the Transfer to the SB

Send the format of the data to the DSP
14h    8 bit                                          
74h    4 bit ADPCM                        (max 12 KHz)
75h    4 bit ADPCM with reference byte    (max 12 KHz)
76h    2.6 bit ADPCM                      (max 13 KHz)
77h    2.6 bit ADPCM with reference byte  (max 13 KHz)
16h    2 bit ADPCM                        (max 11 KHz)
17h    2 bit ADPCM with reference byte    (max 11 KHz)

Other DSP commands
Then send the number of bytes to transfer -1 to the SB. It is a two byte word, sent with the LSB first.



DOUBLE BUFFERED DMA


The above system is fine for small blocks of data, but when you want to play a continus digital waveform, which is larger than 64K you have problems at a sampling rate of 44KHz, this will only last about 2 seconds. The above method of loading the new data when the interrupt occurs does overcome this, but however fast you are you will always have a click when the DSP runns out of data.

The answer is to use double buffered DMA.
(i)     Set up a block of memory which doesn't cross a page
        boundary (must be an even size as well.) Load the data
(ii)    Hook the interrupt
(iii)   Set up the DMA chip to send this to the SB, using the
        full buffer size
(iv)    Set the SB up to recieve a block with half the number
        of samples -1 (command 048)
(v)     Turn on the speaker (command D1)
(vi)    Set the SB up to use auto-initialize DMA (command 01C)
        (see other DSP commands)


When an interrupt occurs
(i)     There is no need to re-program the SB it simply starts
        again at the begining of the buffer.
(ii)    Load the half of the buffer which has just been played
        with new data.
(iii)   Acknowledge the DSP interrupt by reading the DATA
        AVAILABLE port (2xEh) once.
(iv) Output value 20h (EOI) to the interrupt controller port 20h At the end (i) Disable the speaker (command D3) (ii) Halt DMA transfer (command D0) (iii) End auto-initialize mode (command DA) (iv) Unhook the interrupt
Basically this pseudo code implements a pair of continuos buffers, when the SB reaches the end of the first buffer it causes an interrupt, it then auto-initializes itself and reads the same number of bytes from the DMA chip again.

The DMA chip sees the situation a little differently it sends the bytes from the buffer. It never knows that the SB reaches the end of the semi-buffer. It simply sends data whenever it is requested. When it reaches the end of it's buffer it wraps around to the start.

If no new data was ever loaded the situation would just lead to a repeating piece of sound, but because you get an interrupt every time half of the buffer is played, you are able to re-load the other half of the buffer.