📄 playmix.cpp
字号:
/****************************************************************************
* *
* (C) Copyright Creative Technology Ltd. 1994-1996. All rights 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. *
* *
****************************************************************************/
/*
* Program to demonstrate mixing of two VOC files, pitch shifting one of them,
* and playing them using low-level programming.
*
* D. Kaden 8/4/93
*
* Modified 1/31/94
* Got rid of PLAYMIX1 and PLAYMIX2, made PLAYMIX. Made it take file names
* from the command line.
*
*/
#define AUTOINIT /* Define this for auto-initialized mode (no clicks).
* Undefine it for non-auto-init mode, but it will
* click slightly. Auto-init is only available on
* cards with DSP versions of 2 or above.
*/
#include <stdio.h>
#include <dos.h>
#include <stdlib.h>
#include <conio.h>
#include <dir.h>
#include <string.h>
#include "mix.h"
#define SPACEKEY ' '
#define ESC 27
#define LEFTKEY 75 /* really 0, 75 */
#define RIGHTKEY 77
#define F1KEY 59
#define F2KEY 60
#define F3KEY 61
#define F4KEY 62
#define dspReady 0xAA /* byte returned by DSP when ready after reset */
/***
*** WARNING: THESE CONSTANTS DEFINE HOW THE PROGRAM ASSUMES THE BOARD TO
*** BE CONFIGURED. MAKE SURE THESE MATCH YOUR BOARD BEFORE YOU RUN THIS
*** PROGRAM. DON'T USE IRQ 10, THOUGH, BECAUSE THIS PROGRAM ISN'T DESIGNED
*** FOR IT (it uses the other 8259 PIC).
***/
#define SBADDR 0x220 /* Sound Blaster base port address */
#define SBIRQNUM 5 /* Sound Blaster IRQ number */
#define SBDMACHAN 1 /* Sound Blaster DMA channel */
#define IRQ (SBIRQNUM+8) /* interrupt number corresponding to IRQ number */
#define IRQmask (1 << SBIRQNUM) /* interrupt mask corresponding to IRQ number */
/* port addresses for Sound Blaster */
#define dspaddrWrBuf (SBADDR+0xC) /* DSP Write Buffer address */
#define dspaddrReadData (SBADDR+0xA) /* DSP Read Data address */
#define dspaddrDataAvail (SBADDR+0xE) /* DSP Data Available address */
#define mixeraddr (SBADDR+0x4) /* mixer address (register select) port */
#define mixerdata (SBADDR+0x5) /* mixer data port */
#define dspaddrReset (SBADDR+0x6) /* dsp reset port address */
/* port addresses for 8237 DMA controller and its page registers */
#define DMACMaskReg 0xA
#define DMACModeReg 0xB
#define DMACFFReg 0xC
#define DMA1PAGE 0x83
#define DMA1ADDR 0x2
#define DMA1COUNT 0x3
#define DMA3PAGE 0x82
#define DMA3ADDR 0x6
#define DMA3COUNT 0x7
#if SBDMACHAN==1
#define DMAPAGE DMA1PAGE
#define DMAADDR DMA1ADDR
#define DMACOUNT DMA1COUNT
#elif SBDMACHAN==3
#define DMAPAGE DMA3PAGE
#define DMAADDR DMA3ADDR
#define DMACOUNT DMA3COUNT
#else
ERROR: No constants defined for DMA channel.
#endif
/* constants for 8250 Peripheral Interrupt Controller */
#define PICMODE 0x20
#define PICMASK 0x21
#define PICEOI 0x20
/* some Sound Blaster commands */
#define dspcmdDirect8DAC 0x10 /* DSP Direct 8-bit DAC command */
#define dspcmdDMADAC 0x14 /* DSP 8-bit DAC DMA transfer */
#define dspcmdDirectADC 0x20 /* DSP Direct ADC command */
#define dspcmdDMAADC 0x24 /* DSP 8-bit ADC DMA transfer */
#define dspcmdTimeConst 0x40 /* set time constant for DMA transfer */
#define dspcmdBlockSize 0x48 /* DMA transfer block size */
#define dspcmdHaltDMA 0xD0 /* stop DMA */
#define dspcmdContDMA 0xD4 /* continue DMA */
#define dspcmdSpeakerOn 0xD1 /* turn speaker on */
#define dspcmdSpeakerOff 0xD3 /* turn speaker off */
#define dspcmdSpeakerStat 0xD8 /* get speaker status */
#define dspcmdAUTODMADAC 0x1C /* DSP 8-bit ADC Auto-init DMA transfer */
#define dspcmdAUTODMAADC 0x2C /* DSP 8-bit ADC Auto-init DMA transfer */
#define dspcmdHaltAuto 0xDA /* halt auto-init DMA transfer */
/* Sound Blaster mixer registers */
#define MASTERVOL 0x22
#define VOCVOL 0x04
#define FMVOL 0x26
#define CDVOL 0x28
#define MICVOL 0x0A
#define ADCSELECT 0x0C
#define BUFSIZE 1024*1
unsigned char buf0[BUFSIZE], buf1[BUFSIZE];
unsigned int page0, page1, offset0, offset1, count=BUFSIZE-1, outtc=166 /* 11K */;
int BusyBuf, BufToFill;
VOCfile v1,v2;
char filename1[MAXPATH], filename2[MAXPATH];
boolean v2pitchshift=false;
boolean v1repeat=true;
boolean v2repeat=true;
unsigned int v2skip=20;
void dspout(unsigned int val)
/* Outputs a byte to the dsp. This polls the write buffer status bit, waits
* for it to be 0, then outputs the value to the write buffer.
* The C equivalent code is:
* while (inp(dspaddrWrBuf) & 0x80)
* ;
* outp(dspaddrWrBuf,val);
*/
{
asm {
mov dx,dspaddrWrBuf
}
test:
asm {
in al,dx
or al,al
js test
mov al,byte ptr val
out dx,al
}
}
void dspin(unsigned int *val)
/* Read a value from the dsp. Poll the Data Available bit until it is 1,
* then read the value from the read data port.
*/
{
while (!(inp(dspaddrDataAvail) & 0x80)) {
;
}
*val=inp(dspaddrReadData);
}
void SegToPhys(unsigned char far *ptr, unsigned long size, unsigned long *physaddr, unsigned long *endaddr)
/* Converts a segmented address to a physical memory address. Also gives
* the end address ('size' bytes past ptr).
*/
{
*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 page/offset type of address for
* DMA (NOT the same as a segmented address). It returns the number of bytes
* between physaddr and endaddr (the DMA buffer) that are in the DMA page.
* If the return value is less than the buffer size (endaddr-physaddr+1),
* the DMA buffer is crossing a page boundary and must be discarded. In that
* case, allocate some more memory and try again.
*/
{
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 SetupOutputDMA(unsigned int page, unsigned int ofs, int count,
unsigned char tc)
/* This programs the Sound Blaster board and the DMA controller to play
* from a buffer.
*
*/
{
/**** 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.
****/
#ifdef AUTOINIT
/* In this mode, the DMAC and DSP are only programmed once. The DSP is
* programmed for a block size half of what the DMAC is programmed for,
* so interrupts are generated every half-buffer. In this program, buf0
* and buf1 are both half buffers. The DMAC will be programmed to trans-
* fer 2*BUFSIZE, while the DSP is programmed for just BUFSIZE.
*/
outp(DMACMaskReg,4 | SBDMACHAN); /* mask off dma channel */
outp(DMACFFReg,0); /* clear flip-flop */
/* This next value to DMAC is different between auto-init and non-auto-init.
* (0x58 vs. 0x48) */
outp(DMACModeReg,0x58 | SBDMACHAN); /* set mode for DAC, channel 1 */
outp(DMAADDR,(ofs & 0xFF)); /* low byte base addr */
outp(DMAADDR,(ofs >> 8)); /* high byte base addr */
outp(DMAPAGE,page); /* physical page number */
outp(DMACOUNT,((count-1) & 0xFF)); /* count low byte (ENTIRE buffer) */
outp(DMACOUNT,((count-1) >> 8)); /* count high byte */
outp(DMACMaskReg, SBDMACHAN); /* enable dma channel 1 */
dspout(dspcmdTimeConst); /* time constant command */
dspout(tc); /* the time constant (for sample rate) */
dspout(dspcmdBlockSize); /* set block size= HALF DMA buffer size */
dspout((count/2-1) & 0xFF);
dspout((count/2-1) >> 8);
dspout(dspcmdAUTODMADAC); /* program dsp for auto-init dma mode dac */
#else
outp(DMACMaskReg,4 | SBDMACHAN); /* mask off dma channel */
outp(DMACFFReg,0); /* clear flip-flop */
outp(DMACModeReg,0x48 | SBDMACHAN); /* set mode for DAC, channel 1 */
outp(DMAADDR,(ofs & 0xFF)); /* low byte base addr */
outp(DMAADDR,(ofs >> 8)); /* high byte base addr */
outp(DMAPAGE,page); /* physical page number */
outp(DMACOUNT,(count & 0xFF)); /* count low byte */
outp(DMACOUNT,(count >> 8)); /* count high byte */
outp(DMACMaskReg, SBDMACHAN); /* enable dma channel 1 */
dspout(dspcmdTimeConst); /* time constant command */
dspout(tc); /* the time constant (for sample rate) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -