📄 dmademo.c
字号:
/****************************************************************************
** WARNING: You need at least a COMPACT memory model to run this. **
*****************************************************************************
** Demonstration of playing a single wave file using DMA **
** by Steven H Don **
** **
** For questions, feel free to e-mail me. **
** **
** shd@earthling.net **
** http://shd.cjb.net **
** **
****************************************************************************/
//Include files
#include "ALLOC.H"
#include "DOS.H"
#include "MEM.H"
#include "CONIO.H"
#include "STDIO.H"
struct WaveData {
unsigned int SoundLength;
unsigned char *Sample;
};
char Key;
unsigned int CBuffer; //Clear Buffer indicator
unsigned char *DMABuffer; //Pointer to DMA Buffer
struct WaveData VoiceData[4]; //Pointers to Wave files
unsigned int DMA; //The DMA channel
unsigned int IRQ; //The IRQ level
unsigned int Base; //Sound Blaster base address, Word
//Pointers to old interrupt routines
#ifdef __cplusplus
void interrupt (*OldIRQ)(...);
#else
void interrupt (*OldIRQ)();
#endif
/****************************************************************************
** Checks to see if a Sound Blaster exists at a given address, returns **
** true if Sound Blaster found, false if not. **
****************************************************************************/
int ResetDSP(unsigned int Test)
{
//Reset the DSP
outportb (Test + 0x6, 1);
delay(10);
outportb (Test + 0x6, 0);
delay(10);
//Check if (reset was succesfull
if (((inportb(Test + 0xE) & 0x80) == 0x80) && (inportb(Test + 0xA) == 0xAA))
{
//DSP was found
Base = Test;
return (1);
}
else
//No DSP was found
return (0);
}
/****************************************************************************
** Send a byte to the DSP (Digital Signal Processor) on the Sound Blaster **
****************************************************************************/
void WriteDSP(unsigned char Value)
{
//Wait for the DSP to be ready to accept data
while ((inportb(Base + 0xC) & 0x80) == 0x80);
//Send byte
outportb (Base + 0xC, Value);
}
/****************************************************************************
** Starts playback of the buffer. The DMA controller is programmed with **
** a block length of 32K - the entire buffer. The DSP is instructed to **
** play blocks of 8K and then generate an interrupt (which allows the **
** program to clear the parts that have already been played) **
****************************************************************************/
void StartPlayBack ()
{
long LinearAddress;
unsigned int Page, OffSet;
WriteDSP (0xD1); //DSP-command D1h - Enable speaker, required
//on older SB's
WriteDSP (0x40); //DSP-command 40h - Set sample frequency
WriteDSP (165); //Write time constant
//Convert pointer to linear address
LinearAddress = FP_SEG (DMABuffer);
LinearAddress = (LinearAddress << 4) + FP_OFF (DMABuffer);
Page = LinearAddress >> 16; //Calculate page
OffSet = LinearAddress & 0xFFFF; //Calculate offset in the page
outportb (0x0A, 4 | DMA); //Mask DMA channel
outportb (0x0C, 0); //Clear byte pointer
outportb (0x0B, 0x58 | DMA); //Set mode
/*
The mode consists of the following:
0x58 + x = binary 01 00 10 xx
| | | |
| | | +- DMA channel
| | +---- Read operation (the DSP reads from memory)
| +------- Single cycle mode
+---------- Block mode
*/
outportb (DMA << 1, OffSet & 0xFF); //Write the offset to the DMA controller
outportb (DMA << 1, OffSet >> 8);
if (DMA == 0) outportb (0x87, Page);
if (DMA == 1) outportb (0x83, Page);
if (DMA == 3) outportb (0x82, Page);
outportb ((DMA << 1) + 1, 0xFF); //Set the block length to 0x7FFF = 32 Kbyte
outportb ((DMA << 1) + 1, 0x7F);
outportb (0x0A, DMA); //Unmask DMA channel
WriteDSP (0x48); //DSP-command 48h - Set block length
WriteDSP (0xFF); //Set the block length to 0x1FFF = 8 Kbyte
WriteDSP (0x1F);
WriteDSP (0x1C); //DSP-command 1Ch - Start auto-init playback
}
/****************************************************************************
** Clears an 8K part of the DMA buffer **
****************************************************************************/
void ClearBuffer(unsigned int Buffer)
{
char *Address;
//Fill an 8K block in the DMA buffer with 128's - silence
Address = (char *)MK_FP (FP_SEG (DMABuffer), FP_OFF (DMABuffer) + (Buffer << 13));
memset (Address, 128, 8192);
}
/****************************************************************************
** Mixes a sample with the contents of the DMA buffer **
****************************************************************************/
void MixVoice (struct WaveData *Voice)
{
unsigned int Counter, OffSet, DMAPointer;
//Read DMA pointer from DMA controller
DMAPointer = inportb (1 + (DMA << 1));
DMAPointer = DMAPointer + (inportb (1 + (DMA << 1)) << 8);
/*
DMAPointer contains amount that remains to be played.
This is convert to the offset of the current sample
*/
DMAPointer = 0x7FFF - DMAPointer;
OffSet = DMAPointer;
for (Counter = 0; Counter <= Voice->SoundLength; Counter++) {
//Mix byte
DMABuffer [OffSet++] += Voice->Sample [Counter];
OffSet &= 0x7FFF; //Move on to next byte and keep it in the 32K range
}
}
/****************************************************************************
** Loads a wave file into memory. This procedure treats any file as a **
** standard 11025Hz, 8bit, mono .WAV file. It doesn't perform any error **
** checking. **
****************************************************************************/
void LoadVoice (struct WaveData *Voice, char *FileName)
{
FILE *WAVFile;
//If it can't be opened...
WAVFile = fopen(FileName, "rb");
if (WAVFile == NULL) {
//..display error message
printf ("Unable to open wave file\n");
return;
}
//Return length of file for sound length minus 48 bytes for .WAV header
fseek(WAVFile, 0L, SEEK_END);
Voice->SoundLength = ftell (WAVFile) - 48;
fseek(WAVFile, 0L, SEEK_SET);
Voice->Sample = (char *)malloc(Voice->SoundLength + 2); //Assign memory
fseek(WAVFile, 46L, SEEK_SET); //Skip the header
//Load the sample data
fread(Voice->Sample, Voice->SoundLength + 2, 1, WAVFile);
fclose(WAVFile); //Close the file
}
/****************************************************************************
** Converts a wave file so it can be mixed easily **
****************************************************************************/
void ConvertVoice (struct WaveData *Voice)
{
unsigned int OffSet;
//for each sample, decrease sample value to avoid overflow
for (OffSet = 0; OffSet <= Voice->SoundLength; OffSet++) {
Voice->Sample [OffSet] >>= 2;
Voice->Sample [OffSet] -= 32;
}
}
/****************************************************************************
** IRQ service routine - this is called when the DSP has finished playing **
** a block **
****************************************************************************/
#ifdef __cplusplus
void interrupt ServiceIRQ (...)
#else
void interrupt ServiceIRQ ()
#endif
{
//Relieve DSP
inportb (Base + 0xE);
//Acknowledge hardware interrupt
outportb (0x20, 0x20);
//Acknowledge cascade interrupt for IRQ 2, 10 and 11
if (IRQ == 2 || IRQ == 10 || IRQ == 11) outportb (0xA0, 0x20);
//Increase pointer to clear buffer and keep it in the range 0..3
CBuffer++;
CBuffer &= 3;
//Clear buffer
ClearBuffer (CBuffer);
}
/****************************************************************************
** This procedure allocates 32K of memory to the DMA buffer and makes sure **
** that no page boundary is crossed **
****************************************************************************/
void AssignBuffer ()
{
char *TempBuf; //Temporary pointer
long LinearAddress;
unsigned int Page1, Page2; //Words
//Assign 32K of memory
TempBuf = (char *)malloc(32768);
//Calculate linear address
LinearAddress = FP_SEG (TempBuf);
LinearAddress = (LinearAddress << 4) + FP_OFF (TempBuf);
//Calculate page at start of buffer
Page1 = LinearAddress >> 16;
//Calculate page at end of buffer}
Page2 = (LinearAddress + 32767) >> 16;
//Check to see if a page boundary is crossed
if (Page1 != Page2) {
//If so, assign another part of memory to the buffer
DMABuffer = (char *)malloc(32768);
free (TempBuf);
} else //otherwise, use the part we've already allocated
DMABuffer = TempBuf;
memset (DMABuffer, 128, 0x7FFF);
}
void main ()
{
int Temp;
//Clear screen
clrscr ();
printf ("Demonstration of mixing samples using DMA - by Steven Don\n");
printf ("---------------------------------------------------------\n");
//Check for Sound Blaster, address: ports 220, 230, 240, 250, 260 or 280
for (Temp = 1; Temp < 9; Temp++) {
if (Temp != 7)
if (ResetDSP (0x200 + (Temp << 4))) {
break;
}
}
if (Temp == 9) {
//or none at all
printf ("No Sound Blaster found\n");
return;
} else printf ("Sound Blaster found at %Xh\n", Base);
printf ("Please specify DMA channel : "); scanf("%d", &DMA);
printf ("Please specify IRQ level : "); scanf("%d", &IRQ);
//Assign memory to the DMA Buffer
AssignBuffer ();
//Load wave files and convert
LoadVoice (&VoiceData[0], "1.wav"); ConvertVoice (&VoiceData[0]);
LoadVoice (&VoiceData[1], "2.wav"); ConvertVoice (&VoiceData[1]);
LoadVoice (&VoiceData[2], "3.wav"); ConvertVoice (&VoiceData[2]);
LoadVoice (&VoiceData[3], "4.wav"); ConvertVoice (&VoiceData[3]);
//Save old IRQ vector
if (IRQ == 2 || IRQ == 10 || IRQ == 11) {
if (IRQ == 2) OldIRQ = getvect (0x71);
if (IRQ == 10) OldIRQ = getvect (0x72);
if (IRQ == 11) OldIRQ = getvect (0x73);
} else OldIRQ = getvect (8 + IRQ);
//Set new IRQ vector
if (IRQ == 2 || IRQ == 10 || IRQ == 11) {
if (IRQ == 2) setvect (0x71, ServiceIRQ);
if (IRQ == 10) setvect (0x72, ServiceIRQ);
if (IRQ == 11) setvect (0x73, ServiceIRQ);
} else setvect (8 + IRQ, ServiceIRQ);
//Enable IRQ
if (IRQ == 2 || IRQ == 10 || IRQ == 11) {
if (IRQ == 2) outportb (0xA1, inportb (0xA1) & 253);
if (IRQ == 10) outportb (0xA1, inportb (0xA1) & 251);
if (IRQ == 11) outportb (0xA1, inportb (0xA1) & 247);
outportb (0x21, inportb (0x21) & 251);
} else outportb (0x21, inportb (0x21) & !(1 << IRQ));
//Set clear buffer to last buffer
CBuffer = 3;
//Start playback
StartPlayBack ();
//Display message
printf ("\nPress 1..4 to hear the different sounds.\nESC to quit...");
do
{
if (kbhit()) Key = getch();
else Key = 0;
if (Key == '1') MixVoice (&VoiceData[0]);
if (Key == '2') MixVoice (&VoiceData[1]);
if (Key == '3') MixVoice (&VoiceData[2]);
if (Key == '4') MixVoice (&VoiceData[3]);
}
while (Key != 27); //escape is pressed
//Stops DMA-transfer
WriteDSP (0xD0);
WriteDSP (0xDA);
//Free the memory allocated to the sound buffer
free (DMABuffer);
//Free interrupt vectors used to service IRQs
if (IRQ == 2 || IRQ == 10 || IRQ == 11) {
if (IRQ == 2) setvect (0x71, OldIRQ);
if (IRQ == 10) setvect (0x72, OldIRQ);
if (IRQ == 11) setvect (0x73, OldIRQ);
} else setvect (8 + IRQ, OldIRQ);
//Mask IRQs
if (IRQ == 2 || IRQ == 10 || IRQ == 11) {
if (IRQ == 2) outportb (0xA1, inportb (0xA1) | 2);
if (IRQ == 10) outportb (0xA1, inportb (0xA1) | 4);
if (IRQ == 11) outportb (0xA1, inportb (0xA1) | 8);
outportb (0x21, inportb (0x21) | 4);
} else outportb (0x21, inportb (0x21) | (1 << IRQ));
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -