147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
|
|
# Copyright 2025 XMOS LIMITED.
|
||
|
|
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from audio_dsp.design.pipeline import Pipeline, generate_dsp_main
|
||
|
|
from audio_dsp.stages import *
|
||
|
|
|
||
|
|
|
||
|
|
def test_bad_pipelines():
|
||
|
|
with pytest.raises(RuntimeError) as excinfo:
|
||
|
|
p, inputs = Pipeline.begin(2, fs=48000)
|
||
|
|
|
||
|
|
# inputs[1] is not used on thread 0
|
||
|
|
x1 = p.stage(Biquad, inputs[0], 'bq1')
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
|
||
|
|
# inputs[1] first used on thread 1
|
||
|
|
x = p.stage(Biquad, x1 + inputs[1], 'bq2')
|
||
|
|
|
||
|
|
p.set_outputs(x)
|
||
|
|
|
||
|
|
assert "must cross the same number of threads" in str(excinfo.value)
|
||
|
|
|
||
|
|
|
||
|
|
def test_good_pipelines():
|
||
|
|
|
||
|
|
p, inputs = Pipeline.begin(2, fs=48000)
|
||
|
|
|
||
|
|
# both inputs are not used on this thread
|
||
|
|
x1 = p.stage(Biquad, inputs[0], 'bq1')
|
||
|
|
x2 = p.stage(Bypass, inputs[1], 'by1')
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
|
||
|
|
x = p.stage(Biquad, x1 + x2, 'bq2')
|
||
|
|
|
||
|
|
p.set_outputs(x)
|
||
|
|
|
||
|
|
|
||
|
|
def test_parallel_pipelines():
|
||
|
|
|
||
|
|
p, inputs = Pipeline.begin(2, fs=48000)
|
||
|
|
|
||
|
|
x1 = p.stage(Biquad, inputs[0], 'bq1')
|
||
|
|
p.next_thread()
|
||
|
|
|
||
|
|
x2 = p.stage(Bypass, inputs[1], 'by1')
|
||
|
|
p.next_thread()
|
||
|
|
|
||
|
|
# x1 and x2 have both crossed 1 thread already
|
||
|
|
x = p.stage(Biquad, x1 + x2, 'bq2')
|
||
|
|
|
||
|
|
p.set_outputs(x)
|
||
|
|
|
||
|
|
|
||
|
|
def test_good_complex():
|
||
|
|
|
||
|
|
# 4 inputs
|
||
|
|
p, i = Pipeline.begin(4, fs=48000)
|
||
|
|
|
||
|
|
mic1 = p.stage(Fork, i[2], count=3).forks
|
||
|
|
mic2 = p.stage(Fork, i[3], count=3).forks
|
||
|
|
|
||
|
|
p.stage(EnvelopeDetectorRMS, mic1[0], "edr1")
|
||
|
|
p.stage(EnvelopeDetectorRMS, mic2[0], "edr2")
|
||
|
|
|
||
|
|
mic_mono = p.stage(Adder, mic1[1] + mic2[1], "adder1")
|
||
|
|
mic_mono = p.stage(Fork, mic_mono, count=3).forks
|
||
|
|
mic_dnr = p.stage(NoiseSuppressorExpander, mic_mono[0], "ns")
|
||
|
|
peq1 = p.stage(CascadedBiquads, mic_dnr, "peq1")
|
||
|
|
sw_1 = p.stage(SwitchStereo, mic_mono[1] + mic_mono[2] + mic1[2] + mic2[2], "sw_1")
|
||
|
|
i_bypass = p.stage(Bypass, i[:2])
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
mic_peq = p.stage(Fork, peq1, count=3).forks
|
||
|
|
peq2 = p.stage(CascadedBiquads, mic_peq[2], "peq2")
|
||
|
|
mic_rv = p.stage(Fork, peq2).forks
|
||
|
|
i_bypass = p.stage(Bypass, i_bypass)
|
||
|
|
sw_1 = p.stage(Bypass, sw_1)
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
mic_rv = p.stage(ReverbPlateStereo, mic_rv[0] + mic_rv[1], "reverb")
|
||
|
|
cfs1 = p.stage(CrossfaderStereo, mic_peq[0] + mic_peq[1] + mic_rv[0] + mic_rv[1], "cfs1")
|
||
|
|
rv_sw = p.stage(SwitchStereo, cfs1 + sw_1, "reverb_sw")
|
||
|
|
vol1 = p.stage(VolumeControl, rv_sw[0], "vol1")
|
||
|
|
vol2 = p.stage(VolumeControl, rv_sw[1], "vol2")
|
||
|
|
i_bypass = p.stage(Bypass, i_bypass)
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
usb_play, usb_mon = p.stage(Fork, i_bypass).forks
|
||
|
|
usb_play = p.stage(VolumeControl, usb_play, "vol3")
|
||
|
|
mic_play, mic_mon = p.stage(Fork, vol1 + vol2).forks
|
||
|
|
usb0 = p.stage(Adder, usb_play[0] + mic_play[0], "adder2")
|
||
|
|
usb1 = p.stage(Adder, usb_play[1] + mic_play[1], "adder3")
|
||
|
|
cfs2 = p.stage(CrossfaderStereo, usb_mon[0] + usb_mon[1] + mic_mon[0] + mic_mon[1], "cfs2")
|
||
|
|
mon0, mon1 = p.stage(VolumeControl, cfs2, "vol4")
|
||
|
|
|
||
|
|
p.set_outputs(usb0 + usb1 + mon0 + mon1)
|
||
|
|
|
||
|
|
|
||
|
|
def test_bad_complex():
|
||
|
|
with pytest.raises(RuntimeError) as excinfo:
|
||
|
|
# 4 inputs
|
||
|
|
p, i = Pipeline.begin(4, fs=48000)
|
||
|
|
|
||
|
|
mic1 = p.stage(Fork, i[2], count=3).forks
|
||
|
|
mic2 = p.stage(Fork, i[3], count=3).forks
|
||
|
|
|
||
|
|
p.stage(EnvelopeDetectorRMS, mic1[0], "edr1")
|
||
|
|
p.stage(EnvelopeDetectorRMS, mic2[0], "edr2")
|
||
|
|
|
||
|
|
mic_mono = p.stage(Adder, mic1[1] + mic2[1], "adder1")
|
||
|
|
mic_mono = p.stage(Fork, mic_mono, count=3).forks
|
||
|
|
mic_dnr = p.stage(NoiseSuppressorExpander, mic_mono[0], "ns")
|
||
|
|
peq1 = p.stage(CascadedBiquads, mic_dnr, "peq1")
|
||
|
|
sw_1 = p.stage(SwitchStereo, mic_mono[1] + mic_mono[2] + mic1[2] + mic2[2], "sw_1")
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
mic_peq = p.stage(Fork, peq1, count=3).forks
|
||
|
|
peq2 = p.stage(CascadedBiquads, mic_peq[2], "peq2")
|
||
|
|
mic_rv = p.stage(Fork, peq2).forks
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
mic_rv = p.stage(ReverbPlateStereo, mic_rv[0] + mic_rv[1], "reverb")
|
||
|
|
cfs1 = p.stage(CrossfaderStereo, mic_peq[0] + mic_peq[1] + mic_rv[0] + mic_rv[1], "cfs1")
|
||
|
|
rv_sw = p.stage(SwitchStereo, cfs1 + sw_1, "reverb_sw")
|
||
|
|
vol1 = p.stage(VolumeControl, rv_sw[0], "vol1")
|
||
|
|
vol2 = p.stage(VolumeControl, rv_sw[1], "vol2")
|
||
|
|
|
||
|
|
p.next_thread()
|
||
|
|
usb_play, usb_mon = p.stage(Fork, i[:2]).forks
|
||
|
|
usb_play = p.stage(VolumeControl, usb_play, "vol3")
|
||
|
|
mic_play, mic_mon = p.stage(Fork, vol1 + vol2).forks
|
||
|
|
usb0 = p.stage(Adder, usb_play[0] + mic_play[0], "adder2")
|
||
|
|
usb1 = p.stage(Adder, usb_play[1] + mic_play[1], "adder3")
|
||
|
|
cfs2 = p.stage(CrossfaderStereo, usb_mon[0] + usb_mon[1] + mic_mon[0] + mic_mon[1], "cfs2")
|
||
|
|
mon0, mon1 = p.stage(VolumeControl, cfs2, "vol4")
|
||
|
|
|
||
|
|
p.set_outputs(usb0 + usb1 + mon0 + mon1)
|
||
|
|
|
||
|
|
assert "must cross the same number of threads" in str(excinfo.value)
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
# test_bad_pipelines()
|
||
|
|
test_bad_complex()
|
||
|
|
test_good_complex()
|