The cabinets ran the game at unity and shaped volume and tone outside it, in an external amplifier and a 3-way crossover. That is why there is no master volume anywhere in the original code and none in AUDIO.INI - an operator turned a knob on an amp. A desktop player has no amp and no crossover, and the recovered soundbanks are a good deal livelier than what 4.12 shipped with, so the game has to offer the two controls the pod got from hardware. RP412AUDIOVOLUME, 0.0 to 4.0, is the amplifier: a listener gain, which the port had never set at all. RP412AUDIOBASS, 0.0 to 1.0, is the crossover's low band. Both default to leaving the mix exactly as the pod played it, so neither changes anything for anyone who does not go looking. The bass trim is not a filter, and the reason is worth writing down: the OpenAL we ship is Creative's, not OpenAL Soft, and it implements only AL_FILTER_LOWPASS. It rejects highpass and bandpass outright. A bandpass would have been the tidy answer, carrying the authored brightness model on GAINHF and the trim on GAINLF across the single direct filter a source gets. It is not on offer. So the trim scales sample data as it loads, which suits how this low end is actually built: the weight lives in discrete deep layer zones whose per-zone tuning bakes out to a very low playback rate - thirteen zones below 8kHz, three to five octaves under their recorded pitch, against four fifths of the set at 22kHz and up. Baked rate is a dependable proxy for band, so pulling down the low-rate zones is a real low-band trim and not a blunt cut. It eases in below 22kHz and reaches full depth at 5.5kHz. Caught while building this, and the reason for the probe: EFX_Initialize checks alGetError after configuring the scratch filter, so asking for a filter type the driver refuses leaves an error pending and takes the entire bridge down - reverb included. The bandpass attempt did precisely that and would have silently killed the reverb and brightness work. Initialize now survives losing the filter and says so. Builds clean, runs with both knobs set and with neither. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1199 lines
33 KiB
C++
1199 lines
33 KiB
C++
#include "mungal4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "l4audres.h"
|
|
#include "l4audhdw.h"
|
|
#include "l4audio.h"
|
|
#include "l4audlvl.h"
|
|
#include "l4audwtr.h"
|
|
#include "..\munga\audcmp.h"
|
|
#include "..\munga\audseq.h"
|
|
#include "..\munga\audrend.h"
|
|
#include "..\munga\namelist.h"
|
|
#include "..\munga\app.h"
|
|
#include "..\munga\notation.h"
|
|
#include "openal\al.h"
|
|
#include "sndfile.h"
|
|
|
|
ALuint *g_buffers;
|
|
int g_numBuffers;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Bass trim ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
// RP412AUDIOBASS, 0.0..1.0, default 1.0 (the mix exactly as authored).
|
|
//
|
|
// The arcade pod ran the game at unity and did its volume and tone shaping in
|
|
// hardware -- an external amplifier and a 3-way crossover. A desktop player has
|
|
// neither, so the low band needs a control in software. This is the crossover's
|
|
// low trim; RP412AUDIOVOLUME (L4AUDRND.cpp) is the amplifier's.
|
|
//
|
|
// It cannot be an EFX filter: the OpenAL this game ships implements only
|
|
// AL_FILTER_LOWPASS, so there is no high-shelf or bandpass to lean on, and the
|
|
// one direct filter per source is already carrying the authored brightness
|
|
// model. Instead the trim scales sample data as it is loaded.
|
|
//
|
|
// That works because of HOW the low end is built. RP's soundbanks carry their
|
|
// weight in discrete deep layer zones whose per-zone tuning bakes out to a very
|
|
// low playback rate -- 13 zones sit below 8 kHz, between 3.4 and 5.2 octaves
|
|
// below their recorded pitch, against 81% of the set at 22 kHz and up. A zone's
|
|
// baked rate is therefore a reliable proxy for which band it occupies, so
|
|
// attenuating the low-rate zones is a genuine low-band trim rather than a blunt
|
|
// overall cut.
|
|
//
|
|
// Ramp: untouched at or above 22050 Hz, full trim at or below 5512 Hz, log
|
|
// interpolated between, so nothing steps abruptly at a threshold.
|
|
//
|
|
static const ALsizei kBassTrimFullRate = 5512; // at/below: full trim
|
|
static const ALsizei kBassTrimNoneRate = 22050; // at/above: untouched
|
|
|
|
void
|
|
RPApplyBassTrim(char *data, int bytes, unsigned long format_bits, ALsizei rate)
|
|
{
|
|
static float s_trim = -1.0f;
|
|
|
|
if (s_trim < 0.0f)
|
|
{
|
|
s_trim = 1.0f;
|
|
|
|
if (const char *setting = getenv("RP412AUDIOBASS"))
|
|
{
|
|
float value = (float)atof(setting);
|
|
|
|
if (value >= 0.0f && value <= 1.0f)
|
|
{
|
|
s_trim = value;
|
|
Tell("Audio bass trim set to " << s_trim << "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s_trim >= 0.999f || format_bits != 16 || data == NULL || bytes <= 0)
|
|
{
|
|
return; // default: nothing to do
|
|
}
|
|
if (rate >= kBassTrimNoneRate)
|
|
{
|
|
return; // this zone is not part of the low band
|
|
}
|
|
|
|
//
|
|
// How much of the trim this zone takes, 0 at the no-trim rate rising to 1 at
|
|
// the full-trim rate, in octaves so the ramp is even to the ear.
|
|
//
|
|
float depth = 1.0f;
|
|
|
|
if (rate > kBassTrimFullRate)
|
|
{
|
|
const float span = (float)log((double)kBassTrimNoneRate / (double)kBassTrimFullRate);
|
|
depth = (float)log((double)kBassTrimNoneRate / (double)rate) / span;
|
|
}
|
|
|
|
const float scale = 1.0f - (1.0f - s_trim) * depth;
|
|
|
|
short *samples = (short *)data;
|
|
const int count = bytes / 2;
|
|
|
|
for (int s = 0; s < count; s++)
|
|
{
|
|
samples[s] = (short)(samples[s] * scale);
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
//####################### AudioObjectStream #############################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
// AudioObjectStream
|
|
//#############################################################################
|
|
//
|
|
AudioObjectStream::AudioObjectStream(
|
|
ResourceDescription *resource_description,
|
|
Entity *the_entity
|
|
):
|
|
PlugStream(resource_description)
|
|
{
|
|
entity = the_entity;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// AudioObjectStream
|
|
//#############################################################################
|
|
//
|
|
AudioObjectStream::AudioObjectStream()
|
|
{
|
|
entity = NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~AudioObjectStream
|
|
//#############################################################################
|
|
//
|
|
AudioObjectStream::~AudioObjectStream()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioObjectStream::TestInstance() const
|
|
{
|
|
PlugStream::TestInstance();
|
|
if (entity != NULL)
|
|
{
|
|
Check(entity);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MakeObjectImplementation
|
|
//#############################################################################
|
|
//
|
|
RegisteredClass*
|
|
AudioObjectStream::MakeObjectImplementation(Enumeration class_ID)
|
|
{
|
|
Check(this);
|
|
Verify(class_ID != RegisteredClass::NullClassID);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// HACK - Constructors should be referenced through registration, for
|
|
// now just use a switch statement
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
RegisteredClass *object;
|
|
|
|
switch (class_ID)
|
|
{
|
|
//
|
|
// Static resources ...
|
|
//
|
|
case RegisteredClass::PatchLevelOfDetailClassID:
|
|
object = new PatchLevelOfDetail(this);
|
|
break;
|
|
|
|
case RegisteredClass::PatchResourceClassID:
|
|
object = new PatchResource(this);
|
|
break;
|
|
|
|
case RegisteredClass::AudioResourceIndexClassID:
|
|
object = new AudioResourceIndex(this);
|
|
break;
|
|
|
|
//
|
|
// Location and sources ...
|
|
//
|
|
case RegisteredClass::L4AudioLocationClassID:
|
|
object = new L4AudioLocation(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::DirectPatchSourceClassID:
|
|
object = new DirectPatchSource(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::Dynamic3DPatchSourceClassID:
|
|
object = new Dynamic3DPatchSource(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::Static3DPatchSourceClassID:
|
|
object = new Static3DPatchSource(this, entity);
|
|
break;
|
|
|
|
//
|
|
// Audio components ...
|
|
//
|
|
case RegisteredClass::AudioControlMixerClassID:
|
|
object = new AudioControlMixer(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlMultiplierClassID:
|
|
object = new AudioControlMultiplier(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioResourceSelectorClassID:
|
|
object = new AudioResourceSelector(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSmootherClassID:
|
|
object = new AudioControlSmoother(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioSampleAndHoldClassID:
|
|
object = new AudioSampleAndHold(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSendClassID:
|
|
object = new AudioControlSend(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSequenceClassID:
|
|
object = new AudioControlSequence(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSplitterClassID:
|
|
object = new AudioControlSplitter(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioLFOClassID:
|
|
object = new AudioLFO(this, entity);
|
|
break;
|
|
|
|
//
|
|
// Audio watchers ...
|
|
//
|
|
case RegisteredClass::AudioMotionTriggerClassID:
|
|
object = new AudioMotionTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioMotionScaleClassID:
|
|
object = new AudioMotionScale(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioHingeScaleClassID:
|
|
object = new AudioHingeScale(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioScalarTriggerClassID:
|
|
object = new AudioScalarTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioScalarScaleClassID:
|
|
object = new AudioScalarScale(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioLogicalTriggerClassID:
|
|
object = new AudioLogicalTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioEnumerationTriggerClassID:
|
|
object = new AudioEnumerationTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioIntegerTriggerClassID:
|
|
object = new AudioIntegerTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlsButtonTriggerClassID:
|
|
object = new AudioControlsButtonTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioStateTriggerClassID:
|
|
object = new AudioStateTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioIdleWatcherClassID:
|
|
object = new AudioIdleWatcher(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioEnumerationDeltaTriggerClassID:
|
|
object = new AudioEnumerationDeltaTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioScalarDeltaTriggerClassID:
|
|
object = new AudioScalarDeltaTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::L4AudioCollisionTriggerClassID:
|
|
object = new L4AudioCollisionTrigger(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioMessageWatcherClassID:
|
|
object = new AudioMessageWatcher(this, entity);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlsButtonMessageWatcherClassID:
|
|
object = new AudioControlsButtonMessageWatcher(this, entity);
|
|
break;
|
|
|
|
//
|
|
// Inherited behavior
|
|
//
|
|
default:
|
|
object = PlugStream::MakeObjectImplementation(class_ID);
|
|
break;
|
|
}
|
|
return object;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CreatedObjectImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioObjectStream::CreatedObjectImplementation(
|
|
RegisteredClass *object,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(object);
|
|
Verify(object_ID != NullObjectID);
|
|
|
|
//
|
|
// std::Decide which index to add object to
|
|
//
|
|
switch (object->GetClassID())
|
|
{
|
|
case RegisteredClass::PatchLevelOfDetailClassID:
|
|
case RegisteredClass::PatchResourceClassID:
|
|
case RegisteredClass::AudioResourceIndexClassID:
|
|
AddGlobalPlug(Cast_Object(Plug*, object), object_ID);
|
|
break;
|
|
|
|
default:
|
|
AddLocalPlug(Cast_Object(Plug*, object), object_ID);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// BuildFromPageImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioObjectStream::BuildFromPageImplementation(
|
|
NameList *name_list,
|
|
Enumeration class_ID_enumeration,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
Check(this);
|
|
Verify(class_ID_enumeration != RegisteredClass::NullClassID);
|
|
Verify(object_ID != NullObjectID);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// HACK - Page interpreters should be referenced through registration,
|
|
// for now just use a switch statement
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
RegisteredClass::ClassID
|
|
class_ID = (RegisteredClass::ClassID)class_ID_enumeration;
|
|
|
|
switch (class_ID)
|
|
{
|
|
//
|
|
// Static resources ...
|
|
//
|
|
case RegisteredClass::PatchLevelOfDetailClassID:
|
|
PatchLevelOfDetail::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::PatchResourceClassID:
|
|
PatchResource::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioResourceIndexClassID:
|
|
AudioResourceIndex::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
//
|
|
// Location and sources ...
|
|
//
|
|
case RegisteredClass::L4AudioLocationClassID:
|
|
L4AudioLocation::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::DirectPatchSourceClassID:
|
|
DirectPatchSource::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::Dynamic3DPatchSourceClassID:
|
|
Dynamic3DPatchSource::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::Static3DPatchSourceClassID:
|
|
Static3DPatchSource::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
//
|
|
// Audio components ...
|
|
//
|
|
case RegisteredClass::AudioControlMixerClassID:
|
|
AudioControlMixer::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlMultiplierClassID:
|
|
AudioControlMultiplier::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioResourceSelectorClassID:
|
|
AudioResourceSelector::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSmootherClassID:
|
|
AudioControlSmoother::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioSampleAndHoldClassID:
|
|
AudioSampleAndHold::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSendClassID:
|
|
AudioControlSend::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSequenceClassID:
|
|
AudioControlSequence::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlSplitterClassID:
|
|
AudioControlSplitter::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioLFOClassID:
|
|
AudioLFO::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
//
|
|
// Audio watchers ...
|
|
//
|
|
case RegisteredClass::AudioMotionTriggerClassID:
|
|
AudioMotionTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioMotionScaleClassID:
|
|
AudioMotionScale::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioHingeScaleClassID:
|
|
AudioHingeScale::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioScalarTriggerClassID:
|
|
AudioScalarTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioScalarScaleClassID:
|
|
AudioScalarScale::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioLogicalTriggerClassID:
|
|
AudioLogicalTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioEnumerationTriggerClassID:
|
|
AudioEnumerationTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioIntegerTriggerClassID:
|
|
AudioIntegerTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlsButtonTriggerClassID:
|
|
AudioControlsButtonTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioStateTriggerClassID:
|
|
AudioStateTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioIdleWatcherClassID:
|
|
AudioIdleWatcher::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioEnumerationDeltaTriggerClassID:
|
|
AudioEnumerationDeltaTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioScalarDeltaTriggerClassID:
|
|
AudioScalarDeltaTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::L4AudioCollisionTriggerClassID:
|
|
L4AudioCollisionTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioMessageWatcherClassID:
|
|
AudioMessageWatcher::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
case RegisteredClass::AudioControlsButtonMessageWatcherClassID:
|
|
AudioControlsButtonMessageWatcher::BuildFromPage(this, name_list, class_ID, object_ID);
|
|
break;
|
|
|
|
//
|
|
// Inherited behavior
|
|
//
|
|
default:
|
|
PlugStream::BuildFromPageImplementation(name_list, class_ID, object_ID);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// BuiltFromPageImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioObjectStream::BuiltFromPageImplementation(
|
|
Enumeration class_ID,
|
|
ObjectID object_ID,
|
|
const CString &object_name
|
|
)
|
|
{
|
|
Check(this);
|
|
Verify(class_ID != RegisteredClass::NullClassID);
|
|
Verify(object_ID != NullObjectID);
|
|
Check(&object_name);
|
|
|
|
//
|
|
// std::Decide which index to add object to
|
|
//
|
|
if (object_name.Length() == 0)
|
|
return;
|
|
|
|
switch (class_ID)
|
|
{
|
|
case RegisteredClass::PatchLevelOfDetailClassID:
|
|
case RegisteredClass::PatchResourceClassID:
|
|
case RegisteredClass::AudioResourceIndexClassID:
|
|
AddGlobalObjectID(object_ID, object_name);
|
|
break;
|
|
|
|
default:
|
|
AddLocalObjectID(object_ID, object_name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
//###################### L4AudioResourceManager #########################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
// L4AudioResourceManager
|
|
//#############################################################################
|
|
//
|
|
L4AudioResourceManager::L4AudioResourceManager()
|
|
{
|
|
g_buffers = NULL;
|
|
g_numBuffers = 0;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~L4AudioResourceManager
|
|
//#############################################################################
|
|
//
|
|
L4AudioResourceManager::~L4AudioResourceManager()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
L4AudioResourceManager::TestInstance() const
|
|
{
|
|
Node::TestInstance();
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// PreloadResources
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioResourceManager::PreloadResources(
|
|
NotationFile *notation_file
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(notation_file);
|
|
//Check(audio_hardware);
|
|
|
|
//Determine how many loadable samples exist
|
|
g_numBuffers=0;
|
|
|
|
for(int i=1; i <= 2; i++)
|
|
{
|
|
for (int j=0; j < 100; j++)
|
|
{
|
|
if (PRESET_isImplemented(i,j))
|
|
{
|
|
g_numBuffers += PRESET_getNumSamples(i,j);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Generate the buffers
|
|
if (g_numBuffers > 0)
|
|
{
|
|
//Generate buffers
|
|
alGetError();
|
|
g_buffers = new ALuint[g_numBuffers];
|
|
alGenBuffers(g_numBuffers,g_buffers);
|
|
ALenum error;
|
|
if ((error = alGetError()) != AL_NO_ERROR)
|
|
{
|
|
DEBUG_STREAM << "OpenAL has encountered an error while initializing the buffers." << std::endl;
|
|
delete [] g_buffers;
|
|
g_buffers = NULL;
|
|
g_numBuffers = 0;
|
|
}
|
|
}
|
|
|
|
int bufferInd = 0;
|
|
//Load buffers
|
|
for(int i=1; i<=2 && bufferInd < g_numBuffers; i++)
|
|
{
|
|
for(int j=0; j<100 && bufferInd < g_numBuffers; j++)
|
|
{
|
|
if (PRESET_isImplemented(i,j))
|
|
{
|
|
for (int k=0; k < PRESET_getNumSamples(i,j) && bufferInd < g_numBuffers; k++)
|
|
{
|
|
SAMPLEINFO info = PRESET_getSampleInfo(i,j,k);
|
|
|
|
//Load WAV to buffer
|
|
char *data;
|
|
int size;
|
|
|
|
//Open WAV
|
|
char fullname[50];
|
|
strcpy_s(fullname,50,"AUDIO\\");
|
|
strcat_s(fullname,50,info.file);
|
|
SF_INFO *sfInfo = new SF_INFO[2];
|
|
//SF_INFO is working different than expected!
|
|
sfInfo->format = 0;
|
|
SNDFILE *file = sf_open(fullname,SFM_READ,sfInfo);
|
|
|
|
if (file == NULL)
|
|
{
|
|
DEBUG_STREAM << "Failed to open." << std::endl;
|
|
}
|
|
|
|
unsigned long formatBits = 0;
|
|
unsigned long sampleRate = sfInfo->samplerate;
|
|
size = sfInfo->frames;
|
|
|
|
bool isMono = (sfInfo->channels == 1);
|
|
|
|
if (sfInfo->format & SF_FORMAT_PCM_S8)
|
|
{
|
|
formatBits = 8;
|
|
} else if (sfInfo->format & SF_FORMAT_PCM_16)
|
|
{
|
|
formatBits = 16;
|
|
} else
|
|
{
|
|
DEBUG_STREAM << "BAD FORMAT" << std::endl;
|
|
}
|
|
|
|
ALenum format;
|
|
if (formatBits == 8)
|
|
{
|
|
if (isMono)
|
|
{
|
|
format = AL_FORMAT_MONO8;
|
|
} else
|
|
{
|
|
size *= 2;
|
|
format = AL_FORMAT_STEREO8;
|
|
}
|
|
} else if (formatBits == 16)
|
|
{
|
|
size *= 2;
|
|
if (isMono)
|
|
{
|
|
format = AL_FORMAT_MONO16;
|
|
} else
|
|
{
|
|
size *= 2;
|
|
format = AL_FORMAT_STEREO16;
|
|
}
|
|
}
|
|
|
|
ALsizei alSampleRate = sampleRate;
|
|
|
|
//Load size & data
|
|
delete [] sfInfo;
|
|
data = new char[size];
|
|
sf_read_raw(file,data,size);
|
|
sf_close(file);
|
|
|
|
RPApplyBassTrim(data, size, formatBits, alSampleRate);
|
|
|
|
//Feed the buffer
|
|
alBufferData(g_buffers[bufferInd],format,data,size,alSampleRate);
|
|
PRESET_setBufferIndex(i,j,k,bufferInd);
|
|
bufferInd++;
|
|
|
|
delete [] data;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Load the sound banks
|
|
//----------------------------------------------------------------------
|
|
//
|
|
/*NameList *name_list;
|
|
NameList::Entry *entry;
|
|
|
|
Verify(notation_file->EntryCount("AudioResources") >= 2);
|
|
|
|
name_list = notation_file->MakeEntryList("AudioResources");
|
|
Register_Object(name_list);
|
|
entry = name_list->GetFirstEntry();
|
|
Check(entry);
|
|
while (entry != NULL)
|
|
{
|
|
Check(entry);
|
|
if (entry->IsName("front_audio_resource"))
|
|
{
|
|
Check_Pointer(entry->GetChar());
|
|
Check(audio_hardware->GetFrontCard());
|
|
audio_hardware->GetFrontCard()->LoadSBK(entry->GetChar());
|
|
}
|
|
else
|
|
{
|
|
Verify(entry->IsName("rear_audio_resource"));
|
|
Check_Pointer(entry->GetChar());
|
|
Check(audio_hardware->GetRearCard());
|
|
audio_hardware->GetRearCard()->LoadSBK(entry->GetChar());
|
|
}
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
Unregister_Object(name_list);
|
|
delete name_list;*/
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Create static audio objects
|
|
//----------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
// Get static audio stream resource
|
|
//
|
|
ResourceDescription *resource_description;
|
|
|
|
Check(application);
|
|
Check(application->GetResourceFile());
|
|
resource_description =
|
|
application->GetResourceFile()->FindResourceDescription(
|
|
"StaticAudioStream",
|
|
ResourceDescription::StaticAudioStreamResourceType
|
|
);
|
|
Check(resource_description);
|
|
resource_description->Lock();
|
|
|
|
//
|
|
// Parse the audio object stream
|
|
//
|
|
AudioObjectStream audio_object_stream(resource_description, NULL);
|
|
|
|
Check(&audio_object_stream);
|
|
audio_object_stream.CreateObjects();
|
|
resource_description->Unlock();
|
|
}
|
|
|
|
ALuint AL_getBuffer(int index)
|
|
{
|
|
return g_buffers[index];
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// UnloadResources
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioResourceManager::UnloadResources(
|
|
)
|
|
{
|
|
Check(this);
|
|
// Check(audio_hardware);
|
|
|
|
//Unload wav data
|
|
if (g_numBuffers > 0)
|
|
{
|
|
alGetError();
|
|
alDeleteBuffers(g_numBuffers,g_buffers);
|
|
ALenum error = alGetError();
|
|
if (error != AL_NO_ERROR)
|
|
{
|
|
DEBUG_STREAM << "OpenAL reached an error when attempting to delete its buffers." << std::endl;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Release banks
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Check(audio_hardware->GetFrontCard());
|
|
// audio_hardware->GetFrontCard()->ReleaseAllBanks();
|
|
// Check(audio_hardware->GetRearCard());
|
|
// audio_hardware->GetRearCard()->ReleaseAllBanks();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CreateStaticAudioStreamResource
|
|
//#############################################################################
|
|
//
|
|
ResourceDescription::ResourceID
|
|
L4AudioResourceManager::CreateStaticAudioStreamResource(
|
|
ResourceFile *resource_file
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Open the notation file for the static stream resource
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Tell("Opening Audio Script audio\\static.scp\n");
|
|
NotationFile notation_file("audio\\static.scp");
|
|
Check(¬ation_file);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Build the static audio object stream
|
|
//----------------------------------------------------------------------
|
|
//
|
|
AudioObjectStream audio_object_stream;
|
|
|
|
Check(&audio_object_stream);
|
|
audio_object_stream.BuildFromNotationFile(¬ation_file, resource_file);
|
|
Tell("Closed Audio Script\n");
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Add the resource
|
|
//----------------------------------------------------------------------
|
|
//
|
|
ResourceDescription *res_description;
|
|
|
|
res_description =
|
|
resource_file->AddResourceMemoryStream(
|
|
"StaticAudioStream",
|
|
ResourceDescription::StaticAudioStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&audio_object_stream
|
|
);
|
|
Check(res_description);
|
|
return res_description->resourceID;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CreateEntityAudioObjects
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioResourceManager::CreateEntityAudioObjects(
|
|
Entity *entity,
|
|
AudioRepresentation audio_representation
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(entity);
|
|
Verify(
|
|
audio_representation == InternalAudioRepresentation ||
|
|
audio_representation == ExternalAudioRepresentation
|
|
);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Read the audio resource for this entity
|
|
//------------------------------------------------------------------------
|
|
//
|
|
ResourceDescription *audio_resource_description;
|
|
|
|
Check(application);
|
|
Check(application->GetResourceFile());
|
|
audio_resource_description =
|
|
application->GetResourceFile()->SearchList(
|
|
entity->GetResourceID(),
|
|
ResourceDescription::AudioStreamListResourceType
|
|
);
|
|
if (audio_resource_description == NULL)
|
|
return;
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Read the appropriate resource for an internal or external
|
|
// representation
|
|
//------------------------------------------------------------------------
|
|
//
|
|
ResourceDescription *stream_resource_description;
|
|
|
|
Check(application);
|
|
Check(application->GetResourceFile());
|
|
Check(audio_resource_description);
|
|
stream_resource_description =
|
|
application->GetResourceFile()->SearchList(
|
|
audio_resource_description->resourceID,
|
|
(audio_representation == InternalAudioRepresentation) ?
|
|
ResourceDescription::InternalAudioStreamResourceType :
|
|
ResourceDescription::ExternalAudioStreamResourceType
|
|
);
|
|
if (stream_resource_description == NULL)
|
|
return;
|
|
Check(stream_resource_description);
|
|
stream_resource_description->Lock();
|
|
audio_resource_description->Lock();
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Make the audio object stream
|
|
//------------------------------------------------------------------------
|
|
//
|
|
AudioObjectStream
|
|
audio_object_stream(
|
|
stream_resource_description,
|
|
entity
|
|
);
|
|
|
|
Check(&audio_object_stream);
|
|
audio_object_stream.CreateObjects();
|
|
stream_resource_description->Unlock();
|
|
audio_resource_description->Unlock();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// DestroyEntityAudioObjects
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioResourceManager::DestroyEntityAudioObjects(Entity *entity)
|
|
{
|
|
Check(this);
|
|
Check(entity);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Stop and delete all audio objects
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Entity::AudioSocketIterator
|
|
iterator(entity);
|
|
Component
|
|
*component;
|
|
AudioComponent
|
|
*audio_component;
|
|
|
|
Check(&iterator);
|
|
while ((component = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
audio_component = Cast_Object(AudioComponent*, component);
|
|
Check(audio_component);
|
|
audio_component->ReceiveControl(StopAudioControlID, 0.0f);
|
|
audio_component->ReceiveControl(FlushMessagesAudioControlID, 0.0f);
|
|
}
|
|
iterator.DeletePlugs();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CreateModelAudioResource
|
|
//#############################################################################
|
|
//
|
|
ResourceDescription::ResourceID
|
|
L4AudioResourceManager::CreateModelAudioStreamResource(
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories*
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
|
|
const char *script_name = NULL;
|
|
ResourceDescription::ResourceID resource_id_list[3];
|
|
size_t list_length = 0;
|
|
|
|
resource_id_list[0] = ResourceDescription::NullResourceID;
|
|
resource_id_list[1] = ResourceDescription::NullResourceID;
|
|
resource_id_list[2] = ResourceDescription::NullResourceID;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Check for the external audio script
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (model_file->GetEntry("audio", "external", &script_name))
|
|
{
|
|
//
|
|
// See if the resource already exists
|
|
//
|
|
ResourceDescription *res_description =
|
|
resource_file->FindResourceDescription(
|
|
script_name,
|
|
ResourceDescription::ExternalAudioStreamResourceType
|
|
);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If not, create it
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (res_description == NULL)
|
|
{
|
|
//
|
|
// Make the file name
|
|
//
|
|
CString file_name(script_name);
|
|
CString prefix("audio\\");
|
|
|
|
file_name = prefix + file_name;
|
|
|
|
//
|
|
// Build the memory stream
|
|
//
|
|
Tell("Opening Audio Script " << file_name << "\n");
|
|
NotationFile script_file(file_name);
|
|
AudioObjectStream audio_object_stream;
|
|
|
|
Check(&script_file);
|
|
Check(&audio_object_stream);
|
|
audio_object_stream.BuildFromNotationFile(&script_file, resource_file);
|
|
Tell("Closed Audio Script\n");
|
|
|
|
//
|
|
// Add the resource
|
|
//
|
|
res_description =
|
|
resource_file->AddResourceMemoryStream(
|
|
script_name,
|
|
ResourceDescription::ExternalAudioStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&audio_object_stream
|
|
);
|
|
}
|
|
Check(res_description);
|
|
resource_id_list[list_length++] = res_description->resourceID;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Check for the internal audio script
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (model_file->GetEntry("audio", "internal", &script_name))
|
|
{
|
|
//
|
|
// See if the resource already exists
|
|
//
|
|
ResourceDescription *res_description =
|
|
resource_file->FindResourceDescription(
|
|
script_name,
|
|
ResourceDescription::InternalAudioStreamResourceType
|
|
);
|
|
|
|
//
|
|
// If not, create it
|
|
//
|
|
if (res_description == NULL)
|
|
{
|
|
//
|
|
// Make the file name
|
|
//
|
|
CString file_name(script_name);
|
|
CString prefix("audio\\");
|
|
|
|
file_name = prefix + file_name;
|
|
|
|
//
|
|
// Build the memory stream
|
|
//
|
|
Tell("Opening Audio Script " << file_name << "\n");
|
|
NotationFile script_file(file_name);
|
|
AudioObjectStream audio_object_stream;
|
|
|
|
Check(&script_file);
|
|
Check(&audio_object_stream);
|
|
audio_object_stream.BuildFromNotationFile(&script_file, resource_file);
|
|
Tell("Closed Audio Script\n");
|
|
|
|
//
|
|
// Add the resource
|
|
//
|
|
res_description =
|
|
resource_file->AddResourceMemoryStream(
|
|
script_name,
|
|
ResourceDescription::InternalAudioStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&audio_object_stream
|
|
);
|
|
}
|
|
Check(res_description);
|
|
resource_id_list[list_length++] = res_description->resourceID;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Check for the audio entity model
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
const char *audio_entity_model = NULL;
|
|
|
|
if (model_file->GetEntry("audio", "model", &audio_entity_model))
|
|
{
|
|
//
|
|
// The resource should already exist
|
|
//
|
|
ResourceDescription *res_description =
|
|
resource_file->FindResourceDescription(
|
|
audio_entity_model,
|
|
ResourceDescription::ModelListResourceType
|
|
);
|
|
if (res_description == NULL)
|
|
{
|
|
std::cout << "audio_entity_model == " << audio_entity_model;
|
|
Fail("L4AudioResourceManager::CreateModelAudioStreamResource - model not found");
|
|
}
|
|
Check(res_description);
|
|
resource_id_list[list_length++] = res_description->resourceID;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If either script has been created, then create the resource list
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (list_length > 0)
|
|
{
|
|
ResourceDescription *res_description =
|
|
resource_file->AddResourceList(
|
|
model_name,
|
|
ResourceDescription::AudioStreamListResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
resource_id_list,
|
|
list_length
|
|
);
|
|
Check(res_description);
|
|
return res_description->resourceID;
|
|
}
|
|
return ResourceDescription::NullResourceID;
|
|
}
|