📄 win_snd.c
字号:
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: win_snd.c,v 1.13 2001/04/08 10:15:54 bpereira Exp $
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// $Log: win_snd.c,v $
// Revision 1.13 2001/04/08 10:15:54 bpereira
// no message
//
// Revision 1.12 2001/04/04 20:19:07 judgecutor
// Added support for the 3D Sound
//
// Revision 1.11 2001/01/25 22:15:45 bpereira
// added heretic support
//
// Revision 1.10 2001/01/21 04:33:35 judgecutor
// *** empty log message ***
//
// Revision 1.9 2000/10/27 20:38:21 judgecutor
// - Added the SurroundSound support
//
// Revision 1.8 2000/10/23 17:05:00 judgecutor
// Fixed old bug of midi stream
//
// Revision 1.7 2000/10/08 13:30:03 bpereira
// no message
//
// Revision 1.6 2000/09/28 20:57:22 bpereira
// no message
//
// Revision 1.5 2000/09/01 19:34:38 bpereira
// no message
//
// Revision 1.4 2000/08/10 19:58:05 bpereira
// no message
//
// Revision 1.3 2000/04/16 18:38:07 bpereira
// no message
//
// Revision 1.2 2000/02/27 00:42:12 hurdler
// fix CR+LF problem
//
// Revision 1.1.1.1 2000/02/22 20:32:33 hurdler
// Initial import into CVS (v1.29 pr3)
//
//
// DESCRIPTION:
// interface level code for sound
// uses the midiStream* Win32 functions to play MIDI data with low latency and low
// processor overhead.
//
//-----------------------------------------------------------------------------
#include "../doomdef.h"
#include "win_main.h"
#include <mmsystem.h>
#define DXVERSION
#include <dsound.h>
#include "../command.h"
#include "../i_sound.h"
#include "../s_sound.h"
#include "../i_system.h"
#include "../m_argv.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../doomstat.h"
#include "dx_error.h"
#include "../qmus2mid.h"
#include "mid2strm.h"
#ifdef HW3SOUND
#include "../hardware/hw3sound.h"
#endif
#include "win_dll.h"
//#define TESTCODE // remove this for release version
#ifndef SURROUND
#define SURROUND // comment out this to disable the SurroundSound code
#endif
// DirectSound3D mode
#define HWS_DS3D 1
/* briefly described here for convenience:
typedef struct {
WORD wFormatTag; // WAVE_FORMAT_PCM is the only format accepted for DirectSound:
// this tag indicates Pulse Code Modulation (PCM), an uncompressed format
// in which each samples represents the amplitude of the signal at the time
// of sampling.
WORD nChannels; // either one (mono) or two (stereo)
DWORD nSamplesPerSec; // the sampling rate, or frequency, in hertz.
// Typical values are 11,025, 22,050, and 44,100
DWORD nAvgBytesPerSec; // nAvgBytesPerSec is the product of nBlockAlign and nSamplesPerSec
WORD nBlockAlign; // the number of bytes required for each complete sample, for PCM formats
// is equal to (wBitsPerSample * nChannels / 8).
WORD wBitsPerSample; // gives the size of each sample, generally 8 or 16 bits
WORD cbSize; // cbSize gives the size of any extra fields required to describe a
// specialized wave format. This member is always zero for PCM formats.
} WAVEFORMATEX;
*/
// --------------------------------------------------------------------------
// DirectSound stuff
// --------------------------------------------------------------------------
LPDIRECTSOUND DSnd = NULL;
LPDIRECTSOUNDBUFFER DSndPrimary;
// Stack sounds means sounds put on top of each other, since DirectSound can't play
// the same sound buffer at different locations at the same time, we need to dupli-
// cate existing buffers to play multiple instances of the same sound in the same
// time frame. A duplicate sound is freed when it is no more used. The priority that
// comes from the s_sound engine, is kept so that the lowest priority sounds are
// stopped to make place for the new sound, unless the new sound has a lower priority
// than all playing sounds, in which case the sound is not started.
#define MAXSTACKSOUNDS 32 // this is the absolute number of sounds that
// can play simultaneously, whatever the value
// of cv_numChannels
typedef struct {
LPDIRECTSOUNDBUFFER lpSndBuf;
#ifdef SURROUND
// judgecutor:
// Need for produce surround sound
LPDIRECTSOUNDBUFFER lpSurround;
#endif
int priority;
boolean duplicate;
} StackSound_t;
StackSound_t StackSounds[MAXSTACKSOUNDS];
// --------------------------------------------------------------------------
// Fill the DirectSoundBuffer with data from a sample, made separate so that
// sound data cna be reloaded if a sound buffer was lost.
// --------------------------------------------------------------------------
// win9x version olny
static boolean CopySoundData_win95 (LPDIRECTSOUNDBUFFER dsbuffer, byte* data)
{
LPVOID lpvAudio1; // receives address of lock start
DWORD dwBytes1; // receives number of bytes locked
LPVOID lpvAudio2; // receives address of lock start
DWORD dwBytes2; // receives number of bytes locked
HRESULT hr;
// Obtain memory address of write block.
hr = dsbuffer->lpVtbl->Lock (dsbuffer, 0, 0, &lpvAudio1, &dwBytes1, &lpvAudio2, &dwBytes2, DSBLOCK_ENTIREBUFFER);
// If DSERR_BUFFERLOST is returned, restore and retry lock.
if (hr == DSERR_BUFFERLOST)
{
hr = dsbuffer->lpVtbl->Restore (dsbuffer);
if( FAILED (hr) )
I_Error("Restor fail on %x, %s\n",dsbuffer,DXErrorToString(hr));
hr = dsbuffer->lpVtbl->Lock (dsbuffer, 0, 0, &lpvAudio1, &dwBytes1, NULL, NULL, DSBLOCK_ENTIREBUFFER);
if( FAILED (hr) )
I_Error("Lock fail(2) on %x, %s\n",dsbuffer,DXErrorToString(hr));
}
else
if( FAILED (hr) )
I_Error("Lock fail(1) on %x, %s\n",dsbuffer,DXErrorToString(hr));
// copy wave data into the buffer (note: dwBytes1 should equal to dsbdesc->dwBufferBytes ...)
CopyMemory (lpvAudio1, data, dwBytes1);
// finally, unlock the buffer
hr = dsbuffer->lpVtbl->Unlock (dsbuffer, lpvAudio1, dwBytes1, lpvAudio2, dwBytes2);
if( FAILED (hr) )
I_Error("Unlock fail on %x, %s\n",dsbuffer,DXErrorToString(hr));
return true;
}
// NT compatible version
static boolean CopySoundData (LPDIRECTSOUNDBUFFER dsbuffer, byte* data, int length)
{
LPVOID lpvAudio1; // receives address of lock start
DWORD dwBytes1; // receives number of bytes locked
LPVOID lpvAudio2; // receives address of lock start
DWORD dwBytes2; // receives number of bytes locked
HRESULT hr;
// Obtain memory address of write block.
hr = dsbuffer->lpVtbl->Lock (dsbuffer, 0, length, &lpvAudio1, &dwBytes1, &lpvAudio2, &dwBytes2, 0);
// If DSERR_BUFFERLOST is returned, restore and retry lock.
if (hr == DSERR_BUFFERLOST)
{
hr = dsbuffer->lpVtbl->Restore (dsbuffer);
if( FAILED (hr) )
I_Error("Restor fail on %x, %s\n",dsbuffer,DXErrorToString(hr));
hr = dsbuffer->lpVtbl->Lock (dsbuffer, 0, length, &lpvAudio1, &dwBytes1, NULL, NULL, 0);
if( FAILED (hr) )
I_Error("Lock fail(2) on %x, %s\n",dsbuffer,DXErrorToString(hr));
}
else
if( FAILED (hr) )
I_Error("Lock fail(1) on %x, %s\n",dsbuffer,DXErrorToString(hr));
// copy wave data into the buffer (note: dwBytes1 should equal to dsbdesc->dwBufferBytes ...)
CopyMemory (lpvAudio1, data, dwBytes1);
if ( dwBytes2 && lpvAudio2)
CopyMemory(lpvAudio2, data+dwBytes1, dwBytes2);
// finally, unlock the buffer
hr = dsbuffer->lpVtbl->Unlock (dsbuffer, lpvAudio1, dwBytes1, lpvAudio2, dwBytes2);
if( FAILED (hr) )
I_Error("Unlock fail on %x, %s\n",dsbuffer,DXErrorToString(hr));
return true;
}
#ifdef SURROUND
// judgecutor:
// Hmmm... May be this function is not too good...
static void CopyAndInvertMemory(void *dest, void *src, int bytes)
{
_asm
{
push esi
push edi
push ecx
mov ecx,bytes
mov esi,src
mov edi,dest
a:
lodsb
neg al
stosb
loop a
pop ecx
pop edi
pop esi
}
}
// judgecutor:
// Like normal CopySoundData but sound data will be inverted
static boolean CopyAndInvertSoundData(LPDIRECTSOUNDBUFFER dsbuffer, byte* data, int length)
{
LPVOID lpvAudio1; // receives address of lock start
DWORD dwBytes1; // receives number of bytes locked
LPVOID lpvAudio2;
DWORD dwBytes2;
HRESULT hr;
// Obtain memory address of write block.
hr = dsbuffer->lpVtbl->Lock (dsbuffer, 0, length, &lpvAudio1, &dwBytes1, &lpvAudio2, &dwBytes2, 0);
// If DSERR_BUFFERLOST is returned, restore and retry lock.
if (hr == DSERR_BUFFERLOST)
{
hr = dsbuffer->lpVtbl->Restore (dsbuffer);
if( FAILED (hr) )
I_Error("CopyAndInvert: Restore fail on %x, %s\n",dsbuffer,DXErrorToString(hr));
hr = dsbuffer->lpVtbl->Lock (dsbuffer, 0, length, &lpvAudio1, &dwBytes1, NULL, NULL, 0);
if( FAILED (hr) )
I_Error("CopyAndInvert: Lock fail(2) on %x, %s\n",dsbuffer,DXErrorToString(hr));
} else if( FAILED (hr) )
I_Error("CopyAndInvetrt: Lock fail(1) on %x, %s\n",dsbuffer,DXErrorToString(hr));
// copy wave data into the buffer (note: dwBytes1 should equal to dsbdesc->dwBufferBytes ...)
CopyAndInvertMemory (lpvAudio1, data, dwBytes1);
if ( dwBytes2 && lpvAudio2)
CopyAndInvertMemory(lpvAudio2, data+dwBytes1, dwBytes2);
hr = dsbuffer->lpVtbl->Unlock (dsbuffer, lpvAudio1, dwBytes1, lpvAudio2, dwBytes2);
if( FAILED (hr) )
I_Error("CopyAndInvert: Unlock fail on %x, %s\n",dsbuffer,DXErrorToString(hr));
return false;
}
#endif
// --------------------------------------------------------------------------
// raw2DS : convert a raw sound data, returns a LPDIRECTSOUNDBUFFER
// --------------------------------------------------------------------------
// dsdata points a 4 unsigned short header:
// +0 : value 3 what does it mean?
// +2 : sample rate, either 11025 or 22050.
// +4 : number of samples, each sample is a single byte since it's 8bit
// +6 : value 0
//
#ifdef SURROUND
// judgecutor:
// We need an another function definition for supporting the surround sound
// Invert just cause to copy an inverted sound data
static LPDIRECTSOUNDBUFFER raw2DS(unsigned char *dsdata, int len, boolean invert)
#else
static LPDIRECTSOUNDBUFFER raw2DS(unsigned char *dsdata, int len)
#endif
{
HRESULT hr;
WAVEFORMATEX wfm;
DSBUFFERDESC dsbdesc;
LPDIRECTSOUNDBUFFER dsbuffer;
// initialise WAVEFORMATEX structure describing the wave format
ZeroMemory (&wfm, sizeof(WAVEFORMATEX));
wfm.wFormatTag = WAVE_FORMAT_PCM;
wfm.nChannels = 1;
wfm.nSamplesPerSec = *((unsigned short*)dsdata+1); //mostly 11025, but some at 22050.
wfm.wBitsPerSample = 8;
wfm.nBlockAlign = wfm.wBitsPerSample / 8 * wfm.nChannels;
wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nBlockAlign;
// Set up DSBUFFERDESC structure.
ZeroMemory (&dsbdesc, sizeof(DSBUFFERDESC) );
dsbdesc.dwSize = sizeof (DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_CTRLPAN |
DSBCAPS_CTRLVOLUME |
DSBCAPS_STICKYFOCUS |
//DSBCAPS_LOCSOFTWARE |
DSBCAPS_STATIC;
dsbdesc.dwBufferBytes = len-8;
dsbdesc.lpwfxFormat = &wfm; // pointer to WAVEFORMATEX structure
// Create the sound buffer
hr = DSnd->lpVtbl->CreateSoundBuffer (DSnd, &dsbdesc, &dsbuffer, NULL);
if ( FAILED( hr ) )
I_Error ("CreateSoundBuffer() FAILED: %s\n", DXErrorToString(hr));
#ifdef SURROUND
if (invert)
// just invert a sound data for producing the surround sound
CopyAndInvertSoundData(dsbuffer, (byte*)dsdata + 8, dsbdesc.dwBufferBytes);
else
// Do a normal operation
#endif
// fill the DirectSoundBuffer waveform data
CopySoundData (dsbuffer, (byte*)dsdata + 8, dsbdesc.dwBufferBytes);
return dsbuffer;
}
// --------------------------------------------------------------------------
// This function loads the sound data from the WAD lump, for single sound.
// --------------------------------------------------------------------------
void* I_GetSfx (sfxinfo_t* sfx)
{
byte* dssfx;
int size;
if (sfx->lumpnum<0)
sfx->lumpnum = S_GetSfxLumpNum (sfx);
#ifdef HW3SOUND
if (hws_mode != HWS_DEFAULT_MODE)
return W_CacheLumpNum(sfx->lumpnum, PU_STATIC);
#endif
size = W_LumpLength (sfx->lumpnum);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -