ExperimentNodeCatalog.java
package org.hammer.audio.workflow.catalog;
import java.util.List;
import org.hammer.audio.workflow.DataTypes;
import org.hammer.audio.workflow.Node;
import org.hammer.audio.workflow.Port;
import org.hammer.audio.workflow.PortDirection;
import org.hammer.audio.workflow.PortMultiplicity;
/**
* Factory methods for the first experiment node catalog.
*
* <p>Each method returns a fresh {@link Node} prototype that represents one experiment component.
* The nodes are compatible with the existing {@code audio-core} workflow model.
*
* <p><b>Allowed callers</b>: application services and tests. Must not depend on UI, execution
* runtime, persistence or JGit. The catalog lives in the workflow domain layer.
*
* <p>See {@code docs/architecture/experiment-node-catalog.md} for the full catalog specification
* including valid and invalid connection examples.
*/
public final class ExperimentNodeCatalog {
// Shared port identifiers used across multiple node types
private static final String PORT_AUDIO_IN = "audio-in";
private static final String PORT_AUDIO_OUT = "audio-out";
private static final String PORT_NAME_AUDIO_IN = "Audio In";
private static final String PORT_NAME_AUDIO_OUT = "Audio Out";
private ExperimentNodeCatalog() {
// utility class
}
// -------------------------------------------------------------------------
// Input nodes
// -------------------------------------------------------------------------
/**
* Creates a {@code RecordingInput} node that supplies a recorded audio dataset.
*
* <p>Output: one {@code Dataset} port.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node recordingInput(String nodeId) {
return new Node(
nodeId,
"recording-input",
"Recording Input",
List.of(),
List.of(
output(
PORT_AUDIO_OUT, "Audio Dataset", DataTypes.DATASET.id(), PortMultiplicity.SINGLE)));
}
/**
* Creates a {@code SyntheticSignalGenerator} node that generates a synthetic audio signal.
*
* <p>Output: one {@code AudioBlock} port.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node syntheticSignalGenerator(String nodeId) {
return new Node(
nodeId,
"synthetic-signal-generator",
"Synthetic Signal Generator",
List.of(),
List.of(
output(
"signal-out",
"Synthetic Signal",
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)));
}
/**
* Creates a {@code HumBugDbImport} node that imports recordings from the HumBugDB dataset.
*
* <p>Output: one {@code Dataset} port.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node humBugDbImport(String nodeId) {
return new Node(
nodeId,
"humbug-db-import",
"HumBugDB Import",
List.of(),
List.of(
output(
"dataset-out", "HumBug Dataset", DataTypes.DATASET.id(), PortMultiplicity.SINGLE)));
}
// -------------------------------------------------------------------------
// DSP nodes
// -------------------------------------------------------------------------
/**
* Creates a {@code Gain} node that applies a gain factor to an audio block.
*
* <p>Input: one {@code AudioBlock} port. Output: one {@code AudioBlock} port.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node gain(String nodeId) {
return new Node(
nodeId,
"gain",
"Gain",
List.of(
input(
PORT_AUDIO_IN,
PORT_NAME_AUDIO_IN,
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)),
List.of(
output(
PORT_AUDIO_OUT,
PORT_NAME_AUDIO_OUT,
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)));
}
/**
* Creates a {@code BandpassFilter} node.
*
* <p>Input: one {@code AudioBlock}. Output: one {@code AudioBlock}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node bandpassFilter(String nodeId) {
return new Node(
nodeId,
"bandpass-filter",
"Bandpass Filter",
List.of(
input(
PORT_AUDIO_IN,
PORT_NAME_AUDIO_IN,
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)),
List.of(
output(
PORT_AUDIO_OUT,
"Filtered Audio",
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)));
}
/**
* Creates an {@code FFT} node that transforms an audio block to a spectrum.
*
* <p>Input: one {@code AudioBlock}. Output: one {@code Spectrum}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node fft(String nodeId) {
return new Node(
nodeId,
"fft",
"FFT",
List.of(
input(
PORT_AUDIO_IN,
PORT_NAME_AUDIO_IN,
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)),
List.of(
output("spectrum-out", "Spectrum", DataTypes.SPECTRUM.id(), PortMultiplicity.SINGLE)));
}
// -------------------------------------------------------------------------
// Analysis nodes
// -------------------------------------------------------------------------
/**
* Creates a {@code WingbeatFeatureExtraction} node.
*
* <p>Input: one {@code Spectrum}. Output: one {@code FeatureSet}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node wingbeatFeatureExtraction(String nodeId) {
return new Node(
nodeId,
"wingbeat-feature-extraction",
"Wingbeat Feature Extraction",
List.of(input("spectrum-in", "Spectrum", DataTypes.SPECTRUM.id(), PortMultiplicity.SINGLE)),
List.of(
output(
"features-out",
"Feature Set",
DataTypes.FEATURE_SET.id(),
PortMultiplicity.SINGLE)));
}
/**
* Creates a {@code Classifier} node.
*
* <p>Input: one {@code FeatureSet}. Output: one {@code ClassificationResult}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node classifier(String nodeId) {
return new Node(
nodeId,
"classifier",
"Classifier",
List.of(
input(
"features-in", "Feature Set", DataTypes.FEATURE_SET.id(), PortMultiplicity.SINGLE)),
List.of(
output(
"result-out",
"Classification Result",
DataTypes.CLASSIFICATION_RESULT.id(),
PortMultiplicity.SINGLE)));
}
/**
* Creates a {@code Localization} node.
*
* <p>Input: one {@code AudioBlock}. Output: one {@code LocalizationResult}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node localization(String nodeId) {
return new Node(
nodeId,
"localization",
"Localization",
List.of(
input(
PORT_AUDIO_IN,
PORT_NAME_AUDIO_IN,
DataTypes.AUDIO_BLOCK.id(),
PortMultiplicity.SINGLE)),
List.of(
output(
"location-out",
"Localization Result",
DataTypes.LOCALIZATION_RESULT.id(),
PortMultiplicity.SINGLE)));
}
/**
* Creates a {@code Benchmark} node.
*
* <p>Input: one {@code ClassificationResult}. Output: one {@code BenchmarkResult}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node benchmark(String nodeId) {
return new Node(
nodeId,
"benchmark",
"Benchmark",
List.of(
input(
"result-in",
"Classification Result",
DataTypes.CLASSIFICATION_RESULT.id(),
PortMultiplicity.SINGLE)),
List.of(
output(
"benchmark-out",
"Benchmark Result",
DataTypes.BENCHMARK_RESULT.id(),
PortMultiplicity.SINGLE)));
}
// -------------------------------------------------------------------------
// Output nodes
// -------------------------------------------------------------------------
/**
* Creates a {@code Report} node that collects results and emits a human-readable report.
*
* <p>Input: one {@code BenchmarkResult}. Output: one {@code Report}.
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node report(String nodeId) {
return new Node(
nodeId,
"report",
"Report",
List.of(
input(
"benchmark-in",
"Benchmark Result",
DataTypes.BENCHMARK_RESULT.id(),
PortMultiplicity.SINGLE)),
List.of(output("report-out", "Report", DataTypes.REPORT.id(), PortMultiplicity.SINGLE)));
}
/**
* Creates an {@code EvidenceExport} node that exports structured evidence data.
*
* <p>Input: one {@code ClassificationResult}. Output: none (terminal sink).
*
* @param nodeId stable node identifier
* @return prototype node
*/
public static Node evidenceExport(String nodeId) {
return new Node(
nodeId,
"evidence-export",
"Evidence Export",
List.of(
input(
"result-in",
"Classification Result",
DataTypes.CLASSIFICATION_RESULT.id(),
PortMultiplicity.SINGLE)),
List.of());
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
/**
* Creates a required input port. All catalog input ports are required by design: catalog nodes
* define standard experiment components whose inputs must be connected before execution.
*/
private static Port input(
String id, String name, String dataType, PortMultiplicity multiplicity) {
return new Port(id, name, PortDirection.INPUT, dataType, true, multiplicity);
}
private static Port output(
String id, String name, String dataType, PortMultiplicity multiplicity) {
return new Port(id, name, PortDirection.OUTPUT, dataType, false, multiplicity);
}
}