Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
869 lines
20 KiB
C++
869 lines
20 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "audseq.h"
|
|
#include "objstrm.h"
|
|
#include "namelist.h"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlEvent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlEvent::AudioControlEvent(
|
|
AudioTick start_tick,
|
|
AudioControlID audio_control_ID,
|
|
AudioControlValue audio_control_value
|
|
):
|
|
Plug(AudioControlEventClassID)
|
|
{
|
|
startTick = start_tick;
|
|
audioControlID = audio_control_ID;
|
|
audioControlValue = audio_control_value;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlEvent::AudioControlEvent():
|
|
Plug(AudioControlEventClassID)
|
|
{
|
|
startTick = 0;
|
|
audioControlID = NullAudioControlID;
|
|
audioControlValue = 0.0f;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlEvent::~AudioControlEvent()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioControlEvent::TestInstance() const
|
|
{
|
|
Plug::TestInstance();
|
|
Verify(
|
|
(Enumeration)audioControlID >= (Enumeration)0 &&
|
|
(Enumeration)audioControlID < (Enumeration)AudioControlIDCount
|
|
);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlEvent::Send(AudioComponent *audio_component)
|
|
{
|
|
Check(this);
|
|
Check(audio_component);
|
|
audio_component->ReceiveControl(audioControlID, audioControlValue);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioControlEvent::Chase(AudioComponent *audio_component)
|
|
{
|
|
Check(this);
|
|
Check(audio_component);
|
|
|
|
switch (audioControlID)
|
|
{
|
|
case StartAudioControlID:
|
|
return False;
|
|
|
|
case StopAudioControlID:
|
|
Send(audio_component);
|
|
return False;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
MemoryStream&
|
|
MemoryStream_Read(
|
|
MemoryStream *stream,
|
|
AudioControlEvent *control_event
|
|
)
|
|
{
|
|
Check(stream);
|
|
Check_Signature(control_event);
|
|
|
|
MemoryStream_Read(stream, &control_event->startTick);
|
|
MemoryStream_Read(stream, &control_event->audioControlID);
|
|
MemoryStream_Read(stream, &control_event->audioControlValue);
|
|
|
|
Check(control_event);
|
|
return *stream;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
MemoryStream&
|
|
MemoryStream_Write(
|
|
MemoryStream *stream,
|
|
const AudioControlEvent *control_event
|
|
)
|
|
{
|
|
Check(stream);
|
|
Check(control_event);
|
|
|
|
MemoryStream_Write(stream, &control_event->startTick);
|
|
MemoryStream_Write(stream, &control_event->audioControlID);
|
|
MemoryStream_Write(stream, &control_event->audioControlValue);
|
|
return *stream;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
std::ostream& operator << (std::ostream &strm, const AudioControlEvent &control_event)
|
|
{
|
|
Check(&control_event);
|
|
|
|
strm << "[" ;
|
|
strm << control_event.startTick << ",";
|
|
strm << control_event.audioControlID << ",";
|
|
strm << control_event.audioControlValue;
|
|
strm << "]";
|
|
|
|
return strm;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlSequence ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlSequence::AudioControlSequence(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioComponent(stream),
|
|
audioComponentSocket(NULL),
|
|
audioControlEventSocket(NULL)
|
|
{
|
|
Logical dump_value;
|
|
Logical is_looped;
|
|
AudioComponent *audio_component;
|
|
AudioControlEvent *audio_control_event;
|
|
CollectionSize i, number_of_control_events;
|
|
AudioDivisionsPerBeat divisions_per_beat;
|
|
AudioTempo tempo;
|
|
|
|
MemoryStream_Read(stream, &dump_value);
|
|
MemoryStream_Read(stream, &is_looped);
|
|
PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
|
|
MemoryStream_Read(stream, &divisions_per_beat);
|
|
MemoryStream_Read(stream, &tempo);
|
|
MemoryStream_Read(stream, &number_of_control_events);
|
|
|
|
for (i = 0; i < number_of_control_events; i++)
|
|
{
|
|
audio_control_event = new AudioControlEvent;
|
|
Register_Object(audio_control_event);
|
|
MemoryStream_Read(stream, audio_control_event);
|
|
audioControlEventSocket.Add(audio_control_event);
|
|
}
|
|
|
|
AudioControlSequenceX(
|
|
audio_component,
|
|
entity,
|
|
is_looped,
|
|
divisions_per_beat,
|
|
tempo,
|
|
dump_value
|
|
);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
void
|
|
AudioControlSequence::AudioControlSequenceX(
|
|
AudioComponent *audio_component,
|
|
Entity *entity,
|
|
Logical is_looped,
|
|
AudioDivisionsPerBeat divisions_per_beat,
|
|
AudioTempo the_tempo,
|
|
Logical dump_value
|
|
)
|
|
#else
|
|
void
|
|
AudioControlSequence::AudioControlSequenceX(
|
|
AudioComponent *audio_component,
|
|
Entity *entity,
|
|
Logical is_looped,
|
|
AudioDivisionsPerBeat divisions_per_beat,
|
|
AudioTempo the_tempo,
|
|
Logical
|
|
)
|
|
#endif
|
|
{
|
|
Check(audio_component);
|
|
audioComponentSocket.Add(audio_component);
|
|
|
|
isLooped = is_looped;
|
|
isRunning = False;
|
|
startTime = AudioTime::Null;
|
|
audioControlEventIterator = NULL;
|
|
divisionsPerBeat = divisions_per_beat;
|
|
tempo = the_tempo;
|
|
|
|
#if DEBUG_LEVEL>0
|
|
dumpValue = dump_value;
|
|
#endif
|
|
|
|
audio_component->AddWatcher(this);
|
|
Check(entity);
|
|
entity->AddAudioComponent(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlSequence::~AudioControlSequence()
|
|
{
|
|
StopSequence();
|
|
Verify(!isRunning);
|
|
Verify(audioControlEventIterator == NULL);
|
|
|
|
AudioControlEventIterator iterator(&audioControlEventSocket);
|
|
Check(&iterator);
|
|
iterator.DeletePlugs();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlSequence::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, dump_value);
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, is_looped);
|
|
|
|
//
|
|
// Write audio component
|
|
//
|
|
CString audio_component_name("audio_component");
|
|
PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);
|
|
|
|
//
|
|
// Read tempo
|
|
//
|
|
AudioTempo tempo = 100;
|
|
|
|
if (name_list->FindData("tempo") != NULL)
|
|
{
|
|
Check_Pointer(name_list->FindData("tempo"));
|
|
Convert_From_Ascii((const char *)name_list->FindData("tempo"), &tempo);
|
|
}
|
|
|
|
//
|
|
// Read midi file
|
|
//
|
|
DynamicMemoryStream midi_file_stream;
|
|
|
|
{
|
|
CString midi_file_name;
|
|
|
|
Check_Pointer(name_list->FindData("midi_file"));
|
|
midi_file_name = (const char*)name_list->FindData("midi_file");
|
|
|
|
std::ifstream input_midi_file(midi_file_name, std::ios::in | std::ios::binary);
|
|
#if DEBUG_LEVEL>0
|
|
if (!input_midi_file)
|
|
{
|
|
Dump(midi_file_name);
|
|
}
|
|
#endif
|
|
|
|
MemoryStream_Write(&midi_file_stream, &input_midi_file);
|
|
midi_file_stream.Rewind();
|
|
}
|
|
|
|
//
|
|
// Parse midi stream
|
|
//
|
|
DynamicMemoryStream control_event_stream;
|
|
CreateAudioControlEventStream midi_parser;
|
|
|
|
Check(&midi_file_stream);
|
|
Check(&control_event_stream);
|
|
Check(&midi_parser);
|
|
midi_parser.Parse(&midi_file_stream, &control_event_stream, tempo);
|
|
control_event_stream.Rewind();
|
|
|
|
//
|
|
// Write the divisions per beat and tempo
|
|
//
|
|
AudioDivisionsPerBeat divisions_per_beat;
|
|
|
|
divisions_per_beat = midi_parser.GetDivisionsPerBeat();
|
|
tempo = midi_parser.GetTempo();
|
|
MemoryStream_Write(stream, &divisions_per_beat);
|
|
MemoryStream_Write(stream, &tempo);
|
|
|
|
//
|
|
// Write control event stream
|
|
//
|
|
CollectionSize number_of_control_events;
|
|
|
|
number_of_control_events = midi_parser.GetNumberOfEvents();
|
|
MemoryStream_Write(stream, &number_of_control_events);
|
|
MemoryStream_Write(stream, &control_event_stream);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioControlSequence::TestInstance() const
|
|
{
|
|
AudioComponent::TestInstance();
|
|
|
|
Check(&audioComponentSocket);
|
|
Check(&audioControlEventSocket);
|
|
if (isRunning)
|
|
{
|
|
Check(audioControlEventIterator);
|
|
}
|
|
else
|
|
{
|
|
Verify(audioControlEventIterator == NULL);
|
|
}
|
|
Verify(tempo >= 1 && tempo <= 600);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlSequence::StartSequence()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// If already running, stop sequence
|
|
//
|
|
if (isRunning)
|
|
{
|
|
StopSequence();
|
|
}
|
|
|
|
//
|
|
// Make an iterator for the control sequence
|
|
//
|
|
Verify(audioControlEventIterator == NULL);
|
|
audioControlEventIterator =
|
|
new AudioControlEventIterator(&audioControlEventSocket);
|
|
Register_Object(audioControlEventIterator);
|
|
|
|
startTime = AudioTime::Now();
|
|
isRunning = True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlSequence::StopSequence()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// If not running, return
|
|
//
|
|
if (!isRunning)
|
|
return;
|
|
|
|
//
|
|
// Find next on or off event
|
|
//
|
|
AudioControlEvent *control_event;
|
|
|
|
Check(audioControlEventIterator);
|
|
while ((control_event = audioControlEventIterator->ReadAndNext()) != NULL)
|
|
{
|
|
Check(control_event);
|
|
if (!control_event->Chase(audioComponentSocket.GetCurrent()))
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Destroy the iterator for the control sequence
|
|
//
|
|
Unregister_Object(audioControlEventIterator);
|
|
delete audioControlEventIterator;
|
|
audioControlEventIterator = NULL;
|
|
|
|
isRunning = False;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlSequence::RunSequence()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// If the sequence is not running, then return
|
|
//
|
|
if (!isRunning)
|
|
return;
|
|
|
|
//
|
|
// Play events which have a start time less then the
|
|
// current time
|
|
//
|
|
AudioControlEvent *control_event;
|
|
|
|
Check(audioControlEventIterator);
|
|
control_event = audioControlEventIterator->GetCurrent();
|
|
while (
|
|
control_event != NULL &&
|
|
IsEventReady(control_event)
|
|
)
|
|
{
|
|
#if DEBUG_LEVEL>0
|
|
if (dumpValue)
|
|
{
|
|
Dump(*control_event);
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
//
|
|
// HACK - convert start control to use duration
|
|
//
|
|
Verify(control_event->audioControlID != StopAudioControlID);
|
|
if (control_event->audioControlID == StartAudioControlID)
|
|
{
|
|
//
|
|
// Convert ticks to seconds
|
|
// seconds = ticks / (divisionsPerBeat (t/b) * tempo (b/60s))
|
|
//
|
|
Scalar denominator =
|
|
(Scalar)divisionsPerBeat * (Scalar)tempo / 60.0f;
|
|
Verify(!Small_Enough(denominator));
|
|
control_event->audioControlValue =
|
|
control_event->audioControlValue / denominator;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Send the controller to the connected audio component
|
|
//
|
|
Check(control_event);
|
|
control_event->Send(audioComponentSocket.GetCurrent());
|
|
|
|
//
|
|
// Step to next event
|
|
//
|
|
audioControlEventIterator->Next();
|
|
control_event = audioControlEventIterator->GetCurrent();
|
|
}
|
|
|
|
//
|
|
// If there are no more events to be played then stop running
|
|
//
|
|
if (control_event == NULL)
|
|
{
|
|
StopSequence();
|
|
if (isLooped)
|
|
{
|
|
StartSequence();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlSequence::ReceiveControl(
|
|
AudioControlID control_ID,
|
|
AudioControlValue control_value
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
switch(control_ID)
|
|
{
|
|
case StartAudioControlID:
|
|
StartSequence();
|
|
break;
|
|
|
|
case StopAudioControlID:
|
|
StopSequence();
|
|
break;
|
|
|
|
case IdleAudioControlID:
|
|
RunSequence();
|
|
break;
|
|
|
|
case TempoAudioControlID:
|
|
tempo = control_value;
|
|
Verify(tempo >= 1 && tempo <= 600);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioControlSequence::IsEventReady(AudioControlEvent *audio_control_event)
|
|
{
|
|
Check(this);
|
|
|
|
AudioTime offset_time(startTime);
|
|
offset_time += CalculateEventSeconds(audio_control_event);
|
|
return (offset_time <= AudioTime::Now());
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Scalar
|
|
AudioControlSequence::CalculateEventSeconds(
|
|
AudioControlEvent *audio_control_event
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(audio_control_event);
|
|
|
|
//
|
|
// Convert ticks to seconds
|
|
// seconds = ticks / (divisionsPerBeat (t/b) * tempo (b/60s))
|
|
//
|
|
Scalar denominator = (Scalar)divisionsPerBeat * (Scalar)tempo / 60.0f;
|
|
Verify(!Small_Enough(denominator));
|
|
return (Scalar)audio_control_event->GetStartTick() / denominator;
|
|
}
|
|
|
|
//#############################################################################
|
|
//#################### CreateAudioControlEventStream ####################
|
|
//#############################################################################
|
|
|
|
CreateAudioControlEventStream::CreateAudioControlEventStream():
|
|
audioControlEventSocket(NULL, False)
|
|
{
|
|
inputStream = NULL;
|
|
outputStream = NULL;
|
|
divisionsPerBeat = 120;
|
|
tempo = 100;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
CreateAudioControlEventStream::~CreateAudioControlEventStream(void)
|
|
{
|
|
AudioControlEventIterator iterator(&audioControlEventSocket);
|
|
Check(&iterator);
|
|
iterator.DeletePlugs();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
CreateAudioControlEventStream::TestInstance() const
|
|
{
|
|
MidiParse::TestInstance();
|
|
Check(&audioControlEventSocket);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
CreateAudioControlEventStream::Parse(
|
|
MemoryStream *input_stream,
|
|
MemoryStream *output_stream,
|
|
SignedLongWord tempo_argument
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(input_stream);
|
|
Check(output_stream);
|
|
|
|
inputStream = input_stream;
|
|
outputStream = output_stream;
|
|
|
|
//
|
|
// Parse the stream
|
|
//
|
|
tempo = tempo_argument;
|
|
Verify(GetNumberOfEvents() == 0);
|
|
MidiParse::Parse();
|
|
|
|
//
|
|
// Write the events
|
|
//
|
|
AudioControlEventIterator iterator(&audioControlEventSocket);
|
|
AudioControlEvent *control_event;
|
|
|
|
Check(&iterator);
|
|
while ((control_event = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(control_event);
|
|
MemoryStream_Write(outputStream, control_event);
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
CollectionSize
|
|
CreateAudioControlEventStream::GetNumberOfEvents()
|
|
{
|
|
Check(this);
|
|
|
|
AudioControlEventIterator iterator(&audioControlEventSocket);
|
|
Check(&iterator);
|
|
return iterator.GetSize();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
MidiParse::SignedWord
|
|
CreateAudioControlEventStream::Mf_getc()
|
|
{
|
|
Check(this);
|
|
|
|
unsigned char c;
|
|
|
|
MemoryStream_Read(inputStream, &c);
|
|
return c;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
CreateAudioControlEventStream::Mf_error(char *str)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(str);
|
|
Fail(str);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
CreateAudioControlEventStream::Mf_header(
|
|
SignedWord,
|
|
SignedWord,
|
|
SignedWord division
|
|
)
|
|
{
|
|
Check(this);
|
|
divisionsPerBeat = division;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
CreateAudioControlEventStream::Mf_on(
|
|
SignedWord channel,
|
|
SignedWord pitch,
|
|
SignedWord vol
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// Interpret vol 0 as note off
|
|
//
|
|
if (vol == 0)
|
|
{
|
|
Mf_off(channel, pitch, vol);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Verify that last event was a stop
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
AudioControlEventIterator iterator(&audioControlEventSocket);
|
|
AudioControlEvent *last_control_event;
|
|
|
|
iterator.Last();
|
|
if ((last_control_event = iterator.GetCurrent()) != NULL)
|
|
{
|
|
Check(last_control_event);
|
|
Verify(last_control_event->audioControlID == StopAudioControlID);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
AudioTick current_ticks = CurTime();
|
|
AudioControlEvent *audio_control_event;
|
|
|
|
//
|
|
// Add note value event
|
|
//
|
|
audio_control_event
|
|
= new AudioControlEvent(
|
|
current_ticks,
|
|
NoteAudioControlID,
|
|
(Scalar)pitch
|
|
);
|
|
Register_Object(audio_control_event);
|
|
audioControlEventSocket.AddValue(audio_control_event, current_ticks);
|
|
|
|
//
|
|
// Add velocity event
|
|
//
|
|
audio_control_event
|
|
= new AudioControlEvent(
|
|
current_ticks,
|
|
AttackVolumeAudioControlID,
|
|
(Scalar)vol / (Scalar)127
|
|
);
|
|
Register_Object(audio_control_event);
|
|
audioControlEventSocket.AddValue(audio_control_event, current_ticks);
|
|
|
|
//
|
|
// Add start event
|
|
//
|
|
audio_control_event
|
|
= new AudioControlEvent(
|
|
current_ticks,
|
|
StartAudioControlID,
|
|
0.0f
|
|
);
|
|
Register_Object(audio_control_event);
|
|
audioControlEventSocket.AddValue(audio_control_event, current_ticks);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
CreateAudioControlEventStream::Mf_off(
|
|
SignedWord,
|
|
SignedWord pitch,
|
|
SignedWord
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
AudioTick current_ticks = CurTime();
|
|
AudioControlEvent *audio_control_event;
|
|
|
|
#if 0
|
|
//
|
|
// HACK - convert start control to use duration
|
|
//
|
|
AudioControlEventIterator
|
|
iterator(&audioControlEventSocket);
|
|
|
|
iterator.Last();
|
|
audio_control_event = iterator.GetCurrent();
|
|
Check(audio_control_event);
|
|
Verify(audio_control_event->audioControlID == StartAudioControlID);
|
|
|
|
audio_control_event->audioControlValue =
|
|
current_ticks - audio_control_event->startTick;
|
|
#else
|
|
//
|
|
// Add note value event
|
|
//
|
|
audio_control_event
|
|
= new AudioControlEvent(
|
|
current_ticks,
|
|
NoteAudioControlID,
|
|
(Scalar)pitch
|
|
);
|
|
Register_Object(audio_control_event);
|
|
audioControlEventSocket.AddValue(audio_control_event, current_ticks);
|
|
|
|
//
|
|
// Add stop event
|
|
//
|
|
audio_control_event
|
|
= new AudioControlEvent(
|
|
current_ticks,
|
|
StopAudioControlID,
|
|
0.0f
|
|
);
|
|
Register_Object(audio_control_event);
|
|
audioControlEventSocket.AddValue(audio_control_event, current_ticks);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
CreateAudioControlEventStream::Mf_tempo(SignedLongWord)
|
|
{
|
|
Check(this);
|
|
// tempo = i1; // HACK - Cakewalk appears to produce bogus tempo
|
|
}
|