Prototype

This commit is contained in:
Armin 2026-08-05 23:12:17 +02:00
commit 4271fc34bb
14 changed files with 950 additions and 169 deletions

View file

@ -27,7 +27,8 @@ static bool patternsEqual (const monostep::StepSequencer& a, const monostep::Ste
const auto& sb = b.step (i);
if (sa.gate != sb.gate || sa.semitone != sb.semitone
|| std::fabs (sa.cents - sb.cents) > 0.001f || sa.slide != sb.slide)
|| std::fabs (sa.cents - sb.cents) > 0.001f
|| sa.slide != sb.slide || sa.accent != sb.accent)
return false;
}
@ -63,7 +64,7 @@ static int testVst3Binary (const juce::File& pluginFile)
// Part B: drive the actual processor source and render audio.
static int testProcessorRendering()
{
MonoStepAudioProcessor processor;
MonostepAudioProcessor processor;
processor.prepareToPlay (44100.0, 512);
const int totalSamples = (int) (44100.0 * 8.0);
@ -96,7 +97,7 @@ static int testProcessorRendering()
}
// Without a MIDI note the sequencer must stay silent.
MonoStepAudioProcessor idleProcessor;
MonostepAudioProcessor idleProcessor;
idleProcessor.prepareToPlay (44100.0, 512);
double idleSum = 0.0;
@ -129,6 +130,7 @@ static int testProcessorRendering()
processor.setStepNote (0, 7);
processor.setStepCents (0, 42.0f);
processor.setStepSlide (1, true);
processor.setStepAccent (0, true);
if (std::fabs (processor.getSequencer().step (0).cents - 42.0f) > 0.001f)
{
@ -136,12 +138,97 @@ static int testProcessorRendering()
return 1;
}
// accent DSP: a uniform continuous pattern (all same note, gate held open)
// so no per-note retrigger masks whether accent follows the step.
*processor.getAPVTS().getRawParameterValue ("accent") = 1.0f;
*processor.getAPVTS().getRawParameterValue ("seqGateLen") = 1.0f;
for (int i = 0; i < monostep::numSteps; ++i)
{
processor.setStepGate (i, true);
processor.setStepNote (i, 0);
processor.setStepSlide (i, false);
}
const auto renderAccent = [&] (const std::function<bool (int)>& accented) -> double
{
for (int i = 0; i < monostep::numSteps; ++i)
processor.setStepAccent (i, accented (i));
processor.reset();
processor.prepareToPlay (44100.0, 512);
juce::AudioBuffer<float> accBlock (2, blockSize);
juce::MidiBuffer accMidi;
double sum = 0.0;
for (int b = 0; b < numBlocks; ++b)
{
accBlock.clear();
accMidi.clear();
if (b == 0)
accMidi.addEvent (juce::MidiMessage::noteOn (1, 60, 0.9f), 0);
processor.processBlock (accBlock, accMidi);
for (int c = 0; c < 2; ++c)
for (int i = 0; i < blockSize; ++i)
sum += (double) accBlock.getSample (c, i) * accBlock.getSample (c, i);
}
return std::sqrt (sum / (double) (numBlocks * blockSize * 2));
};
const double accRms = renderAccent ([] (int) { return true; });
const double noAccRms = renderAccent ([] (int) { return false; });
const double altRms = renderAccent ([] (int i) { return (i % 2) == 0; });
if (accRms < noAccRms * 1.05f)
{
std::cout << "FAILED: accent does not boost the step (acc=" << accRms
<< " noacc=" << noAccRms << ")\n";
return 1;
}
if (altRms < noAccRms * 1.05f || altRms > accRms * 0.9f)
{
std::cout << "FAILED: accent does not track individual steps (all=" << accRms
<< " alt=" << altRms << " noacc=" << noAccRms << ")\n";
return 1;
}
std::cout << "Accent boosts step volume OK (all=" << accRms
<< " alternating=" << altRms << " none=" << noAccRms << ")\n";
// pattern shift rotates the steps
processor.setStepAccent (0, true);
processor.setStepNote (0, 7);
processor.setStepNote (1, 3);
processor.shiftPattern (1);
if (processor.getSequencer().step (1).semitone != 7)
{
std::cout << "FAILED: shiftPattern did not rotate steps\n";
return 1;
}
processor.shiftPattern (-1);
if (processor.getSequencer().step (0).semitone != 7 || ! processor.getSequencer().step (0).accent)
{
std::cout << "FAILED: shiftPattern did not rotate back\n";
return 1;
}
std::cout << "Pattern shift rotates steps OK\n";
// state round trip through a fresh instance
juce::MemoryBlock state;
processor.getStateInformation (state);
std::cout << "State bytes: " << (int) state.getSize() << "\n";
MonoStepAudioProcessor processor2;
MonostepAudioProcessor processor2;
processor2.prepareToPlay (44100.0, 512);
processor2.setStateInformation (state.getData(), (int) state.getSize());
@ -152,7 +239,8 @@ static int testProcessorRendering()
std::cout << "step " << i << ": gate=" << a.gate << "/" << b.gate
<< " note=" << a.semitone << "/" << b.semitone
<< " cents=" << a.cents << "/" << b.cents
<< " slide=" << a.slide << "/" << b.slide << "\n";
<< " slide=" << a.slide << "/" << b.slide
<< " accent=" << a.accent << "/" << b.accent << "\n";
}
if (! patternsEqual (processor.getSequencer(), processor2.getSequencer()))
@ -218,7 +306,7 @@ int main (int argc, char** argv)
{
if (argc < 2)
{
std::cout << "Usage: MonoStepTestHost <plugin.vst3> [out.wav]\n";
std::cout << "Usage: MonostepTestHost <plugin.vst3> [out.wav]\n";
return 1;
}