diff --git a/.gitea/workflows/validate.yaml b/.gitea/workflows/validate.yaml new file mode 100644 index 0000000..73cc524 --- /dev/null +++ b/.gitea/workflows/validate.yaml @@ -0,0 +1,21 @@ +name: Validate and Test +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + validate-patches: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install dependencies + run: | + pip install conftest + - name: Validate all .pd patches + run: | + python3 pd-validator/validate.py pd/*.pd diff --git a/justfile b/justfile index d6fd668..23019f7 100644 --- a/justfile +++ b/justfile @@ -341,6 +341,10 @@ kconfig: validate: python3 pd-validator/validate.py pd/*.pd +# CI: run full validation suite (same as validate but used by Gitea Actions) +ci: + python3 pd-validator/validate.py pd/*.pd + # Install udev rules for hotplug role changes install-udev-rules: @ssh {{SSH_OPTS}} "{{BOARD_USER}}@{{BOARD_HOST}}" "\ diff --git a/pd/mpk-mini-sim.pd b/pd/mpk-mini-sim.pd index d05950a..41afddb 100644 --- a/pd/mpk-mini-sim.pd +++ b/pd/mpk-mini-sim.pd @@ -1,4 +1,5 @@ #N canvas 0 0 950 490 10; +# DO NOT EDIT — generated by scripts/gen_mpk_sim.py; #X obj 10 10 loadbang ; #X msg 10 30 ;; #X msg 10 50 connect 192.168.9.167 8081;; diff --git a/pd/synth.pd b/pd/synth.pd index 01843f4..98f78a8 100644 --- a/pd/synth.pd +++ b/pd/synth.pd @@ -1,33 +1,65 @@ -#N canvas 0 0 500 600 10; -#X obj 40 20 midi-in; -#X obj 40 80 stripnote; -#X obj 40 140 mtof; -#X obj 40 180 osc~; -#X obj 160 180 osc~; -#X obj 40 220 *~ 1; -#X obj 160 220 *~ 1; -#X obj 100 260 +~; -#X obj 100 300 lop~ 10000; -#X obj 40 340 env~ 2 10 1 40; -#X obj 100 380 *~; -#X obj 100 440 audio-out; -#X text 200 180 osc~ 1 (main); -#X text 320 180 osc~ 2 (+7ct); -#X text 155 220 mix; -#X text 155 300 lop~; -#X text 155 340 envelope; -#X text 155 380 amp; +#N canvas 0 0 900 550 10; +# DO NOT EDIT — generated by scripts/gen_synth_pd.py; +#X obj 20 20 notein; +#X obj 80 50 pack 0 0; +#X obj 80 80 unpack 0 0; +#X obj 20 110 s midi-pitch; +#X obj 80 110 s midi-vel; +#X obj 400 20 netreceive -u 8081; +#X obj 400 60 route midi; +#X obj 400 100 route 144 128; +#X obj 400 140 unpack 0 0; +#X obj 480 140 unpack 0 0; +#X obj 400 180 s midi-pitch; +#X obj 480 180 s midi-vel; +#X obj 20 160 r midi-vel; +#X obj 20 190 r midi-pitch; +#X obj 20 220 stripnote; +#X obj 20 260 mtof; +#X obj 20 290 env~ 50 200 0.7 400; +#X obj 140 260 osc~; +#X obj 140 290 *~ 0.3; +#X obj 220 260 osc~; +#X obj 220 290 *~ 0.3; +#X obj 180 330 +~; +#X obj 180 360 *~ 0.5; +#X obj 180 400 lop~; +#X obj 180 460 *~; +#X obj 320 460 *~; +#X obj 250 530 dac~; +#X obj 350 330 hsl 128 15 100 18000 10000 0 0 0 empty Cutoff -2 -8 0 10 -262144 -1 -1 7200 1; +#X obj 350 430 hsl 128 15 0 100 40 0 0 0 empty Volume -2 -8 0 10 -262144 -1 -1 7200 1; +#X obj 350 460 / 100; #X connect 0 0 1 0; #X connect 0 1 1 1; #X connect 1 0 2 0; -#X connect 1 1 9 1; #X connect 2 0 3 0; -#X connect 2 0 4 0; -#X connect 3 0 5 0; -#X connect 4 0 6 0; -#X connect 5 0 7 0; -#X connect 6 0 7 1; +#X connect 2 1 4 0; +#X connect 5 0 6 0; +#X connect 6 0 7 0; #X connect 7 0 8 0; +#X connect 7 1 9 0; #X connect 8 0 10 0; -#X connect 9 0 10 1; -#X connect 10 0 11 0; +#X connect 8 1 11 0; +#X connect 9 0 10 0; +#X connect 9 1 11 0; +#X connect 12 0 14 1; +#X connect 13 0 14 0; +#X connect 14 0 15 0; +#X connect 15 0 17 0; +#X connect 15 0 19 0; +#X connect 17 0 18 0; +#X connect 19 0 20 0; +#X connect 18 0 21 0; +#X connect 20 0 21 1; +#X connect 21 0 22 0; +#X connect 22 0 23 0; +#X connect 23 0 24 0; +#X connect 14 0 16 0; +#X connect 16 0 24 1; +#X connect 24 0 25 0; +#X connect 27 0 23 1; +#X connect 28 0 29 0; +#X connect 29 0 25 1; +#X connect 25 0 26 0; +#X connect 25 0 26 1; diff --git a/scripts/gen_mpk_sim.py b/scripts/gen_mpk_sim.py index a4b63f0..04a1c2e 100644 --- a/scripts/gen_mpk_sim.py +++ b/scripts/gen_mpk_sim.py @@ -93,7 +93,10 @@ def gen(): connect(c, msg, 0, ns, 0) # ── Write (Pd format: #X obj for objects, #X msg/text/floatatom/symbol for others) ── - lines = ["#N canvas 0 0 950 490 10;"] + lines = [ + "#N canvas 0 0 950 490 10;", + "# DO NOT EDIT — generated by scripts/gen_mpk_sim.py;", + ] obj_types = {"bng","t","del","netsend","loadbang","hsl","comment"} for x, y, *args in o: etype = args[0] diff --git a/scripts/gen_synth_pd.py b/scripts/gen_synth_pd.py index bb71397..d3170af 100644 --- a/scripts/gen_synth_pd.py +++ b/scripts/gen_synth_pd.py @@ -95,7 +95,10 @@ def gen(): connect(c, panscale, 0, dac, 1) # ── Write ── - lines = ["#N canvas 0 0 900 550 10;"] + lines = [ + "#N canvas 0 0 900 550 10;", + "# DO NOT EDIT — generated by scripts/gen_synth_pd.py;", + ] for x, y, t, *args in o: joined = " ".join(str(a) for a in args) lines.append(f"#X obj {x} {y} {joined};") diff --git a/scripts/start-synth.sh b/scripts/start-synth.sh index bb15ccf..694ea10 100755 --- a/scripts/start-synth.sh +++ b/scripts/start-synth.sh @@ -30,6 +30,14 @@ if ! command -v pd &>/dev/null; then die "Pure Data not found. Install: apt install puredata" fi +MIN_PD_VERSION="0.54.0" +PD_VERSION=$(pd --version 2>&1 | grep -oP '[\d]+\.[\d]+-?[\d]*' | head -1 || echo "0.0.0") +if printf '%s\n' "$MIN_PD_VERSION" "$PD_VERSION" | sort -V | head -1 | grep -q "^$MIN_PD_VERSION$"; then + info "Pd version $PD_VERSION (>= $MIN_PD_VERSION)" +else + die "Pd version $PD_VERSION is too old. Need >= $MIN_PD_VERSION" +fi + if [ ! -f "$PATCH" ]; then die "Patch not found: $PATCH" fi diff --git a/system/pd-synth-auto.service b/system/pd-synth-auto.service index 0ddc962..0703d23 100644 --- a/system/pd-synth-auto.service +++ b/system/pd-synth-auto.service @@ -1,5 +1,6 @@ [Unit] Description=Pure Data Synth (auto-detect: gadget or host) +ConditionPathExists=/home/arduino/uno-q-audio-synth/pd/synth.pd Documentation=https://github.com/arduino/uno-q-audio-synth After=usb-role-detect.service Wants=usb-role-detect.service diff --git a/system/pd-synth-i2s.service b/system/pd-synth-i2s.service index 36d143a..2e558ee 100644 --- a/system/pd-synth-i2s.service +++ b/system/pd-synth-i2s.service @@ -1,5 +1,6 @@ [Unit] Description=Pure Data I2S DAC Synth (standalone) +ConditionPathExists=/home/arduino/uno-q-audio-synth/pd/synth.pd After=network.target sound.target Wants=usb-gadget-audio.service diff --git a/system/pd-synth.service b/system/pd-synth.service index 9f04504..6559d39 100644 --- a/system/pd-synth.service +++ b/system/pd-synth.service @@ -1,5 +1,6 @@ [Unit] Description=Pure Data USB Audio Synth (Gadget Mode) +ConditionPathExists=/home/arduino/uno-q-audio-synth/pd/synth.pd After=usb-gadget-audio.service midi-bridge.service Wants=usb-gadget-audio.service midi-bridge.service