Digital Sound (Signal) Processing

Sound Blaster (1989) - Sim Wong Hoo and Creative Labs

Capability to record and reproduce any wave form

DAC - digital to analog convertor - playback
ADC - analog to digital convertor - recording

Sample size: bits per sample (8 or 16)
Sampling rate: number of samples per second
Number of channels: 1- mono. 2 -stereo

Brand Playback Record
SB 1.0, 1.5 8bit 22.5 Khz mono 8bit 11.25 KHz mono
SB 2.0 8bit 44.1 KHz mono 8bit 22.05 KHz mono
SB Pro 8bit 44.1 KHz mono 8bit 44.1 KHz Mono
8bit 22.5 KHz stereo
SB 16 8bit and 16bit 44.1 KHz mono and stereo 8bit and 16bit 44.1 KHz mono and stereo

DMA - Direct Memory Access - allows to transfer data between RAM and external
device bypassing CPU: 8bit channels - 0 to 3, 16bit channels 4 -7

Use interrupts to inform when the full or half of the data have been sent ("ping-pong" technique).

How ? The are DOS examples (int, WORD = 16 bits, needs accomodation)

Starting DMA:

INT dma_address [8] = {0x00,0x02,0x04,0x06,0xC0,0xC4,0xC8,0xCC};
INT dma_count [8] = {0x01,0x03,0x05,0x07,0xC2,0xC6,0xCA,0xCE};
INT dma_page [8] = {0x87,0x83,0x81,0x82,0x88,0x8B,0x89,0x8A};
INT dma_status [2] = {0x08,0xD0};   /* status register [read] */
INT dma_command [2] = {0x08,0xD0};  /* command register [write] */
INT dma_request [2] = {0x09,0xD2};  /* release DMA request */
INT dma_chmask [2] = {0x0A,0xD4};   /* mask channels individually */
INT dma_mode [2] = {0x0B,0xD6};     /* transfer mode */
INT dma_flipflop [2] = {0x0C,0xD8}; /* address-counter-flipflop */
INT dma_masterclr[2] = {0x0D,0xDA}; /* reset controller */
INT dma_temp [2] = {0x0D,0xDA};     /* temporary register */
INT dma_maskclr [2] = {0x0E,0xDC};  /* free all channels */
INT dma_mask [2] = {0x0F,0xDE};     /* completely mask channels */

/**********************************************************************/
/* dma_SetChannel : prepare DMA channel for transfer                  */
/**------------------------------------------------------------------**/
/* input : iChan : channel number (0-7)                               */
/* lpMem : memory address                                             */
/* uSize : number of bytes to be transferred                          */
/* bMode : transfer mode                                              */
/* bAlign : DMA_BIT8 or DMA_BIT16 transfer                            */
/**------------------------------------------------------------------**/
/* Info : To make 16 bit transfers possible, the linear address       */
/* passed to the DMA controller must be multiplied by 2.              */
/* In 16 bit transfers, up to 128K of data can be transferred.        */
/**------------------------------------------------------------------**/
/* M.G. 20/04/95                                                      */
/**********************************************************************/
VOID dma_SetChannel( INT iChan, LPVOID lpMem, WORD uSize, BYTE bMode )
{
	WORD uAddress;
	BYTE bPage;
	iChan &= 0x0007; /* Max.  8 DMA channels */
	dma_SetMask( iChan ); /*-block channel */
	/* DMA transferred 1 byte more than specified! */
	uSize = uSize ? uSize - 1 : 0; /* transfer min. 1 byte (0==1) */
	/*- create linear 20 bit address --------------------------------*/
	if( iChan <= 3 ) /* 8Bit DMA */
	{ /* address = lower 16 bit of the 20 bit address */
		uAddress = ( WORD ) (((((LONG) lpMem ) & 0xFFFF0000L ) >> 12 ) + ((( LONG ) lpMem ) & 0xFFFFL));
		/* page = top 4 bit of the 20 bit address */
		bPage = ( BYTE) (((((( LONG) lpMem ) & 0xFFFF0000L ) >> 12 ) + ((( LONG ) lpMem ) & 0xFFFFL)) >> 16);
	}
	else /* 16 bit DMA */
	{ /* address = lower 16 bit of the 20 bit address */
		uAddress = ( WORD ) ((((( LONG) lpMem ) & 0xFFFF0000L ) >> 13 ) + (((( LONG ) lpMem ) & 0xFFFFL) >> 1 ));
		/* page = top 4 Bit of the 20 bit address */
		bPage = ( BYTE) (((((( LONG) lpMem ) & 0xFFFF0000L ) >> 12 ) + ((( LONG ) lpMem ) & 0xFFFFL)) >> 16);
		bPage &= ( BYTE ) 0xFE;
		uSize /= 2; /* WORDs, not BYTEs, are counted! */
	}
	outp(dma_mode[iChan/4], bMode | ( iChan & MODE_CHANNELMSK ) );
	dma_ClrFlipFlop( iChan ); /* clear address/counter flipflop and...*/
			/* send address to the DMA controller (LO/HI-BYTE) */
	outp( dma_address[ iChan ], LOBYTE( uAddress ) );
	outp( dma_address[ iChan ], HIBYTE( uAddress ) );
	outp( dma_page[ iChan ], bPage); /* set memory page */
	dma_ClrFlipFlop( iChan ); /* clear address/counter flipflop and ...*/
			/* send counter to the DMA controller (LO/HI-BYTE) */
	outp( dma_count[ iChan ], LOBYTE( uSize ) );
	outp( dma_count[ iChan ], HIBYTE( uSize ) );
	dma_ClrMask( iChan ); /* clear DMA channel once again */
}

Starting DSP:

WORD dspStartPlay(LPBYTE buf, WORD size, USERFUNC lpUserFunction)
{
	INT buflen;
	INT iDivisor;
	BYTE m;
	BYTE dsp4_Mode = 0;
	// lIRQServed = 0; /* Clear number of previous calls */
	lpUserFunc = lpUserFunction;
	buflen = size >> 1; // Buffer length
	buf1 = buf; // First buffer
	buf2 = buf + buflen; // Second buffer
	Busy = Last = 0;
	if (lpUserFunc)
	{
		if ((*lpUserFunc)(buf1)) // Fill 1st buffer
			return 0; // Nothing to play
		if ((*lpUserFunc)(buf2)) // Fill 2nd buffer
		{
			dspStopLoop(); Last = 1;
		}
		// One buffer only
		cur_buf = buf1; // current buffer is the second
	}
	Finished = 0;
	lpOldInt = irq_SetHandler(sbSettings.iDspIrq, dsp_IrqHandler);
	/*- DSP3xx ---------------------------------------------------------*/
	if (sbSettings.uDspVersion < DSP_4XX)
	{
		if (stereo) mix3_PrepareForStereo(DAC);
		dsp_Write( DSP3_MONOADC ); /* Recording mode always MONO */
		if (stereo) /* Stereo playback */
		{
			/* With stereo playback, output 80H via DSP first */
			/* using DMA. */
			/* Program DMA channel for outputting a byte... */
			dma_SetChannel( sbSettings.iDspDmaB, &tmpbuf, 1, MODE_SINGLE | MODE_READ );
			dsp_Write(DSP_8DMADAC); /* ...and output a byte via DSP */
			dsp_Write( 0 );
			dsp_Write( 0 );
			DSP_cmd = DSP2p_8DMAHIAUTODAC;
			/*Stereo playback always HiSpeed*/
		}
		else /* Mono playback */
		{
			DSP_cmd = (BYTE) DSP_8DMAAUTODAC; /* DSP_201 */
			if(dsp_IsHIMONODACFrq(mixspeed))
				DSP_cmd = (BYTE) DSP2p_8DMAHIAUTODAC;
		}
		// Playback
		/*-- Program DMA for playback -------------------*/
		dma_SetChannel( sbSettings.iDspDmaB, buf, size, (BYTE) ( MODE_SINGLE | MODE_AUTOINIT | MODE_READ ) );
		dsp_SetFrq(&(mixspeed), stereo);
		/* An IRQ is to be triggered after each buffer half: */
		dsp_SetTransferSize( size / 2 );
		dsp_Write( DSP_cmd ); /* Send DSP command */
	}
	else
	/*- DSP 4.XX -------------------------------------------------------*/
	{
		/* Program DMA channel for 8 or 16 bits */
		m = (BYTE) (MODE_SINGLE | MODE_AUTOINIT | MODE_READ );
		if (bit16)
			dma_SetChannel( sbSettings.iDspDmaW, buf, size, m);
		else
			dma_SetChannel( sbSettings.iDspDmaB, buf, size, m);
		/* Set input/output frequency */
		dsp4_DACFrq( mixspeed );
		/* Playback is specified by command byte */
		DSP_cmd = DSP4_CMDDAC;
		DSP_cmd = DSP_cmd | DSP4_CMDAUTOINIT | DSP4_CMDFIFO;
		if(bit16) DSP_cmd = DSP_cmd | DSP4_CMD16DMA;
		else DSP_cmd = DSP_cmd | DSP4_CMD8DMA;
		if(stereo) dsp4_Mode = DSP4_MODESTEREO;
		else dsp4_Mode = DSP4_MODEMONO;
		dsp4_Mode = dsp4_Mode | DSP4_MODESIGNED;
		/* Send command, mode and transfer length to DSP */
		dsp_Write( DSP_cmd );
		dsp_Write( dsp4_Mode );
		/* Half buffer in words or bytes */
		iDivisor = 2;
		if( bit16) iDivisor = iDivisor * 2;
		dsp_Write( LOBYTE( ( size / iDivisor ) - 1 ) );
		dsp_Write( HIBYTE( ( size / iDivisor ) - 1 ) );
	}
	Lost = 0;
	return 1;
}
Back Next