📄 delay.c
字号:
/* -------------------------------------------------------------------------- */
/* */
/* (C) Copyright Creative Technology Ltd 1994-1996. All right reserved */
/* */
/* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY */
/* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR */
/* PURPOSE. */
/* */
/* You have a royalty-free right to use, modify, reproduce and */
/* distribute the Sample Files (and/or any modified version) in */
/* any way you find useful, provided that you agree that */
/* Creative has no warranty obligations or liability for any Sample Files. */
/* */
/*----------------------------------------------------------------------------*/
/* DELAY.C
*
* D. Kaden 6/22/94
*
* Copyright (c) 1993, 1994 Creative Labs, Inc.
*
* This program records and plays simultaneously using two Sound Blaster
* cards.
*
* In addition to the normal BLASTER environment variable, you should also
* have a BLASTER2 environment variable for the second Sound Blaster card.
*
* For example, your environment should look something like this:
*
* BLASTER=A220 I5 D1 H5 P330 T6
* BLASTER2=A240 I7 D3 H6 P330 T6
*
* The first card (BLASTER) does the recording. The second (BLASTER2) does
* the playback.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>
#include <dos.h>
#include <mem.h>
#include <fcntl.h>
#include "mydefs.h"
#include "sbdefs.h"
#define FILENAME "delay.raw"
// Structure for storing data associated with each SB card.
typedef struct {
int base, // SB card base I/O address
MixerAddr, MixerData, // mixer chip addresses
Reset, WrBuf, // DSP addresses
DataAvail, ReadData, // DSP addresses
irq, // IRQ number
dma8, // 8-bit DMA channel
dma16, // 16-bit DMA channel
midi, // midi port
cardtype; // card type number from environment
void interrupt (*intvecsave)(); // previous interrupt vector
int intrnum; // interrupt number
int intrmask; // interrupt mask
} cardinfo;
// Structure for retrieving DMA controller register port addresses.
typedef struct {
unsigned char addr, count, page, mask, mode, FF;
} dmaportstruct;
// Constant array that defines port addresses for DMA controller registers.
const dmaportstruct dmaportarray[8] = {
{0x0, 0x1, 0x87, 0xA, 0xB, 0xC}, // chan 0
{0x2, 0x3, 0x83, 0xA, 0xB, 0xC}, // chan 1
{0x4, 0x5, 0x81, 0xA, 0xB, 0xC}, // chan 2 DON'T USE
{0x6, 0x7, 0x82, 0xA, 0xB, 0xC}, // chan 3
{0x0, 0x0, 0x00, 0xD4, 0xD6, 0xD8}, // chan 4 DON'T USE
{0xC4, 0xC6, 0x8B, 0xD4, 0xD6, 0xD8}, // chan 5
{0xC8, 0xCA, 0x89, 0xD4, 0xD6, 0xD8}, // chan 6
{0xCC, 0xCE, 0x8A, 0xD4, 0xD6, 0xD8} // chan 7
};
#define BUFSIZE (8*1024) // size of the DMA buffer
#define NUMBUFS 2
#define TIMECONST 165 // 165 = approx 11.5 KHz sample rate
cardinfo card1, card2;
// It would be better to dynamically allocate the DMA buffers
// to guarantee that they don't cross a DMA page boundary.
// This is quick and dirty, but it usually works.
unsigned char recbuf[BUFSIZE], // recording DMA buffer
playbuf[BUFSIZE]; // playback DMA buffer
unsigned int inpage, outpage, // DMA pages for input and output buffers
inoffset, outoffset, // offsets for DMA input and output buffers
count = BUFSIZE; // size of the DMA buffers
char *inbuffers[NUMBUFS]; // Input (recording) buffers to
// transfer data from DMA buffer
// to disk.
char *outbuffers[NUMBUFS]; // Output (playback) buffers to
// transfer data from disk to DMA
// buffer.
int NumFilledInBufs = 0, // number of filled input buffers
NumFilledOutBufs = 0, // number of filled output buffers
NumRecBufToFill = 0, // next input buffer to fill from DMA buffer
NumRecBufToEmpty = 0, // next input buffer to copy to disk
NumPlayBufToFill = 0, // next output buffer to fill from disk
NumPlayBufToEmpty = 0, // next output buffer to copy to DMA buffer
HalfBufToEmpty = 0, // which half of input DMA buffer to empty
HalfBufToFill = 0, // which half of output DMA buffer to fill
NumBufsWritten = 0; // number of buffers written to disk
int base16(char **str, int *val)
/* Takes a double pointer to a string, interprets the characters as a
* base-16 number, and advances the pointer.
* Returns 0 if successful, 1 if not.
*/
{
char c;
int digit;
*val = 0;
while ((**str != ' ') && (**str != '\0')) {
c = toupper(**str);
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'A' && c <= 'F')
digit = c - 'A' + 10;
else
return 1; // error in string
*val = *val * 16 + digit;
(*str)++;
}
return 0;
}
int base10(char **str, int *val)
/* Takes a double pointer to a string, interprets the characters as a
* base-10 number, and advances the pointer.
* Returns 0 if successful, 1 if not.
*/
{
char c;
int digit;
*val = 0;
while ((**str != ' ') && (**str != '\0')) {
c = toupper(**str);
if (c >= '0' && c <= '9')
digit = c - '0';
else
return 1; // error in string
*val = *val * 10 + digit;
(*str)++;
}
return 0;
}
int ReadBlasterEnv(char *name, int *port, int *irq, int *dma8, int *dma16,
int *midi, int *cardtype)
/* Gets the Blaster environment statement and stores the values in the
* variables whose addresses were passed to it.
*
* Input:
* name - environment name to get, usually "BLASTER"
*
* Returns:
* 0 if successful
* 1 if there was an error reading the port address.
* 2 if there was an error reading the IRQ number.
* 3 if there was an error reading the 8-bit DMA channel.
* 4 if there was an error reading the 16-bit DMA channel.
* 5 if there was an error reading the MIDI address.
* 6 if there was an error reading the card type number.
*/
{
char *env;
unsigned val;
int digit;
env = getenv(name);
if (!env)
return 7; // if there was no environment entry for name.
while (*env) {
switch(toupper( *(env++) )) {
case 'A':
if (base16(&env, port))
return 1;
break;
case 'I':
if (base10(&env, irq))
return 2;
break;
case 'D':
if (base10(&env, dma8))
return 3;
break;
case 'H':
if (base10(&env, dma16))
return 4;
break;
case 'P':
if (base16(&env, midi))
return 5;
break;
case 'T':
if (base10(&env, cardtype))
return 6;
break;
default:
break;
}
}
return 0;
}
void SegToPhys(unsigned char far *ptr, unsigned long size, unsigned long *physaddr, unsigned long *endaddr)
// Converts a segmented address to a physical memory address.
{
*physaddr=(unsigned long)(((unsigned long)FP_SEG(ptr) << 4) + FP_OFF(ptr));
*endaddr = *physaddr+size-1;
}
unsigned int PhysToPage(unsigned long physaddr, unsigned long endaddr, unsigned int *page, unsigned int *offset)
/* Converts a physical memory address to a DMA-page address. Returns the
* number of bytes in this block of memory that aren't in a different DMA
* page.
* INPUT:
* physaddr - a physical (not segmented) address specifying the start
* of the DMA buffer.
* endaddr - a physical address specifying the last byte of the DMA
* buffer.
* OUTPUT:
* page - the DMA page of the buffer.
* offset - the offset of the buffer.
* RETURN VALUE:
* The minimum of (endaddr-physaddr+1, number of bytes in this page
* starting at physaddr).
*/
{
unsigned int remaining;
*page=physaddr >> 16;
*offset=physaddr & 0xFFFF;
remaining=0xFFFF - *offset + 1; /* bytes remaining in page */
if (remaining>endaddr-physaddr) /* if more bytes left in page than in buffer */
return endaddr-physaddr+1; /* return number of bytes in buffer */
else return remaining; /* else return number of bytes left in page */
}
void dspout(cardinfo *card, unsigned int val)
// Output a byte to the Sound Blaster Digital Sound Processor.
{
while (inp(card->WrBuf) & 0x80)
;
outp(card->WrBuf, val);
}
boolean dspin(cardinfo *card, unsigned int *val)
// Read a byte from the Digital Sound Processor.
{
while (!(inp(card->DataAvail) & 0x80)) {
;
}
*val=inp(card->ReadData);
return true; // used to be meaningful
}
void SetupDMA(int dmachan, unsigned short page, unsigned short ofs,
unsigned short DMAcount, unsigned char dmacmd)
// This programs the DMA controller.
// NOTE: This function supports 8-bit DMA channels ONLY (because this pro-
// gram only uses 8-bit voice mode. The SetupDMA function in the program
// DELAY2.C supports both 8- and 16-bit DMA channels.
{
dmaportstruct dmaports;
/**** There MUST be parens around the values to be output, or the compiler
**** will convert them to unsigned chars BEFORE it does the operations
**** on them.
****/
dmaports = dmaportarray[dmachan];
outp(dmaports.mask, 4 | dmachan); // mask off dma channel
outp(dmaports.FF, 0); // clear flip-flop
/* This next value to DMAC is different between auto-init and non-auto-init.
* (0x58 vs. 0x48) */
outp(dmaports.mode , dmacmd | dmachan); // set mode
outp(dmaports.addr, (ofs & 0xFF)); // low byte base addr
outp(dmaports.addr, (ofs >> 8)); // high byte base addr
outp(dmaports.page, page); // physical page number
outp(dmaports.count,((DMAcount-1) & 0xFF)); // count low byte
outp(dmaports.count,((DMAcount-1) >> 8)); // count high byte
outp(dmaports.mask, dmachan); // enable dma channel
}
void SetupDSP(cardinfo *card, unsigned char dspcmd, unsigned short DSPcount,
unsigned char tc)
// Programs the Sound Blaster card.
{
// Program the time constant.
dspout(card, dspcmdTimeConst);
dspout(card, tc);
// Program the DMA buffer size.
dspout(card, dspcmdBlockSize);
dspout(card, (DSPcount-1) & 0xFF);
dspout(card, (DSPcount-1) >> 8);
// Send the play or record command.
dspout(card, dspcmd);
}
void SetupInputDMA(unsigned int page, unsigned int ofs, int count,
unsigned char tc)
// Program the DMA Controller and Sound Blaster for DMA input.
{
SetupDMA(card1.dma8, page, ofs, count, DMAMODEWRITE);
SetupDSP(&card1, dspcmdAUTODMAADC, count/2, tc);
}
void SetupOutputDMA(unsigned int page, unsigned int ofs, int count,
unsigned char tc)
// Program the DMA Controller and Sound Blaster for DMA output.
{
SetupDMA(card2.dma8, page, ofs, count, DMAMODEREAD);
SetupDSP(&card2, dspcmdAUTODMADAC, count/2, tc);
}
void SetMixer(cardinfo *card1, cardinfo *card2)
// Resets the SB cards and selects the proper input.
{
unsigned int val;
/* reset sb card #1 */
outp(card1->Reset,1);
delay(1);
outp(card1->Reset,0);
if (dspin(card1, &val))
if (val!=dspReady)
printf("Sound Blaster card #1 not ready.");
switch (card1->cardtype) {
case 2:
case 4: // SBPRO
outp(card1->MixerAddr, ADCSELECT);
outp(card1->MixerData, 0); // select microphone
dspout(card1, 0xD3); // turn speaker off
break;
case 6: // SB16
outp(card1->MixerAddr, SB16INPUTL);
outp(card1->MixerData, 1); // select microphone
outp(card1->MixerAddr, SB16INPUTR);
outp(card1->MixerData, 1); // select microphone
break;
}
/* reset sb card #2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -