📄 wavepdd.c
字号:
pwh = pwh->lpNext;
if (pwh == NULL)
break;
pm16 = (SAMPLE_16_MONO *)(pwh->lpData + pwh->dwBytesRecorded);
}
sample = ((LONG)pm16->sample*Volume) >> 22;
pbuf[a] = (UINT16)(sample + AUDIO_SILENCE_PLAY);
pm16++;
pwh->dwBytesRecorded += 2;
}
// fill rest space
while (a < nSamples) {
pbuf[a++] = AUDIO_SILENCE_PLAY;
}
}
static void
AudOut_FillBufferS16(PWAVEHDR pwh, ULONG page, ULONG nSamples)
{
ULONG a;
LONG sample;
SAMPLE_16_STEREO *ps16;
volatile UINT16 *pbuf;
if (pwh == NULL) {
return;
}
pbuf = (volatile UINT16 *)dma_page[WAPI_OUT][page];
ps16 = (SAMPLE_16_STEREO *)(pwh->lpData + pwh->dwBytesRecorded);
for (a=0; a<nSamples; a++) {
if (pwh->dwBytesRecorded >= pwh->dwBufferLength) {
pwh = pwh->lpNext;
if (pwh == NULL)
break;
ps16 = (SAMPLE_16_STEREO *)(pwh->lpData + pwh->dwBytesRecorded);
}
sample = ((LONG)ps16->sample_left + (LONG)ps16->sample_right) >> 1;
sample = (sample*Volume) >> 22;
pbuf[a] = (UINT16)(sample + AUDIO_SILENCE_PLAY);
ps16++;
pwh->dwBytesRecorded += 4;
}
// fill rest space
while (a < nSamples) {
pbuf[a++] = AUDIO_SILENCE_PLAY;
}
}
static void
AudOut_Start(PWAVEHDR pwh)
{
(*FillBuf)(pwh, 0, AUDIO_DMA_PAGE_SIZE/2);
(*FillBuf)(pwh, 1, AUDIO_DMA_PAGE_SIZE/2);
v_fMoreData[WAPI_OUT] = TRUE; // we expect to have more data coming...
v_nNextPage[WAPI_OUT] = 0;
// setup output DMA address
REG32(pVRC4173+AIUOBALREG) = physDMAbase[WAPI_OUT];
REG32(pVRC4173+AIUOALREG) = physDMAbase[WAPI_OUT];
// enable output DMA
InterlockedAndOr16((volatile UINT16 *)(pVRC4173+DMA73MSKREG), 0xFFFF, DMAMSKAOUT);
// enable AIU Tclock
AIU_ClockEnable(WAPI_OUT);
// set output sampling rate
switch (SampleRate[WAPI_OUT]) {
case 8000:
REG16(pVRC4173+AIUSCNVRREG) = CNVR_8K;
break;
case 11025:
REG16(pVRC4173+AIUSCNVRREG) = CNVR_11K;
break;
case 22050:
REG16(pVRC4173+AIUSCNVRREG) = CNVR_22K;
break;
case 44100:
REG16(pVRC4173+AIUSCNVRREG) = CNVR_44K;
break;
default:
RETAILMSG(1, (TEXT("AudOut_Start: bad sampling rate\r\n")));
}
// D/A Vref power on
REG16(pVRC4173+AIUSCNTREG) = DAENAIU; // SSTOPEN=0, DAENAIU=1
RtcWait(1); // wait Vref stable (5us)
// clear output interrupts
REG16(pVRC4173+AIUINTREG) = AIUINTSIDLE | AIUINTS | AIUINTSEND;
// activate AIU output
InterlockedAndOr16((volatile UINT16 *)(pVRC4173+AIUSEQREG), 0xFFFF, AIUSEN);
}
// prepare next output DMA buffer
static void
AudOut_Continue(PWAVEHDR pwh)
{
// fill DMA buffer
(*FillBuf)(pwh, v_nNextPage[WAPI_OUT], AUDIO_DMA_PAGE_SIZE/2);
// flip page
v_nNextPage[WAPI_OUT] ^= 1;
}
// stop output at next DMA buffer boundary
static void
AudOut_EndOfData(void)
{
v_fMoreData[WAPI_OUT] = FALSE;
// SSTOPEN=1
REG16(pVRC4173+AIUSCNTREG) = SSTOPEN | DAENAIU;
}
// stop output immediately
static void
AudOut_Stop(void)
{
v_fMoreData[WAPI_OUT] = FALSE;
AudOut_Standby();
}
// power down AIU output
static void
AudOut_Standby(void)
{
// disable AIU output
InterlockedAndOr16((volatile UINT16 *)(pVRC4173+AIUSEQREG), ~AIUSEN, 0);
// clear output interrupts
REG16(pVRC4173+AIUINTREG) = AIUINTSIDLE | AIUINTS | AIUINTSEND;
// D/A Vref power off
REG16(pVRC4173+AIUSCNTREG) = 0; // SSTOPEN=0, DAENAIU=0
// disable output DMA
InterlockedAndOr16((volatile UINT16 *)(pVRC4173+DMA73MSKREG), ~DMAMSKAOUT, 0);
AIU_ClockDisable(WAPI_OUT);
}
//
// Wavedev DDSI functions
// ---------------------------------------------------------------------------
//
// @doc WPDD_EXT
//
// @func AUDIO_STATE | PDD_AudioGetInterruptType |
// Determines the cause of the SYSINTR_AUDIO interrupt. The PDD
// should report the status of the playback or record circuitry.
// The interrupt should occur as a result of the audio DMA passing
// a buffer boundary. See the <t AUDIO_STATE> return codes for the
// possible states that an interrupt can represent.
//
// @rdesc Returns one of the <t AUDIO_STATE> values.
//
// ---------------------------------------------------------------------------
AUDIO_STATE
PDD_AudioGetInterruptType(VOID)
{
UINT16 intr;
AUDIO_STATE stat;
stat = AUDIO_STATE_IGNORE;
intr = REG16(pVRC4173+AIUINTREG);
// input interrupts
if (intr & AIUINTM) {
stat |= AUDIO_STATE_IN_RECORDING;
REG16(pVRC4173+AIUINTREG) = AIUINTM;
} else if (intr & (AIUINTMIDLE | AIUINTMEND)) {
stat |= AUDIO_STATE_IN_OVERFLOW;
REG16(pVRC4173+AIUINTREG) = AIUINTMIDLE | AIUINTMEND;
}
// output interrupts
if (intr & AIUINTS) {
if (v_fMoreData[WAPI_OUT]) {
stat |= AUDIO_STATE_OUT_PLAYING;
} else {
// stop AIU output
InterlockedAndOr16((volatile UINT16 *)(pVRC4173+AIUSEQREG), ~AIUSEN, 0);
stat |= AUDIO_STATE_OUT_STOPPED;
}
REG16(pVRC4173+AIUINTREG) = AIUINTS;
} else if (intr & (AIUINTSIDLE | AIUINTSEND)) {
stat |= AUDIO_STATE_OUT_UNDERFLOW;
REG16(pVRC4173+AIUINTREG) = AIUINTSIDLE | AIUINTSEND;
}
return stat;
}
//------------------------------------------------------------------------------
//
// @doc WPDD_EXT
//
// @func DWORD | PDD_AudioMessage | This function sends messages to the
// Audio PDD. Custom functions can be accessed via this routine.
// This function is a pass through of both the <f waveOutMessage> and
// <f waveInMessage> functions.
//
// @parm UINT | uMsg | The message to send.
//
// @parm DWORD | dwParam1 | Parameter 1.
//
// @parm DWORD | dwParam2 | Parameter 2.
//
// @rdesc Dependent on uMsg.
//
//------------------------------------------------------------------------------
DWORD
PDD_AudioMessage(UINT uMsg, DWORD dwParam1, DWORD dwParam2)
{
return MMSYSERR_NOTSUPPORTED;
}
// ---------------------------------------------------------------------------
//
// @doc WPDD_EXT
//
// @func BOOL | PDD_AudioInitialize | Initializes
// the audio device for operation. PDD_AudioInitialize performs
// any one time initialization. This may include allocating memory
// for device registers and any DMA buffer pages, etc.
//
// @rdesc TRUE if successful; otherwise, FALSE.
//
// ---------------------------------------------------------------------------
BOOL
PDD_AudioInitialize(DWORD dwIndex)
{
if (!MapVR4122intio()) {
ERRMSG("PDD_AudioInitialize: VR4122 internal I/O map failed");
return FALSE;
}
if (!Find4173()) {
ERRMSG("PDD_AudioInitialize: VRC4173 not found");
FreeVR4122intio();
return FALSE;
}
// get DMA buffer physical address
// HAL sets initial values to registers at VRC4173init()
physDMAbase[WAPI_IN ] = REG32(pVRC4173+AIUIBALREG) & ~(AUDIO_DMA_PAGE_SIZE-1);
physDMAbase[WAPI_OUT] = REG32(pVRC4173+AIUOBALREG) & ~(AUDIO_DMA_PAGE_SIZE-1);
RETAILMSG(1, (TEXT("in=%X, out=%X\r\n"), physDMAbase[WAPI_IN], physDMAbase[WAPI_OUT]));
//
// Map both DMA pages into the local address space
//
dma_page[WAPI_IN][0] = VirtualAlloc(0, AUDIO_DMA_PAGE_SIZE * 2,
MEM_RESERVE, PAGE_NOACCESS);
if (!dma_page[WAPI_IN][0]) {
ERRMSG("PDD_AudioInitialize: DMA input buffer allocation failed");
return FALSE;
}
if (!VirtualCopy((PVOID) dma_page[WAPI_IN][0],
(PVOID)(physDMAbase[WAPI_IN]>>8),
AUDIO_DMA_PAGE_SIZE * 2,
PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)) {
ERRMSG("PDD_AudioInitialize: DMA input buffer VirtualCopy failed");
return FALSE;
}
dma_page[WAPI_OUT][0] = VirtualAlloc(0, AUDIO_DMA_PAGE_SIZE * 2,
MEM_RESERVE, PAGE_NOACCESS);
if (!dma_page[WAPI_OUT][0]) {
ERRMSG("PDD_AudioInitialize: DMA output buffer allocation failed");
return FALSE;
}
if (!VirtualCopy((PVOID) dma_page[WAPI_OUT][0],
(PVOID)(physDMAbase[WAPI_OUT]>>8),
AUDIO_DMA_PAGE_SIZE * 2,
PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)) {
ERRMSG("PDD_AudioInitialize: DMA output buffer VirtualCopy failed");
return FALSE;
}
dma_page[WAPI_IN ][1] = dma_page[WAPI_IN ][0] + AUDIO_DMA_PAGE_SIZE;
dma_page[WAPI_OUT][1] = dma_page[WAPI_OUT][0] + AUDIO_DMA_PAGE_SIZE;
InPowerHandler = FALSE;
// initialize DMA address
REG32(pVRC4173+AIUIBALREG) = physDMAbase[WAPI_IN];
REG32(pVRC4173+AIUOBALREG) = physDMAbase[WAPI_OUT];
REG32(pVRC4173+AIUIALREG) = physDMAbase[WAPI_IN];
REG32(pVRC4173+AIUOALREG) = physDMAbase[WAPI_OUT];
// enable AIU Tclock
AIU_ClockEnable(WAPI_IN);
AIU_ClockEnable(WAPI_OUT);
//reset AIU
REG16(pVRC4173+AIUSEQREG) = AIURST;
RtcWait(1);
REG16(pVRC4173+AIUSEQREG) = 0;
RtcWait(1);
// force AIU into powerdown state
AudIn_Standby();
AudOut_Standby();
v_nVolume = 0xFFFFFFFF;
Volume = 0x0000FFFF;
v_nNextPage[WAPI_IN ] = 0;
v_nNextPage[WAPI_OUT] = 0;
v_fMoreData[WAPI_IN ] = FALSE;
v_fMoreData[WAPI_OUT] = FALSE;
initialized = TRUE;
return TRUE;
}
// ---------------------------------------------------------------------------
//
// @doc WPDD_EXT
//
// @func VOID | PDD_AudioDeinitialize |
// Powers down and disconnects the audio device. This function is
// called if the audio driver gets unloaded from the OS or if the
// driver loading process fails.
//
// @rdesc None.
//
// ---------------------------------------------------------------------------
VOID
PDD_AudioDeinitialize(VOID)
{
//
// The driver is being unloaded. Deinitialize. Free up memory, etc.
//
if (!initialized)
return;
// put input/output AIU into powerdown state
AudIn_Standby();
AudOut_Standby();
FreeVR4122intio();
Free4173();
VirtualFree((void *)dma_page[WAPI_IN][0], 0, MEM_RELEASE);
VirtualFree((void *)dma_page[WAPI_OUT][0], 0, MEM_RELEASE);
initialized = FALSE;
}
// ---------------------------------------------------------------------------
//
// @doc WPDD_EXT
//
// @func VOID | PDD_AudioPowerHandler |
// Powers up or down the PDD.
//
// @parm BOOL | power_down | If the value of power_down is TRUE, the audio
// device is shut down. If FALSE, then this is a power_up request.
//
// @rdesc None.
//
// ---------------------------------------------------------------------------
VOID
PDD_AudioPowerHandler(BOOL power_down)
{
InPowerHandler = TRUE;
if (power_down) {
// power down request
AudIn_Standby();
AudOut_Standby();
} else {
// power up request
}
InPowerHandler = FALSE;
}
// ---------------------------------------------------------------------------
//
// @doc WPDD_EXT
//
// @func MMRESULT | PDD_WaveProc | This function sends messages to the
// Audio PDD. It is used to implement all of the waveOut and waveIn
// functions at the PDD level.
//
// @parm WAPI_INOUT | apidir | The direction of the API requested.
// See the <t WAPI_INOUT> definition.
//
// @parm DWORD | dwCode | The function to execute.
//
// @parm DWORD | dwParam1 | Parameter 1 of the function.
//
// @parm DWORD | dwParam2 | Parameter 2 of the function.
//
// @rdesc MMRESULT dependent on dwCode. If dwCode and apidir indicate a
// function that is not supported, the PDD_WaveProc should return
// MMSYSERR_NOTSUPPORTED.
//
// ---------------------------------------------------------------------------
MMRESULT
PDD_WaveProc(WAPI_INOUT apidir, DWORD dwCode, DWORD dwParam1, DWORD dwParam2)
{
MMRESULT mmRet = MMSYSERR_NOERROR;
if (apidir == WAPI_IN) {
// wave input function
switch (dwCode) {
case WPDM_START:
AudIn_Start((PWAVEHDR)dwParam1);
break;
case WPDM_CONTINUE:
AudIn_Continue((PWAVEHDR)dwParam1);
break;
case WPDM_STANDBY:
AudIn_Standby();
break;
case WPDM_OPEN:
mmRet = Aud_Open(apidir, (LPWAVEFORMATEX)dwParam1, (BOOL)dwParam2);
break;
case WPDM_CLOSE:
mmRet = Aud_Close(apidir);
break;
case WPDM_GETDEVCAPS:
mmRet = Aud_GetDevCaps(apidir, (PVOID)dwParam1, (UINT)dwParam2);
break;
case WPDM_STOP:
AudIn_Stop((PWAVEHDR)dwParam1);
break;
default:
mmRet = MMSYSERR_NOTSUPPORTED;
}
} else {
// wave output fuction
switch (dwCode) {
case WPDM_SETVOLUME:
v_nVolume = (ULONG)dwParam1;
Volume = v_nVolume & 0x0000FFFF; // use low 16 bit
break;
case WPDM_GETVOLUME:
*((PULONG)dwParam1) = v_nVolume;
break;
case WPDM_START:
AudOut_Start((PWAVEHDR)dwParam1);
break;
case WPDM_ENDOFDATA:
AudOut_EndOfData();
break;
case WPDM_CONTINUE:
AudOut_Continue((PWAVEHDR)dwParam1);
break;
case WPDM_RESTART:
AudOut_Start((PWAVEHDR)dwParam1);
break;
case WPDM_PAUSE:
AudOut_EndOfData();
break;
case WPDM_STANDBY:
AudOut_Standby();
break;
case WPDM_OPEN:
mmRet = Aud_Open(apidir, (LPWAVEFORMATEX)dwParam1, (BOOL)dwParam2);
break;
case WPDM_CLOSE:
mmRet = Aud_Close(apidir);
break;
case WPDM_GETDEVCAPS:
mmRet = Aud_GetDevCaps(apidir, (PVOID)dwParam1, (UINT)dwParam2);
break;
case WPDM_STOP:
AudOut_Stop();
break;
default:
mmRet = MMSYSERR_NOTSUPPORTED;
}
}
return mmRet;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -