doom3-bfg/doomclassic/doom/i_sound_win32.cpp

1206 lines
27 KiB
C++
Raw Normal View History

2012-11-26 18:58:24 +00:00
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
2022-09-05 20:25:33 +00:00
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code 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.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "Precompiled.h"
#include "globaldata.h"
//
// DESCRIPTION:
// System interface for sound.
//
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <sys/types.h>
#include <fcntl.h>
// Timer stuff. Experimental.
#include <time.h>
#include <signal.h>
#include "z_zone.h"
#include "i_system.h"
#include "i_sound.h"
#include "m_argv.h"
#include "m_misc.h"
#include "w_wad.h"
#include "d_main.h"
#include "doomdef.h"
#include "../timidity/timidity.h"
#include "../timidity/controls.h"
#include "sound/snd_local.h"
2012-12-02 21:32:00 +00:00
#ifdef _MSC_VER // DG: xaudio can only be used with MSVC
2022-09-05 20:25:33 +00:00
#include <xaudio2.h>
#include <x3daudio.h>
2012-12-02 21:32:00 +00:00
#endif // DG end
2012-11-26 18:58:24 +00:00
#pragma warning ( disable : 4244 )
#define MIDI_CHANNELS 2
#if 1
2022-09-05 20:25:33 +00:00
#define MIDI_RATE 22050
#define MIDI_SAMPLETYPE XAUDIOSAMPLETYPE_8BITPCM
#define MIDI_FORMAT AUDIO_U8
#define MIDI_FORMAT_BYTES 1
2012-11-26 18:58:24 +00:00
#else
2022-09-05 20:25:33 +00:00
#define MIDI_RATE 48000
#define MIDI_SAMPLETYPE XAUDIOSAMPLETYPE_16BITPCM
#define MIDI_FORMAT AUDIO_S16MSB
#define MIDI_FORMAT_BYTES 2
2012-11-26 18:58:24 +00:00
#endif
2012-12-02 21:32:00 +00:00
#ifdef _MSC_VER // DG: xaudio can only be used with MSVC
2022-09-05 20:25:33 +00:00
IXAudio2SourceVoice* pMusicSourceVoice;
2012-12-02 21:32:00 +00:00
#endif
2012-11-26 18:58:24 +00:00
MidiSong* doomMusic;
byte* musicBuffer;
int totalBufferSize;
HANDLE hMusicThread;
bool waitingForMusic;
bool musicReady;
2022-09-05 20:25:33 +00:00
typedef struct tagActiveSound_t
{
2012-11-26 18:58:24 +00:00
IXAudio2SourceVoice* m_pSourceVoice; // Source voice
X3DAUDIO_DSP_SETTINGS m_DSPSettings;
X3DAUDIO_EMITTER m_Emitter;
X3DAUDIO_CONE m_Cone;
int id;
int valid;
int start;
int player;
bool localSound;
2022-09-05 20:25:33 +00:00
mobj_t* originator;
2012-11-26 18:58:24 +00:00
} activeSound_t;
// cheap little struct to hold a sound
2022-09-05 20:25:33 +00:00
typedef struct
{
2012-11-26 18:58:24 +00:00
int vol;
int player;
int pitch;
int priority;
2022-09-05 20:25:33 +00:00
mobj_t* originator;
mobj_t* listener;
2012-11-26 18:58:24 +00:00
} soundEvent_t;
// array of all the possible sounds
// in split screen we only process the loudest sound of each type per frame
soundEvent_t soundEvents[128];
extern int PLAYERCOUNT;
// Real volumes
const float GLOBAL_VOLUME_MULTIPLIER = 0.5f;
float x_SoundVolume = GLOBAL_VOLUME_MULTIPLIER;
float x_MusicVolume = GLOBAL_VOLUME_MULTIPLIER;
// The actual lengths of all sound effects.
static int lengths[NUMSFX];
activeSound_t activeSounds[NUM_SOUNDBUFFERS] = {0};
int S_initialized = 0;
bool Music_initialized = false;
// XAUDIO
float g_EmitterAzimuths [] = { 0.f };
static int numOutputChannels = 0;
static bool soundHardwareInitialized = false;
2012-12-02 21:32:00 +00:00
// DG: xaudio can only be used with MSVC
#ifdef _MSC_VER
2022-09-05 20:25:33 +00:00
X3DAUDIO_HANDLE X3DAudioInstance;
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
X3DAUDIO_LISTENER doom_Listener;
2012-12-02 21:32:00 +00:00
#endif
2012-11-26 18:58:24 +00:00
//float localSoundVolumeEntries[] = { 0.f, 0.f, 0.9f, 0.5f, 0.f, 0.f };
float localSoundVolumeEntries[] = { 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f };
void I_InitSoundChannel( int channel, int numOutputChannels_ );
/*
======================
getsfx
======================
*/
// This function loads the sound data from the WAD lump,
// for single sound.
//
2022-09-05 20:25:33 +00:00
void* getsfx( const char* sfxname, int* len )
2012-11-26 18:58:24 +00:00
{
unsigned char* sfx;
unsigned char* sfxmem;
int size;
char name[20];
int sfxlump;
float scale = 1.0f;
// Get the sound data from the WAD, allocate lump
// in zone memory.
2022-09-05 20:25:33 +00:00
sprintf( name, "ds%s", sfxname );
2012-11-26 18:58:24 +00:00
// Scale down the plasma gun, it clips
2022-09-05 20:25:33 +00:00
if( strcmp( sfxname, "plasma" ) == 0 )
{
2012-11-26 18:58:24 +00:00
scale = 0.75f;
}
2022-09-05 20:25:33 +00:00
if( strcmp( sfxname, "itemup" ) == 0 )
{
2012-11-26 18:58:24 +00:00
scale = 1.333f;
}
// If sound requested is not found in current WAD, use pistol as default
2022-09-05 20:25:33 +00:00
if( W_CheckNumForName( name ) == -1 )
{
sfxlump = W_GetNumForName( "dspistol" );
}
2012-11-26 18:58:24 +00:00
else
2022-09-05 20:25:33 +00:00
{
sfxlump = W_GetNumForName( name );
}
2012-11-26 18:58:24 +00:00
// Sound lump headers are 8 bytes.
const int SOUND_LUMP_HEADER_SIZE_IN_BYTES = 8;
size = W_LumpLength( sfxlump ) - SOUND_LUMP_HEADER_SIZE_IN_BYTES;
2022-09-05 20:25:33 +00:00
sfx = ( unsigned char* )W_CacheLumpNum( sfxlump, PU_CACHE_SHARED );
const unsigned char* sfxSampleStart = sfx + SOUND_LUMP_HEADER_SIZE_IN_BYTES;
2012-11-26 18:58:24 +00:00
// Allocate from zone memory.
//sfxmem = (float*)DoomLib::Z_Malloc( size*(sizeof(float)), PU_SOUND_SHARED, 0 );
2022-09-05 20:25:33 +00:00
sfxmem = ( unsigned char* )malloc( size * sizeof( unsigned char ) );
2012-11-26 18:58:24 +00:00
// Now copy, and convert to Xbox360 native float samples, do initial volume ramp, and scale
2022-09-05 20:25:33 +00:00
for( int i = 0; i < size; i++ )
{
2012-11-26 18:58:24 +00:00
sfxmem[i] = sfxSampleStart[i];// * scale;
}
// Remove the cached lump.
Z_Free( sfx );
// Set length.
*len = size;
// Return allocated padded data.
2022-09-05 20:25:33 +00:00
return ( void* )( sfxmem );
2012-11-26 18:58:24 +00:00
}
/*
======================
I_SetChannels
======================
*/
2022-09-05 20:25:33 +00:00
void I_SetChannels()
{
2012-11-26 18:58:24 +00:00
// Original Doom set up lookup tables here
2022-09-05 20:25:33 +00:00
}
2012-11-26 18:58:24 +00:00
/*
======================
I_SetSfxVolume
======================
*/
2022-09-05 20:25:33 +00:00
void I_SetSfxVolume( int volume )
{
x_SoundVolume = ( ( float )volume / 15.f ) * GLOBAL_VOLUME_MULTIPLIER;
2012-11-26 18:58:24 +00:00
}
/*
======================
I_GetSfxLumpNum
======================
*/
//
// Retrieve the raw data lump index
// for a given SFX name.
//
2022-09-05 20:25:33 +00:00
int I_GetSfxLumpNum( sfxinfo_t* sfx )
2012-11-26 18:58:24 +00:00
{
char namebuf[9];
2022-09-05 20:25:33 +00:00
sprintf( namebuf, "ds%s", sfx->name );
return W_GetNumForName( namebuf );
2012-11-26 18:58:24 +00:00
}
/*
======================
I_StartSound2
======================
*/
// Starting a sound means adding it
// to the current list of active sounds
// in the internal channels.
// As the SFX info struct contains
// e.g. a pointer to the raw data,
// it is ignored.
// As our sound handling does not handle
// priority, it is ignored.
// Pitching (that is, increased speed of playback) is set
//
2022-09-05 20:25:33 +00:00
int I_StartSound2( int id, int player, mobj_t* origin, mobj_t* listener_origin, int pitch, int priority )
{
if( !soundHardwareInitialized )
{
2012-11-26 18:58:24 +00:00
return id;
}
2022-09-05 20:25:33 +00:00
2012-11-26 18:58:24 +00:00
int i;
2022-09-05 20:25:33 +00:00
XAUDIO2_VOICE_STATE state;
2012-11-26 18:58:24 +00:00
activeSound_t* sound = 0;
int oldest = 0, oldestnum = -1;
// these id's should not overlap
2022-09-05 20:25:33 +00:00
if( id == sfx_sawup || id == sfx_sawidl || id == sfx_sawful || id == sfx_sawhit || id == sfx_stnmov )
{
2012-11-26 18:58:24 +00:00
// Loop all channels, check.
2022-09-05 20:25:33 +00:00
for( i = 0 ; i < NUM_SOUNDBUFFERS ; i++ )
2012-11-26 18:58:24 +00:00
{
sound = &activeSounds[i];
2022-09-05 20:25:33 +00:00
if( sound->valid && ( sound->id == id && sound->player == player ) )
{
2012-11-26 18:58:24 +00:00
I_StopSound( sound->id, player );
break;
}
}
}
// find a valid channel, or one that has finished playing
2022-09-05 20:25:33 +00:00
for( i = 0; i < NUM_SOUNDBUFFERS; ++i )
{
2012-11-26 18:58:24 +00:00
sound = &activeSounds[i];
2022-09-05 20:25:33 +00:00
if( !sound->valid )
{
2012-11-26 18:58:24 +00:00
break;
2022-09-05 20:25:33 +00:00
}
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( !oldest || oldest > sound->start )
{
2012-11-26 18:58:24 +00:00
oldestnum = i;
oldest = sound->start;
}
sound->m_pSourceVoice->GetState( &state );
2022-09-05 20:25:33 +00:00
if( state.BuffersQueued == 0 )
{
2012-11-26 18:58:24 +00:00
break;
}
}
// none found, so use the oldest one
2022-09-05 20:25:33 +00:00
if( i == NUM_SOUNDBUFFERS )
2012-11-26 18:58:24 +00:00
{
i = oldestnum;
sound = &activeSounds[i];
}
// stop the sound with a FlushPackets
sound->m_pSourceVoice->Stop();
sound->m_pSourceVoice->FlushSourceBuffers();
// Set up packet
XAUDIO2_BUFFER Packet = { 0 };
Packet.Flags = XAUDIO2_END_OF_STREAM;
Packet.AudioBytes = lengths[id];
2022-09-05 20:25:33 +00:00
Packet.pAudioData = ( BYTE* )S_sfx[id].data;
2012-11-26 18:58:24 +00:00
Packet.PlayBegin = 0;
Packet.PlayLength = 0;
Packet.LoopBegin = XAUDIO2_NO_LOOP_REGION;
Packet.LoopLength = 0;
Packet.LoopCount = 0;
Packet.pContext = NULL;
// Set voice volumes
sound->m_pSourceVoice->SetVolume( x_SoundVolume );
// Set voice pitch
2022-09-05 20:25:33 +00:00
sound->m_pSourceVoice->SetFrequencyRatio( 1 + ( ( float )pitch - 128.f ) / 95.f );
2012-11-26 18:58:24 +00:00
// Set initial spatialization
2022-09-05 20:25:33 +00:00
if( origin && origin != listener_origin )
{
2012-11-26 18:58:24 +00:00
// Update Emitter Position
2022-09-05 20:25:33 +00:00
sound->m_Emitter.Position.x = ( float )( origin->x >> FRACBITS );
2012-11-26 18:58:24 +00:00
sound->m_Emitter.Position.y = 0.f;
2022-09-05 20:25:33 +00:00
sound->m_Emitter.Position.z = ( float )( origin->y >> FRACBITS );
2012-11-26 18:58:24 +00:00
// Calculate 3D positioned speaker volumes
DWORD dwCalculateFlags = X3DAUDIO_CALCULATE_MATRIX;
X3DAudioCalculate( X3DAudioInstance, &doom_Listener, &sound->m_Emitter, dwCalculateFlags, &sound->m_DSPSettings );
// Pan the voice according to X3DAudio calculation
sound->m_pSourceVoice->SetOutputMatrix( NULL, 1, numOutputChannels, sound->m_DSPSettings.pMatrixCoefficients );
sound->localSound = false;
2022-09-05 20:25:33 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
// Local(or Global) sound, fixed speaker volumes
sound->m_pSourceVoice->SetOutputMatrix( NULL, 1, numOutputChannels, localSoundVolumeEntries );
sound->localSound = true;
}
// Submit packet
HRESULT hr;
2022-09-05 20:25:33 +00:00
if( FAILED( hr = sound->m_pSourceVoice->SubmitSourceBuffer( &Packet ) ) )
{
2012-11-26 18:58:24 +00:00
int fail = 1;
}
// Play the source voice
2022-09-05 20:25:33 +00:00
if( FAILED( hr = sound->m_pSourceVoice->Start( 0 ) ) )
{
2012-11-26 18:58:24 +00:00
int fail = 1;
}
// set id, and start time
sound->id = id;
sound->start = ::g->gametic;
sound->valid = 1;
sound->player = player;
sound->originator = origin;
return id;
}
/*
======================
I_ProcessSoundEvents
======================
*/
2022-09-05 20:25:33 +00:00
void I_ProcessSoundEvents()
{
for( int i = 0; i < 128; i++ )
{
if( soundEvents[i].pitch )
{
2012-11-26 18:58:24 +00:00
I_StartSound2( i, soundEvents[i].player, soundEvents[i].originator, soundEvents[i].listener, soundEvents[i].pitch, soundEvents[i].priority );
}
}
memset( soundEvents, 0, sizeof( soundEvents ) );
}
/*
======================
I_StartSound
======================
*/
2022-09-05 20:25:33 +00:00
int I_StartSound( int id, mobj_t* origin, mobj_t* listener_origin, int vol, int pitch, int priority )
{
2012-11-26 18:58:24 +00:00
// only allow player 0s sounds in intermission and finale screens
2022-09-05 20:25:33 +00:00
if( ::g->gamestate != GS_LEVEL && DoomLib::GetPlayer() != 0 )
{
2012-11-26 18:58:24 +00:00
return 0;
}
// if we're only one player or we're trying to play the chainsaw sound, do it normal
// otherwise only allow one sound of each type per frame
2022-09-05 20:25:33 +00:00
if( PLAYERCOUNT == 1 || id == sfx_sawup || id == sfx_sawidl || id == sfx_sawful || id == sfx_sawhit )
{
2012-11-26 18:58:24 +00:00
return I_StartSound2( id, ::g->consoleplayer, origin, listener_origin, pitch, priority );
}
2022-09-05 20:25:33 +00:00
else
{
if( soundEvents[ id ].vol < vol )
{
2012-11-26 18:58:24 +00:00
soundEvents[ id ].player = DoomLib::GetPlayer();
soundEvents[ id ].pitch = pitch;
soundEvents[ id ].priority = priority;
soundEvents[ id ].vol = vol;
soundEvents[ id ].originator = origin;
soundEvents[ id ].listener = listener_origin;
}
return id;
}
}
/*
======================
I_StopSound
======================
*/
2022-09-05 20:25:33 +00:00
void I_StopSound( int handle, int player )
2012-11-26 18:58:24 +00:00
{
// You need the handle returned by StartSound.
// Would be looping all channels,
// tracking down the handle,
// an setting the channel to zero.
int i;
activeSound_t* sound = 0;
2022-09-05 20:25:33 +00:00
for( i = 0; i < NUM_SOUNDBUFFERS; ++i )
2012-11-26 18:58:24 +00:00
{
sound = &activeSounds[i];
2022-09-05 20:25:33 +00:00
if( !sound->valid || sound->id != handle || ( player >= 0 && sound->player != player ) )
{
2012-11-26 18:58:24 +00:00
continue;
2022-09-05 20:25:33 +00:00
}
2012-11-26 18:58:24 +00:00
break;
}
2022-09-05 20:25:33 +00:00
if( i == NUM_SOUNDBUFFERS )
{
2012-11-26 18:58:24 +00:00
return;
2022-09-05 20:25:33 +00:00
}
2012-11-26 18:58:24 +00:00
// stop the sound
2022-09-05 20:25:33 +00:00
if( sound->m_pSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
sound->m_pSourceVoice->Stop( 0 );
}
sound->valid = 0;
sound->player = -1;
}
/*
======================
I_SoundIsPlaying
======================
*/
2022-09-05 20:25:33 +00:00
int I_SoundIsPlaying( int handle )
{
if( !soundHardwareInitialized )
{
2012-11-26 18:58:24 +00:00
return 0;
}
int i;
XAUDIO2_VOICE_STATE state;
activeSound_t* sound;
2022-09-05 20:25:33 +00:00
for( i = 0; i < NUM_SOUNDBUFFERS; ++i )
2012-11-26 18:58:24 +00:00
{
sound = &activeSounds[i];
2022-09-05 20:25:33 +00:00
if( !sound->valid || sound->id != handle )
{
2012-11-26 18:58:24 +00:00
continue;
2022-09-05 20:25:33 +00:00
}
2012-11-26 18:58:24 +00:00
sound->m_pSourceVoice->GetState( &state );
2022-09-05 20:25:33 +00:00
if( state.BuffersQueued > 0 )
{
2012-11-26 18:58:24 +00:00
return 1;
}
}
return 0;
}
/*
======================
I_UpdateSound
======================
*/
// Update Listener Position and go through all the
// channels and update speaker volumes for 3D sound.
2022-09-05 20:25:33 +00:00
void I_UpdateSound()
{
if( !soundHardwareInitialized )
{
2012-11-26 18:58:24 +00:00
return;
}
int i;
XAUDIO2_VOICE_STATE state;
activeSound_t* sound;
2022-09-05 20:25:33 +00:00
for( i = 0; i < NUM_SOUNDBUFFERS; i++ )
{
2012-11-26 18:58:24 +00:00
sound = &activeSounds[i];
2022-09-05 20:25:33 +00:00
if( !sound->valid || sound->localSound )
{
2012-11-26 18:58:24 +00:00
continue;
}
sound->m_pSourceVoice->GetState( &state );
2022-09-05 20:25:33 +00:00
if( state.BuffersQueued > 0 )
{
mobj_t* playerObj = ::g->players[ sound->player ].mo;
2012-11-26 18:58:24 +00:00
// Update Listener Orientation and Position
angle_t pAngle = playerObj->angle;
fixed_t fx, fz;
pAngle >>= ANGLETOFINESHIFT;
fx = finecosine[pAngle];
fz = finesine[pAngle];
2022-09-05 20:25:33 +00:00
doom_Listener.OrientFront.x = ( float )( fx ) / 65535.f;
2012-11-26 18:58:24 +00:00
doom_Listener.OrientFront.y = 0.f;
2022-09-05 20:25:33 +00:00
doom_Listener.OrientFront.z = ( float )( fz ) / 65535.f;
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
doom_Listener.Position.x = ( float )( playerObj->x >> FRACBITS );
2012-11-26 18:58:24 +00:00
doom_Listener.Position.y = 0.f;
2022-09-05 20:25:33 +00:00
doom_Listener.Position.z = ( float )( playerObj->y >> FRACBITS );
2012-11-26 18:58:24 +00:00
// Update Emitter Position
2022-09-05 20:25:33 +00:00
sound->m_Emitter.Position.x = ( float )( sound->originator->x >> FRACBITS );
2012-11-26 18:58:24 +00:00
sound->m_Emitter.Position.y = 0.f;
2022-09-05 20:25:33 +00:00
sound->m_Emitter.Position.z = ( float )( sound->originator->y >> FRACBITS );
2012-11-26 18:58:24 +00:00
// Calculate 3D positioned speaker volumes
DWORD dwCalculateFlags = X3DAUDIO_CALCULATE_MATRIX;
X3DAudioCalculate( X3DAudioInstance, &doom_Listener, &sound->m_Emitter, dwCalculateFlags, &sound->m_DSPSettings );
// Pan the voice according to X3DAudio calculation
sound->m_pSourceVoice->SetOutputMatrix( NULL, 1, numOutputChannels, sound->m_DSPSettings.pMatrixCoefficients );
}
}
}
/*
======================
I_UpdateSoundParams
======================
*/
2022-09-05 20:25:33 +00:00
void I_UpdateSoundParams( int handle, int vol, int sep, int pitch )
{
2012-11-26 18:58:24 +00:00
}
/*
======================
I_ShutdownSound
======================
*/
2022-09-05 20:25:33 +00:00
void I_ShutdownSound( void )
{
2012-11-26 18:58:24 +00:00
int done = 0;
int i;
2022-09-05 20:25:33 +00:00
if( S_initialized )
{
2012-11-26 18:58:24 +00:00
// Stop all sounds, but don't destroy the XAudio2 buffers.
2022-09-05 20:25:33 +00:00
for( i = 0; i < NUM_SOUNDBUFFERS; ++i )
{
activeSound_t* sound = &activeSounds[i];
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( sound == NULL )
{
2012-11-26 18:58:24 +00:00
continue;
}
I_StopSound( sound->id, 0 );
2022-09-05 20:25:33 +00:00
if( sound->m_pSourceVoice )
{
2012-11-26 18:58:24 +00:00
sound->m_pSourceVoice->FlushSourceBuffers();
}
}
2022-09-05 20:25:33 +00:00
for( i = 1 ; i < NUMSFX ; i++ )
{
if( S_sfx[i].data && !( S_sfx[i].link ) )
{
2012-11-26 18:58:24 +00:00
//Z_Free( S_sfx[i].data );
free( S_sfx[i].data );
}
}
}
I_StopSong( 0 );
S_initialized = 0;
// Done.
return;
}
/*
======================
I_InitSoundHardware
Called from the tech4x initialization code. Sets up Doom classic's
sound channels.
======================
*/
2022-09-05 20:25:33 +00:00
void I_InitSoundHardware( int numOutputChannels_, int channelMask )
{
2012-11-26 18:58:24 +00:00
::numOutputChannels = numOutputChannels_;
// Initialize the X3DAudio
// Speaker geometry configuration on the final mix, specifies assignment of channels
// to speaker positions, defined as per WAVEFORMATEXTENSIBLE.dwChannelMask
// SpeedOfSound - not used by doomclassic
X3DAudioInitialize( channelMask, 340.29f, X3DAudioInstance );
2022-09-05 20:25:33 +00:00
for( int i = 0; i < NUM_SOUNDBUFFERS; ++i )
{
2012-11-26 18:58:24 +00:00
// Initialize source voices
I_InitSoundChannel( i, numOutputChannels );
}
I_InitMusic();
soundHardwareInitialized = true;
}
/*
======================
I_ShutdownitSoundHardware
Called from the tech4x shutdown code. Tears down Doom classic's
sound channels.
======================
*/
2022-09-05 20:25:33 +00:00
void I_ShutdownSoundHardware()
{
2012-11-26 18:58:24 +00:00
soundHardwareInitialized = false;
I_ShutdownMusic();
2022-09-05 20:25:33 +00:00
for( int i = 0; i < NUM_SOUNDBUFFERS; ++i )
{
activeSound_t* sound = &activeSounds[i];
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( sound == NULL )
{
2012-11-26 18:58:24 +00:00
continue;
}
2022-09-05 20:25:33 +00:00
if( sound->m_pSourceVoice )
{
2012-11-26 18:58:24 +00:00
sound->m_pSourceVoice->Stop();
sound->m_pSourceVoice->FlushSourceBuffers();
sound->m_pSourceVoice->DestroyVoice();
sound->m_pSourceVoice = NULL;
}
2022-09-05 20:25:33 +00:00
if( sound->m_DSPSettings.pMatrixCoefficients )
{
2012-11-26 18:58:24 +00:00
delete [] sound->m_DSPSettings.pMatrixCoefficients;
sound->m_DSPSettings.pMatrixCoefficients = NULL;
}
}
}
/*
======================
I_InitSoundChannel
======================
*/
2022-09-05 20:25:33 +00:00
void I_InitSoundChannel( int channel, int numOutputChannels_ )
{
activeSound_t* soundchannel = &activeSounds[ channel ];
2012-11-26 18:58:24 +00:00
// RB: fixed non-aggregates cannot be initialized with initializer list
#if defined(USE_WINRT) //(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
X3DAUDIO_VECTOR ZeroVector( 0.0f, 0.0f, 0.0f );
#else
X3DAUDIO_VECTOR ZeroVector = { 0.0f, 0.0f, 0.0f };
#endif
// RB end
2012-11-26 18:58:24 +00:00
// Set up emitter parameters
soundchannel->m_Emitter.OrientFront.x = 0.0f;
soundchannel->m_Emitter.OrientFront.y = 0.0f;
soundchannel->m_Emitter.OrientFront.z = 1.0f;
soundchannel->m_Emitter.OrientTop.x = 0.0f;
soundchannel->m_Emitter.OrientTop.y = 1.0f;
soundchannel->m_Emitter.OrientTop.z = 0.0f;
soundchannel->m_Emitter.Position = ZeroVector;
soundchannel->m_Emitter.Velocity = ZeroVector;
2022-09-05 20:25:33 +00:00
soundchannel->m_Emitter.pCone = &( soundchannel->m_Cone );
2012-11-26 18:58:24 +00:00
soundchannel->m_Emitter.pCone->InnerAngle = 0.0f; // Setting the inner cone angles to X3DAUDIO_2PI and
// outer cone other than 0 causes
// the emitter to act like a point emitter using the
// INNER cone settings only.
soundchannel->m_Emitter.pCone->OuterAngle = 0.0f; // Setting the outer cone angles to zero causes
// the emitter to act like a point emitter using the
// OUTER cone settings only.
soundchannel->m_Emitter.pCone->InnerVolume = 0.0f;
soundchannel->m_Emitter.pCone->OuterVolume = 1.0f;
soundchannel->m_Emitter.pCone->InnerLPF = 0.0f;
soundchannel->m_Emitter.pCone->OuterLPF = 1.0f;
soundchannel->m_Emitter.pCone->InnerReverb = 0.0f;
soundchannel->m_Emitter.pCone->OuterReverb = 1.0f;
soundchannel->m_Emitter.ChannelCount = 1;
soundchannel->m_Emitter.ChannelRadius = 0.0f;
soundchannel->m_Emitter.pVolumeCurve = NULL;
soundchannel->m_Emitter.pLFECurve = NULL;
soundchannel->m_Emitter.pLPFDirectCurve = NULL;
soundchannel->m_Emitter.pLPFReverbCurve = NULL;
soundchannel->m_Emitter.pReverbCurve = NULL;
soundchannel->m_Emitter.CurveDistanceScaler = 1200.0f;
soundchannel->m_Emitter.DopplerScaler = 1.0f;
soundchannel->m_Emitter.pChannelAzimuths = g_EmitterAzimuths;
soundchannel->m_DSPSettings.SrcChannelCount = 1;
soundchannel->m_DSPSettings.DstChannelCount = numOutputChannels_;
soundchannel->m_DSPSettings.pMatrixCoefficients = new FLOAT[ numOutputChannels_ ];
// Create Source voice
WAVEFORMATEX voiceFormat = {0};
voiceFormat.wFormatTag = WAVE_FORMAT_PCM;
voiceFormat.nChannels = 1;
2022-09-05 20:25:33 +00:00
voiceFormat.nSamplesPerSec = 11025;
voiceFormat.nAvgBytesPerSec = 11025;
voiceFormat.nBlockAlign = 1;
voiceFormat.wBitsPerSample = 8;
voiceFormat.cbSize = 0;
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
soundSystemLocal.hardware.GetIXAudio2()->CreateSourceVoice( &soundchannel->m_pSourceVoice, ( WAVEFORMATEX* )&voiceFormat );
2012-11-26 18:58:24 +00:00
}
/*
======================
I_InitSound
======================
*/
2022-09-05 20:25:33 +00:00
void I_InitSound()
{
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( S_initialized == 0 )
{
2012-11-26 18:58:24 +00:00
int i;
// RB: non-aggregates cannot be initialized with initializer list
#if defined(USE_WINRT) // (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
X3DAUDIO_VECTOR ZeroVector( 0.0f, 0.0f, 0.0f );
#else
X3DAUDIO_VECTOR ZeroVector = { 0.0f, 0.0f, 0.0f };
#endif
// RB end
2012-11-26 18:58:24 +00:00
// Set up listener parameters
doom_Listener.OrientFront.x = 0.0f;
doom_Listener.OrientFront.y = 0.0f;
doom_Listener.OrientFront.z = 1.0f;
doom_Listener.OrientTop.x = 0.0f;
doom_Listener.OrientTop.y = 1.0f;
doom_Listener.OrientTop.z = 0.0f;
doom_Listener.Position = ZeroVector;
doom_Listener.Velocity = ZeroVector;
2022-09-05 20:25:33 +00:00
for( i = 1 ; i < NUMSFX ; i++ )
{
2012-11-26 18:58:24 +00:00
// Alias? Example is the chaingun sound linked to pistol.
2022-09-05 20:25:33 +00:00
if( !S_sfx[i].link )
2012-11-26 18:58:24 +00:00
{
// Load data from WAD file.
S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );
2022-09-05 20:25:33 +00:00
}
2012-11-26 18:58:24 +00:00
else
{
// Previously loaded already?
S_sfx[i].data = S_sfx[i].link->data;
2022-09-05 20:25:33 +00:00
lengths[i] = lengths[( S_sfx[i].link - S_sfx ) / sizeof( sfxinfo_t )];
2012-11-26 18:58:24 +00:00
}
}
S_initialized = 1;
}
}
/*
======================
I_SubmitSound
======================
*/
2022-09-05 20:25:33 +00:00
void I_SubmitSound( void )
2012-11-26 18:58:24 +00:00
{
// Only do this for player 0, it will still handle positioning
2022-09-05 20:25:33 +00:00
// for other players, but it can't be outside the game
2012-11-26 18:58:24 +00:00
// frame like the soundEvents are.
2022-09-05 20:25:33 +00:00
if( DoomLib::GetPlayer() == 0 )
{
2012-11-26 18:58:24 +00:00
// Do 3D positioning of sounds
I_UpdateSound();
// Check for XMP notifications
I_UpdateMusic();
}
}
// =========================================================
// =========================================================
// Background Music
// =========================================================
// =========================================================
/*
======================
I_SetMusicVolume
======================
*/
2022-09-05 20:25:33 +00:00
void I_SetMusicVolume( int volume )
2012-11-26 18:58:24 +00:00
{
2022-09-05 20:25:33 +00:00
x_MusicVolume = ( float )volume / 15.f;
2012-11-26 18:58:24 +00:00
}
/*
======================
I_InitMusic
======================
*/
2022-09-05 20:25:33 +00:00
void I_InitMusic( void )
2012-11-26 18:58:24 +00:00
{
2022-09-05 20:25:33 +00:00
if( !Music_initialized )
{
2012-11-26 18:58:24 +00:00
// Initialize Timidity
Timidity_Init( MIDI_RATE, MIDI_FORMAT, MIDI_CHANNELS, MIDI_RATE, "classicmusic/gravis.cfg" );
hMusicThread = NULL;
musicBuffer = NULL;
totalBufferSize = 0;
waitingForMusic = false;
musicReady = false;
// Create Source voice
WAVEFORMATEX voiceFormat = {0};
voiceFormat.wFormatTag = WAVE_FORMAT_PCM;
voiceFormat.nChannels = 2;
voiceFormat.nSamplesPerSec = MIDI_RATE;
voiceFormat.nAvgBytesPerSec = MIDI_RATE * MIDI_FORMAT_BYTES * 2;
voiceFormat.nBlockAlign = MIDI_FORMAT_BYTES * 2;
voiceFormat.wBitsPerSample = MIDI_FORMAT_BYTES * 8;
voiceFormat.cbSize = 0;
// RB: XAUDIO2_VOICE_MUSIC not available on Windows 8 SDK
2022-09-05 20:25:33 +00:00
soundSystemLocal.hardware.GetIXAudio2()->CreateSourceVoice( &pMusicSourceVoice, ( WAVEFORMATEX* )&voiceFormat/*, XAUDIO2_VOICE_MUSIC*/ );
// RB end
2012-11-26 18:58:24 +00:00
Music_initialized = true;
}
}
/*
======================
I_ShutdownMusic
======================
*/
2022-09-05 20:25:33 +00:00
void I_ShutdownMusic( void )
2012-11-26 18:58:24 +00:00
{
I_StopSong( 0 );
2022-09-05 20:25:33 +00:00
if( Music_initialized )
{
if( pMusicSourceVoice )
{
2012-11-26 18:58:24 +00:00
pMusicSourceVoice->Stop();
pMusicSourceVoice->FlushSourceBuffers();
pMusicSourceVoice->DestroyVoice();
pMusicSourceVoice = NULL;
}
2022-09-05 20:25:33 +00:00
if( hMusicThread )
{
2012-11-26 18:58:24 +00:00
DWORD rc;
2022-09-05 20:25:33 +00:00
do
{
2012-11-26 18:58:24 +00:00
GetExitCodeThread( hMusicThread, &rc );
2022-09-05 20:25:33 +00:00
if( rc == STILL_ACTIVE )
{
2012-11-26 18:58:24 +00:00
Sleep( 1 );
}
2022-09-05 20:25:33 +00:00
}
while( rc == STILL_ACTIVE );
2012-11-26 18:58:24 +00:00
CloseHandle( hMusicThread );
}
2022-09-05 20:25:33 +00:00
if( musicBuffer )
{
2012-11-26 18:58:24 +00:00
free( musicBuffer );
}
Timidity_Shutdown();
}
pMusicSourceVoice = NULL;
hMusicThread = NULL;
musicBuffer = NULL;
totalBufferSize = 0;
waitingForMusic = false;
musicReady = false;
Music_initialized = false;
}
2022-09-05 20:25:33 +00:00
int Mus2Midi( unsigned char* bytes, unsigned char* out, int* len );
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
namespace
{
const int MaxMidiConversionSize = 1024 * 1024;
unsigned char midiConversionBuffer[MaxMidiConversionSize];
2012-11-26 18:58:24 +00:00
}
/*
======================
I_LoadSong
======================
*/
2022-09-05 20:25:33 +00:00
DWORD WINAPI I_LoadSong( LPVOID songname )
{
2012-11-26 18:58:24 +00:00
idStr lumpName = "d_";
2022-09-05 20:25:33 +00:00
lumpName += static_cast< const char* >( songname );
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
unsigned char* musFile = static_cast< unsigned char* >( W_CacheLumpName( lumpName.c_str(), PU_STATIC_SHARED ) );
2012-11-26 18:58:24 +00:00
int length = 0;
Mus2Midi( musFile, midiConversionBuffer, &length );
doomMusic = Timidity_LoadSongMem( midiConversionBuffer, length );
2022-09-05 20:25:33 +00:00
if( doomMusic )
{
musicBuffer = ( byte* )malloc( MIDI_CHANNELS * MIDI_FORMAT_BYTES * doomMusic->samples );
2012-11-26 18:58:24 +00:00
totalBufferSize = doomMusic->samples * MIDI_CHANNELS * MIDI_FORMAT_BYTES;
Timidity_Start( doomMusic );
int rc = RC_NO_RETURN_VALUE;
int num_bytes = 0;
int offset = 0;
2022-09-05 20:25:33 +00:00
do
{
2012-11-26 18:58:24 +00:00
rc = Timidity_PlaySome( musicBuffer + offset, MIDI_RATE, &num_bytes );
offset += num_bytes;
2022-09-05 20:25:33 +00:00
}
while( rc != RC_TUNE_END );
2012-11-26 18:58:24 +00:00
Timidity_Stop();
Timidity_FreeSong( doomMusic );
}
musicReady = true;
return ERROR_SUCCESS;
}
/*
======================
I_PlaySong
======================
*/
2022-09-05 20:25:33 +00:00
void I_PlaySong( const char* songname, int looping )
2012-11-26 18:58:24 +00:00
{
2022-09-05 20:25:33 +00:00
if( !Music_initialized )
{
2012-11-26 18:58:24 +00:00
return;
}
2022-09-05 20:25:33 +00:00
if( pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
// Stop the voice and flush packets before freeing the musicBuffer
pMusicSourceVoice->Stop();
pMusicSourceVoice->FlushSourceBuffers();
}
// Make sure voice is stopped before we free the buffer
bool isStopped = false;
int d = 0;
2022-09-05 20:25:33 +00:00
while( !isStopped )
{
XAUDIO2_VOICE_STATE test = {};
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
pMusicSourceVoice->GetState( &test );
}
2022-09-05 20:25:33 +00:00
if( test.pCurrentBufferContext == NULL && test.BuffersQueued == 0 )
{
2012-11-26 18:58:24 +00:00
isStopped = true;
}
//I_Printf( "waiting to stop (%d)\n", d++ );
}
// Clear old state
2022-09-05 20:25:33 +00:00
if( musicBuffer != NULL )
{
2012-11-26 18:58:24 +00:00
free( musicBuffer );
musicBuffer = NULL;
}
musicReady = false;
2022-09-05 20:25:33 +00:00
I_LoadSong( ( LPVOID )songname );
2012-11-26 18:58:24 +00:00
waitingForMusic = true;
2022-09-05 20:25:33 +00:00
if( DoomLib::GetPlayer() >= 0 )
{
2012-11-26 18:58:24 +00:00
::g->mus_looping = looping;
}
}
/*
======================
I_UpdateMusic
======================
*/
2022-09-05 20:25:33 +00:00
void I_UpdateMusic()
{
if( !Music_initialized )
{
2012-11-26 18:58:24 +00:00
return;
}
2022-09-05 20:25:33 +00:00
if( waitingForMusic )
{
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( musicReady && pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
2022-09-05 20:25:33 +00:00
if( musicBuffer )
{
2012-11-26 18:58:24 +00:00
// Set up packet
XAUDIO2_BUFFER Packet = { 0 };
Packet.Flags = XAUDIO2_END_OF_STREAM;
Packet.AudioBytes = totalBufferSize;
2022-09-05 20:25:33 +00:00
Packet.pAudioData = ( BYTE* )musicBuffer;
2012-11-26 18:58:24 +00:00
Packet.PlayBegin = 0;
Packet.PlayLength = 0;
Packet.LoopBegin = 0;
Packet.LoopLength = 0;
Packet.LoopCount = ::g->mus_looping ? XAUDIO2_LOOP_INFINITE : 0;
Packet.pContext = NULL;
// Submit packet
HRESULT hr;
2022-09-05 20:25:33 +00:00
if( FAILED( hr = pMusicSourceVoice->SubmitSourceBuffer( &Packet ) ) )
{
2012-11-26 18:58:24 +00:00
int fail = 1;
}
// Play the source voice
2022-09-05 20:25:33 +00:00
if( FAILED( hr = pMusicSourceVoice->Start( 0 ) ) )
{
2012-11-26 18:58:24 +00:00
int fail = 1;
}
}
waitingForMusic = false;
}
}
2022-09-05 20:25:33 +00:00
if( pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
// Set the volume
pMusicSourceVoice->SetVolume( x_MusicVolume * GLOBAL_VOLUME_MULTIPLIER );
}
}
/*
======================
I_PauseSong
======================
*/
2022-09-05 20:25:33 +00:00
void I_PauseSong( int handle )
2012-11-26 18:58:24 +00:00
{
2022-09-05 20:25:33 +00:00
if( !Music_initialized )
{
2012-11-26 18:58:24 +00:00
return;
}
2022-09-05 20:25:33 +00:00
if( pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
// Stop the music source voice
pMusicSourceVoice->Stop( 0 );
}
}
/*
======================
I_ResumeSong
======================
*/
2022-09-05 20:25:33 +00:00
void I_ResumeSong( int handle )
2012-11-26 18:58:24 +00:00
{
2022-09-05 20:25:33 +00:00
if( !Music_initialized )
{
2012-11-26 18:58:24 +00:00
return;
}
// Stop the music source voice
2022-09-05 20:25:33 +00:00
if( pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
pMusicSourceVoice->Start( 0 );
}
}
/*
======================
I_StopSong
======================
*/
2022-09-05 20:25:33 +00:00
void I_StopSong( int handle )
2012-11-26 18:58:24 +00:00
{
2022-09-05 20:25:33 +00:00
if( !Music_initialized )
{
2012-11-26 18:58:24 +00:00
return;
}
// Stop the music source voice
2022-09-05 20:25:33 +00:00
if( pMusicSourceVoice != NULL )
{
2012-11-26 18:58:24 +00:00
pMusicSourceVoice->Stop( 0 );
}
}
/*
======================
I_UnRegisterSong
======================
*/
2022-09-05 20:25:33 +00:00
void I_UnRegisterSong( int handle )
2012-11-26 18:58:24 +00:00
{
// does nothing
}
/*
======================
I_RegisterSong
======================
*/
2022-09-05 20:25:33 +00:00
int I_RegisterSong( void* data, int length )
2012-11-26 18:58:24 +00:00
{
// does nothing
return 0;
}