home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- *
- * D M A A U T O - I N I T D E M O P R O G R A M
- *
- * AUTHOR: Tom Bouril (October 1993) Special thanks to
- * Douglas "DMA" Kaden for technical assistance.
- *
- * PURPOSE: This program demonstrates how to play .VOC files using
- * DMA auto-init mode. Only this file need be compiled.
- * No drivers are required by this program.
- *
- * DESCRIPTION: A file of audio data is loaded into a DMA buffer piece
- * by piece. The DMA buffer is divded into two sections.
- * To start off, fill the bottom half of the buffer and then
- * begin playing it. As the bottom half is playing, load
- * data from the file into the top half. After the top
- * half is loaded, wait for the bottom half to finish
- * playing. (An interrupt will be generated in auto-init
- * mode every time a 1/2 buffer has finished playing. This
- * is how to detect when a 1/2 buffer is done playing.)
- *
- * After the bottom half of the buffer finishes playing,
- * the top half will immediately start playing. At this
- * time load the bottom half of the buffer with
- * more data and wait for the top half to finish playing
- * (an interrupt will occur) until you load it with more
- * data. This process continues for the length of the file.
- *
- * For all files to be played, the final 1/2 buffer to be
- * played will be programmed for single-cycle mode for the
- * number of audio bytes remaining in the 1/2 buffer. This
- * includes the case in which the audio data in the file
- * is smaller than the size of a 1/2 buffer--so less than
- * 1/2 of a buffer of data is played in total.
- *
- * NOTE: The DMA chip is always programmed for auto-init mode.
- * The DSP chip is programmed for auto-init or
- * single-cycle mode depending on conditions--see
- * BeginPlaying() for more details.
- *
- *
- * COMPATIBILITY: The compiler used is Turbo C++ Ver. 1.01. This program
- * uses NO C++ except for the double-slash comments (//).
- * This program will run on Sound Blaster PRO (all versions)
- * and Sound Blaster 16. A "BLASTER" environment variable
- * must be defined that specifies the IRQ number, base I/O
- * address (in hex), and DMA channel.
- * Example: BLASTER=A220 I5 D1
- *
- * LIMITATIONS: * The size of the DMA buffer used is defined below as
- * DMA_BUF_SIZE. The value of DMA_BUF_SIZE may need to
- * be adjusted depending upon the types of files you
- * play and the speed of your system. Files with high
- * sample rates require a larger DMA buffer than files
- * with low sample rates.
- *
- * Example: On my 50 Mhz 486 DX, an 8-bit mono file
- * sampled 11025 times per second (11025 bytes
- * per second) plays perfect with a DMA_BUF_SIZE
- * of only 256, and is very intelligible with
- * size of only 2! But a 16-bit stereo file
- * sampled 44100 times per second (176,400 bytes
- * per second) requires a DMA_BUF_SIZE of 8192
- * to play with no audible degradation. If you
- * hear a file play too slow, detect skipping,
- * or clicking, try increasing DMA_BUF_SIZE.
- *
- * * Only the Creative Voice File format (.VOC files) of
- * the following types are supported:
- *
- * A) 8-bit PCM (MONO Only)
- * B) 8-bit ADPCM (4-bit compressed Creative format)
- * C) 8-bit ADPCM (2.6-bit compressed Creative format)
- * D) 8-bit ADPCM (2-bit compressed Creative format)
- * E) 16-bit PCM (STEREO and MONO)
- *
- * NOTE: Audio data must be SIGNED for all 8-bit files
- * and must be UNSIGNED for all 16-bit files.
- * This program does NOT support 8-bit STEREO!
- *
- * * Only Creative Voice File format block types 0, 1, 2,
- * 8, and 9 are supported.
- *
- * * 16-bit files must use the SB16 and a 16-bit DMA channel.
- *
- * DISCLAIMER: Although this program has been tested with all supported
- * file types (8-bit PCM, 8-bit ADPCM, 16-bit PCM) and
- * all supported block types (0, 1, 2, 8, 9), there could
- * exist some unknown error(s) (bugs).
- *
- * Because of this possibilty coupled with the existence
- * of lawyers, I must say that neither Creative Labs, Inc.
- * nor the author is responsible for anything that occurs,
- * directly or indirectly, as a result of using or following
- * this piece of sample code.
- *
- **************************************************************************/
- #include <conio.h>
- #include <dos.h>
- #include <mem.h>
- #include <stdio.h>
- #include <stdlib.h>
-
-
- #define DMA_BUF_SIZE 8192
- #define DMA8_FF_REG 0xC
- #define DMA8_MASK_REG 0xA
- #define DMA8_MODE_REG 0xB
- #define DMA16_FF_REG 0xD8
- #define DMA16_MASK_REG 0xD4
- #define DMA16_MODE_REG 0xD6
-
- #define DMA0_ADDR 0
- #define DMA0_COUNT 1
- #define DMA0_PAGE 0x87
- #define DMA1_ADDR 2
- #define DMA1_COUNT 3
- #define DMA1_PAGE 0x83
- #define DMA3_ADDR 6
- #define DMA3_COUNT 7
- #define DMA3_PAGE 0x82
- #define DMA5_ADDR 0xC4
- #define DMA5_COUNT 0xC6
- #define DMA5_PAGE 0x8B
- #define DMA6_ADDR 0xC8
- #define DMA6_COUNT 0xCA
- #define DMA6_PAGE 0x89
- #define DMA7_ADDR 0xCC
- #define DMA7_COUNT 0xCE
- #define DMA7_PAGE 0x8A
-
- #define DSP_BLOCK_SIZE 0x0048
- #define DSP_DATA_AVAIL 0xE
- #define DSP_HALT_SINGLE_CYCLE_DMA 0x00D0
- #define DSP_READ_PORT 0xA
- #define DSP_READY 0xAA
- #define DSP_RESET 0x6
- #define DSP_TIME_CONSTANT 0x0040
- #define DSP_WRITE_PORT 0xC
-
- #define AUTO_INIT 1
- #define FAIL 0
- #define FALSE 0
- #define MASTER_VOLUME 0x22
- #define MIC_VOLUME 0x0A
- #define MIXER_ADDR 0x4
- #define MIXER_DATA 0x5
- #define MONO 0
- #define PIC_END_OF_INT 0x20
- #define PIC_MASK 0x21
- #define PIC_MODE 0x20
- #define SUCCESS 1
- #define SINGLE_CYCLE 0
- #define STEREO 1
- #define TRUE 1
- #define VOICE_VOLUME 0x04
-
-
- /*--------- FUNCTION PROTOTYPES --------------------------------*/
- /*----------------------------------------------------------------*/
- char GetBlasterEnv(int *, int *, int *),
- InitDMADSP(unsigned long, int, int, FILE *),
- ResetDSP(int);
-
- unsigned int FillHalfOfBuffer(int *, FILE *, unsigned char *);
-
- unsigned long AllocateDMABuffer(unsigned char **),
- OnSamePage(unsigned char *);
-
- void BeginPlaying(unsigned int, char),
- DSPOut(int, int),
- FillBuffers(unsigned char *, int *, FILE *),
- SetMixer(void);
-
- void interrupt DMAOutputISR(void); // Interrupt Service Routine
- /*----------------------------------------------------------------*/
-
- /*--------- GLOBAL DECLARATIONS --------------------------------*/
- /*----------------------------------------------------------------*/
- char gBufNowPlaying,
- gEndOfFile,
- gLastBufferDonePlaying,
- gMode, // indicates MONO or STEREO
- g16BitDMA;
-
- int gFormat, // Same as "Pack" for 8-bit files (compression mode)
- gIOPort;
-
- unsigned long gNoOfBytesLeftInBlock,
- gSamplesPerSec;
- /*----------------------------------------------------------------*/
-
-
- /*--- BEGIN main() -----------------------------------------------*/
- /*----------------------------------------------------------------*/
- int main(void)
- {
- char Filename[80],
- Filetype[20],
- RetValue;
-
- FILE *FileToPlay;
-
- int BufToFill,
- DMAChan8Bit,
- DMAChan16Bit,
- IRQNumber,
- IRQMask,
- MaskSave,
- Offset;
-
- unsigned char *DMABuffer;
- unsigned int BytesLeftToPlay;
- unsigned long BufPhysAddr;
-
- void interrupt (*IRQSave)(void);
-
-
- clrscr(); // Clear the screen
-
-
- /*--- OPEN FILE TO BE PLAYED -------------------------------*/
- /*----------------------------------------------------------*/
- printf(" Enter the .VOC file to play: ");
- scanf("%s", Filename);
- putchar('\n');
- if ((FileToPlay = fopen(Filename, "rb")) == NULL)
- {
- printf("%s file non-existent or invalid format--PROGRAM TERMINATED!\n",
- Filename);
- exit(0);
- }
-
- /*--- VERIFY FILE IS .VOC FORMAT---------------------------*/
- /*---------------------------------------------------------*/
- fread(Filetype, 20, 1, FileToPlay); // Get file type description.
- if (memcmp(Filetype, "Creative Voice File", 19))
- {
- puts("File is not a valid .VOC file--PROGRAM ABORTED");
- fclose(FileToPlay);
- exit(0);
- }
- else // Point file pointer to beginning of the first data block.
- {
- fread(&Offset, 2, 1, FileToPlay); // Offset is No. of bytes from start
- rewind(FileToPlay); // File ptr. to beg. of file
- fseek(FileToPlay, (long) Offset, SEEK_CUR); // Go to first data block
- }
-
-
- /*--- ALLOCATE BUFFERS -----------------------------------------*/
- /*--------------------------------------------------------------*/
- BufPhysAddr = AllocateDMABuffer(&DMABuffer);
- if (BufPhysAddr == FAIL)
- {
- puts("DMA Buffer allocation failed!--PROGRAM ABORTED");
- fclose(FileToPlay);
- exit(0);
- }
-
-
- /*--- GET ENVIRONMENT VALUES -----------------------------------*/
- /*--------------------------------------------------------------*/
- RetValue = GetBlasterEnv(&DMAChan8Bit, &DMAChan16Bit, &IRQNumber);
-
-
- /*--- PRINT OUT INFO -------------------------------------------*/
- /*--------------------------------------------------------------*/
- printf(" DMA Buffer Address = %4x:%-4x (SEG:OFF) (hex)\n",
- FP_SEG(DMABuffer), FP_OFF(DMABuffer));
- printf(" DMA Buffer Phys. Addr. = %-7lu (decimal)\n", BufPhysAddr);
- printf(" 8-bit DMA channel = %-5d (decimal)\n", DMAChan8Bit);
- printf(" 16-bit DMA channel = %-5d (decimal)\n", DMAChan16Bit);
- printf(" I/O port address = %-3x (hex)\n", gIOPort);
- printf(" IRQ number = %-2d (decimal)\n\n", IRQNumber);
-
-
- /*--- ARE ENVIRONMENT VALUES VALID? --------------------------*/
- /*------------------------------------------------------------*/
- if (RetValue == FAIL)
- {
- puts("BLASTER env. string or parameter(s) missing--PROGRAM ABORTED!");
- free(DMABuffer);
- fclose(FileToPlay);
- exit(0);
- }
-
- /*--- RESET THE DSP ----------------------------------------*/
- /*----------------------------------------------------------*/
- if(ResetDSP(gIOPort) == FAIL)
- {
- puts("Unable to reset DSP chip--PROGRAM TERMINATED!");
- free(DMABuffer);
- fclose(FileToPlay);
- exit(0);
- }
-
-
- /*--- SAVE CURRENT ISR FOR IRQNumber, THEN GIVE IRQNumber NEW ISR ---*/
- /*-------------------------------------------------------------------*/
- IRQSave = getvect(IRQNumber + 8);
- setvect(IRQNumber + 8, DMAOutputISR);
-
-
- /*--- SAVE CURRENT INTERRUPT MASK AND SET NEW INTERRUPT MASK -------*/
- /*------------------------------------------------------------------*/
- MaskSave = inp((int) PIC_MASK);
- IRQMask = ((int) 1 << IRQNumber); // Shift a 1 left IRQNumber of bits
- outp(PIC_MASK, (MaskSave & ~IRQMask)); // Enable previous AND new interrupts
-
-
- /*--- PROGRAM THE DMA, DSP CHIPS -----------------------------------*/
- /*------------------------------------------------------------------*/
- if (InitDMADSP(BufPhysAddr, DMAChan8Bit, DMAChan16Bit, FileToPlay) == FAIL)
- {
- puts("InitDMADSP() fails--PROGRAM ABORTED!");
- free(DMABuffer);
- fclose(FileToPlay);
- exit(0);
- }
-
- /*--- FILL THE FIRST 1/2 OF DMA BUFFER BEFORE PLAYING BEGINS -------*/
- /*------------------------------------------------------------------*/
- BufToFill = 0; // Altered by FillHalfOfBuffer()
- gEndOfFile = FALSE; // Altered by FillHalfOfBuffer()
- gBufNowPlaying = 0; // Altered by ISR
- gLastBufferDonePlaying = FALSE; // Set in ISR
- SetMixer();
- BytesLeftToPlay = FillHalfOfBuffer(&BufToFill, FileToPlay, DMABuffer);
-
-
- /*--- BEGIN PLAYING THE FILE ---------------------------------------*/
- /*------------------------------------------------------------------*/
- if (BytesLeftToPlay < DMA_BUF_SIZE / 2) // File size is < 1/2 buffer size.
- {
- BeginPlaying(BytesLeftToPlay, SINGLE_CYCLE);
- while (gBufNowPlaying == 0); // Wait for playing to finish (ISR called)
- }
- else // File size >= 1/2 buffer size
- {
- BeginPlaying(BytesLeftToPlay, AUTO_INIT);
- FillBuffers(DMABuffer, &BufToFill, FileToPlay);
- }
-
- DSPOut(gIOPort, DSP_HALT_SINGLE_CYCLE_DMA); // Done playing, halt DMA
-
-
- /*--- RESTORE ISR AND ORIGINAL IRQ VECTOR -------------------------*/
- /*-----------------------------------------------------------------*/
- outp(PIC_MASK, MaskSave);
- setvect(IRQNumber + 8, IRQSave);
-
- free(DMABuffer);
- fclose(FileToPlay);
- return(0);
- }
-
- /*************************************************************************
- *
- * FUNCTION: BeginPlaying()
- *
- * DESCRIPTION: If the file to play is smaller than 1/2 the DMA buffer,
- * this function will be called only once. It will program
- * the DSP chip for single-cycle mode with a count equal to
- * the number of audio bytes to play. The audio will then
- * begin playing.
- *
- * If the file to play is larger than 1/2 the DMA buffer,
- * this function will be called twice. The first time it
- * will be called by main() and it will:
- *
- * A) Program the DSP chip count (1/2 size of buffer).
- *
- * B) Program the DSP chip for auto-init mode.
- *
- * C) At this time the audio begins.
- *
- * The second (and last) time this function is called,
- * it will be called by FillBuffers(). This will occur
- * while the second to last buffer is playing. In this
- * case BeginPlaying() will do the following.
- *
- * A) Program the DSP chip for single-cycle mode.
- *
- * B) Program the DSP count for the number of bytes left to
- * play in the last buffer.
- *
- * C) Now, when the second to last buffer ends playing,
- * the final buffer will play in single-cycle mode.
- * This allows only the remaining bytes in the last
- * buffer to be played, instead of the entire buffer,
- * as would be the case in auto-init mode.
- *
- * NOTE: The last 1/2 buffer will always be programmed for
- * single-cycle DSP mode. So when this function returns
- * to main(), main() sends a DSP command to halt DSP
- * single-cycle mode.
- *
- *************************************************************************/
- void BeginPlaying(unsigned int BytesLeftToPlay, char DMAMode)
- {
- int Command;
-
- /*--- IF BytesLeftToPlay IS 0 OR 1, MAKE SURE THAT WHEN DSPOut() IS ---*/
- /*--- CALLED, THE COUNT DOESN'T WRAP AROUND TO A + NUMBER WHEN 1 IS ---*/
- /* SUBTRACTED! -----------------------------------------------------*/
- if(BytesLeftToPlay <= 1 && g16BitDMA)
- BytesLeftToPlay = 2;
- else if (BytesLeftToPlay == 0 && !g16BitDMA)
- BytesLeftToPlay = 1;
-
- if (DMAMode == AUTO_INIT)
- {
- if (gFormat < 4) // Program DSP size for 8-bit files
- {
- DSPOut(gIOPort, DSP_BLOCK_SIZE);
- DSPOut(gIOPort, (int) ((BytesLeftToPlay - 1) & 0x00FF)); // LO byte
- DSPOut(gIOPort, (int) ((BytesLeftToPlay - 1) >> 8)); // HI byte
- }
-
- switch(gFormat)
- {
- case 0: // 8-Bit PCM
- DSPOut(gIOPort, 0x001C);
- break;
-
- case 1: // 4-Bit ADPCM
- DSPOut(gIOPort, 0x007D);
- break;
-
- case 2: // 2.6-bit ADPCM
- DSPOut(gIOPort, 0x007F);
- break;
-
- case 3: // 2-bit ADPCM
- DSPOut(gIOPort, 0x001F);
- break;
-
- case 4: // 16-bit PCM
- DSPOut(gIOPort, 0x0041);
- DSPOut(gIOPort, (int) ((gSamplesPerSec & 0x0000FF00) >> 8)); // Hi byte
- DSPOut(gIOPort, (int) (gSamplesPerSec & 0x000000FF)); // Lo byte
- DSPOut(gIOPort, 0x00B4);
-
- if (gMode == MONO)
- DSPOut(gIOPort, 0x0010);
- else if (gMode == STEREO)
- DSPOut(gIOPort, 0x0030);
-
- // For 16-bits, count is 1/2 of buffer size in WORDS (1 word = 2 bytes)
- DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) & 0x00FF); // LO byte size
- DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) >> 8); // HI byte size
- break;
-
- default:
- puts("ERROR in BeginPlaying(): Invalid \"Format\" compression type.");
- break;
- }
-
- }
- else if (DMAMode == SINGLE_CYCLE)
- {
- switch(gFormat)
- {
- case 0: // 8-Bit PCM
- puts("File: 8-bit PCM");
- Command = 0x0014;
- break;
-
- case 1: // 4-Bit ADPCM
- puts("File: 4-bit ADPCM");
- Command = 0x0074;
- break;
-
- case 2: // 2.6-Bit ADPCM
- puts("File: 2.6-bit ADPCM");
- Command = 0x0076;
- break;
-
- case 3: // 2-bit ADPCM
- puts("File: 2-bit ADPCM");
- Command = 0x0016;
- break;
-
- case 4: // 16-bit PCM
- DSPOut(gIOPort, 0x0041);
- DSPOut(gIOPort, (int) ((gSamplesPerSec & 0x0000FF00) >> 8)); // Hi byte
- DSPOut(gIOPort, (int) (gSamplesPerSec & 0x000000FF)); // Lo byte
- DSPOut(gIOPort, 0x00B0);
- if (gMode == MONO)
- {
- puts("16-BIT PCM MONO");
- DSPOut(gIOPort, 0x0010);
- }
- else if (gMode == STEREO)
- {
- puts("16-BIT PCM STEREO");
- DSPOut(gIOPort, 0x0030);
- }
-
- // For 16-bits, count is in WORDS (1 word = 2 bytes)
- DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) & 0x00FF); // LO byte size
- DSPOut(gIOPort, (BytesLeftToPlay/2 - 1) >> 8); // HI byte size
- break;
-
- default:
- puts("ERROR in BeginPlaying(): Invalid \"Format\" compression type.");
- break;
- }
-
- if (gFormat < 4) // Program DSP for 8-bit files
- {
- DSPOut(gIOPort, Command);
- DSPOut(gIOPort, (BytesLeftToPlay - 1) & 0x00FF); // LO byte size
- DSPOut(gIOPort, (BytesLeftToPlay - 1) >> 8); // HI byte size
- }
-
- }
- return;
- }
-
- /*************************************************************************
- *
- * FUNCTION: FillBuffers()
- *
- * DESCRIPTION: While one half of the buffer is playing, load the other
- * half with data from the file. After filling a buffer, wait
- * for the other half to finish playing before filling it with
- * new data. Continue doing this until FillHalfOfBuffer()
- * sets gEndOfFile to TRUE.
- *
- * This function calls FillHalfOfBuffer(), which will return
- * the number of bytes in the 1/2 buffer that should be played.
- * This number will always be 1/2 of the DMA buffer size, except
- * for the last buffer to be played. When the final buffer is
- * loaded, BeginPlaying() is called, which reprograms the DSP
- * chip to play the last buffer in single-cycle mode, and
- * reprograms the DSP chip to play only the bytes in the
- * buffer that contain audio data. (Doing this will prevent
- * the contents of the entire buffer from being played.)
- *
- * After the last buffer is programmed for single-cycle mode,
- * the do-while loop finishes. We now must wait until the
- * last buffer finishes playing before we can return to main()
- * and terminate the program.
- *
- *************************************************************************/
- void FillBuffers(unsigned char *DMABuffer, int *BufToFill, FILE *FileToPlay)
- {
- unsigned int NumberOfAudioBytesInBuffer;
-
- do
- {
- while (*BufToFill == gBufNowPlaying); // Wait for buffer to finish playing
-
- NumberOfAudioBytesInBuffer = FillHalfOfBuffer(BufToFill, FileToPlay,
- DMABuffer);
- if (NumberOfAudioBytesInBuffer < DMA_BUF_SIZE / 2)
- BeginPlaying(NumberOfAudioBytesInBuffer, SINGLE_CYCLE);
-
- } while (!gEndOfFile); // gEndOfFile set in FillHalfOfBuffer()
-
- while (gLastBufferDonePlaying == FALSE); // Wait until done playing
-
- return;
- }
-
- /*************************************************************************
- *
- * FUNCTION: FillHalfOfBuffer()
- *
- * DESCRIPTION: Depending upon the value of *BufToFill, fill either the
- * bottom or top half of the DMA buffer with audio data from
- * the file.
- *
- * This function returns the number of bytes in the 1/2 buffer
- * that should be played. This number will always be 1/2
- * the buffer size, except for the last buffer to be played.
- * When the last buffer is loaded from the file, end of file
- * will be reached and the number of bytes to be played will
- * be <= 1/2 of the buffer size.
- *
- *************************************************************************/
- unsigned int FillHalfOfBuffer(int *BufToFill, FILE *FileToPlay,
- unsigned char *DMABuffer)
- {
- unsigned char Temp[4];
- unsigned int Count;
-
- if (*BufToFill == 1) // Fill top 1/2 of DMA buffer
- DMABuffer += DMA_BUF_SIZE / 2;
-
- fread(DMABuffer, DMA_BUF_SIZE/2, 1, FileToPlay);
-
-
- for (Count = 0; Count < DMA_BUF_SIZE/2; Count++)
- {
- if (gNoOfBytesLeftInBlock != 0) // Originally init'd in InitDMADSP()
- gNoOfBytesLeftInBlock--;
- else
- {
- switch(DMABuffer[Count])
- {
- /*--- A NEW DATA BLOCK HAS BEEN DETECTED--PROCESS OR IGNORE IT ---*/
- case 0:
- puts("BLOCK 0 detected in FillHalfOfBuffer()");
- gEndOfFile = TRUE;
- break;
-
- case 2:
- puts("BLOCK 2 detected in FillHalfOfBuffer()");
- if (Count < DMA_BUF_SIZE/2 - 3)
- {
- /*--- DATA BLOCK NOT ON BUFFER BOUNDARY -------------------*/
- Count++; // Skip past block type
- gNoOfBytesLeftInBlock = (unsigned long) DMABuffer[Count];
- Count++;
- gNoOfBytesLeftInBlock += (unsigned long) (DMABuffer[Count] << 8);
- Count++;
- gNoOfBytesLeftInBlock += (unsigned long) (DMABuffer[Count] << 16);
-
- /*--- ELIMINATE BLOCK 2 FROM BEING PLAYED AS AUDIO DATA ---*/
- memcpy(&DMABuffer[Count-3], &DMABuffer[Count+1],
- DMA_BUF_SIZE/2 - 1 - Count);
- fread(Temp, 4, 1, FileToPlay);
- memcpy(DMABuffer + DMA_BUF_SIZE/2-3, Temp, 4);
-
- }
- else
- {
- /*--- DATA BLOCK IS ON BUFFER BOUNDARY --------------------*/
- switch(Count)
- {
- case DMA_BUF_SIZE/2 - 3:
- fread(Temp, 4, 1, FileToPlay);
- gNoOfBytesLeftInBlock += (unsigned long) DMABuffer[Count+1];
- gNoOfBytesLeftInBlock += (unsigned long)
- (((unsigned long) DMABuffer[Count+2]) << 8);
- gNoOfBytesLeftInBlock += (unsigned long)
- (((unsigned long) Temp[0]) << 16);
- memcpy(&DMABuffer[Count], &Temp[1], 3);
- Count += 3;
- break;
-
- case DMA_BUF_SIZE/2 - 2:
- fread(Temp, 4, 1, FileToPlay);
- gNoOfBytesLeftInBlock += (unsigned long) DMABuffer[Count+1];
- gNoOfBytesLeftInBlock += (unsigned long)
- (((unsigned long) Temp[0]) << 8);
- gNoOfBytesLeftInBlock += (unsigned long)
- (((unsigned long) Temp[1]) << 16);
- memcpy(&DMABuffer[Count], &Temp[2], 2);
- Count += 2;
- break;
-
- case DMA_BUF_SIZE/2 - 1:
- fread(Temp, 1, 1, FileToPlay);
- gNoOfBytesLeftInBlock += (unsigned long) Temp[0];
- gNoOfBytesLeftInBlock += (unsigned long)
- (((unsigned long) Temp[1]) << 8);
- gNoOfBytesLeftInBlock += (unsigned long)
- (((unsigned long) Temp[2]) << 16);
- memcpy(&DMABuffer[Count], &Temp[3], 1);
- Count++;
- break;
-
- default:
- printf("ERROR in FillHalfOfBuffer()--Count = %u\n", Count);
- break;
- }
- }
- break; // End: "case 2:"
-
- default:
- printf("ERROR: Block type %d detected in FillHalfOfBuffer()",
- (int) DMABuffer[Count]);
- break;
- }
-
- if (gEndOfFile) // Exit the for() loop if end-of-file is reached.
- break;
- }
- }
-
- *BufToFill ^= 1; // Toggle to fill other 1/2 of buffer next time.
-
- return(Count);
- }
-
- /*************************************************************************
- *
- * FUNCTION: DMAOutputISR()
- *
- * DESCRIPTION: Interrupt service routine. Every time the DSP chip finishes
- * playing half of the DMA buffer in auto-init mode, an
- * interrupt is generated, which invokes this routine.
- *
- * After gEndOfFile is set TRUE in FillHalfOfBuffer(), this
- * ISR can determine that the "second to last buffer" has
- * finished playing (set SecondToLastBufferPlayed to TRUE).
- *
- * When this ISR is called again, we will know that the last
- * buffer has finished playing--gLastBufferDonePlaying is
- * set to TRUE. gLastBufferDonePlaying is used by
- * FillBuffers() to determine when to return control to
- * main()--where the program will be terminated.
- *
- *************************************************************************/
- void interrupt DMAOutputISR(void)
- {
- static char SecondToLastBufferPlayed = FALSE;
- int IntStatus;
-
- if (g16BitDMA == TRUE)
- {
- outp(gIOPort + 4, 0x82); // Select interrupt status reg. in mixer
- IntStatus = inp(gIOPort + 5); // Read interrupt status reg.
-
- if (IntStatus & 2)
- inp(gIOPort + 0xF); // Acknowledge interrupt (16-bit)
- }
- else
- inp(gIOPort + (int) DSP_DATA_AVAIL); // Acknowledge interrupt (8-bit)
-
- gBufNowPlaying ^= 1;
- outp(PIC_MODE, (int) PIC_END_OF_INT); // End of interrupt
-
- if (SecondToLastBufferPlayed)
- gLastBufferDonePlaying = TRUE;
-
- if (gEndOfFile)
- SecondToLastBufferPlayed = TRUE;
-
- return;
- }
-
-
- /*************************************************************************
- *
- * FUNCTION: InitDMADSP()
- *
- * DESCRIPTION: This function reads the first data block of the file and
- * from it obtains information that is needed to program the
- * DMA and DSP chips. After reading the data block, the file
- * pointer points to the first byte of the voice data.
- *
- * NOTE: The DMA chip is ALWAYS programmed for auto-init mode
- * (command 0x58)! The DSP chip will be programmed for
- * auto-init or single-cycle mode depending upon
- * conditions--see BeginPlaying() for details.
- *
- *************************************************************************/
- char InitDMADSP(unsigned long BufPhysAddr, int DMAChan8Bit, int DMAChan16Bit,
- FILE *FileToPlay)
- {
- char BitsPerSample,
- BlockType,
- Pack;
-
- int DMAAddr,
- DMACount,
- DMAPage,
- Offset,
- Page,
- Temp;
-
- unsigned char ByteTimeConstant;
- unsigned int WordTimeConstant;
-
-
- /*--- GET INFO NEEDED TO PROG DMA & DSP CHIPS FROM FIRST DATA BLOCK -----*/
- /*-----------------------------------------------------------------------*/
- fread(&BlockType, 1, 1, FileToPlay);
-
- switch(BlockType)
- {
- case 0:
- puts("Data Block Type = 0");
- return(FAIL); // 1st block is terminator, play nothing
-
- case 1:
- puts("Data Block Type = 1");
- fread(&gNoOfBytesLeftInBlock, 3, 1, FileToPlay);
- fread(&ByteTimeConstant, 1, 1, FileToPlay);
- fread(&Pack, 1, 1, FileToPlay);
- gFormat = (int) Pack;
- gMode = MONO; // Only MONO supported for block type 1
- gNoOfBytesLeftInBlock -= 2;
- break;
-
- case 8:
- puts("Data Block Type = 8");
- // Get info from block type 8, get voice data after block type 1
- fseek(FileToPlay, 3, SEEK_CUR);
- fread(&WordTimeConstant, 2, 1, FileToPlay);
- ByteTimeConstant = (char) (WordTimeConstant >> 8); // Use upper byte
- fread(&Pack, 1, 1, FileToPlay);
- gFormat = (int) Pack;
- fread(&gMode, 1, 1, FileToPlay);
-
- // Block type 1 immediately follows block type 8.
- // So move file pointer to voice data.
- fseek(FileToPlay, 1, SEEK_CUR); // Skip block type (it's 1)
- fread(&gNoOfBytesLeftInBlock, 3, 1, FileToPlay);
- fseek(FileToPlay, 2, SEEK_CUR); // File pointer @ voice data
- gNoOfBytesLeftInBlock -= 2;
- break;
-
- case 9:
- puts("Data Block Type = 9");
- fread(&gNoOfBytesLeftInBlock, 3, 1, FileToPlay);
- fread(&gSamplesPerSec, 4, 1, FileToPlay);
- ByteTimeConstant = (unsigned char) (256-1000000/gSamplesPerSec);
- fread(&BitsPerSample, 1, 1, FileToPlay);
- fread(&gMode, 1, 1, FileToPlay); // "gMode" is same as "Channels"
- fread(&gFormat, 2, 1, FileToPlay);
- fseek(FileToPlay, 4, SEEK_CUR); // Point to voice data
- gNoOfBytesLeftInBlock -= 12;
- gMode--; // Make compatible with old format (0 = MONO, 1 = STEREO)
- break;
-
- default:
- printf("ERROR: InitDMADSP() BlockType = %d unsupported\n",
- (int) BlockType);
- return(FAIL);
- }
-
-
- /*--- GET DMA ADDR., COUNT, AND PAGE FOR THE DMA CHANNEL USED ----------*/
- /*----------------------------------------------------------------------*/
- if (gFormat < 4)
- {
- g16BitDMA = FALSE; // DMA is not 16-bit (it's 8-bit).
-
- switch(DMAChan8Bit) // File is 8-bit. Program DMA 8-bit DMA channel
- {
- case 0:
- DMAAddr = DMA0_ADDR;
- DMACount = DMA0_COUNT;
- DMAPage = DMA0_PAGE;
- break;
-
- case 1:
- DMAAddr = DMA1_ADDR;
- DMACount = DMA1_COUNT;
- DMAPage = DMA1_PAGE;
- break;
-
- case 3:
- DMAAddr = DMA3_ADDR;
- DMACount = DMA3_COUNT;
- DMAPage = DMA3_PAGE;
- break;
-
- default:
- puts("File is 8-bit--invalid 8-bit DMA channel");
- return(FAIL);
- }
- }
- else
- {
- g16BitDMA = TRUE; // DMA is 16-bit (not 8-bit).
-
- switch(DMAChan16Bit) // File is 16-bit. Program DMA 16-bit DMA channel
- {
- case 5:
- DMAAddr = DMA5_ADDR;
- DMACount = DMA5_COUNT;
- DMAPage = DMA5_PAGE;
- break;
-
- case 6:
- DMAAddr = DMA6_ADDR;
- DMACount = DMA6_COUNT;
- DMAPage = DMA6_PAGE;
- break;
-
- case 7:
- DMAAddr = DMA7_ADDR;
- DMACount = DMA7_COUNT;
- DMAPage = DMA7_PAGE;
- break;
-
- default:
- puts("File is 16-bit--invalid 16-bit DMA channel");
- return(FAIL);
- }
-
- DMAChan16Bit -= 4; // Convert
- }
-
-
- /*--- PROGRAM THE DMA CHIP ---------------------------------------------*/
- /*----------------------------------------------------------------------*/
- Page = (int) (BufPhysAddr >> 16);
- Offset = (int) (BufPhysAddr & 0xFFFF);
-
- if (gFormat < 4) // 8-bit file--Program 8-bit DMA controller
- {
- outp(DMA8_MASK_REG, (int) (DMAChan8Bit | 4)); // Disable DMA while prog.
- outp(DMA8_FF_REG, (int) 0); // Clear the flip-flop
-
- outp(DMA8_MODE_REG, (int) (DMAChan8Bit | 0x58)); // 8-bit auto-init
- outp(DMACount, (int) ((DMA_BUF_SIZE - 1) & 0xFF)); // LO byte of count
- outp(DMACount, (int) ((DMA_BUF_SIZE - 1) >> 8)); // HI byte of count
- }
- else // 16-bit file--Program 16-bit DMA controller
- {
- // Offset for 16-bit DMA channel must be calculated differently...
- // Shift Offset 1 bit right, then copy LSB of Page to MSB of Offset.
- Temp = Page & 0x0001; // Get LSB of Page and...
- Temp <<= 15; // ...move it to MSB of Temp.
- Offset >>= 1; // Divide Offset by 2
- Offset &= 0x7FFF; // Clear MSB of Offset
- Offset |= Temp; // Put LSB of Page into MSB of Offset
-
- outp(DMA16_MASK_REG, (int) (DMAChan16Bit | 4)); // Disable DMA while prog.
- outp(DMA16_FF_REG, (int) 0); // Clear the flip-flop
-
- outp(DMA16_MODE_REG, (int) (DMAChan16Bit | 0x58)); // 16-bit auto-init
- outp(DMACount, (int) ((DMA_BUF_SIZE/2 - 1) & 0xFF)); // LO byte of count
- outp(DMACount, (int) ((DMA_BUF_SIZE/2 - 1) >> 8)); // HI byte of count
- }
-
-
- outp(DMAPage, Page); // Physical page number
- outp(DMAAddr, (int) (Offset & 0xFF)); // LO byte address of buffer
- outp(DMAAddr, (int) (Offset >> 8)); // HI byte address of buffer
-
-
- // Done programming the DMA, enable it
- if (gFormat < 4)
- outp(DMA8_MASK_REG, DMAChan8Bit);
- else
- outp(DMA16_MASK_REG, DMAChan16Bit);
-
-
- /*--- PROGRAM THE DSP CHIP ------------------------------------------*/
- /*-------------------------------------------------------------------*/
- DSPOut(gIOPort, (int) DSP_TIME_CONSTANT);
- DSPOut(gIOPort, (int) ByteTimeConstant);
- DSPOut(gIOPort, 0x00D1); // Must turn speaker ON before doing D/A conv.
-
- return(SUCCESS);
- }
-
-
- /*************************************************************************
- *
- * FUNCTION: AllocateDMABuffer()
- *
- * DESCRIPTION : Allocate memory for the DMA buffer. After memory is
- * allocated for the buffer, call OnSamePage() to verify
- * that the entire buffer is located on the same page.
- * If the buffer crosses a page boundary, allocate another
- * buffer. Continue this process until the DMA buffer resides
- * entirely within the same page.
- *
- * For every malloc() called, save a pointer that points to
- * the block of memory allocated. Deallocate ALL memory blocks
- * allocated that cross a page boundary. Once a memory block
- * is allocated that does NOT cross a page boudary, this block
- * will be used for the DMA buffer--any previously allocated
- * memory blocks will be deallocated.
- *
- * ENTRY: **DMABuffer is the address of the pointer that will point to
- * the memory allocated.
- *
- * EXIT: If a buffer is succesfully allocated, *DMABuffer will point to
- * the buffer and the physical address of the buffer pointer will
- * be returned.
- *
- * If a buffer is NOT successfully allocated, return FAIL.
- *
- *************************************************************************/
- unsigned long AllocateDMABuffer(unsigned char **DMABuffer)
- {
- unsigned char BufferNotAllocated = TRUE,
- Done = FALSE,
- *PtrAllocated[100];
-
- int i,
- Index = 0;
-
- unsigned long PhysAddress;
-
- do
- {
- *DMABuffer = (unsigned char *) malloc(DMA_BUF_SIZE);
-
- if (*DMABuffer != NULL)
- {
- /*--- Save the ptr for every malloc() performed ---*/
- PtrAllocated[Index] = *DMABuffer;
- Index++;
-
- /*--- If entire buffer is within one page, we're out of here! ---*/
- PhysAddress = OnSamePage(*DMABuffer);
- if (PhysAddress != FAIL)
- {
- BufferNotAllocated = FALSE;
- Done = TRUE;
- }
- }
- else
- Done = TRUE; // malloc() couldn't supply requested memory
-
- } while (!Done);
-
-
- if (BufferNotAllocated)
- {
- Index++; // Incr. Index so most recent malloc() gets free()d
- PhysAddress = FAIL; // return FAIL
- }
-
- /*--- Deallocate all memory blocks crossing a page boundary ---*/
- for (i= 0; i < Index - 1; i++)
- free(PtrAllocated[i]);
-
- return(PhysAddress);
- }
-
-
- /**************************************************************************
- *
- * FUNCTION: OnSamePage()
- *
- * DESCRIPTION: Check the memory block pointed to by the parameter
- * passed to make sure the entire block of memory is on the
- * same page. If a buffer DOES cross a page boundary,
- * return FAIL. Otherwise, return the physical address
- * of the beginning of the DMA buffer.
- *
- * A page corresponds to the following addresses:
- *
- * PAGE NO. SEG:OFF ADDRESS PHYSICAL ADDRESS
- * -------- ---------------------- ----------------
- * 0 0000:0000 to 0000:FFFF 00000 to 0FFFF
- * 1 1000:0000 to 1000:FFFF 10000 to 1FFFF
- * . . .
- * . . .
- * E E000:0000 to E000:FFFF E0000 to EFFFF
- * F F000:0000 to F000:FFFF F0000 to FFFFF
- *
- * NOTE: The upper nibble of the physical address is the
- * same as the page number!
- *
- * ENTRY: *DMABuffer - Points to beginning of DMA buffer.
- *
- * EXIT: If the buffer is located entirely within one page, return the
- * physical address of the buffer pointer. Otherwise return FAIL.
- *
- **************************************************************************/
- unsigned long OnSamePage(unsigned char *DMABuffer)
- {
- unsigned long BegBuffer,
- EndBuffer,
- PhysAddress;
-
- /*----- Obtain the physical address of DMABuffer -----*/
- BegBuffer = ((unsigned long) (FP_SEG(DMABuffer)) << 4) +
- (unsigned long) FP_OFF(DMABuffer);
- EndBuffer = BegBuffer + DMA_BUF_SIZE - 1;
- PhysAddress = BegBuffer;
-
- /*-- Get page numbers for start and end of DMA buffer. --*/
- BegBuffer >>= 16;
- EndBuffer >>= 16;
-
- if (BegBuffer == EndBuffer)
- return(PhysAddress); // Entire buffer IS on same page!
- return(FAIL); // Entire buffer NOT on same page. Thanks Intel!
- }
-
-
- /**************************************************************************
- *
- * FUNCTION: GetBlasterEnv()
- *
- * DESCRIPTION : Get the BLASTER environment variable and search its
- * string for the DMA channel, I/O address port, and
- * IRQ number. Assign these values to the parameters passed
- * by the caller.
- *
- * ENTRY: All parameters passed are pointers to integers. They will be
- * assigned the values found in the environment string.
- *
- * EXIT: If DMA channel, I/O address, and IRQ number are found, return
- * PASS, otherwise return FAIL.
- *
- *
- **************************************************************************/
- char GetBlasterEnv(int *DMAChan8Bit, int *DMAChan16Bit, int *IRQNumber)
- {
- char Buffer[5],
- DMAChannelNotFound = TRUE,
- *EnvString,
- IOPortNotFound = TRUE,
- IRQNotFound = TRUE,
- SaveChar;
-
- int digit,
- i,
- multiplier;
-
- EnvString = getenv("BLASTER");
-
- if (EnvString == NULL)
- return(FAIL);
-
- do
- {
- switch(*EnvString)
- {
- case 'A': // I/O base port address found
- case 'a':
- EnvString++;
- for (i = 0; i < 3; i++) // Grab the digits
- {
- Buffer[i] = *EnvString;
- EnvString++;
- }
-
- // The string is in HEX, convert it to decimal
- multiplier = 1;
- gIOPort = 0;
- for (i -= 1; i >= 0; i--)
- {
- // Convert to HEX
- if (Buffer[i] >= '0' && Buffer[i] <= '9')
- digit = Buffer[i] - '0';
- else if (Buffer[i] >= 'A' && Buffer[i] <= 'F')
- digit = Buffer[i] - 'A' + 10;
- else if (Buffer[i] >= 'a' && Buffer[i] <= 'f')
- digit = Buffer[i] - 'a' + 10;
-
- gIOPort = gIOPort + digit * multiplier;
- multiplier *= 16;
- }
-
- IOPortNotFound = FALSE;
- break;
-
-
- case 'D': // 8-bit DMA channel
- case 'd':
- case 'H': // 16-bit DMA channel
- case 'h':
- SaveChar = *EnvString;
- EnvString++;
- Buffer[0] = *EnvString;
- EnvString++;
-
- if (*EnvString >= '0' && *EnvString <= '9')
- {
- Buffer[1] = *EnvString; // DMA Channel No. is 2 digits
- Buffer[2] = NULL;
- EnvString++;
- }
- else
- Buffer[1] = NULL; // DMA Channel No. is 1 digit
-
- if (SaveChar == 'D' || SaveChar == 'd')
- *DMAChan8Bit = atoi(Buffer); // 8-Bit DMA channel
- else
- *DMAChan16Bit = atoi(Buffer); // 16-bit DMA channel
-
- DMAChannelNotFound = FALSE;
- break;
-
-
- case 'I': // IRQ number
- case 'i':
- EnvString++;
- Buffer[0] = *EnvString;
- EnvString++;
-
- if (*EnvString >= '0' && *EnvString <= '9')
- {
- Buffer[1] = *EnvString; // IRQ No. is 2 digits
- Buffer[2] = NULL;
- EnvString++;
- }
- else
- Buffer[1] = NULL; // IRQ No. is 1 digit
-
- *IRQNumber = atoi(Buffer);
- IRQNotFound = FALSE;
- break;
-
-
- default:
- EnvString++;
- break;
- }
-
- } while (*EnvString != NULL);
-
- if (DMAChannelNotFound || IOPortNotFound || IRQNotFound)
- return(FAIL);
-
- return(SUCCESS);
- }
-
-
- /*************************************************************************
- *
- * FUNCTION: DSPOut()
- *
- * DESCRIPTION: Writes the value passed to this function to the DSP chip.
- *
- *************************************************************************/
- void DSPOut(int IOBasePort, int WriteValue)
- {
- // Wait until DSP is ready before writing the command
- while ((inp(IOBasePort + DSP_WRITE_PORT) & 0x80) != 0);
-
- outp(IOBasePort + DSP_WRITE_PORT, WriteValue);
- return;
- }
-
-
- /*************************************************************************
- *
- * FUNCTION: ResetDSP()
- *
- * DESCRIPTION: Self explanatory
- *
- *************************************************************************/
- char ResetDSP(int IOBasePort)
- {
- unsigned long i;
-
- outp(IOBasePort + DSP_RESET, (int) 1);
- delay(10); // wait 10 mS
- outp(IOBasePort + DSP_RESET, (int) 0);
-
- // Wait until data is available
- while ((inp(IOBasePort + DSP_DATA_AVAIL) & 0x80) == 0);
-
- if (inp(IOBasePort + DSP_READ_PORT) == DSP_READY)
- return(SUCCESS);
- return(FAIL);
- }
-
-
-
- /**************************************************************************
- *
- * FUNCTION: SetMixer()
- *
- * DESCRIPTION: Self explanatory
- *
- **************************************************************************/
- void SetMixer(void)
- {
- outp(gIOPort + MIXER_ADDR, (int) MIC_VOLUME);
- outp(gIOPort + MIXER_DATA, (int) 0x00);
-
- outp(gIOPort + MIXER_ADDR, (int) VOICE_VOLUME);
- outp(gIOPort + MIXER_DATA, (int) 0xFF);
-
- outp(gIOPort + MIXER_ADDR, (int) MASTER_VOLUME);
- outp(gIOPort + MIXER_DATA, (int) 0xFF);
-
- return;
- }
-