mirror of https://github.com/AxioDL/amuse.git
3253 lines
85 KiB
C++
3253 lines
85 KiB
C++
#include "amuse/SoundMacroState.hpp"
|
|
#include "amuse/Voice.hpp"
|
|
#include "amuse/Engine.hpp"
|
|
#include "amuse/Common.hpp"
|
|
#include "amuse/AudioGroup.hpp"
|
|
#include "amuse/AudioGroupPool.hpp"
|
|
#include <cstring>
|
|
|
|
using namespace std::literals;
|
|
|
|
/* C++17 will error out if an offsetof cannot be computed, so ignore this warning */
|
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
|
|
|
/* Squelch Win32 macro pollution >.< */
|
|
#undef SendMessage
|
|
#undef GetMessage
|
|
|
|
namespace amuse
|
|
{
|
|
|
|
void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType)
|
|
{
|
|
m_comps.push_back({midiCtrl, scale, combine, varType});
|
|
}
|
|
|
|
float SoundMacroState::Evaluator::evaluate(double time, const Voice& vox, const SoundMacroState& st) const
|
|
{
|
|
float value = 0.f;
|
|
|
|
/* Iterate each component */
|
|
for (auto it = m_comps.cbegin(); it != m_comps.cend(); ++it)
|
|
{
|
|
const Component& comp = *it;
|
|
float thisValue = 0.f;
|
|
|
|
/* Load selected data */
|
|
if (comp.m_varType == VarType::Ctrl)
|
|
{
|
|
switch (comp.m_midiCtrl)
|
|
{
|
|
case 128:
|
|
/* Pitchbend */
|
|
thisValue = (vox.getPitchWheel() * 0.5f + 0.5f) * 127.f;
|
|
break;
|
|
case 129:
|
|
/* Aftertouch */
|
|
thisValue = vox.getAftertouch();
|
|
break;
|
|
case 130:
|
|
/* LFO1 */
|
|
if (vox.m_lfoPeriods[0])
|
|
thisValue = (std::sin(time / vox.m_lfoPeriods[0] * 2.f * M_PIF) * 0.5f + 0.5f) * 127.f;
|
|
break;
|
|
case 131:
|
|
/* LFO2 */
|
|
if (vox.m_lfoPeriods[1])
|
|
thisValue = (std::sin(time / vox.m_lfoPeriods[1] * 2.f * M_PIF) * 0.5f + 0.5f) * 127.f;
|
|
break;
|
|
case 132:
|
|
/* Surround panning */
|
|
thisValue = (vox.m_curSpan * 0.5f + 0.5f) * 127.f;
|
|
break;
|
|
case 133:
|
|
/* Macro-starting key */
|
|
thisValue = st.m_initKey;
|
|
break;
|
|
case 134:
|
|
/* Macro-starting velocity */
|
|
thisValue = st.m_initVel;
|
|
break;
|
|
case 135:
|
|
/* Time since macro-start (ms) */
|
|
thisValue = clamp(0.f, float(st.m_execTime * 1000.f), 16383.f);
|
|
break;
|
|
default:
|
|
thisValue = vox.getCtrlValue(comp.m_midiCtrl);
|
|
break;
|
|
}
|
|
}
|
|
else if (comp.m_varType == VarType::Var)
|
|
thisValue = st.m_variables[comp.m_midiCtrl & 0x1f];
|
|
|
|
/* Apply scale */
|
|
thisValue *= comp.m_scale;
|
|
|
|
/* Combine */
|
|
if (it != m_comps.cbegin())
|
|
{
|
|
switch (comp.m_combine)
|
|
{
|
|
case Combine::Add:
|
|
value += thisValue;
|
|
break;
|
|
case Combine::Mult:
|
|
value *= thisValue;
|
|
break;
|
|
default:
|
|
value = thisValue;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
value = thisValue;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void SoundMacroState::initialize(ObjectId id, const SoundMacro* macro, int step)
|
|
{
|
|
initialize(id, macro, step, 1000.f, 0, 0, 0);
|
|
}
|
|
|
|
void SoundMacroState::initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec,
|
|
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod)
|
|
{
|
|
m_ticksPerSec = ticksPerSec;
|
|
m_initKey = midiKey;
|
|
m_initVel = midiVel;
|
|
m_initMod = midiMod;
|
|
m_curVel = midiVel;
|
|
m_curMod = midiMod;
|
|
m_curPitch = midiKey * 100;
|
|
m_pc.clear();
|
|
m_pc.emplace_back(id, macro, macro->assertPC(step));
|
|
m_inWait = false;
|
|
m_execTime = 0.f;
|
|
m_keyoff = false;
|
|
m_sampleEnd = false;
|
|
m_loopCountdown = -1;
|
|
m_lastPlayMacroVid = -1;
|
|
m_useAdsrControllers = false;
|
|
m_portamentoMode = SoundMacro::CmdPortamento::PortState::MIDIControlled;
|
|
m_portamentoTime = 0.5f;
|
|
}
|
|
|
|
template <class T, std::enable_if_t<!std::is_enum_v<T>, int> = 0>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::Invalid; }
|
|
template <class T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType()
|
|
{ static_assert(sizeof(T) == 1, "Enum must be an 8-bit type");
|
|
return SoundMacro::CmdIntrospection::Field::Type::Choice; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<bool>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::Bool; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<atInt8>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::Int8; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<atUint8>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::UInt8; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<atInt16>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::Int16; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<atUint16>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::UInt16; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<atInt32>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::Int32; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<atUint32>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::UInt32; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<SoundMacroIdDNA<athena::Little>>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::SoundMacroId; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<SoundMacroStepDNA<athena::Little>>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::SoundMacroStep; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<TableIdDNA<athena::Little>>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::TableId; }
|
|
template <>
|
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<SampleIdDNA<athena::Little>>()
|
|
{ return SoundMacro::CmdIntrospection::Field::Type::SampleId; }
|
|
|
|
#define FIELD_HEAD(tp, var) GetFieldType<decltype(var)>(), offsetof(tp, var)
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdEnd::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"End"sv,
|
|
"End of the macro. This always appears at the end of a given SoundMacro."sv,
|
|
};
|
|
bool SoundMacro::CmdEnd::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st._setPC(-1);
|
|
return true;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdStop::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Stop"sv,
|
|
"Stops the macro at any point."sv,
|
|
};
|
|
bool SoundMacro::CmdStop::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st._setPC(-1);
|
|
return true;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSplitKey::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Split Key"sv,
|
|
"Conditionally branches macro execution based on MIDI key."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitKey, key),
|
|
"Key"sv,
|
|
0, 127, 60
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitKey, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitKey, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSplitKey::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (st.m_initKey >= key)
|
|
{
|
|
/* Do Branch */
|
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
|
st._setPC(macroStep.step);
|
|
else
|
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSplitVel::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Split Velocity"sv,
|
|
"Conditionally branches macro execution based on velocity."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitVel, velocity),
|
|
"Key"sv,
|
|
0, 127, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitVel, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitVel, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSplitVel::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (st.m_curVel >= velocity)
|
|
{
|
|
/* Do Branch */
|
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
|
st._setPC(macroStep.step);
|
|
else
|
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdWaitTicks::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Wait Ticks"sv,
|
|
"Suspend SoundMacro execution for specified length of time. Value of 65535 "
|
|
"will wait indefinitely, relying on Key Off or Sample End to signal stop. "
|
|
"Absolute mode waits relative to the start of the SoundMacro."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitTicks, keyOff),
|
|
"Key Off"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitTicks, random),
|
|
"Random"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitTicks, sampleEnd),
|
|
"Sample End"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitTicks, absolute),
|
|
"Absolute"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitTicks, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitTicks, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 96
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdWaitTicks::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
/* Set wait state */
|
|
if (ticksOrMs >= 0)
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
/* Randomize at the proper resolution */
|
|
if (random)
|
|
secTime = std::fmod(vox.getEngine().nextRandom() / q, secTime);
|
|
|
|
if (absolute)
|
|
{
|
|
if (secTime <= st.m_execTime)
|
|
return false;
|
|
st.m_waitCountdown = secTime - st.m_execTime;
|
|
}
|
|
else
|
|
st.m_waitCountdown = secTime;
|
|
|
|
st.m_indefiniteWait = false;
|
|
}
|
|
else
|
|
st.m_indefiniteWait = true;
|
|
|
|
st.m_inWait = true;
|
|
st.m_keyoffWait = keyOff;
|
|
st.m_sampleEndWait = sampleEnd;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdLoop::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Loop"sv,
|
|
"Branch to specified location in a loop for a specified number of Times. "
|
|
"65535 will cause an endless loop, relying on Key Off or Sample End to signal stop."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLoop, keyOff),
|
|
"Key Off"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLoop, random),
|
|
"Random"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLoop, sampleEnd),
|
|
"Sample End"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLoop, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLoop, times),
|
|
"Times"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdLoop::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if ((keyOff && st.m_keyoff) || (sampleEnd && st.m_sampleEnd))
|
|
{
|
|
/* Break out of loop */
|
|
st.m_loopCountdown = -1;
|
|
return false;
|
|
}
|
|
|
|
uint32_t useTimes = times;
|
|
if (random)
|
|
useTimes = vox.getEngine().nextRandom() % times;
|
|
|
|
if (st.m_loopCountdown == -1 && useTimes != -1)
|
|
st.m_loopCountdown = useTimes;
|
|
|
|
if (st.m_loopCountdown > 0)
|
|
{
|
|
/* Loop back to step */
|
|
--st.m_loopCountdown;
|
|
st._setPC(macroStep.step);
|
|
}
|
|
else /* Break out of loop */
|
|
st.m_loopCountdown = -1;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdGoto::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Goto"sv,
|
|
"Unconditional branch to specified SoundMacro location."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdGoto, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdGoto, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdGoto::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
/* Do Branch */
|
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
|
st._setPC(macroStep.step);
|
|
else
|
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdWaitMs::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Wait Millisec"sv,
|
|
"Suspend SoundMacro execution for specified length of time. Value of 65535 "
|
|
"will wait indefinitely, relying on Key Off or Sample End to signal stop. "
|
|
"Absolute mode waits relative to the start of the SoundMacro."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitMs, keyOff),
|
|
"Key Off"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitMs, random),
|
|
"Random"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitMs, sampleEnd),
|
|
"Sample End"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitMs, absolute),
|
|
"Absolute"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdWaitMs, ms),
|
|
"Millisec"sv,
|
|
0, 65535, 96
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdWaitMs::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
/* Set wait state */
|
|
if (ms >= 0)
|
|
{
|
|
float secTime = ms / 1000.f;
|
|
/* Randomize at the proper resolution */
|
|
if (random)
|
|
secTime = std::fmod(vox.getEngine().nextRandom() / 1000.f, secTime);
|
|
|
|
if (absolute)
|
|
{
|
|
if (secTime <= st.m_execTime)
|
|
return false;
|
|
st.m_waitCountdown = secTime - st.m_execTime;
|
|
}
|
|
else
|
|
st.m_waitCountdown = secTime;
|
|
|
|
st.m_indefiniteWait = false;
|
|
}
|
|
else
|
|
st.m_indefiniteWait = true;
|
|
|
|
st.m_inWait = true;
|
|
st.m_keyoffWait = keyOff;
|
|
st.m_sampleEndWait = sampleEnd;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPlayMacro::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Play Macro"sv,
|
|
"Play a SoundMacro in parallel to this one. Add Note is added to the "
|
|
"current SoundMacro note to evaluate the new note."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, addNote),
|
|
"Add Note"sv,
|
|
-128, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, priority),
|
|
"Priority"sv,
|
|
0, 127, 50
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, maxVoices),
|
|
"Max Voices"sv,
|
|
0, 255, 255
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
ObjToken<Voice> sibVox = vox.startChildMacro(addNote, macro.id, macroStep.step);
|
|
if (sibVox)
|
|
st.m_lastPlayMacroVid = sibVox->vid();
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSendKeyOff::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Send Key Off"sv,
|
|
"Send Key Off to voice specified by VID stored in a variable or the last started voice."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendKeyOff, variable),
|
|
"Variable"sv,
|
|
0, 31, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendKeyOff, lastStarted),
|
|
"Last Started"sv,
|
|
0, 1, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSendKeyOff::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (lastStarted)
|
|
{
|
|
if (st.m_lastPlayMacroVid != -1)
|
|
{
|
|
ObjToken<Voice> otherVox = vox.getEngine().findVoice(st.m_lastPlayMacroVid);
|
|
if (otherVox)
|
|
otherVox->keyOff();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ObjToken<Voice> otherVox = vox.getEngine().findVoice(st.m_variables[variable & 0x1f]);
|
|
if (otherVox)
|
|
otherVox->keyOff();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSplitMod::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Split Mod"sv,
|
|
"Conditionally branch if mod wheel is greater than or equal to specified value."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitMod, modValue),
|
|
"Mod Value"sv,
|
|
0, 127, 64
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitMod, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitMod, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSplitMod::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (st.m_curMod >= modValue)
|
|
{
|
|
/* Do Branch */
|
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
|
st._setPC(macroStep.step);
|
|
else
|
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPianoPan::Introspective =
|
|
{
|
|
CmdType::Control,
|
|
"Piano Pan"sv,
|
|
"Gives piano-like sounds a natural-sounding stereo spread. The current key delta "
|
|
"from Center Key is scaled with Scale and biased with Center Pan to evaluate panning."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPianoPan, scale),
|
|
"Scale"sv,
|
|
0, 127, 127
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPianoPan, centerKey),
|
|
"Center Key"sv,
|
|
0, 127, 36
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPianoPan, centerPan),
|
|
"Center Pan"sv,
|
|
0, 127, 64
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPianoPan::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t pan = int32_t(st.m_initKey - centerKey) * scale / 127 + centerPan;
|
|
pan = std::max(-127, std::min(127, pan));
|
|
vox.setPan(pan / 127.f);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetAdsr::Introspective =
|
|
{
|
|
CmdType::Volume,
|
|
"Set ADSR"sv,
|
|
"Specify ADSR envelope using a pool object. DLS mode must match setting in ADSR."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAdsr, table),
|
|
"ADSR"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAdsr, dlsMode),
|
|
"DLS Mode"sv,
|
|
0, 1, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetAdsr::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setAdsr(table.id, dlsMode);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdScaleVolume::Introspective =
|
|
{
|
|
CmdType::Volume,
|
|
"Scale Volume"sv,
|
|
"Calculates volume by scaling and biasing velocity. "
|
|
"The result may be passed through an optional Curve."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdScaleVolume, scale),
|
|
"Scale"sv,
|
|
0, 127, 127
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdScaleVolume, add),
|
|
"Add"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdScaleVolume, table),
|
|
"Curve"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdScaleVolume, originalVol),
|
|
"Original Vol"sv,
|
|
0, 1, 1
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdScaleVolume::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t eval = int32_t(originalVol ? st.m_initVel : st.m_curVel) * scale / 127 + add;
|
|
eval = clamp(0, eval, 127);
|
|
|
|
if (table.id != 0)
|
|
{
|
|
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id);
|
|
if (curveData && curveData->data.size() >= 128)
|
|
{
|
|
vox.m_curVol = curveData->data[eval] / 127.f;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
vox.m_curVol = eval / 127.f;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPanning::Introspective =
|
|
{
|
|
CmdType::Control,
|
|
"Panning"sv,
|
|
"Start pan-sweep from Pan Position offset by Width over specified time period."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanning, panPosition),
|
|
"Pan Position"sv,
|
|
0, 127, 64
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanning, timeMs),
|
|
"Time Millisec"sv,
|
|
0, 65535, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanning, width),
|
|
"Width"sv,
|
|
-128, 127, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPanning::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.startPanning(timeMs / 1000.0, panPosition, width);
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdEnvelope::Introspective =
|
|
{
|
|
CmdType::Volume,
|
|
"Envelope"sv,
|
|
"Start a velocity envelope by fading the current velocity to the one "
|
|
"evaluated by Scale and Add. The result is optionally transformed with a Curve object."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdEnvelope, scale),
|
|
"Scale"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdEnvelope, add),
|
|
"Add"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdEnvelope, table),
|
|
"Curve"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdEnvelope, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdEnvelope, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 500
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdEnvelope::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
double q = msSwitch ? 1000.0 : st.m_ticksPerSec;
|
|
double secTime = ticksOrMs / q;
|
|
|
|
int32_t eval = int32_t(st.m_curVel) * scale / 127 + add;
|
|
eval = clamp(0, eval, 127);
|
|
|
|
const Curve* curveData;
|
|
if (table.id != 0)
|
|
curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id);
|
|
else
|
|
curveData = nullptr;
|
|
|
|
vox.startEnvelope(secTime, eval, curveData);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdStartSample::Introspective =
|
|
{
|
|
CmdType::Sample,
|
|
"Start Sample"sv,
|
|
"Start a Sample playing on the voice. An Offset in samples may be applied. "
|
|
"This offset may be scaled with the current velocity."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdStartSample, sample),
|
|
"Sample"sv,
|
|
0, 65535, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdStartSample, mode),
|
|
"Velocity Scale"sv,
|
|
0, 2, 0,
|
|
{
|
|
"No Scale"sv,
|
|
"Negative"sv,
|
|
"Positive"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdStartSample, offset),
|
|
"Offset"sv,
|
|
0, 0xffffff, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdStartSample::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
uint32_t useOffset = offset;
|
|
|
|
switch (mode)
|
|
{
|
|
case Mode::Negative:
|
|
useOffset = offset * (127 - st.m_curVel) / 127;
|
|
break;
|
|
case Mode::Positive:
|
|
useOffset = offset * st.m_curVel / 127;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
vox.startSample(sample.id, useOffset);
|
|
vox.setPitchKey(st.m_curPitch);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdStopSample::Introspective =
|
|
{
|
|
CmdType::Sample,
|
|
"Stop Sample"sv,
|
|
"Stops the sample playing on the voice."sv
|
|
};
|
|
bool SoundMacro::CmdStopSample::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.stopSample();
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdKeyOff::Introspective =
|
|
{
|
|
CmdType::Control,
|
|
"Key Off"sv,
|
|
"Sends a Key Off to the current voice."sv
|
|
};
|
|
bool SoundMacro::CmdKeyOff::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox._macroKeyOff();
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSplitRnd::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Split Rnd"sv,
|
|
"Conditionally branch if a random value is greater than or equal to RND. "
|
|
"A lower RND will cause a higher probability of branching."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, rnd),
|
|
"RND"sv,
|
|
0, 255, 128
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSplitRnd::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (rnd <= vox.getEngine().nextRandom() % 256)
|
|
{
|
|
/* Do branch */
|
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
|
st._setPC(macroStep.step);
|
|
else
|
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdFadeIn::Introspective =
|
|
{
|
|
CmdType::Volume,
|
|
"Fade In"sv,
|
|
"Start a velocity envelope by fading from silence to the velocity "
|
|
"evaluated by Scale and Add. The result is optionally transformed with a Curve object."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdFadeIn, scale),
|
|
"Scale"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdFadeIn, add),
|
|
"Add"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdFadeIn, table),
|
|
"Curve"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdFadeIn, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdFadeIn, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 500
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdFadeIn::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
|
|
int32_t eval = int32_t(st.m_curVel) * scale / 127 + add;
|
|
eval = clamp(0, eval, 127);
|
|
|
|
const Curve* curveData;
|
|
if (table.id != 0)
|
|
curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id);
|
|
else
|
|
curveData = nullptr;
|
|
|
|
vox.startFadeIn(secTime, eval, curveData);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSpanning::Introspective =
|
|
{
|
|
CmdType::Control,
|
|
"Spanning"sv,
|
|
"Start span-sweep from Span Position offset by Width over specified time period."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanning, spanPosition),
|
|
"Span Position"sv,
|
|
0, 127, 64
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanning, timeMs),
|
|
"Time Millisec"sv,
|
|
0, 65535, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanning, width),
|
|
"Width"sv,
|
|
-128, 127, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSpanning::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.startSpanning(timeMs / 1000.0, spanPosition, width);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetAdsrCtrl::Introspective =
|
|
{
|
|
CmdType::Volume,
|
|
"Set ADSR Ctrl"sv,
|
|
"Bind MIDI controls to ADSR parameters."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAdsrCtrl, attack),
|
|
"Attack Ctrl"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAdsrCtrl, decay),
|
|
"Decay Ctrl"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAdsrCtrl, sustain),
|
|
"Sustain Ctrl"sv,
|
|
0, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAdsrCtrl, release),
|
|
"Release Ctrl"sv,
|
|
0, 127, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetAdsrCtrl::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_useAdsrControllers = true;
|
|
st.m_midiAttack = attack;
|
|
st.m_midiDecay = decay;
|
|
st.m_midiSustain = sustain;
|
|
st.m_midiRelease = release;
|
|
|
|
/* Bootstrap ADSR defaults here */
|
|
if (!vox.getCtrlValue(st.m_midiSustain))
|
|
{
|
|
vox.setCtrlValue(st.m_midiAttack, 10);
|
|
vox.setCtrlValue(st.m_midiSustain, 127);
|
|
vox.setCtrlValue(st.m_midiRelease, 10);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdRndNote::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Random Note"sv,
|
|
"Sets random pitch between Note Lo and Note Hi, biased by Detune in cents. "
|
|
"If Free is set, the note will not snap to key steps."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdRndNote, noteLo),
|
|
"Note Lo"sv,
|
|
-127, 127, 48
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdRndNote, detune),
|
|
"Detune"sv,
|
|
0, 99, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdRndNote, noteHi),
|
|
"Note Hi"sv,
|
|
-127, 127, 72
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdRndNote, fixedFree),
|
|
"Free"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdRndNote, absRel),
|
|
"Absolute"sv,
|
|
0, 1, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdRndNote::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useNoteLo = noteLo;
|
|
int32_t useNoteHi = noteHi;
|
|
|
|
if (absRel)
|
|
{
|
|
useNoteLo = st.m_initKey - noteLo;
|
|
useNoteHi = noteLo + noteHi;
|
|
}
|
|
|
|
useNoteLo *= 100;
|
|
useNoteHi *= 100;
|
|
|
|
if (useNoteHi == useNoteLo)
|
|
st.m_curPitch = useNoteHi;
|
|
else
|
|
st.m_curPitch = (vox.getEngine().nextRandom() % (useNoteHi - useNoteLo)) + useNoteLo;
|
|
|
|
if (!fixedFree)
|
|
st.m_curPitch = st.m_curPitch / 100 * 100 + detune;
|
|
|
|
vox.setPitchKey(st.m_curPitch);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAddNote::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Add Note"sv,
|
|
"Sets new pitch by adding Add, biased by Detune in cents. "
|
|
"The time parameters behave like a WAIT command when non-zero."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddNote, add),
|
|
"Add"sv,
|
|
-128, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddNote, detune),
|
|
"Detune"sv,
|
|
-99, 99, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddNote, originalKey),
|
|
"Original Key"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddNote, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddNote, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAddNote::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_curPitch += add * 100 + detune;
|
|
|
|
/* Set wait state */
|
|
if (msSwitch)
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
st.m_waitCountdown = secTime;
|
|
st.m_inWait = true;
|
|
}
|
|
|
|
vox.setPitchKey(st.m_curPitch);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetNote::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Set Note"sv,
|
|
"Sets new pitch to Key, biased by Detune in cents. "
|
|
"The time parameters behave like a WAIT command when non-zero."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetNote, key),
|
|
"Key"sv,
|
|
0, 127, 60
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetNote, detune),
|
|
"Detune"sv,
|
|
-99, 99, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetNote, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetNote, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetNote::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_curPitch = key * 100 + detune;
|
|
|
|
/* Set wait state */
|
|
if (ticksOrMs)
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
st.m_waitCountdown = secTime;
|
|
st.m_inWait = true;
|
|
}
|
|
|
|
vox.setPitchKey(st.m_curPitch);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdLastNote::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Last Note"sv,
|
|
"Sets new pitch by adding Add to last played MIDI note, biased by Detune in cents. "
|
|
"The time parameters behave like a WAIT command when non-zero."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLastNote, add),
|
|
"Key"sv,
|
|
-128, 127, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLastNote, detune),
|
|
"Detune"sv,
|
|
-99, 99, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLastNote, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdLastNote, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdLastNote::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_curPitch = (add + vox.getLastNote()) * 100 + detune;
|
|
|
|
/* Set wait state */
|
|
if (msSwitch)
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
st.m_waitCountdown = secTime;
|
|
st.m_inWait = true;
|
|
}
|
|
|
|
vox.setPitchKey(st.m_curPitch);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPortamento::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Portamento"sv,
|
|
"Setup portamento mode for this voice."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamento, portState),
|
|
"Port. State"sv,
|
|
0, 2, 1,
|
|
{
|
|
"Enable"sv,
|
|
"Disable"sv,
|
|
"MIDI Controlled"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamento, portType),
|
|
"Port. Type"sv,
|
|
0, 1, 0,
|
|
{
|
|
"Last Pressed"sv,
|
|
"Always"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamento, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamento, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPortamento::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_portamentoMode = portState;
|
|
st.m_portamentoType = portType;
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
st.m_portamentoTime = ticksOrMs / q;
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdVibrato::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Vibrato"sv,
|
|
"Setup vibrato mode for this voice. Voice pitch will be "
|
|
"modulated using the Level magnitude or the modwheel."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVibrato, levelNote),
|
|
"Level Note"sv,
|
|
-127, 127, 0,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVibrato, levelFine),
|
|
"Level Fine"sv,
|
|
-99, 99, 15,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVibrato, modwheelFlag),
|
|
"Use Modwheel"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVibrato, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVibrato, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 250
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdVibrato::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
vox.setVibrato(levelNote * 100 + levelFine, modwheelFlag, ticksOrMs / q);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPitchSweep1::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Pitch Sweep 1"sv,
|
|
"Setup pitch sweep 1 for this voice. Voice pitch will accumulate Add for Times frames. "
|
|
"If the time values are non-zero, this command also functions as a WAIT."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep1, times),
|
|
"Times"sv,
|
|
0, 127, 100,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep1, add),
|
|
"Add"sv,
|
|
-32768, 32767, 100,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep1, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep1, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 1000
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPitchSweep1::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
/* Set wait state */
|
|
if (msSwitch)
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
st.m_waitCountdown = secTime;
|
|
st.m_inWait = true;
|
|
}
|
|
|
|
vox.setPitchSweep1(times, add);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPitchSweep2::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Pitch Sweep 2"sv,
|
|
"Setup pitch sweep 2 for this voice. Voice pitch will accumulate Add for Times frames. "
|
|
"If the time values are non-zero, this command also functions as a WAIT."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep2, times),
|
|
"Times"sv,
|
|
0, 127, 100,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep2, add),
|
|
"Add"sv,
|
|
-32768, 32767, 100,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep2, msSwitch),
|
|
"Use Millisec"sv,
|
|
0, 1, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchSweep2, ticksOrMs),
|
|
"Ticks/Millisec"sv,
|
|
0, 65535, 1000
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPitchSweep2::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
/* Set wait state */
|
|
if (msSwitch)
|
|
{
|
|
float q = msSwitch ? 1000.f : st.m_ticksPerSec;
|
|
float secTime = ticksOrMs / q;
|
|
st.m_waitCountdown = secTime;
|
|
st.m_inWait = true;
|
|
}
|
|
|
|
vox.setPitchSweep2(times, add);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetPitch::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Set Pitch"sv,
|
|
"Set the playback sample rate directly."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetPitch, hz.val),
|
|
"Hz"sv,
|
|
0, 0xffffff, 22050,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetPitch, fine),
|
|
"Level Fine"sv,
|
|
0, 65535, 0,
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetPitch::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setPitchFrequency(hz, fine);
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetPitchAdsr::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Set Pitch ADSR"sv,
|
|
"Define the pitch ADSR from a DLS ADSR pool object. The pitch range is "
|
|
"specified using Note and Cents parameters."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, table),
|
|
"ADSR"sv,
|
|
0, 65535, 65535,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, keys),
|
|
"Note range"sv,
|
|
-128, 127, 0,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, cents),
|
|
"Cents range"sv,
|
|
-99, 99, 0,
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetPitchAdsr::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setPitchAdsr(table.id, keys * 100 + cents);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdScaleVolumeDLS::Introspective =
|
|
{
|
|
CmdType::Volume,
|
|
"Scale Volume DLS"sv,
|
|
"Sets new volume by scaling the velocity. A value of 4096 == 100%."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdScaleVolumeDLS, scale),
|
|
"Scale"sv,
|
|
0, 16383, 4096,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdScaleVolumeDLS, originalVol),
|
|
"Original Vol"sv,
|
|
0, 1, 1,
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdScaleVolumeDLS::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.m_curVol = int32_t(originalVol ? st.m_initVel : st.m_curVel) * scale / 4096.f / 127.f;
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdMod2Vibrange::Introspective =
|
|
{
|
|
CmdType::Pitch,
|
|
"Mod 2 Vibrange"sv,
|
|
"Values used to scale the modwheel control for vibrato."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMod2Vibrange, keys),
|
|
"Key range"sv,
|
|
0, 16383, 4096,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMod2Vibrange, cents),
|
|
"Cent range"sv,
|
|
0, 1, 1,
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdMod2Vibrange::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setMod2VibratoRange(keys * 100 + cents);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetupTremolo::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Setup Tremolo"sv,
|
|
"Setup tremolo effect. Must be combined with Tremolo Select to connect "
|
|
"with a configured LFO. A value of 4096 == 100%."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetupTremolo, scale),
|
|
"Scale"sv,
|
|
0, 16383, 8192,
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetupTremolo, modwAddScale),
|
|
"Modw. add scale"sv,
|
|
0, 16383, 0,
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetupTremolo::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setTremolo(scale / 4096.f, scale / 4096.f);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdReturn::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Return"sv,
|
|
"Branch to after last Go Subroutine command and pop call stack."sv
|
|
};
|
|
bool SoundMacro::CmdReturn::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (st.m_pc.size() > 1)
|
|
{
|
|
st.m_pc.pop_back();
|
|
vox._setObjectId(std::get<0>(st.m_pc.back()));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdGoSub::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Go Subroutine"sv,
|
|
"Push location onto call stack and branch to specified location."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdGoSub::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
|
st.m_pc.emplace_back(std::get<0>(st.m_pc.back()), std::get<1>(st.m_pc.back()),
|
|
std::get<1>(st.m_pc.back())->assertPC(macroStep.step));
|
|
else
|
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod, true);
|
|
|
|
vox._setObjectId(std::get<0>(st.m_pc.back()));
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdTrapEvent::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Trap Event"sv,
|
|
"Register event-based branch to a specified location."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTrapEvent, event),
|
|
"Event"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Key Off"sv,
|
|
"Sample End"sv,
|
|
"Message Recv"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTrapEvent, macro),
|
|
"Macro"sv,
|
|
0, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTrapEvent, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdTrapEvent::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
switch (event)
|
|
{
|
|
case EventType::KeyOff:
|
|
vox.m_keyoffTrap.macroId = macro.id;
|
|
vox.m_keyoffTrap.macroStep = macroStep.step;
|
|
break;
|
|
case EventType::SampleEnd:
|
|
vox.m_sampleEndTrap.macroId = macro.id;
|
|
vox.m_sampleEndTrap.macroStep = macroStep.step;
|
|
break;
|
|
case EventType::MessageRecv:
|
|
vox.m_messageTrap.macroId = macro.id;
|
|
vox.m_messageTrap.macroStep = macroStep.step;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdUntrapEvent::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"Untrap Event"sv,
|
|
"Unregister event-based branch."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdUntrapEvent, event),
|
|
"Event"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Key Off"sv,
|
|
"Sample End"sv,
|
|
"Message Recv"sv
|
|
}
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdUntrapEvent::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
switch (event)
|
|
{
|
|
case CmdTrapEvent::EventType::KeyOff:
|
|
vox.m_keyoffTrap.macroId = 0xffff;
|
|
vox.m_keyoffTrap.macroStep = 0xffff;
|
|
break;
|
|
case CmdTrapEvent::EventType::SampleEnd:
|
|
vox.m_sampleEndTrap.macroId = 0xffff;
|
|
vox.m_sampleEndTrap.macroStep = 0xffff;
|
|
break;
|
|
case CmdTrapEvent::EventType::MessageRecv:
|
|
vox.m_messageTrap.macroId = 0xffff;
|
|
vox.m_messageTrap.macroStep = 0xffff;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSendMessage::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Send Message"sv,
|
|
"Send message to SoundMacro or Voice referenced in a variable. "
|
|
"The message value is retrieved from a variable."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendMessage, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendMessage, macro),
|
|
"Macro"sv,
|
|
1, 65535, 65535
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendMessage, voiceVar),
|
|
"Voice Var"sv,
|
|
0, 31, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendMessage, valueVar),
|
|
"Value Var"sv,
|
|
0, 31, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSendMessage::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (isVar)
|
|
{
|
|
ObjToken<Voice> findVox = vox.getEngine().findVoice(st.m_variables[voiceVar & 0x1f]);
|
|
if (findVox)
|
|
findVox->message(st.m_variables[valueVar & 0x1f]);
|
|
}
|
|
else
|
|
vox.getEngine().sendMacroMessage(macro.id, st.m_variables[valueVar & 0x1f]);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdGetMessage::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Get Message"sv,
|
|
"Get voice's latest received message and store its value in Variable."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdGetMessage, variable),
|
|
"Variable"sv,
|
|
0, 31, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdGetMessage::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_variables[variable & 0x1f] = vox.m_latestMessage;
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdGetVid::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Get VID"sv,
|
|
"Get ID of current voice or last voice started by Play Macro command and store in Variable."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdGetVid, variable),
|
|
"Variable"sv,
|
|
0, 31, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdGetVid, playMacro),
|
|
"Play Macro"sv,
|
|
0, 1, 1
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdGetVid::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_variables[variable & 0x1f] = playMacro ? st.m_lastPlayMacroVid : vox.vid();
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAddAgeCount::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Add Age Count"sv,
|
|
"Adds a value to the current voice's age counter."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddAgeCount, add),
|
|
"Add"sv,
|
|
-32768, 32767, -30000
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAddAgeCount::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetAgeCount::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Set Age Count"sv,
|
|
"Set a value into the current voice's age counter."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetAgeCount, counter),
|
|
"Counter"sv,
|
|
0, 65535, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetAgeCount::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSendFlag::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Send Flag"sv,
|
|
"Send a flag value to the host application."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendFlag, flagId),
|
|
"Flag ID"sv,
|
|
0, 15, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSendFlag, value),
|
|
"Value"sv,
|
|
0, 255, 255
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSendFlag::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
/* TODO: figure out a good API */
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPitchWheelR::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Pitch Wheel Range"sv,
|
|
"Specifies the number of note steps for the range of the pitch wheel."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelR, rangeUp),
|
|
"Range Up"sv,
|
|
0, 127, 2
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelR, rangeDown),
|
|
"Range Down"sv,
|
|
0, 127, 2
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPitchWheelR::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setPitchWheelRange(rangeUp, rangeDown);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetPriority::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Set Priority"sv,
|
|
"Sets the priority of the current voice."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetPriority, prio),
|
|
"Priority"sv,
|
|
0, 254, 50
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetPriority::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAddPriority::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Add Priority"sv,
|
|
"Adds to the priority of the current voice."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddPriority, prio),
|
|
"Priority"sv,
|
|
-255, 255, 1
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAddPriority::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAgeCntSpeed::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Age Count Speed"sv,
|
|
"Sets the speed the current voice's age counter is decremented."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAgeCntSpeed, time),
|
|
"Millisec"sv,
|
|
0, 16777215, 1080000
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAgeCntSpeed::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAgeCntVel::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Age Count Velocity"sv,
|
|
"Sets the current voice's age counter by scaling the velocity."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAgeCntVel, ageBase),
|
|
"Base"sv,
|
|
0, 65535, 60000
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAgeCntVel, ageScale),
|
|
"Scale"sv,
|
|
0, 65535, 127
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAgeCntVel::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdVolSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Volume Select"sv,
|
|
"Appends an evaluator component for computing the voice's volume."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVolSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 7
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVolSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVolSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVolSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdVolSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdVolSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_volumeSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPanSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Pan Select"sv,
|
|
"Appends an evaluator component for computing the voice's pan."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 10
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPanSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPanSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_panSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPitchWheelSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Pitch Wheel Select"sv,
|
|
"Appends an evaluator component for computing the voice's pitch wheel."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 128
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPitchWheelSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPitchWheelSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_pitchWheelSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdModWheelSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Mod Wheel Select"sv,
|
|
"Appends an evaluator component for computing the voice's mod wheel."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModWheelSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModWheelSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModWheelSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModWheelSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModWheelSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdModWheelSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_modWheelSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPedalSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Pedal Select"sv,
|
|
"Appends an evaluator component for computing the voice's pedal."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPedalSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPedalSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPedalSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPedalSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPedalSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPedalSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_pedalSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPortamentoSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Portamento Select"sv,
|
|
"Appends an evaluator component for computing the voice's portamento."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamentoSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamentoSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamentoSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamentoSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPortamentoSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPortamentoSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_portamentoSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdReverbSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Reverb Select"sv,
|
|
"Appends an evaluator component for computing the voice's reverb."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdReverbSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdReverbSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdReverbSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdReverbSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdReverbSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdReverbSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_reverbSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSpanSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Span Select"sv,
|
|
"Appends an evaluator component for computing the voice's span."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 19
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSpanSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSpanSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_spanSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdDopplerSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Doppler Select"sv,
|
|
"Appends an evaluator component for computing the voice's doppler."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDopplerSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 132
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDopplerSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDopplerSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDopplerSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDopplerSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdDopplerSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_dopplerSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdTremoloSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Tremolo Select"sv,
|
|
"Appends an evaluator component for computing the voice's tremolo."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTremoloSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTremoloSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTremoloSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTremoloSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdTremoloSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdTremoloSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_tremoloSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPreASelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"PreA Select"sv,
|
|
"Appends an evaluator component for computing the voice's pre-AUXA."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreASelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreASelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreASelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreASelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreASelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPreASelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_preAuxASel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPreBSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"PreB Select"sv,
|
|
"Appends an evaluator component for computing the voice's pre-AUXB."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreBSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreBSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreBSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreBSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPreBSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPreBSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_preAuxBSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdPostBSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"PostB Select"sv,
|
|
"Appends an evaluator component for computing the voice's post-AUXB."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPostBSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPostBSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPostBSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPostBSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdPostBSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdPostBSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_postAuxB.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAuxAFXSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"AuxA FX Select"sv,
|
|
"Appends an evaluator component for computing the AUXA Parameter."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxAFXSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxAFXSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxAFXSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxAFXSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxAFXSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxAFXSelect, paramIndex),
|
|
"Param Index"sv,
|
|
0, 2, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAuxAFXSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_auxAFxSel[std::min(paramIndex, atUint8(3))].
|
|
addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAuxBFXSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"AuxB FX Select"sv,
|
|
"Appends an evaluator component for computing the AUXB Parameter."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxBFXSelect, midiControl),
|
|
"MIDI Control"sv,
|
|
0, 132, 1
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxBFXSelect, scalingPercentage),
|
|
"Scale Percentage"sv,
|
|
-10000, 10000, 100
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxBFXSelect, combine),
|
|
"Combine Mode"sv,
|
|
0, 2, 0,
|
|
{
|
|
"Set"sv,
|
|
"Add"sv,
|
|
"Mult"sv
|
|
}
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxBFXSelect, isVar),
|
|
"Is Var"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxBFXSelect, fineScaling),
|
|
"Fine Scaling"sv,
|
|
-100, 100, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAuxBFXSelect, paramIndex),
|
|
"Param Index"sv,
|
|
0, 2, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAuxBFXSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
st.m_auxBFxSel[std::min(paramIndex, atUint8(3))].
|
|
addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f,
|
|
SoundMacroState::Evaluator::Combine(combine),
|
|
SoundMacroState::Evaluator::VarType(isVar));
|
|
return false;
|
|
}
|
|
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetupLFO::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Setup LFO"sv,
|
|
"Configures voice's LFO period in milliseconds."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetupLFO, lfoNumber),
|
|
"LFO Number"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetupLFO, periodInMs),
|
|
"Period"sv,
|
|
-10000, 10000, 100
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetupLFO::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (lfoNumber == 0)
|
|
vox.setLFO1Period(periodInMs / 1000.f);
|
|
else if (lfoNumber == 1)
|
|
vox.setLFO2Period(periodInMs / 1000.f);
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdModeSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Mode Select"sv,
|
|
"Sets operating modes for current voice."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModeSelect, dlsVol),
|
|
"DLS Vol"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdModeSelect, itd),
|
|
"ITD"sv,
|
|
0, 1, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdModeSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.m_dlsVol = dlsVol;
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetKeygroup::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"Set Keygroup"sv,
|
|
"Selects keygroup for current voice."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetKeygroup, group),
|
|
"Group"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetKeygroup, killNow),
|
|
"Kill now"sv,
|
|
0, 1, 0
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetKeygroup::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
vox.setKeygroup(0);
|
|
if (group)
|
|
{
|
|
vox.getEngine().killKeygroup(group, killNow);
|
|
vox.setKeygroup(group);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSRCmodeSelect::Introspective =
|
|
{
|
|
CmdType::Setup,
|
|
"SRC Mode Select"sv,
|
|
"Sets operating modes for sample rate converter."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSRCmodeSelect, srcType),
|
|
"SRC Type"sv,
|
|
0, 2, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSRCmodeSelect, type0SrcFilter),
|
|
"Type 0 SRC Filter"sv,
|
|
0, 2, 1
|
|
}
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSRCmodeSelect::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAddVars::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Add Vars"sv,
|
|
"A = B + C"sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddVars, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddVars, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddVars, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddVars, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddVars, varCtrlC),
|
|
"Use Ctrl C"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddVars, c),
|
|
"C"sv,
|
|
0, 255, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAddVars::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useB, useC;
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if (varCtrlC)
|
|
useC = vox.getCtrlValue(c);
|
|
else
|
|
useC = st.m_variables[c & 0x1f];
|
|
|
|
if (varCtrlA)
|
|
vox.setCtrlValue(a, useB + useC);
|
|
else
|
|
st.m_variables[a & 0x1f] = useB + useC;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSubVars::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Sub Vars"sv,
|
|
"A = B - C"sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSubVars, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSubVars, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSubVars, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSubVars, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSubVars, varCtrlC),
|
|
"Use Ctrl C"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSubVars, c),
|
|
"C"sv,
|
|
0, 255, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSubVars::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useB, useC;
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if (varCtrlC)
|
|
useC = vox.getCtrlValue(c);
|
|
else
|
|
useC = st.m_variables[c & 0x1f];
|
|
|
|
if (varCtrlA)
|
|
vox.setCtrlValue(a, useB - useC);
|
|
else
|
|
st.m_variables[a & 0x1f] = useB - useC;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdMulVars::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Mul Vars"sv,
|
|
"A = B * C"sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMulVars, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMulVars, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMulVars, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMulVars, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMulVars, varCtrlC),
|
|
"Use Ctrl C"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdMulVars, c),
|
|
"C"sv,
|
|
0, 255, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdMulVars::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useB, useC;
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if (varCtrlC)
|
|
useC = vox.getCtrlValue(c);
|
|
else
|
|
useC = st.m_variables[c & 0x1f];
|
|
|
|
if (varCtrlA)
|
|
vox.setCtrlValue(a, useB * useC);
|
|
else
|
|
st.m_variables[a & 0x1f] = useB * useC;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdDivVars::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Div Vars"sv,
|
|
"A = B / C"sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDivVars, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDivVars, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDivVars, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDivVars, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDivVars, varCtrlC),
|
|
"Use Ctrl C"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdDivVars, c),
|
|
"C"sv,
|
|
0, 255, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdDivVars::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useB, useC;
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if (varCtrlC)
|
|
useC = vox.getCtrlValue(c);
|
|
else
|
|
useC = st.m_variables[c & 0x1f];
|
|
|
|
if (varCtrlA)
|
|
vox.setCtrlValue(a, useB / useC);
|
|
else
|
|
st.m_variables[a & 0x1f] = useB / useC;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdAddIVars::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Add Imm Vars"sv,
|
|
"A = B + Immediate"sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddIVars, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddIVars, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddIVars, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddIVars, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdAddIVars, imm),
|
|
"Immediate"sv,
|
|
-32768, 32767, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdAddIVars::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useB;
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if (varCtrlA)
|
|
vox.setCtrlValue(a, useB + imm);
|
|
else
|
|
st.m_variables[a & 0x1f] = useB + imm;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdSetVar::Introspective =
|
|
{
|
|
CmdType::Special,
|
|
"Set Var"sv,
|
|
"A = Immediate"sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetVar, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetVar, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdSetVar, imm),
|
|
"Immediate"sv,
|
|
-32768, 32767, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdSetVar::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
if (varCtrlA)
|
|
vox.setCtrlValue(a, imm);
|
|
else
|
|
st.m_variables[a & 0x1f] = imm;
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdIfEqual::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"If Equal"sv,
|
|
"Branches to specified step if A == B."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfEqual, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfEqual, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfEqual, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfEqual, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfEqual, notEq),
|
|
"Not"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfEqual, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdIfEqual::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useA, useB;
|
|
|
|
if (varCtrlA)
|
|
useA = vox.getCtrlValue(a);
|
|
else
|
|
useA = st.m_variables[a & 0x1f];
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if ((useA == useB) ^ notEq)
|
|
st._setPC(macroStep.step);
|
|
|
|
return false;
|
|
}
|
|
|
|
const SoundMacro::CmdIntrospection SoundMacro::CmdIfLess::Introspective =
|
|
{
|
|
CmdType::Structure,
|
|
"If Less"sv,
|
|
"Branches to specified step if A < B."sv,
|
|
{
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfLess, varCtrlA),
|
|
"Use Ctrl A"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfLess, a),
|
|
"A"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfLess, varCtrlB),
|
|
"Use Ctrl B"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfLess, b),
|
|
"B"sv,
|
|
0, 255, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfLess, notLt),
|
|
"Not"sv,
|
|
0, 1, 0
|
|
},
|
|
{
|
|
FIELD_HEAD(SoundMacro::CmdIfLess, macroStep),
|
|
"Macro Step"sv,
|
|
0, 65535, 0
|
|
},
|
|
}
|
|
};
|
|
bool SoundMacro::CmdIfLess::Do(SoundMacroState& st, Voice& vox) const
|
|
{
|
|
int32_t useA, useB;
|
|
|
|
if (varCtrlA)
|
|
useA = vox.getCtrlValue(a);
|
|
else
|
|
useA = st.m_variables[a & 0x1f];
|
|
|
|
if (varCtrlB)
|
|
useB = vox.getCtrlValue(b);
|
|
else
|
|
useB = st.m_variables[b & 0x1f];
|
|
|
|
if ((useA < useB) ^ notLt)
|
|
st._setPC(macroStep.step);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SoundMacroState::advance(Voice& vox, double dt)
|
|
{
|
|
/* Nothing if uninitialized or finished */
|
|
if (m_pc.empty() || std::get<1>(m_pc.back()) == nullptr || std::get<2>(m_pc.back()) == -1)
|
|
return true;
|
|
|
|
/* Loop through as many commands as we can for this time period */
|
|
while (true)
|
|
{
|
|
/* Advance wait timer if active, returning if waiting */
|
|
if (m_inWait)
|
|
{
|
|
if (m_keyoffWait && m_keyoff)
|
|
m_inWait = false;
|
|
else if (m_sampleEndWait && m_sampleEnd)
|
|
m_inWait = false;
|
|
else if (!m_indefiniteWait)
|
|
{
|
|
m_waitCountdown -= dt;
|
|
if (m_waitCountdown < 0.f)
|
|
m_inWait = false;
|
|
}
|
|
|
|
if (m_inWait)
|
|
{
|
|
m_execTime += dt;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Load next command based on counter */
|
|
const SoundMacro::ICmd& cmd = std::get<1>(m_pc.back())->getCmd(std::get<2>(m_pc.back())++);
|
|
|
|
/* Perform function of command */
|
|
if (cmd.Do(*this, vox))
|
|
return true;
|
|
}
|
|
|
|
m_execTime += dt;
|
|
return false;
|
|
}
|
|
|
|
void SoundMacroState::keyoffNotify(Voice& vox) { m_keyoff = true; }
|
|
|
|
void SoundMacroState::sampleEndNotify(Voice& vox) { m_sampleEnd = true; }
|
|
}
|