This commit is contained in:
Steven Dan
2025-12-11 09:43:42 +08:00
commit d8b2974133
1822 changed files with 280037 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test biquad filter pipeline creation.
"""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
from audio_dsp.models.biquad import Biquad
def test_simple_biquad_pipeline():
"""Test creating a simple biquad filter pipeline."""
print("Creating simple stereo biquad pipeline...")
# Create a simple stereo biquad pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_biquad",
"producer_version": "0.1",
"graph": {
"name": "Simple Biquad",
"fs": 48000,
"nodes": [
{
"op_type": "Biquad",
"parameters": {
"filter_type": {
"type": "lowpass",
"filter_freq": 1000,
"q_factor": 0.707
},
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoBiquad",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoBiquad", 0], ["StereoBiquad", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our biquad stage
biquad_stage = None
for stage in pipeline.stages:
if stage.name == "biquad":
biquad_stage = stage
break
assert biquad_stage is not None, "Could not find Biquad stage in pipeline"
assert biquad_stage.parameters.filter_type.type == "lowpass", \
f"Expected filter_type 'lowpass', got {biquad_stage.parameters.filter_type.type}"
assert biquad_stage.parameters.filter_type.filter_freq == 1000, \
f"Expected filter_freq 1000, got {biquad_stage.parameters.filter_type.filter_freq}"
assert biquad_stage.parameters.filter_type.q_factor == 0.707, \
f"Expected q_factor 0.707, got {biquad_stage.parameters.filter_type.q_factor}"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_simple_biquad_pipeline()

View File

@@ -0,0 +1,96 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test cascaded biquads pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_parametric_eq_pipeline():
"""Test creating a parametric EQ pipeline."""
print("Creating parametric EQ pipeline...")
# Create a parametric EQ pipeline JSON with different filter types
pipeline_json = {
"ir_version": 1,
"producer_name": "test_cascaded_biquads",
"producer_version": "0.1",
"graph": {
"name": "Parametric EQ",
"fs": 48000,
"nodes": [
{
"op_type": "ParametricEq8b",
"parameters": {
"filters": [
{
"type": "lowpass",
"filter_freq": 10000,
"q_factor": 0.707
},
{
"type": "highpass",
"filter_freq": 20,
"q_factor": 0.707
},
{
"type": "peaking",
"filter_freq": 1000,
"q_factor": 2.0,
"gain_db": 6.0
},
{
"type": "bypass"
},
{
"type": "bypass"
},
{
"type": "bypass"
},
{
"type": "bypass"
},
{
"type": "bypass"
}
]
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoEQ",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoEQ", 0], ["StereoEQ", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our parametric EQ stage
eq_stage = None
for stage in pipeline.stages:
if stage.name == "cascaded_biquads":
eq_stage = stage
break
assert eq_stage is not None, "Could not find Parametric EQ stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_parametric_eq_pipeline()

View File

@@ -0,0 +1,69 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test compressor pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_simple_compressor_pipeline():
"""Test creating a simple compressor pipeline."""
print("Creating simple stereo compressor pipeline...")
# Create a simple stereo compressor pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_compressor",
"producer_version": "0.1",
"graph": {
"name": "Simple Compressor",
"fs": 48000,
"nodes": [
{
"op_type": "CompressorRMS",
"parameters": {
"ratio": 4.0,
"threshold_db": -20.0,
"attack_t": 0.01,
"release_t": 0.2
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoCompressor",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoCompressor", 0], ["StereoCompressor", 1]],
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our compressor stage
compressor_stage = None
for stage in pipeline.stages:
if stage.name == "compressor_rms":
compressor_stage = stage
break
assert compressor_stage is not None, "Could not find Compressor stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_simple_compressor_pipeline()

View File

@@ -0,0 +1,74 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test delay pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
from audio_dsp.stages.signal_chain import Delay
def test_simple_delay_pipeline():
"""Test creating a simple delay pipeline."""
print("Creating simple stereo delay pipeline...")
# Create a simple stereo delay pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_delay",
"producer_version": "0.1",
"graph": {
"name": "Simple Delay",
"fs": 48000,
"nodes": [
{
"op_type": "Delay",
"config": {
"max_delay": 2048,
"units": "samples"
},
"parameters": {
"delay": 1024,
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoDelay",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoDelay", 0], ["StereoDelay", 1]],
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our delay stage
delay_stage = None
for stage in pipeline.stages:
if isinstance(stage, Delay):
delay_stage = stage
break
assert delay_stage is not None, "Could not find Delay stage in pipeline"
assert delay_stage.max_delay == 2048, f"Expected max_delay 2048, got {delay_stage.max_delay}"
assert delay_stage.units == "samples", f"Expected units 'samples', got {delay_stage.units}"
assert delay_stage.parameters.delay == 1024, f"Expected delay 1024, got {delay_stage.parameters.delay}"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_simple_delay_pipeline()

View File

@@ -0,0 +1,125 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test envelope detector pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_peak_envelope_detector_pipeline():
"""Test creating a peak envelope detector pipeline."""
print("Creating peak envelope detector pipeline...")
# Create a peak envelope detector pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_envelope_detector",
"producer_version": "0.1",
"graph": {
"name": "Peak Envelope Detector",
"fs": 48000,
"nodes": [
{
"op_type": "EnvelopeDetectorPeak",
"parameters": {
"attack_t": 0.01,
"release_t": 0.1
},
"placement": {
"input": [["inputs", 0]],
"name": "PeakDetector",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["inputs", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our envelope detector stage
detector_stage = None
for stage in pipeline.stages:
if stage.name == "envelope_detector_peak":
detector_stage = stage
break
assert detector_stage is not None, "Could not find Peak Envelope Detector stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
def test_rms_envelope_detector_pipeline():
"""Test creating an RMS envelope detector pipeline."""
print("Creating RMS envelope detector pipeline...")
# Create an RMS envelope detector pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_envelope_detector",
"producer_version": "0.1",
"graph": {
"name": "RMS Envelope Detector",
"fs": 48000,
"nodes": [
{
"op_type": "EnvelopeDetectorRMS",
"parameters": {
"attack_t": 0.05,
"release_t": 0.2
},
"placement": {
"input": [["inputs", 0]],
"name": "RMSDetector",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["inputs", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our envelope detector stage
detector_stage = None
for stage in pipeline.stages:
if stage.name == "envelope_detector_rms":
detector_stage = stage
break
assert detector_stage is not None, "Could not find RMS Envelope Detector stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_peak_envelope_detector_pipeline()
print("\n" + "="*50 + "\n")
test_rms_envelope_detector_pipeline()

View File

@@ -0,0 +1,408 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
from pathlib import Path
import annotated_types
from audio_dsp.design.parse_json import Graph, make_pipeline, insert_forks, DspJson, pipeline_to_dspjson
from audio_dsp.models.stage import all_models
from audio_dsp.stages import all_stages
from typing import get_origin, get_args, Literal
from types import UnionType
def find_autoforks(graph):
for node in graph.nodes:
if 'AutoFork' in node.placement.name:
return True
assert False, "No AutoFork node found in the graph after insert_forks."
def test_no_shared_edge():
json_str = """
{
"name": "No shared edge",
"fs": 44100,
"inputs": [{
"name": "inputs",
"channels": 1
}],
"nodes": [
{
"op_type": "VolumeControl",
"config": {},
"placement": {
"input": [["inputs", 0]],
"name": "VolumeControl_1",
"thread": 0
}
}
],
"outputs": [{
"name": "outputs",
"input": [["VolumeControl_1", 0]]
}]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_no_shared_edge"))
new_graph = insert_forks(graph)
print(f"Before insert_forks: {graph.model_dump_json()}")
print(f"After insert_forks: {new_graph.model_dump_json()}")
dsp_json = DspJson(ir_version=1, producer_name="pipeline_to_dspjson", producer_version="1.0", graph=new_graph)
new_json = pipeline_to_dspjson(a)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
for node in graph.nodes:
if 'AutoFork' in node.placement.name:
assert False, "AutoFork node found in the graph after insert_forks, but not needed."
def test_shared_edge_from_graph_input():
json_str = """
{
"name": "Shared edge from graph input",
"fs": 44100,
"inputs": [{
"name": "inputs",
"channels": 1
}],
"nodes": [
{
"op_type": "VolumeControl",
"config": {},
"placement": {
"input": [["inputs", 0]],
"name": "VolumeControl_A",
"thread": 0
}
},
{
"op_type": "Mixer",
"config": {},
"placement": {
"input": [["inputs", 0]],
"name": "Mixer_B",
"thread": 0
}
}
],
"outputs": [{
"name": "outputs",
"input": [["VolumeControl_A", 0]]
}]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_shared_edge_from_graph_input"))
new_graph = insert_forks(graph)
print(f"Before insert_forks: {graph.model_dump_json()}")
print(f"After insert_forks: {new_graph.model_dump_json()}")
find_autoforks(new_graph)
def test_shared_edge_from_producer_node():
json_str = """
{
"name": "Shared edge from producer node",
"fs": 44100,
"inputs": [{
"name": "inputs",
"channels": 1
}],
"nodes": [
{
"op_type": "VolumeControl",
"config": {},
"placement": {
"input": [["inputs", 0]],
"name": "Producer",
"thread": 0
}
},
{
"op_type": "Mixer",
"config": {},
"placement": {
"input": [["Producer", 0]],
"name": "Consumer_1",
"thread": 0
}
},
{
"op_type": "Mixer",
"config": {},
"placement": {
"input": [["Producer", 0]],
"name": "Consumer_2",
"thread": 0
}
}
],
"outputs": [{
"name": "outputs",
"input": [["Consumer_1", 0]]
}]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_shared_edge_from_producer_node"))
new_graph = insert_forks(graph)
print(f"Before insert_forks: {graph.model_dump_json()}")
print(f"After insert_forks: {new_graph.model_dump_json()}")
find_autoforks(new_graph)
def test_shared_edge_with_graph_output():
json_str = """
{
"name": "Shared edge with graph output",
"fs": 44100,
"inputs": [{
"name": "inputs",
"channels": 1
}],
"nodes": [
{
"op_type": "VolumeControl",
"config": {},
"placement": {
"input": [["inputs", 0]],
"name": "Producer",
"thread": 0
}
},
{
"op_type": "Mixer",
"config": {},
"placement": {
"input": [["Producer", 0]],
"name": "Consumer",
"thread": 0
}
}
],
"outputs": [{
"name": "outputs",
"input": [["Producer", 0]]
}]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_shared_edge_with_graph_output"))
new_graph = insert_forks(graph)
print(f"Before insert_forks: {graph.model_dump_json()}")
print(f"After insert_forks: {new_graph.model_dump_json()}")
find_autoforks(new_graph)
def test_again():
json_str = """
{
"name": "Stereo Mixer with Volume",
"fs": 48000,
"inputs": [{
"name": "stereo_in",
"channels": 2
}],
"nodes": [
{"op_type": "Mixer", "placement": {"input": [["stereo_in", 0], ["stereo_in", 1]], "name": "Mixer", "thread": 0}},
{"op_type": "VolumeControl", "placement": {"input": [["Mixer", 0]], "name": "Volume", "thread": 0}}
],
"outputs": [{
"name": "stereo_out",
"input": [["Volume", 0], ["Volume", 0]]
}]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_again"))
new_graph = insert_forks(graph)
print(f"Before insert_forks: {graph.model_dump_json()}")
print(f"After insert_forks: {new_graph.model_dump_json()}")
find_autoforks(new_graph)
def test_multiple_inputs_outputs_non_shared():
"""
Test a graph with two inputs (one mono and one stereo) and two outputs (one mono and one stereo)
where no edge is shared between consumers.
"""
json_str = """
{
"name": "Multiple Inputs and Outputs Non-Shared Test",
"fs": 48000,
"inputs": [
{"name": "mono_in", "channels": 1},
{"name": "stereo_in", "channels": 2}
],
"nodes": [
{
"op_type": "VolumeControl",
"config": {},
"placement": {
"input": [["mono_in", 0]],
"name": "VolumeControl_A",
"thread": 0
}
},
{
"op_type": "Mixer",
"config": {},
"placement": {
"input": [["stereo_in", 0], ["stereo_in", 1]],
"name": "Mixer_B",
"thread": 0
}
}
],
"outputs": [
{"name": "mono_out", "input": [["VolumeControl_A", 0]]},
{"name": "stereo_out", "input": [["Mixer_B", 0], ["VolumeControl_A", 0]]}
]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_multiple_inputs_outputs_non_shared"))
new_graph = insert_forks(graph)
print("Test: Multiple Inputs and Outputs Non-Shared Test")
print(f"Before insert_forks: {graph.model_dump_json(indent=2)}")
print(f"After insert_forks: {new_graph.model_dump_json(indent=2)}")
find_autoforks(new_graph)
def test_multiple_inputs_outputs_shared():
"""
Test a graph with two inputs and two outputs where a node produces an output
that is shared by both graph outputs. A Fork should be inserted.
"""
json_str = """
{
"name": "Multiple Inputs and Outputs Shared Test",
"fs": 48000,
"inputs": [
{"name": "input1", "channels": 1},
{"name": "input2", "channels": 1}
],
"nodes": [
{
"op_type": "Mixer",
"config": {},
"placement": {
"input": [["input1", 0], ["input2", 0]],
"name": "Mixer",
"thread": 0
}
}
],
"outputs": [
{"name": "output1", "input": [["Mixer", 0]]},
{"name": "output2", "input": [["Mixer", 0]]}
]
}
"""
graph = Graph.model_validate_json(json_str)
dsp_json = DspJson(ir_version=1, producer_name="test", producer_version="1.0", graph=graph)
a = make_pipeline(dsp_json)
a.draw(Path("test_multiple_inputs_outputs_shared"))
new_graph = insert_forks(graph)
print("Test: Multiple Inputs and Outputs Shared Test")
print(f"Before insert_forks: {graph.model_dump_json(indent=2)}")
print(f"After insert_forks: {new_graph.model_dump_json(indent=2)}")
find_autoforks(new_graph)
def test_all_stages_models():
all_m = all_models()
all_s = all_stages()
failed = False
for s in all_s:
if s.startswith("_") or s in ["DSPThreadStage", "PipelineStage"]:
continue
try:
assert s in all_m, f"Stage {s} not found in all models"
assert s == all_m[s].model_fields["op_type"].default, f"Stage {s} op_type mismatch"
except AssertionError as e:
print(e)
failed = True
continue
if "biquad" in s.lower() or "parametric" in s.lower():
continue
if "parameters" not in all_m[s].model_fields:
continue
try:
if type(all_s[s].set_parameters.__annotations__["parameters"]) is UnionType:
set_params_input = get_args(all_s[s].set_parameters.__annotations__["parameters"])
model_params_type = all_m[s].model_fields["parameters"].default_factory
assert model_params_type in set_params_input, f"Stage {s} set_parameters input type mismatch"
else:
set_params_input = all_s[s].set_parameters.__annotations__["parameters"].__name__
model_params_type = all_m[s].model_fields["parameters"].default_factory.__name__
assert set_params_input == model_params_type, f"Stage {s} set_parameters input type mismatch"
except AssertionError as e:
print(e)
failed = True
for field, value in type(all_m[s].model_fields["parameters"].default_factory()).model_fields.items():
try:
if get_origin(value.annotation) is list:
item_type = get_args(value.annotation)[0]
meta = get_args(item_type)[1].metadata
elif get_origin(value.annotation) is Literal:
continue # Skip Literal types
else:
meta = value.metadata
min = [g.ge for g in meta if isinstance(g, annotated_types.Ge)] or [
g.gt for g in meta if isinstance(g, annotated_types.Gt)]
max = [g.le for g in meta if isinstance(g, annotated_types.Le)] or [
g.lt for g in meta if isinstance(g, annotated_types.Lt)]
assert min, f"Minimum not defined for field {field} in {s}"
if field not in ["position", "delay"]:
assert max, f"Maximum not defined for field {field} in {s}"
assert min[0] < max[0], f"Range not correct for field {field} in {s}"
except AssertionError as e:
print(e)
failed = True
continue
assert not failed, "Some stages failed the test."
if __name__ == "__main__":
test_shared_edge_from_graph_input()

View File

@@ -0,0 +1,189 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test limiter pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_rms_limiter_pipeline():
"""Test creating an RMS limiter pipeline."""
print("Creating RMS limiter pipeline...")
# Create an RMS limiter pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_limiter",
"producer_version": "0.1",
"graph": {
"name": "RMS Limiter",
"fs": 48000,
"nodes": [
{
"op_type": "LimiterRMS",
"parameters": {
"threshold_db": -6.0,
"attack_t": 0.01,
"release_t": 0.1
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoRMSLimiter",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoRMSLimiter", 0], ["StereoRMSLimiter", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our limiter stage
limiter_stage = None
for stage in pipeline.stages:
if stage.name == "limiter_rms":
limiter_stage = stage
break
assert limiter_stage is not None, "Could not find RMS Limiter stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
def test_peak_limiter_pipeline():
"""Test creating a peak limiter pipeline."""
print("Creating peak limiter pipeline...")
# Create a peak limiter pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_limiter",
"producer_version": "0.1",
"graph": {
"name": "Peak Limiter",
"fs": 48000,
"nodes": [
{
"op_type": "LimiterPeak",
"parameters": {
"threshold_db": -3.0,
"attack_t": 0.005,
"release_t": 0.05
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoPeakLimiter",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoPeakLimiter", 0], ["StereoPeakLimiter", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our limiter stage
limiter_stage = None
for stage in pipeline.stages:
if stage.name == "limiter_peak":
limiter_stage = stage
break
assert limiter_stage is not None, "Could not find Peak Limiter stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
def test_hard_peak_limiter_pipeline():
"""Test creating a hard peak limiter pipeline."""
print("Creating hard peak limiter pipeline...")
# Create a hard peak limiter pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_limiter",
"producer_version": "0.1",
"graph": {
"name": "Hard Peak Limiter",
"fs": 48000,
"nodes": [
{
"op_type": "HardLimiterPeak",
"parameters": {
"threshold_db": -1.0,
"attack_t": 0.001,
"release_t": 0.02
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoHardPeakLimiter",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoHardPeakLimiter", 0], ["StereoHardPeakLimiter", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our limiter stage
limiter_stage = None
for stage in pipeline.stages:
if stage.name == "hard_limiter_peak":
limiter_stage = stage
break
assert limiter_stage is not None, "Could not find Hard Peak Limiter stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_rms_limiter_pipeline()
print("\n" + "="*50 + "\n")
test_peak_limiter_pipeline()
print("\n" + "="*50 + "\n")
test_hard_peak_limiter_pipeline()

View File

@@ -0,0 +1,67 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test noise gate pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_noise_gate_pipeline():
"""Test creating a noise gate pipeline."""
print("Creating noise gate pipeline...")
# Create a noise gate pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_noise_gate",
"producer_version": "0.1",
"graph": {
"name": "Noise Gate",
"fs": 48000,
"nodes": [
{
"op_type": "NoiseGate",
"parameters": {
"threshold_db": -40.0,
"attack_t": 0.01,
"release_t": 0.1
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoNoiseGate",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoNoiseGate", 0], ["StereoNoiseGate", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our noise gate stage
gate_stage = None
for stage in pipeline.stages:
if stage.name == "noise_gate": # This matches the config file name
gate_stage = stage
break
assert gate_stage is not None, "Could not find Noise Gate stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_noise_gate_pipeline()

View File

@@ -0,0 +1,68 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test noise suppressor expander pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_noise_suppressor_expander_pipeline():
"""Test creating a noise suppressor expander pipeline."""
print("Creating noise suppressor expander pipeline...")
# Create a noise suppressor expander pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_noise_suppressor",
"producer_version": "0.1",
"graph": {
"name": "Noise Suppressor Expander",
"fs": 48000,
"nodes": [
{
"op_type": "NoiseSuppressorExpander",
"parameters": {
"ratio": 3.0,
"threshold_db": -35.0,
"attack_t": 0.005,
"release_t": 0.120
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoNoiseSuppressor",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoNoiseSuppressor", 0], ["StereoNoiseSuppressor", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our noise suppressor stage
suppressor_stage = None
for stage in pipeline.stages:
if stage.name == "noise_suppressor_expander":
suppressor_stage = stage
break
assert suppressor_stage is not None, "Could not find Noise Suppressor Expander stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_noise_suppressor_expander_pipeline()

View File

@@ -0,0 +1,75 @@
# Copyright 2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
"""Test reverb pipeline creation."""
from audio_dsp.design.parse_json import DspJson, make_pipeline, pipeline_to_dspjson
def test_plate_reverb_pipeline():
"""Test creating a plate reverb pipeline."""
print("Creating plate reverb pipeline...")
# Create a plate reverb pipeline JSON
pipeline_json = {
"ir_version": 1,
"producer_name": "test_reverb",
"producer_version": "0.1",
"graph": {
"name": "Plate Reverb",
"fs": 48000,
"nodes": [
{
"op_type": "ReverbPlateStereo",
"config": {
"max_predelay": 30.0
},
"parameters": {
"predelay": 15.0,
"width": 0.8,
"damping": 0.4,
"decay": 0.6,
"early_diffusion": 0.7,
"late_diffusion": 0.6,
"bandwidth": 0.9,
"wet_dry_mix": 0.4
},
"placement": {
"input": [["inputs", 0], ["inputs", 1]],
"name": "StereoPlateReverb",
"thread": 0
}
}
],
"inputs": [
{
"name": "inputs",
"channels": 2
}
],
"outputs": [
{
"name": "outputs",
"input": [["StereoPlateReverb", 0], ["StereoPlateReverb", 1]]
}
]
}
}
dsp_json = DspJson(**pipeline_json)
pipeline = make_pipeline(dsp_json)
# Find our reverb stage
reverb_stage = None
for stage in pipeline.stages:
if stage.name == "reverb_plate":
reverb_stage = stage
break
assert reverb_stage is not None, "Could not find Plate Reverb stage in pipeline"
new_json = pipeline_to_dspjson(pipeline)
assert dsp_json.graph == new_json.graph, "Pipeline JSON does not match original"
if __name__ == "__main__":
test_plate_reverb_pipeline()