The final link: the footstep source's volume + brightness mix from two AudioControlMixer inputs (ctl 100 walk / 101 run) behind an entity-registered AudioControlSplitter. The object stream only ZERO-initializes those inputs (AudioControlSend one-shot initializers; mixer ctor inits 0.0) -- the runtime feed was BT game code (lost with the source), using the engine's game-facing Entity::AudioSocketIterator broadcast (the only path to those anonymous components; the splitter registers via entity->AddAudioComponent). Reconstruction [T3]: on each foot plant (the SetBodyAnimation locomotion-clip pulse), broadcast the step intensity (live ground speed on the authored 0..40 -> 0..1 velocity-curve family, floored at 0.35 to clear the 0.3 transient-drop gate) on the gait-appropriate input (run-family clips 0x0a..0x0f -> 101, walk family -> 100; the other input zeroed so gait changes don't stack). Patch-source components (VDATA 1001/1002/1005) are skipped -- AudioSource::ReceiveControl Fail()s on input-range ids. VERIFIED: FootFallInt01.wav (bank2 patch39) delivers + plays per stride; mixer sums nonzero (vol=1 at run speed); 30s drive+fire regression clean -- weapon charge/fire/sustain cycles, ready dings, buttons, engine loops, state audio (0 skips), gait advancing normally, no crashes, no stuck loops. Also this session: [seqcfg] sequence config dump (decoded the authored event streams: ctl 8=note/6=attack-vol/1=start/2=stop patterns), BT_AUDIO_NODROP diagnostic (force low-volume transient delivery at a 0.7 floor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
883 lines
21 KiB
C++
883 lines
21 KiB
C++
#include <cstdlib>
|
|
#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);
|
|
if (getenv("BT_AUDIO_SPATIAL")) { static int s_ev=0; if (s_ev++<200)
|
|
DEBUG_STREAM << "[seqev] tgt=" << (void*)audio_component
|
|
<< " ctl=" << (int)audioControlID << "/" << audioControlValue << "\n" << std::flush; }
|
|
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);
|
|
}
|
|
|
|
if (getenv("BT_ATTRBIND_LOG")) { static int s_sq=0; if (s_sq++<60) {
|
|
DEBUG_STREAM << "[seqcfg] seq=" << (void*)this << " tgt=" << (void*)audio_component
|
|
<< " looped=" << (int)is_looped << " div=" << (int)divisions_per_beat
|
|
<< " tempo=" << (int)tempo << " events=" << (int)number_of_control_events << " ";
|
|
{ SChainIteratorOf<AudioControlEvent*> it(&audioControlEventSocket);
|
|
AudioControlEvent *e; int n=0;
|
|
while ((e = it.ReadAndNext()) != NULL && n++ < 24)
|
|
DEBUG_STREAM << *e; }
|
|
DEBUG_STREAM << "\n" << std::flush; } }
|
|
|
|
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
|
|
}
|