- TestScenario: parses a JSON file describing sample rate/block size/ duration, an input signal config (silence/sine/noise/impulse), and a timed sequence of events (noteOn/noteOff/controller/param/loadPatch/ loadBank/savePatch/saveBank), sorted by sample position. Relative file paths inside the JSON resolve against the scenario file's own directory, not the process cwd, so scenario files are portable. Nothing here is JaySynth-specific: an event type a given plugin doesn't care about (e.g. MIDI sent to a plugin with no MIDI input) is simply inert, since AudioProcessor/MidiBuffer already tolerate that generically. - main.cpp: unified the ad-hoc CLI flags (--note/--param/--input/etc.) and --scenario JSON files through one render loop, by synthesizing a TestScenario from the CLI flags when no JSON file is given. Added --bank/--savePatch/--saveBank for the ad-hoc path (loadPatch/wav already existed). Non-MIDI events (param/patch/bank) scheduled inside a block apply at the start of that block; MIDI events keep full per-sample timing via MidiBuffer's own offset parameter. - PerformanceStats: wraps each processBlock call with a high-resolution timer, reports min/mean/max render time and a real-time factor (audio seconds rendered / wall-clock seconds), plus optional per-block CSV export for tracking regressions over time. Never paces the loop to real-time - the point is to run faster than that. Verified end-to-end: - Ad-hoc CLI path against JaySynth.so still renders correctly and now reports performance (e.g. ~74x real-time on this machine). - A JSON scenario driving JaySynth (timed noteOn/param/noteOff/ savePatch) produces correct WAV output and a valid saved patch file. - That saved patch round-trips back in via --patch and renders cleanly (proves the save/load path is consistent, not just one-directional). - A JSON scenario driving a real third-party effect (ZamComp-vst.so, 2 in/1 out - a genuinely asymmetric channel count, not just "0 in" or "stereo") with generated pink noise and a param event produces correct mono output at >1000x real-time. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011dhtwRLARk4eiPngcQykLJ
86 lines
2.2 KiB
C++
86 lines
2.2 KiB
C++
#ifndef TESTHOST_TESTSCENARIO_H_INCLUDED
|
|
#define TESTHOST_TESTSCENARIO_H_INCLUDED
|
|
|
|
#include <JuceHeader.h>
|
|
#include "AudioInputSource.h"
|
|
|
|
/** A timed sequence of actions (MIDI events, parameter changes, patch/bank
|
|
load & save) plus render/I-O configuration, parsed from a JSON file.
|
|
|
|
This is what makes test runs repeatable and batchable instead of ad-hoc
|
|
CLI invocations - e.g. one file per regression case, run unattended.
|
|
Nothing in here is JaySynth-specific: events that don't apply to a given
|
|
plugin (MIDI events sent to a plugin with no MIDI input, say) are simply
|
|
harmless, since JUCE's MidiBuffer/AudioProcessor API already tolerates
|
|
that generically.
|
|
*/
|
|
class TestScenario
|
|
{
|
|
public:
|
|
TestScenario() {}
|
|
|
|
struct Event
|
|
{
|
|
enum Type
|
|
{
|
|
noteOn = 0,
|
|
noteOff,
|
|
controller,
|
|
param,
|
|
loadPatch,
|
|
loadBank,
|
|
savePatch,
|
|
saveBank
|
|
};
|
|
|
|
int64 sample = 0;
|
|
Type type = noteOn;
|
|
|
|
// MIDI note/controller fields
|
|
int channel = 1;
|
|
int note = 60;
|
|
int velocity = 100;
|
|
int controllerNumber = 0;
|
|
int controllerValue = 0;
|
|
|
|
// Parameter-change fields
|
|
int paramIndex = 0;
|
|
float paramValue = 0.0f;
|
|
|
|
// Patch/bank load & save fields
|
|
String file;
|
|
};
|
|
|
|
/** Parses jsonFile. Relative file paths named inside it (input.wavFile,
|
|
loadPatch/loadBank, and any event's "file") are resolved relative to
|
|
the JSON file's own directory, not the process's working directory,
|
|
so a scenario file is portable regardless of where it's run from.
|
|
*/
|
|
bool loadFromFile (const File &jsonFile);
|
|
|
|
double sampleRate = 44100.0;
|
|
int blockSize = 512;
|
|
int64 durationSamples = 0;
|
|
|
|
AudioInputSource::Type inputType = AudioInputSource::silence;
|
|
double inputFrequencyHz = 440.0;
|
|
float inputAmplitude = 0.5f;
|
|
|
|
String initialLoadPatchFile;
|
|
String initialLoadBankFile;
|
|
String recordWavFile;
|
|
|
|
/** Sorted ascending by .sample. */
|
|
Array<Event> events;
|
|
|
|
private:
|
|
bool parseTopLevel (const var &root, const File &baseDir);
|
|
bool parseInput (const var &inputVar);
|
|
bool parseEvents (const var &eventsVar, const File &baseDir);
|
|
static bool parseEventType (const String &typeName, Event::Type &outType);
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestScenario)
|
|
};
|
|
|
|
#endif // TESTHOST_TESTSCENARIO_H_INCLUDED
|