Files
TeslaRel410/CODE/RP/MUNGA_L4/L4AUDIO.CPP
T
CydandClaude Fable 5 fdd9ac9d97 Initial import: Tesla Release 4.10 (Tesla:BattleTech & Tesla:Red Planet)
Archival snapshot of the Virtual World Entertainment Tesla cockpit
software, 1994-1996: MUNGA engine and L4 pod layer source (Borland
C++ 5.0), BT/RP game code, and game content (models, audio, maps,
gauges, Division renderer data). Includes third-party libraries:
Division dVS/DPL graphics, HMI SOS audio, WATTCP networking.

Files are preserved byte-for-byte (.gitattributes disables all
line-ending conversion). README.md documents the layout, target
hardware, and toolchain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:21:58 -05:00

2330 lines
64 KiB
C++

//===========================================================================//
// File: l4audio.cc //
// Project: MUNGA Brick: Audio Renderer //
// Contents: //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 01/30/95 ECH Initial coding. //
//---------------------------------------------------------------------------//
// Copyright (C) 1994, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <mungal4.hpp>
#pragma hdrstop
#if !defined(L4AUDIO_HPP)
# include <l4audio.hpp>
#endif
#if !defined(L4AUDLVL_HPP)
# include <l4audlvl.hpp>
#endif
#if !defined(L4APP_HPP)
# include <l4app.hpp>
#endif
#if !defined(L4AUDRND_HPP)
# include <l4audrnd.hpp>
#endif
#if !defined(NAMELIST_HPP)
# include <namelist.hpp>
#endif
//#############################################################################
//####################### L4AudioSpatialization #########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
L4AudioSpatialization::L4AudioSpatialization()
{
azimuthOfSource = 0.0f;
frontLeftScale = 0.0f;
frontRightScale = 0.0f;
rearLeftScale = 0.0f;
rearRightScale = 0.0f;
frontLeftDelay = 0.0f;
frontRightDelay = 0.0f;
rearLeftDelay = 0.0f;
rearRightDelay = 0.0f;
quadrant = Quadrant1;
}
//
//#############################################################################
//#############################################################################
//
L4AudioSpatialization::~L4AudioSpatialization()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
L4AudioSpatialization::TestInstance() const
{
return True;
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioSpatialization::CalculateSpatialization(Radian azimuth_of_source)
{
azimuthOfSource = azimuth_of_source;
//
//---------------------------------------------------------------
// Make azimuth usable for channel volume calculations
//---------------------------------------------------------------
//
#define DEG_90 (90.0f*(RAD_PER_DEG))
#define DEG_180 (180.0f*(RAD_PER_DEG))
#define DEG_360 (360.0f*(RAD_PER_DEG))
azimuthOfSource = DEG_180 + azimuthOfSource;
if (azimuthOfSource > DEG_180)
azimuthOfSource = azimuthOfSource - DEG_360;
Verify(
azimuthOfSource <= DEG_180 &&
azimuthOfSource >= -DEG_180
);
//
//---------------------------------------------------------------
// Channel volume scale and ITD calculations
//---------------------------------------------------------------
//
//
// Get audio head constants
//
L4AudioRenderer *audio_renderer;
AudioHead *audio_head;
Check(application);
audio_renderer =
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer());
Check(audio_renderer);
audio_head = audio_renderer->GetAudioHead();
Check(audio_head);
//
// Init channel volume scale variables
//
const Radian azimuth_max = 45.0*RAD_PER_DEG; // HACK - should come from audio.ini
Scalar tangent_ratio;
frontLeftScale = 0.0f;
frontRightScale = 0.0f;
rearLeftScale = 0.0f;
rearRightScale = 0.0f;
//
// Init ITD parameters
//
const Scalar itd_delay = audio_head->GetITDDifference();
frontLeftDelay = 0.0f;
frontRightDelay = 0.0f;
rearLeftDelay = 0.0f;
rearRightDelay = 0.0f;
//
//---------------------------------------------------------------
// Quadrant 1
//---------------------------------------------------------------
//
if (
azimuthOfSource > -azimuth_max &&
azimuthOfSource < azimuth_max
)
{
quadrant = Quadrant1;
// volume scale
Verify(!Small_Enough(tan(azimuth_max)));
tangent_ratio =
(tan(azimuthOfSource) / tan(azimuth_max)) * 0.5f;
frontLeftScale = Sqrt(0.5f + tangent_ratio);
frontRightScale = Sqrt(0.5f - tangent_ratio);
Verify(frontLeftScale >= 0.0f && frontLeftScale <= 1.0f);
Verify(frontRightScale >= 0.0f && frontRightScale <= 1.0f);
// pitch offset
if (azimuthOfSource > 0.0f)
{
frontLeftDelay = itd_delay * tangent_ratio * 2.0f;
}
else
{
frontRightDelay = itd_delay * -tangent_ratio * 2.0f;
}
}
//
//---------------------------------------------------------------
// Quadrant 2
//---------------------------------------------------------------
//
else if (
azimuthOfSource > azimuth_max &&
azimuthOfSource < (DEG_180 - azimuth_max)
)
{
quadrant = Quadrant2;
// volume scale
azimuthOfSource = azimuthOfSource - DEG_90;
Verify(!Small_Enough(tan(DEG_90 - azimuth_max)));
tangent_ratio =
(tan(azimuthOfSource) / tan(DEG_90 - azimuth_max)) * 0.5f;
rearLeftScale = Sqrt(0.5f + tangent_ratio);
frontLeftScale = Sqrt(0.5f - tangent_ratio);
Verify(rearLeftScale >= 0.0f && rearLeftScale <= 1.0f);
Verify(frontLeftScale >= 0.0f && frontLeftScale <= 1.0f);
// pitch offset
if (azimuthOfSource > 0.0f)
{
rearLeftDelay = itd_delay * tangent_ratio * 2.0f;
}
else
{
frontLeftDelay = itd_delay * -tangent_ratio * 2.0f;
}
}
//
//---------------------------------------------------------------
// Quadrant 3
//---------------------------------------------------------------
//
else if (
azimuthOfSource > (DEG_180 - azimuth_max) ||
azimuthOfSource < (-DEG_180 + azimuth_max)
)
{
quadrant = Quadrant3;
// volume scale
azimuthOfSource = azimuthOfSource - DEG_180;
Verify(!Small_Enough(tan(azimuth_max)));
tangent_ratio =
(tan(azimuthOfSource) / tan(azimuth_max)) * 0.5f;
rearRightScale = Sqrt(0.5f + tangent_ratio);
rearLeftScale = Sqrt(0.5f - tangent_ratio);
Verify(rearRightScale >= 0.0f && rearRightScale <= 1.0f);
Verify(rearLeftScale >= 0.0f && rearLeftScale <= 1.0f);
// pitch offset
if (azimuthOfSource > -azimuth_max)
{
rearLeftDelay = -(itd_delay * tangent_ratio * 2.0f);
}
else
{
rearRightDelay = -(itd_delay * -tangent_ratio * 2.0f);
}
#if 0
Tell(
"azimuthOfSource " << azimuthOfSource <<
" rearRightDelay " << rearRightDelay <<
" rearLeftDelay " << rearLeftDelay <<
"\n"
);
#endif
}
//
//---------------------------------------------------------------
// Quadrant 4
//---------------------------------------------------------------
//
else
{
quadrant = Quadrant4;
Verify(
azimuthOfSource > (-DEG_180 + azimuth_max) &&
azimuthOfSource < -azimuth_max
);
// volume scale
azimuthOfSource = azimuthOfSource + DEG_90;
Verify(!Small_Enough(tan(DEG_90 - azimuth_max)));
tangent_ratio =
(tan(azimuthOfSource) / tan(DEG_90 - azimuth_max)) * 0.5f;
frontRightScale = Sqrt(0.5f + tangent_ratio);
rearRightScale = Sqrt(0.5f - tangent_ratio);
Verify(frontRightScale >= 0.0f && frontRightScale <= 1.0f);
Verify(rearRightScale >= 0.0f && rearRightScale <= 1.0f);
// pitch offset
if (azimuthOfSource > 0.0f)
{
frontRightDelay = itd_delay * tangent_ratio * 2.0f;
}
else
{
rearRightDelay = itd_delay * -tangent_ratio * 2.0f;
}
}
#if 0
Tell(
"FL " << frontLeftScale <<
" FR " << frontRightScale <<
" RL " << rearLeftScale <<
" RR " << rearRightScale <<
"\n"
);
#endif
#if 0
Tell(
"Quadrant " << quadrant <<
" FL " << frontLeftDelay <<
" FR " << frontRightDelay <<
" RL " << rearLeftDelay <<
" RR " << rearRightDelay <<
"\n"
);
#endif
Verify(frontLeftScale >= 0.0f && frontLeftScale <= 1.0f);
Verify(frontRightScale >= 0.0f && frontRightScale <= 1.0f);
Verify(rearLeftScale >= 0.0f && rearLeftScale <= 1.0f);
Verify(rearRightScale >= 0.0f && rearRightScale <= 1.0f);
}
//#############################################################################
//######################### L4AudioLocation #############################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
L4AudioLocation::L4AudioLocation(
PlugStream *stream,
Entity *entity
):
AudioLocation(stream, entity)
{
L4AudioLocationX();
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioLocation::L4AudioLocationX()
{
//
// Get audio renderer frame duration
//
Check(application)
L4AudioRenderer *audio_renderer =
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer());
Check(audio_renderer);
Time frame_duration =
audio_renderer->GetCalibrationFrameDuration();
//
// Init ITD pitch offset variables
//
lastITDFrameTime = Now();
lastITDFrameTime -= frame_duration;
currentFrontLeftDelay = 0.0f;
currentFrontRightDelay = 0.0f;
currentRearLeftDelay = 0.0f;
currentRearRightDelay = 0.0f;
frontLeftITDPitchOffset = 0.0f;
frontRightITDPitchOffset = 0.0f;
rearLeftITDPitchOffset = 0.0f;
rearRightITDPitchOffset = 0.0f;
}
//
//#############################################################################
//#############################################################################
//
L4AudioLocation::~L4AudioLocation()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
L4AudioLocation::TestInstance() const
{
AudioLocation::TestInstance();
Check(&spatialization);
return True;
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioLocation::UpdateSpatialModelImplementation(AudioHead *audio_head)
{
Check(this);
Check(audio_head);
//
//---------------------------------------------------------------
// Call inherited method
//---------------------------------------------------------------
//
AudioLocation::UpdateSpatialModelImplementation(audio_head);
//
//---------------------------------------------------------------
// Catch case of head == source
//---------------------------------------------------------------
//
if (IsHeadSource())
{
spatialization.frontLeftScale = 1.0f;
spatialization.frontRightScale = 1.0f;
spatialization.rearLeftScale = 1.0f;
spatialization.rearRightScale = 1.0f;
frontLeftITDPitchOffset = 0.0f;
frontRightITDPitchOffset = 0.0f;
rearLeftITDPitchOffset = 0.0f;
rearRightITDPitchOffset = 0.0f;
return;
}
//
//---------------------------------------------------------------
// Calculate channel spatialization
//---------------------------------------------------------------
//
spatialization.CalculateSpatialization(GetAzimuthOfSource());
//
//---------------------------------------------------------------
// Calculate ITD pitch offsets
//---------------------------------------------------------------
//
const Scalar itd_pitch_offset_constant =
0.003831f / 0.000002f; // period / delay // HACK - should come from audio head
Time itd_delta_time;
itd_delta_time = Now();
itd_delta_time -= lastITDFrameTime;
lastITDFrameTime = Now();
frontLeftITDPitchOffset = 0.0f;
frontRightITDPitchOffset = 0.0f;
rearLeftITDPitchOffset = 0.0f;
rearRightITDPitchOffset = 0.0f;
if (!Small_Enough((Scalar)itd_delta_time))
{
if (currentFrontLeftDelay != spatialization.frontLeftDelay)
{
Verify(!Small_Enough((Scalar)itd_delta_time));
frontLeftITDPitchOffset =
itd_pitch_offset_constant *
(spatialization.frontLeftDelay - currentFrontLeftDelay) /
(Scalar)itd_delta_time;
currentFrontLeftDelay = spatialization.frontLeftDelay;
}
if (currentFrontRightDelay != spatialization.frontRightDelay)
{
Verify(!Small_Enough((Scalar)itd_delta_time));
frontRightITDPitchOffset =
itd_pitch_offset_constant *
(spatialization.frontRightDelay - currentFrontRightDelay) /
(Scalar)itd_delta_time;
currentFrontRightDelay = spatialization.frontRightDelay;
}
if (currentRearLeftDelay != spatialization.rearLeftDelay)
{
Verify(!Small_Enough((Scalar)itd_delta_time));
rearLeftITDPitchOffset =
itd_pitch_offset_constant *
(spatialization.rearLeftDelay - currentRearLeftDelay) /
(Scalar)itd_delta_time;
currentRearLeftDelay = spatialization.rearLeftDelay;
}
if (currentRearRightDelay != spatialization.rearRightDelay)
{
Verify(!Small_Enough((Scalar)itd_delta_time));
rearRightITDPitchOffset =
itd_pitch_offset_constant *
(spatialization.rearRightDelay - currentRearRightDelay) /
(Scalar)itd_delta_time;
currentRearRightDelay = spatialization.rearRightDelay;
}
}
#if 0
Tell(
"Quadrant " << spatialization.quadrant <<
" FL " << frontLeftITDPitchOffset <<
" FR " << frontRightITDPitchOffset <<
" RL " << rearLeftITDPitchOffset <<
" RR " << rearRightITDPitchOffset <<
"\n"
);
#endif
//
//---------------------------------------------------------------
// Check for source "between ears"
//---------------------------------------------------------------
//
const Scalar ear_radius =
audio_head->GetDistanceBetweenEars() * 0.5;
Scalar distance_to_source_listener_plane =
GetDistanceToSourceListenerPlane();
if (distance_to_source_listener_plane < ear_radius)
{
Verify(!Small_Enough(ear_radius));
const Scalar proportion =
0.5f *
(ear_radius - distance_to_source_listener_plane) /
ear_radius;
Scalar powerA, powerB;
#if DEBUG_LEVEL>0
if (!(proportion >= 0.0f && proportion <= 1.0f))
{
Dump(ear_radius);
Dump(distance_to_source_listener_plane);
Dump(proportion);
}
Verify(proportion >= 0.0f && proportion <= 1.0f);
#endif
powerA = Sqrt(1.0f - proportion);
powerB = Sqrt(proportion);
Verify(powerA >= 0.0f && powerA <= 1.0f);
Verify(powerB >= 0.0f && powerB <= 1.0f);
switch (spatialization.quadrant)
{
case L4AudioSpatialization::Quadrant1:
spatialization.rearLeftScale = spatialization.frontLeftScale * powerB;
spatialization.rearRightScale = spatialization.frontRightScale * powerB;
spatialization.frontLeftScale *= powerA;
spatialization.frontRightScale *= powerA;
break;
case L4AudioSpatialization::Quadrant2:
spatialization.rearRightScale = spatialization.rearLeftScale * powerB;
spatialization.frontRightScale = spatialization.frontLeftScale * powerB;
spatialization.rearLeftScale *= powerA;
spatialization.frontLeftScale *= powerA;
break;
case L4AudioSpatialization::Quadrant3:
spatialization.frontRightScale = spatialization.rearRightScale * powerB;
spatialization.frontLeftScale = spatialization.rearLeftScale * powerB;
spatialization.rearRightScale *= powerA;
spatialization.rearLeftScale *= powerA;
break;
case L4AudioSpatialization::Quadrant4:
spatialization.frontLeftScale = spatialization.frontRightScale * powerB;
spatialization.rearLeftScale = spatialization.rearRightScale * powerB;
spatialization.frontRightScale *= powerA;
spatialization.rearRightScale *= powerA;
break;
}
#if 0
Tell(
"FL " << spatialization.frontLeftScale <<
" FR " << spatialization.frontRightScale <<
" RL " << spatialization.rearLeftScale <<
" RR " << spatialization.rearRightScale <<
"\n"
);
#endif
}
Verify(spatialization.frontLeftScale >= 0.0f && spatialization.frontLeftScale <= 1.0f);
Verify(spatialization.frontRightScale >= 0.0f && spatialization.frontRightScale <= 1.0f);
Verify(spatialization.rearLeftScale >= 0.0f && spatialization.rearLeftScale <= 1.0f);
Verify(spatialization.rearRightScale >= 0.0f && spatialization.rearRightScale <= 1.0f);
}
//#############################################################################
//######################### AudioChannelSet #############################
//#############################################################################
AudioChannelSet AudioChannelSet::Null;
//
//#############################################################################
//#############################################################################
//
AudioChannelSet::AudioChannelSet()
{
for (int i = 0; i < AudioChannelSetSize; i++)
{
channels[i] = NULL;
enables[i] = False;
}
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioChannelSet::TestInstance() const
{
for (int i = 0; i < AudioChannelSetSize; i++)
{
if (channels[i] != NULL)
{
Verify(enables[i] == True);
Check(channels[i]);
}
}
return True;
}
//
//#############################################################################
//#############################################################################
//
void
AudioChannelSet::EnableAll()
{
//
// Must set all channels to null and all enables to True
//
for (int i = 0; i < AudioChannelSetSize; i++)
{
channels[i] = NULL;
enables[i] = True;
}
}
//
//#############################################################################
//#############################################################################
//
void
AudioChannelSet::ReleaseAll()
{
//
// Must release all channels without changing enable values
//
for (int i = 0; i < AudioChannelSetSize; i++)
{
if (channels[i] != NULL)
{
Check(channels[i]);
channels[i]->Release();
channels[i] = NULL;
}
}
}
//#############################################################################
//########################## L4AudioSource ##############################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
L4AudioSource::L4AudioSource(
PlugStream *stream,
Entity *entity
):
AudioSource(stream, entity)
{
L4AudioSourceX();
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioSource::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioSource::BuildFromPage(stream, name_list, class_ID, object_ID);
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioSource::L4AudioSourceX()
{
}
//
//#############################################################################
//#############################################################################
//
L4AudioSource::~L4AudioSource()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
L4AudioSource::TestInstance() const
{
AudioSource::TestInstance();
Check(&channelSet);
return True;
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioSource::StopMessageImplementation()
{
Check(this);
AudioSource::StopMessageImplementation();
//
// Request stop from renderer
//
Check(application);
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer())->
StopRequest(this);
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioSource::SetAudioChannelSet(const AudioChannelSet &audio_channel_set)
{
Check(this);
channelSet = audio_channel_set;
}
//
//#############################################################################
//#############################################################################
//
const AudioChannelSet*
L4AudioSource::GetAudioChannelSet()
{
Check(this);
return &channelSet;
}
//
//#############################################################################
//#############################################################################
//
void
L4AudioSource::ReleaseChannels()
{
Check(this);
channelSet.ReleaseAll();
}
//#############################################################################
//######################## DirectPatchSource ############################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
DirectPatchSource::DirectPatchSource(
PlugStream *stream,
Entity *entity
):
L4AudioSource(stream, entity)
{
MemoryStream_Read(stream, &audioPosition);
channel = NULL;
}
//
//#############################################################################
//#############################################################################
//
void
DirectPatchSource::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
L4AudioSource::BuildFromPage(stream, name_list, class_ID, object_ID);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, position);
}
//
//#############################################################################
//#############################################################################
//
DirectPatchSource::~DirectPatchSource()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
DirectPatchSource::TestInstance() const
{
L4AudioSource::TestInstance();
if (channel != NULL)
{
Check(channel);
}
return True;
}
//
//#############################################################################
//#############################################################################
//
void
DirectPatchSource::StartMessageImplementation()
{
Check(this);
L4AudioSource::StartMessageImplementation();
//
//--------------------------------------------------------------------------
// Init channel set to requested channels
//--------------------------------------------------------------------------
//
channelSet = AudioChannelSet::Null;
switch (audioPosition)
{
case FrontDirectPatchPosition:
case FrontLeftDirectPatchPosition:
case FrontRightDirectPatchPosition:
channelSet.EnableFrontLeft(True);
break;
case RearDirectPatchPosition:
case RearLeftDirectPatchPosition:
case RearRightDirectPatchPosition:
channelSet.EnableRearLeft(True);
break;
}
//
//--------------------------------------------------------------------------
// Request start from renderer
//--------------------------------------------------------------------------
//
Check(application);
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer())->
StartRequest(this);
}
//
//#############################################################################
//#############################################################################
//
void
DirectPatchSource::StartImplementation()
{
Check(this);
//
//--------------------------------------------------------------------------
// Call inherited method
//--------------------------------------------------------------------------
//
L4AudioSource::StartImplementation();
//
//--------------------------------------------------------------------------
// Remember the channel
//--------------------------------------------------------------------------
//
Verify(channel == NULL);
switch (audioPosition)
{
case FrontDirectPatchPosition:
case FrontLeftDirectPatchPosition:
case FrontRightDirectPatchPosition:
channel = channelSet.GetFrontLeft();
break;
case RearDirectPatchPosition:
case RearLeftDirectPatchPosition:
case RearRightDirectPatchPosition:
channel = channelSet.GetRearLeft();
break;
}
Check(channel);
//
//--------------------------------------------------------------------------
// Setup the channel
//--------------------------------------------------------------------------
//
ExecuteWatchers();
//
// Turn the volume down while setting up the channel
//
channel->SendController(MIDI_VOLUME_CONTROL, MIDI_MIN_CONTROL_VALUE);
//
// Set the channel to the bank and program
//
PatchResource *patch_resource;
patch_resource = Cast_Object(PatchResource*, GetAudioResource());
Check(patch_resource);
patch_resource->SetDistance(GetDistanceToSource());
patch_resource->SetupPatch(channel);
//
// Set the channel to default control values
//
channel->SendController(MIDI_REVERB_CONTROL, MIDI_MIN_CONTROL_VALUE);
channel->SendController(MIDI_CHORUS_CONTROL, MIDI_MIN_CONTROL_VALUE);
switch (audioPosition)
{
case FrontDirectPatchPosition:
case RearDirectPatchPosition:
channel->SendController(MIDI_PAN_CONTROL, MIDI_CENTER_PAN_VALUE);
break;
case FrontLeftDirectPatchPosition:
case RearLeftDirectPatchPosition:
channel->SendController(MIDI_PAN_CONTROL, MIDI_LEFT_PAN_VALUE);
break;
case FrontRightDirectPatchPosition:
case RearRightDirectPatchPosition:
channel->SendController(MIDI_PAN_CONTROL, MIDI_RIGHT_PAN_VALUE);
break;
}
//
// Set midi history to default control values
//
lastMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastMIDIPitchBend = 0; // Set by force update
lastMIDIFilterCutoff = MIDI_MAX_CONTROL_VALUE; // Set by patch load
//
//--------------------------------------------------------------------------
// Calculate attack time, velocity, and execute model
//--------------------------------------------------------------------------
//
MIDIValue midi_velocity =
CalculateSourceAttackVolumeScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (UseSourceAttackTime())
{
MIDINRPNValue midi_attack_time =
CalculateSourceAttackTime() *
(Scalar)AWE_VOL_ATTACK_TIME_RANGE + 0.5f;
channel->SendNRPN(AWE_VOL_ATTACK_TIME_NRPN, midi_attack_time);
}
ExecuteModel(True);
//
//--------------------------------------------------------------------------
// Start note
//--------------------------------------------------------------------------
//
channel->SendNoteOn(GetCurrentNoteValue(), midi_velocity);
#if 0
Tell("On " << (int)GetCurrentNoteValue() << "\n");
#endif
}
//
//#############################################################################
//#############################################################################
//
void
DirectPatchSource::StopImplementation()
{
Check(this);
//
// Stop note
//
Check(channel);
channel->SendNoteOff(GetCurrentNoteValue(), MIDI_MIN_CONTROL_VALUE);
channel = NULL;
}
//
//#############################################################################
//#############################################################################
//
void
DirectPatchSource::ExecuteModel(Logical force_update)
{
Check(this);
//
//--------------------------------------------------------------------------
// Apply pitch offset
//--------------------------------------------------------------------------
//
const MIDIPitchBend pitch_resolution = 12; // HACK - should come from audio.ini
AudioPitchCents pitch_offset;
MIDIPitchBend midi_pitch_bend;
MIDIPitchBend pitch_difference;
pitch_offset = CalculateSourcePitchOffset();
midi_pitch_bend = (Scalar)pitch_offset * MIDI_PITCH_PER_CENTS + 0.5f;
Clamp(midi_pitch_bend, -MIDI_PITCH_BEND_MAX, MIDI_PITCH_BEND_MAX);
pitch_difference = lastMIDIPitchBend - midi_pitch_bend;
if ((Abs(pitch_difference) >= pitch_resolution) || force_update)
{
lastMIDIPitchBend = midi_pitch_bend;
Check(channel);
channel->SendPitchBend(midi_pitch_bend);
}
//
//--------------------------------------------------------------------------
// Apply filter scale
//--------------------------------------------------------------------------
//
if (UseSourceBrightnessScale())
{
const MIDINRPNValue filter_resolution = 2;// HACK - should come from audio.ini
Scalar filter_scale;
PatchResource *patch_resource;
MIDINRPNValue max_midi_filter_cutoff;
MIDINRPNValue midi_filter_cutoff;
MIDINRPNValue filter_difference;
filter_scale = CalculateSourceBrightnessScale();
patch_resource = Cast_Object(PatchResource*, GetAudioResource());
max_midi_filter_cutoff = patch_resource->GetMaxMIDIFilterCutoff();
midi_filter_cutoff = filter_scale * (Scalar)max_midi_filter_cutoff + 0.5f;
filter_difference = lastMIDIFilterCutoff - midi_filter_cutoff;
if (Abs(filter_difference) >= filter_resolution)
{
lastMIDIFilterCutoff = midi_filter_cutoff;
Check(channel);
channel->SendNRPN(AWE_FILTER_CUTOFF_NRPN, midi_filter_cutoff);
}
}
//
//--------------------------------------------------------------------------
// Apply volume scale
//--------------------------------------------------------------------------
//
Scalar volume_scale;
const MIDIValue volume_resolution = 2; // HACK - should come from audio.ini
MIDIValue midi_volume;
int volume_difference;
volume_scale = CalculateSourceVolumeScale();
midi_volume = volume_scale * (Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
volume_difference = lastMIDIVolume - midi_volume;
if (Abs(volume_difference) >= volume_resolution)
{
lastMIDIVolume = midi_volume;
Check(channel);
channel->SendController(MIDI_VOLUME_CONTROL, midi_volume);
}
}
//#############################################################################
//###################### Dynamic3DPatchSource ###########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
Dynamic3DPatchSource::Dynamic3DPatchSource(
PlugStream *stream,
Entity *entity
):
L4AudioSource(stream, entity)
{
}
//
//#############################################################################
//#############################################################################
//
Dynamic3DPatchSource::~Dynamic3DPatchSource()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
Dynamic3DPatchSource::TestInstance() const
{
L4AudioSource::TestInstance();
return True;
}
//
//#############################################################################
//#############################################################################
//
void
Dynamic3DPatchSource::StartMessageImplementation()
{
Check(this);
L4AudioSource::StartMessageImplementation();
//
// Init channel set to requested channels
//
channelSet.EnableAll();
//
// Request start from renderer
//
Check(application);
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer())->
StartRequest(this);
}
//
//#############################################################################
//#############################################################################
//
void
Dynamic3DPatchSource::StartImplementation()
{
Check(this);
//
//--------------------------------------------------------------------------
// Call inherited method
//--------------------------------------------------------------------------
//
L4AudioSource::StartImplementation();
//
//--------------------------------------------------------------------------
// Get application, renderer and head pointers
//--------------------------------------------------------------------------
//
L4AudioRenderer *audio_renderer;
AudioHead *audio_head;
Check(application);
audio_renderer =
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer());
Check(audio_renderer);
audio_head = audio_renderer->GetAudioHead();
Check(audio_head);
//
//--------------------------------------------------------------------------
// Setup the channel
//--------------------------------------------------------------------------
//
ExecuteWatchers();
//
// Turn the volume down while setting up the channels
//
int i;
AudioChannel *channel;
for (i = 0; i < AudioChannelSetSize; i++)
{
channel = channelSet.GetNth(i);
Check(channel);
channel->SendController(MIDI_VOLUME_CONTROL, MIDI_MIN_CONTROL_VALUE);
}
//
// Set the channels to the bank and program
//
PatchResource *patch_resource;
patch_resource = Cast_Object(PatchResource*, GetAudioResource());
Check(patch_resource);
patch_resource->SetDistance(GetDistanceToSource());
for (i = 0; i < AudioChannelSetSize; i++)
{
channel = channelSet.GetNth(i);
patch_resource->SetupPatch(channel);
}
//
// Set the channels to default control values
//
MIDIValue midi_reverb_level =
audio_head->GetGlobalReverbScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
for (i = 0; i < AudioChannelSetSize; i++)
{
channel = channelSet.GetNth(i);
Check(channel);
channel->SendController(MIDI_REVERB_CONTROL, midi_reverb_level);
channel->SendController(MIDI_CHORUS_CONTROL, MIDI_MIN_CONTROL_VALUE);
}
//
// Set the channels to correct pan
//
channel = channelSet.GetFrontLeft();
Check(channel);
channel->SendController(MIDI_PAN_CONTROL, MIDI_LEFT_PAN_VALUE);
channel = channelSet.GetFrontRight();
Check(channel);
channel->SendController(MIDI_PAN_CONTROL, MIDI_RIGHT_PAN_VALUE);
channel = channelSet.GetRearLeft();
Check(channel);
channel->SendController(MIDI_PAN_CONTROL, MIDI_LEFT_PAN_VALUE);
channel = channelSet.GetRearRight();
Check(channel);
channel->SendController(MIDI_PAN_CONTROL, MIDI_RIGHT_PAN_VALUE);
//
// Set midi history to default control values
//
lastFrontLeftMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastFrontRightMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastRearLeftMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastRearRightMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastFrontLeftMIDIPitchBend = 0; // Set by force update
lastFrontRightMIDIPitchBend = 0; // Set by force update
lastRearLeftMIDIPitchBend = 0; // Set by force update
lastRearRightMIDIPitchBend = 0; // Set by force update
lastMIDIFilterCutoff = MIDI_MAX_CONTROL_VALUE; // Set by patch
//
//--------------------------------------------------------------------------
// Calculate attack time, velocity, and execute model
//--------------------------------------------------------------------------
//
MIDIValue midi_velocity =
CalculateSourceAttackVolumeScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (UseSourceAttackTime())
{
MIDINRPNValue midi_attack_time =
CalculateSourceAttackTime() *
(Scalar)AWE_VOL_ATTACK_TIME_RANGE + 0.5f;
for (i = 0; i < AudioChannelSetSize; i++)
{
channel = channelSet.GetNth(i);
Check(channel);
channel->SendNRPN(AWE_VOL_ATTACK_TIME_NRPN, midi_attack_time);
}
}
ExecuteModel(True);
//
//--------------------------------------------------------------------------
// Start note
//--------------------------------------------------------------------------
//
for (i = 0; i < AudioChannelSetSize; i++)
{
channel = channelSet.GetNth(i);
Check(channel);
channel->SendNoteOn(GetCurrentNoteValue(), midi_velocity);
}
}
//
//#############################################################################
//#############################################################################
//
void
Dynamic3DPatchSource::StopImplementation()
{
Check(this);
//
// Stop note
//
AudioChannel *channel;
for (int i = 0; i < AudioChannelSetSize; i++)
{
channel = channelSet.GetNth(i);
Check(channel);
channel->SendNoteOff(GetCurrentNoteValue(), MIDI_MIN_CONTROL_VALUE);
}
}
//
//#############################################################################
//#############################################################################
//
void
Dynamic3DPatchSource::ExecuteModel(Logical force_update)
{
Check(this);
//
//--------------------------------------------------------------------------
// Call inherited method
//--------------------------------------------------------------------------
//
L4AudioSource::Execute();
//
//--------------------------------------------------------------------------
// Get audio resource
//--------------------------------------------------------------------------
//
PatchResource *patch_resource;
patch_resource = Cast_Object(PatchResource*, GetAudioResource());
Check(patch_resource);
//
//--------------------------------------------------------------------------
// Get audio location
//--------------------------------------------------------------------------
//
L4AudioLocation *audio_location;
audio_location = Cast_Object(L4AudioLocation*, GetAudioLocation());
Check(audio_location);
//
//--------------------------------------------------------------------------
// Render spatial model
//--------------------------------------------------------------------------
//
AudioChannel *front_left_channel = channelSet.GetFrontLeft();
AudioChannel *front_right_channel = channelSet.GetFrontRight();
AudioChannel *rear_left_channel = channelSet.GetRearLeft();
AudioChannel *rear_right_channel = channelSet.GetRearRight();
Check(front_left_channel);
Check(front_right_channel);
Check(rear_left_channel);
Check(rear_right_channel);
//
// Apply distance attenuation to volume scale
//
Scalar volume_scale = CalculateSourceVolumeScale();
//
// Apply speaker scaling to volume and calculate MIDI volume
//
Scalar volume_temp;
MIDIValue front_left_midi_volume;
MIDIValue front_right_midi_volume;
MIDIValue rear_left_midi_volume;
MIDIValue rear_right_midi_volume;
volume_temp = volume_scale * (Scalar)MIDI_MAX_CONTROL_VALUE;
front_left_midi_volume =
audio_location->GetFrontLeftScale() *
volume_temp + 0.5f;
front_right_midi_volume =
audio_location->GetFrontRightScale() *
volume_temp + 0.5f;
rear_left_midi_volume =
audio_location->GetRearLeftScale() *
volume_temp + 0.5f;
rear_right_midi_volume =
audio_location->GetRearRightScale() *
volume_temp + 0.5f;
//
// Apply Doppler offset to pitch
//
Scalar pitch_offset;
pitch_offset = CalculateSourcePitchOffset();
pitch_offset += audio_location->GetDopplerCents();
//
// Apply ITD pitch offset and calculate MIDI pitch bend
//
MIDIPitchBend front_left_midi_pitch_bend;
MIDIPitchBend front_right_midi_pitch_bend;
MIDIPitchBend rear_left_midi_pitch_bend;
MIDIPitchBend rear_right_midi_pitch_bend;
front_left_midi_pitch_bend =
(pitch_offset + audio_location->GetFrontLeftITDPitchOffset()) *
MIDI_PITCH_PER_CENTS + 0.5f;
front_right_midi_pitch_bend =
(pitch_offset + audio_location->GetFrontRightITDPitchOffset()) *
MIDI_PITCH_PER_CENTS + 0.5f;
rear_left_midi_pitch_bend =
(pitch_offset + audio_location->GetRearLeftITDPitchOffset()) *
MIDI_PITCH_PER_CENTS + 0.5f;
rear_right_midi_pitch_bend =
(pitch_offset + audio_location->GetRearRightITDPitchOffset()) *
MIDI_PITCH_PER_CENTS + 0.5f;
Clamp(front_left_midi_pitch_bend, -MIDI_PITCH_BEND_MAX, MIDI_PITCH_BEND_MAX);
Clamp(front_right_midi_pitch_bend, -MIDI_PITCH_BEND_MAX, MIDI_PITCH_BEND_MAX);
Clamp(rear_left_midi_pitch_bend, -MIDI_PITCH_BEND_MAX, MIDI_PITCH_BEND_MAX);
Clamp(rear_right_midi_pitch_bend, -MIDI_PITCH_BEND_MAX, MIDI_PITCH_BEND_MAX);
//
// Apply high frequency scale to cutoff and calculate NRPN cutoff
//
Scalar filter_scale;
MIDINRPNValue max_midi_filter_cutoff;
MIDINRPNValue midi_filter_cutoff;
filter_scale = CalculateSourceBrightnessScale();
filter_scale *= audio_location->GetHighFreqCutoffScale();
max_midi_filter_cutoff = patch_resource->GetMaxMIDIFilterCutoff();
midi_filter_cutoff = filter_scale * (Scalar)max_midi_filter_cutoff + 0.5f;
//
//--------------------------------------------------------------------------
// Send pitch bend
//--------------------------------------------------------------------------
//
const MIDIPitchBend pitch_resolution = 2;
MIDIPitchBend pitch_difference;
pitch_difference = front_left_midi_pitch_bend - lastFrontLeftMIDIPitchBend;
if (Abs(pitch_difference) >= pitch_resolution || force_update)
{
lastFrontLeftMIDIPitchBend = front_left_midi_pitch_bend;
front_left_channel->SendPitchBend(front_left_midi_pitch_bend);
}
pitch_difference = front_right_midi_pitch_bend - lastFrontRightMIDIPitchBend;
if (Abs(pitch_difference) >= pitch_resolution || force_update)
{
lastFrontRightMIDIPitchBend = front_right_midi_pitch_bend;
front_right_channel->SendPitchBend(front_right_midi_pitch_bend);
}
pitch_difference = rear_left_midi_pitch_bend - lastRearLeftMIDIPitchBend;
if (Abs(pitch_difference) >= pitch_resolution || force_update)
{
lastRearLeftMIDIPitchBend = rear_left_midi_pitch_bend;
rear_left_channel->SendPitchBend(rear_left_midi_pitch_bend);
}
pitch_difference = rear_right_midi_pitch_bend - lastRearRightMIDIPitchBend;
if (Abs(pitch_difference) >= pitch_resolution || force_update)
{
lastRearRightMIDIPitchBend = rear_right_midi_pitch_bend;
rear_right_channel->SendPitchBend(rear_right_midi_pitch_bend);
}
//
//--------------------------------------------------------------------------
// Send MIDI volume
//--------------------------------------------------------------------------
//
Logical front_left_turned_on = False;
Logical front_right_turned_on = False;
Logical rear_left_turned_on = False;
Logical rear_right_turned_on = False;
if (front_left_midi_volume != lastFrontLeftMIDIVolume)
{
if (lastFrontLeftMIDIVolume == 0)
front_left_turned_on = True;
lastFrontLeftMIDIVolume = front_left_midi_volume;
Check(front_left_channel);
front_left_channel->SendController(
MIDI_VOLUME_CONTROL,
front_left_midi_volume
);
}
if (front_right_midi_volume != lastFrontRightMIDIVolume)
{
if (lastFrontRightMIDIVolume == 0)
front_right_turned_on = True;
lastFrontRightMIDIVolume = front_right_midi_volume;
Check(front_right_channel);
front_right_channel->SendController(
MIDI_VOLUME_CONTROL,
front_right_midi_volume
);
}
if (rear_left_midi_volume != lastRearLeftMIDIVolume)
{
if (lastRearLeftMIDIVolume == 0)
rear_left_turned_on = True;
lastRearLeftMIDIVolume = rear_left_midi_volume;
Check(rear_left_channel);
rear_left_channel->SendController(
MIDI_VOLUME_CONTROL,
rear_left_midi_volume
);
}
if (rear_right_midi_volume != lastRearRightMIDIVolume)
{
if (lastRearRightMIDIVolume == 0)
rear_right_turned_on = True;
lastRearRightMIDIVolume = rear_right_midi_volume;
Check(rear_right_channel);
rear_right_channel->SendController(
MIDI_VOLUME_CONTROL,
rear_right_midi_volume
);
}
//
//--------------------------------------------------------------------------
// Send NRPN filter cutoff
//--------------------------------------------------------------------------
//
const MIDIValue filter_resolution = 4;
MIDINRPNValue filter_difference;
Logical filter_changed;
filter_difference = midi_filter_cutoff - lastMIDIFilterCutoff;
filter_changed = (Abs(filter_difference) >= filter_resolution);
if (
front_left_turned_on ||
(filter_changed && front_left_midi_volume > 0)
)
{
lastMIDIFilterCutoff = midi_filter_cutoff;
Check(front_left_channel);
front_left_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
}
if (
front_right_turned_on ||
(filter_changed && front_right_midi_volume > 0)
)
{
lastMIDIFilterCutoff = midi_filter_cutoff;
Check(front_right_channel);
front_right_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
}
if (
rear_left_turned_on ||
(filter_changed && rear_left_midi_volume > 0)
)
{
lastMIDIFilterCutoff = midi_filter_cutoff;
Check(rear_left_channel);
rear_left_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
}
if (
rear_right_turned_on ||
(filter_changed && rear_right_midi_volume > 0)
)
{
lastMIDIFilterCutoff = midi_filter_cutoff;
Check(rear_right_channel);
rear_right_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
}
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
Dynamic3DPatchSource::CalculateSourceVolumeScale()
{
Check(this);
//
// Call inherited method to calculate volume scale
//
Scalar
volume_scale = L4AudioSource::CalculateSourceVolumeScale();
//
// Update the spatial model that will result in the value
// for distance related volume attenuation
//
Check(application);
Check(application->GetAudioRenderer());
UpdateSpatialModel(application->GetAudioRenderer()->GetAudioHead());
//
// Apply distance attenuation to the volume scale
//
Check(GetAudioLocation());
volume_scale *= GetAudioLocation()->GetDistanceVolumeScale();
return volume_scale;
}
//#############################################################################
//####################### Static3DPatchSource ###########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
Static3DPatchSource::Static3DPatchSource(
PlugStream *stream,
Entity *entity
):
L4AudioSource(stream, entity),
position(0.0f, 0.0f, 0.0f)
{
MemoryStream_Read(stream, &useInternalSpatialization);
}
//
//#############################################################################
//#############################################################################
//
Static3DPatchSource::~Static3DPatchSource()
{
}
//
//#############################################################################
//#############################################################################
//
void
Static3DPatchSource::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
L4AudioSource::BuildFromPage(stream, name_list, class_ID, object_ID);
if (name_list->FindData("use_internal") != NULL)
{
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, use_internal);
}
else
{
Logical use_internal = True;
MemoryStream_Write(stream, &use_internal);
}
}
//
//#############################################################################
//#############################################################################
//
Logical
Static3DPatchSource::TestInstance() const
{
L4AudioSource::TestInstance();
if (useInternalSpatialization)
{
Check(&internalSpatialization);
}
return True;
}
//
//#############################################################################
//#############################################################################
//
void
Static3DPatchSource::SetPosition(const Vector3D &vector3D)
{
position = vector3D;
}
//
//#############################################################################
//#############################################################################
//
void
Static3DPatchSource::StartMessageImplementation()
{
Check(this);
L4AudioSource::StartMessageImplementation();
//
//--------------------------------------------------------------------------
// Get audio renderer
//--------------------------------------------------------------------------
//
L4AudioRenderer *audio_renderer;
Check(application);
audio_renderer =
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer());
Check(audio_renderer);
//
//--------------------------------------------------------------------------
// Update spatial parameters
//--------------------------------------------------------------------------
//
if (useInternalSpatialization)
{
//
//-----------------------------------------------------------------------
// Calculate azimuth of position
//-----------------------------------------------------------------------
//
Radian azimuth_of_source;
if (Small_Enough(position.x) && Small_Enough(position.z))
{
azimuth_of_source = 0.0f;
}
else
{
Verify(!(Small_Enough(position.x) && Small_Enough(position.z)));
azimuth_of_source = Arctan(position.x, position.z);
}
Verify(azimuth_of_source <= PI && azimuth_of_source >= -PI);
internalSpatialization.CalculateSpatialization(azimuth_of_source);
}
else
{
UpdateSpatialModel(audio_renderer->GetAudioHead());
Cast_Object(L4AudioLocation*, GetAudioLocation())->GetSpatialization(
&internalSpatialization
);
}
//
//--------------------------------------------------------------------------
// Init channel set to required channels
//--------------------------------------------------------------------------
//
channelSet.EnableFrontLeft(False);
channelSet.EnableFrontRight(False);
channelSet.EnableRearLeft(False);
channelSet.EnableRearRight(False);
switch (GetQuadrant())
{
case L4AudioSpatialization::Quadrant1:
channelSet.EnableFrontLeft(True);
break;
case L4AudioSpatialization::Quadrant2:
channelSet.EnableFrontLeft(True);
channelSet.EnableRearLeft(True);
break;
case L4AudioSpatialization::Quadrant3:
channelSet.EnableRearLeft(True);
break;
case L4AudioSpatialization::Quadrant4:
channelSet.EnableFrontRight(True);
channelSet.EnableRearRight(True);
break;
}
//
//--------------------------------------------------------------------------
// Request start from renderer
//--------------------------------------------------------------------------
//
audio_renderer->StartRequest(this);
}
//
//#############################################################################
//#############################################################################
//
void
Static3DPatchSource::StartImplementation()
{
Check(this);
L4AudioSource::StartImplementation();
//
//--------------------------------------------------------------------------
// Get audio head constants
//--------------------------------------------------------------------------
//
L4AudioRenderer *audio_renderer;
AudioHead *audio_head;
Check(application);
audio_renderer =
Cast_Object(L4AudioRenderer*, application->GetAudioRenderer());
Check(audio_renderer);
audio_head = audio_renderer->GetAudioHead();
Check(audio_head);
//
//--------------------------------------------------------------------------
// Setup the channel
//--------------------------------------------------------------------------
//
ExecuteWatchers();
//
// Turn the volume down while setting up the channels
//
int i;
AudioChannel *channel;
for (i = 0; i < AudioChannelSetSize; i++)
{
if ((channel = channelSet.GetNth(i)) != NULL)
{
Check(channel);
channel->SendController(MIDI_VOLUME_CONTROL, MIDI_MIN_CONTROL_VALUE);
}
}
//
// Set the channels to the bank and program
//
PatchResource *patch_resource;
patch_resource = Cast_Object(PatchResource*, GetAudioResource());
Check(patch_resource);
patch_resource->SetDistance(GetDistanceToSource());
for (i = 0; i < AudioChannelSetSize; i++)
{
if ((channel = channelSet.GetNth(i)) != NULL)
{
patch_resource->SetupPatch(channel);
}
}
//
// Set the channels to default control values
//
MIDIValue midi_reverb_level =
audio_head->GetGlobalReverbScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
for (i = 0; i < AudioChannelSetSize; i++)
{
if ((channel = channelSet.GetNth(i)) != NULL)
{
Check(channel);
channel->SendController(MIDI_REVERB_CONTROL, midi_reverb_level);
channel->SendController(MIDI_CHORUS_CONTROL, MIDI_MIN_CONTROL_VALUE);
}
}
//
// Set the channels to correct pan
//
switch (GetQuadrant())
{
case L4AudioSpatialization::Quadrant1:
{
const Radian azimuth_max =
45.0*RAD_PER_DEG; // HACK - should come from head
const Scalar slope =
-(Scalar)MIDI_MAX_CONTROL_VALUE / (2.0f * azimuth_max);
const Scalar intercept =
(MIDI_MAX_CONTROL_VALUE * 0.5f);
MIDIValue
midi_pan;
midi_pan = slope * GetAzimuthOfSource() + intercept;
Check(channelSet.GetFrontLeft());
channelSet.GetFrontLeft()->SendController(
MIDI_PAN_CONTROL,
midi_pan
);
}
break;
case L4AudioSpatialization::Quadrant2:
{
Check(channelSet.GetFrontLeft());
Check(channelSet.GetRearLeft());
channelSet.GetFrontLeft()->SendController(
MIDI_PAN_CONTROL,
MIDI_MIN_CONTROL_VALUE
);
channelSet.GetRearLeft()->SendController(
MIDI_PAN_CONTROL,
MIDI_MIN_CONTROL_VALUE
);
}
break;
case L4AudioSpatialization::Quadrant3:
{
const Radian azimuth_max =
45.0*RAD_PER_DEG; // HACK - should come from head
const Scalar slope =
(Scalar)MIDI_MAX_CONTROL_VALUE / (2.0f * azimuth_max);
const Scalar intercept =
(MIDI_MAX_CONTROL_VALUE * 0.5f);
MIDIValue
midi_pan;
midi_pan = slope * GetAzimuthOfSource() + intercept;
Check(channelSet.GetRearLeft());
channelSet.GetRearLeft()->SendController(
MIDI_PAN_CONTROL,
midi_pan
);
}
break;
case L4AudioSpatialization::Quadrant4:
{
Check(channelSet.GetFrontRight());
Check(channelSet.GetRearRight());
channelSet.GetFrontRight()->SendController(
MIDI_PAN_CONTROL,
MIDI_MAX_CONTROL_VALUE
);
channelSet.GetRearRight()->SendController(
MIDI_PAN_CONTROL,
MIDI_MAX_CONTROL_VALUE
);
}
break;
}
//
// Set midi history to default control values
//
lastFrontLeftMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastFrontRightMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastRearLeftMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastRearRightMIDIVolume = MIDI_MIN_CONTROL_VALUE;
lastMIDIPitchBend = 0; // Set by force update
lastMIDIFilterCutoff = MIDI_MAX_CONTROL_VALUE; // Set by patch
//
//--------------------------------------------------------------------------
// Calculate attack time, velocity, and execute model
//--------------------------------------------------------------------------
//
MIDIValue midi_velocity =
CalculateSourceAttackVolumeScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (UseSourceAttackTime())
{
MIDINRPNValue midi_attack_time =
CalculateSourceAttackTime() *
(Scalar)AWE_VOL_ATTACK_TIME_RANGE + 0.5f;
for (i = 0; i < AudioChannelSetSize; i++)
{
if ((channel = channelSet.GetNth(i)) != NULL)
{
Check(channel);
channel->SendNRPN(AWE_VOL_ATTACK_TIME_NRPN, midi_attack_time);
}
}
}
ExecuteModel(True);
//
//--------------------------------------------------------------------------
// Start note
//--------------------------------------------------------------------------
//
for (i = 0; i < AudioChannelSetSize; i++)
{
if ((channel = channelSet.GetNth(i)) != NULL)
{
Check(channel);
channel->SendNoteOn(GetCurrentNoteValue(), midi_velocity);
}
}
}
//
//#############################################################################
//#############################################################################
//
void
Static3DPatchSource::StopImplementation()
{
Check(this);
//
//------------------------------------------------------------
// Stop note
//------------------------------------------------------------
//
AudioChannel *channel;
for (int i = 0; i < AudioChannelSetSize; i++)
{
if ((channel = channelSet.GetNth(i)) != NULL)
{
Check(channel);
channel->SendNoteOff(GetCurrentNoteValue(), MIDI_MIN_CONTROL_VALUE);
}
}
}
//
//#############################################################################
//#############################################################################
//
void
Static3DPatchSource::ExecuteModel(Logical force_update)
{
Check(this);
//
//--------------------------------------------------------------------------
// Get audio resource
//--------------------------------------------------------------------------
//
PatchResource *patch_resource;
patch_resource = Cast_Object(PatchResource*, GetAudioResource());
Check(patch_resource);
//
//--------------------------------------------------------------------------
// Render spatial model
//--------------------------------------------------------------------------
//
AudioChannel *front_left_channel = channelSet.GetFrontLeft();
AudioChannel *front_right_channel = channelSet.GetFrontRight();
AudioChannel *rear_left_channel = channelSet.GetRearLeft();
AudioChannel *rear_right_channel = channelSet.GetRearRight();
//
// Apply volume scale offset
//
Scalar volume_scale = CalculateSourceVolumeScale();
//
// Apply pitch offset and calculate MIDI pitch bend
//
Scalar pitch_offset;
MIDIPitchBend midi_pitch_bend;
pitch_offset = CalculateSourcePitchOffset();
midi_pitch_bend = pitch_offset * MIDI_PITCH_PER_CENTS + 0.5f;
Clamp(midi_pitch_bend, -MIDI_PITCH_BEND_MAX, MIDI_PITCH_BEND_MAX);
//
// Apply high frequency scale and calculate NRPN filter cutoff
//
Scalar filter_scale;
MIDINRPNValue max_midi_filter_cutoff;
MIDINRPNValue midi_filter_cutoff;
filter_scale = CalculateSourceBrightnessScale();
max_midi_filter_cutoff = patch_resource->GetMaxMIDIFilterCutoff();
midi_filter_cutoff = filter_scale * (Scalar)max_midi_filter_cutoff + 0.5f;
//
//--------------------------------------------------------------------------
// Send pitch bend
//--------------------------------------------------------------------------
//
const MIDIPitchBend pitch_resolution = 2;
MIDIPitchBend pitch_difference = midi_pitch_bend - lastMIDIPitchBend;
if (Abs(pitch_difference) >= pitch_resolution || force_update)
{
lastMIDIPitchBend = midi_pitch_bend;
switch (GetQuadrant())
{
case L4AudioSpatialization::Quadrant1:
Check(front_left_channel);
front_left_channel->SendPitchBend(midi_pitch_bend);
break;
case L4AudioSpatialization::Quadrant2:
Check(front_left_channel);
front_left_channel->SendPitchBend(midi_pitch_bend);
Check(rear_left_channel);
rear_left_channel->SendPitchBend(midi_pitch_bend);
break;
case L4AudioSpatialization::Quadrant3:
Check(rear_left_channel);
rear_left_channel->SendPitchBend(midi_pitch_bend);
break;
case L4AudioSpatialization::Quadrant4:
Check(front_right_channel);
front_right_channel->SendPitchBend(midi_pitch_bend);
Check(rear_right_channel);
rear_right_channel->SendPitchBend(midi_pitch_bend);
break;
}
}
//
//--------------------------------------------------------------------------
// Send NRPN filter cutoff
//--------------------------------------------------------------------------
//
if (UseSourceBrightnessScale())
{
const MIDIValue filter_resolution = 2;
MIDINRPNValue filter_difference;
filter_difference = midi_filter_cutoff - lastMIDIFilterCutoff;
if (Abs(filter_difference) >= filter_resolution)
{
lastMIDIFilterCutoff = midi_filter_cutoff;
switch (GetQuadrant())
{
case L4AudioSpatialization::Quadrant1:
Check(front_left_channel);
front_left_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
break;
case L4AudioSpatialization::Quadrant2:
Check(front_left_channel);
front_left_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
Check(rear_left_channel);
rear_left_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
break;
case L4AudioSpatialization::Quadrant3:
Check(rear_left_channel);
rear_left_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
break;
case L4AudioSpatialization::Quadrant4:
Check(front_right_channel);
front_right_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
Check(rear_right_channel);
rear_right_channel->SendNRPN(
AWE_FILTER_CUTOFF_NRPN,
midi_filter_cutoff
);
break;
}
}
}
//
//--------------------------------------------------------------------------
// Send MIDI volume
//--------------------------------------------------------------------------
//
switch (GetQuadrant())
{
case L4AudioSpatialization::Quadrant1:
{
MIDIValue front_left_midi_volume =
volume_scale *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (front_left_midi_volume != lastFrontLeftMIDIVolume)
{
lastFrontLeftMIDIVolume = front_left_midi_volume;
Check(front_left_channel);
front_left_channel->SendController(
MIDI_VOLUME_CONTROL,
front_left_midi_volume
);
}
}
break;
case L4AudioSpatialization::Quadrant2:
{
MIDIValue front_left_midi_volume =
volume_scale * GetFrontLeftScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
MIDIValue rear_left_midi_volume =
volume_scale * GetRearLeftScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (front_left_midi_volume != lastFrontLeftMIDIVolume)
{
lastFrontLeftMIDIVolume = front_left_midi_volume;
Check(front_left_channel);
front_left_channel->SendController(
MIDI_VOLUME_CONTROL,
front_left_midi_volume
);
}
if (rear_left_midi_volume != lastRearLeftMIDIVolume)
{
lastRearLeftMIDIVolume = rear_left_midi_volume;
Check(rear_left_channel);
rear_left_channel->SendController(
MIDI_VOLUME_CONTROL,
rear_left_midi_volume
);
}
}
break;
case L4AudioSpatialization::Quadrant3:
{
MIDIValue rear_left_midi_volume =
volume_scale *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (rear_left_midi_volume != lastRearLeftMIDIVolume)
{
lastRearLeftMIDIVolume = rear_left_midi_volume;
Check(rear_left_channel);
rear_left_channel->SendController(
MIDI_VOLUME_CONTROL,
rear_left_midi_volume
);
}
}
break;
case L4AudioSpatialization::Quadrant4:
{
MIDIValue front_right_midi_volume =
volume_scale * GetFrontRightScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
MIDIValue rear_right_midi_volume =
volume_scale * GetRearRightScale() *
(Scalar)MIDI_MAX_CONTROL_VALUE + 0.5f;
if (front_right_midi_volume != lastFrontRightMIDIVolume)
{
lastFrontRightMIDIVolume = front_right_midi_volume;
Check(front_right_channel);
front_right_channel->SendController(
MIDI_VOLUME_CONTROL,
front_right_midi_volume
);
}
if (rear_right_midi_volume != lastRearRightMIDIVolume)
{
lastRearRightMIDIVolume = rear_right_midi_volume;
Check(rear_right_channel);
rear_right_channel->SendController(
MIDI_VOLUME_CONTROL,
rear_right_midi_volume
);
}
}
break;
}
}