amuse/lib/SoundMacroState.cpp

1786 lines
69 KiB
C++

#include "amuse/SoundMacroState.hpp"
#include <cmath>
#include <cstring>
#include "amuse/AudioGroup.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/Common.hpp"
#include "amuse/Engine.hpp"
#include "amuse/Voice.hpp"
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 = std::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 != 65535) {
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;
}
uint16_t useTimes = times;
if (random)
useTimes = vox.getEngine().nextRandom() % times;
if (st.m_loopCountdown == -1 && useTimes != 65535)
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 != 65535) {
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 = std::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 = std::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 = std::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::CmdWiiUnknown::Introspective = {
CmdType::Setup, "Wii Unknown"sv, "????"sv, {{FIELD_HEAD(SoundMacro::CmdWiiUnknown, flag), "?"sv, 0, 1, 0}}};
bool SoundMacro::CmdWiiUnknown::Do(SoundMacroState& st, Voice& vox) const { return false; }
const SoundMacro::CmdIntrospection SoundMacro::CmdWiiUnknown2::Introspective = {
CmdType::Setup, "Wii Unknown 2"sv, "????"sv, {{FIELD_HEAD(SoundMacro::CmdWiiUnknown2, flag), "?"sv, 0, 1, 0}}};
bool SoundMacro::CmdWiiUnknown2::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; }
} // namespace amuse