feat: add script module for note-to-command mapping with FIFO support

This commit is contained in:
Loic Coenen
2026-05-18 21:12:29 +00:00
committed by Loic Coenen (aider)
parent 16a800209f
commit f776b8a361
7 changed files with 638 additions and 5 deletions

View File

@@ -0,0 +1,301 @@
# Manual Test Protocols Guitar / Audio Looper
This document provides stepbystep manual testing procedures using a real guitar (or any linelevel mono audio source) with the `looper` engine.
## Prerequisites
- A running JACK server (e.g. `jackd -d alsa -r 48000 -p 256`)
- The looper binary compiled (`cd engine && make`)
- An audio interface recognised by ALSA (or PulseAudio JACK bridge)
- A guitar connected to your interfaces input
- `qjackctl` (optional, for visual wiring) or knowledge of `jack_connect` commands
## Test 1 Basic Audio PassThrough (Guitar Monitor)
1. Start the looper in a terminal:
```sh
./looper
```
2. Launch `qjackctl` (or use `jack_connect` from shell) to view available ports.
3. Connect your interfaces capture port to the loopers input:
```sh
jack_connect system:capture_1 looper:input
```
4. Connect the loopers output to your interface playback ports:
```sh
jack_connect looper:output system:playback_1
```
5. Pluck a few strings you should hear your guitar coming through the looper immediately (channel 0 is in **IDLE** state, which passes input straight to output).
6. To stop, press `Ctrl+C` in the looper terminal.
**Expected result**: You hear your guitar with no latency issues (depending on JACK buffer size). If you hear nothing, check port names with `jack_lsp`.
---
## Test 2 Record a Short Loop (MIDI Control)
### 2a Using MIDI keyboard (or a MIDI controller)
1. Start the looper as above.
2. Connect your MIDI controller to the loopers control port:
```sh
jack_connect <controller>:midi_out looper:control
```
Replace `<controller>` with the actual MIDI port name (use `jack_lsp` to find it).
3. Send **note 1** (velocity 127) to switch channel 0 into **RECORD** state.
- On most keyboards, this is the C# key two octaves above middle C (MIDI note 1). Press it once.
4. Play your guitar for about 2 seconds. The looper is recording.
5. Press **note 1** again. The looper transitions to **LOOPING** state. The recorded 2second phrase starts playing back repeatedly.
6. You should hear the loop repeating. Pluck strings while the loop plays the IDLE monitoring is still active on channel 0 (the loop is mixed with the live input).
7. Press **note 1** a third time to **PAUSE** the loop; press again to resume.
### 2b Using FIFO commands (if you have no MIDI keyboard)
1. Start the looper.
2. Open a second terminal.
3. Send:
```sh
echo "record 0" > /tmp/looper_cmd
```
4. Play guitar for a few seconds.
5. Send again:
```sh
echo "record 0" > /tmp/looper_cmd
```
6. Loop should start repeating. Test pause by sending again (second command cycles IDLE→RECORD→LOOPING→PAUSED→…).
**Expected result**: The loop repeats seamlessly. If you hold a chord while the loop is playing, the live input still passes through.
---
## Test 3 Save the Loop to a WAV File
1. Ensure a loop is playing (LOOPING state) on channel 0.
2. Send the save command:
```sh
echo "save" > /tmp/looper_cmd
```
or (MIDI) press controlkey (note 64) + note 71.
3. After a brief delay (the loop buffer is written synchronously), a file `save.wav` appears in the engine directory.
4. Check the file size is > 44 bytes and play it with any media player:
```sh
aplay save.wav
```
**Expected result**: The saved file contains exactly what the looper was playing (your recorded guitar phrase). The RMS of the playback should be similar to the live signal.
---
## Test 4 Load a WAV File into a Channel
1. Put a mono 16bit WAV file named `loop.wav` in the engine directory (e.g. a short drum loop or a guitar riff).
2. Start the looper and send the load command:
```sh
echo "load" > /tmp/looper_cmd
```
or (MIDI) controlkey + note 70.
3. The loaded audio begins playing immediately on channel 0 (state = LOOPING).
4. Verify you hear the loop repeating.
**Expected result**: The WAV is loaded and plays correctly. The loop length matches the duration of the file (up to `LOOP_BUF_SIZE` frames, default 8 seconds).
---
## Test 5 Dynamic Channel Creation and Binding
1. Start the looper.
2. Add a second audio channel:
```sh
echo "add" > /tmp/looper_cmd
```
3. Check that new ports appear:
```sh
jack_lsp | grep channel1
```
4. Bind the client to channel 1:
```sh
echo "bind 1" > /tmp/looper_cmd
```
5. Connect your guitar to both channels for stereo testing? Not necessary. But you can route differently.
6. Now when you send `record 1`, the bind ensures the command affects channel 1 instead of channel 0.
7. Repeat the record/loop process on channel 1, while channel 0 continues its own loop.
**Expected result**: Two independent loops can play simultaneously without interfering.
---
## Test 6 Scene Switching
1. Make sure a loop is playing on channel 0.
2. Add a second scene to channel 0:
```sh
echo "scene_add" > /tmp/looper_cmd
```
(Only adds scene if `MAX_SCENES` not exceeded, default 4.)
3. Switch to the new scene:
```sh
echo "scene_next" > /tmp/looper_cmd
```
The playback stops because the new scene is IDLE.
4. Record a different phrase on the new scene (send `record 0`).
5. Switch back to the first scene (`scene_prev`) the original loop resumes.
**Expected result**: Different independent loops in separate scenes; switching scenes does not lose previously recorded loops.
---
## Test 7 MIDI Clock Sync
If you have an external MIDI clock source (e.g. a drum machine or DAW sending MIDI start/stop):
1. Connect the clock source to `looper:clock` port.
2. Send MIDI Start (`0xFA`). The loopers current scene (if IDLE) transitions to RECORD.
3. Send MIDI Stop (`0xFC`). The current scene goes IDLE (loop stops).
4. Send MIDI Continue (`0xFB`) while the scene is PAUSED it resumes LOOPING.
**Expected result**: Transport commands control looper state reliably.
---
## Test 8 Edge Cases
### 8a Rapid toggling
Cycle the RECORD/LOOPING/PAUSED states many times in quick succession (send `record 0` every 200 ms for 5 seconds). The looper should not crash or produce glitches.
### 8b Remove channel while playing
Add a channel, start a loop on it, then remove the channel with:
```sh
echo "remove" > /tmp/looper_cmd
```
The loop should stop gracefully after a onesecond grace period; the client should not crash.
### 8c Save empty loop
Attempt to `save` when the current scene is not LOOPING or loop_count == 0. No file should be created. The engine should log a message to stderr.
---
## Environment Variables
- `LOOPER_CMD_FIFO` (overrides `/tmp/looper_cmd`) useful for running multiple instances for testing.
- `JACK_DEFAULT_SERVER` (JACK environment) can be set to run a separate JACK server.
---
## Troubleshooting
- **No audio after connection**: Ensure `jack_lsp` shows both source and destination ports, and that the looper is the only client using those ports.
- **MIDI not recognised**: Verify that `midi_control_port` is created (`looper:control`). Use `jack_midi_dump` to see if note events arrive.
- **“save.wav not created“** after save command: The scene must be in LOOPING state and `loop_count` > 0. Check the engines terminal output for error messages.
---
## Carla Plugin Management Manual Tests
### Test C1 Load a plugin via colon command
1. Ensure the looper engine is running, and the client (`looper-client`) is also running.
2. In the client, enter colon mode (`:`) and type:
```
from looper:output
```
then press Enter.
3. Enter colon mode again and type:
```
to system:playback_1
```
4. Load a test LV2 plugin (e.g., /usr/lib/lv2/amsynth.lv2/amsynth.so):
```
addplugin /usr/lib/lv2/amsynth.lv2/amsynth.so
```
5. The plugin should be loaded into Carla and its JACK ports are automatically connected (if `from` and `to` were set). You should see the plugin appear in the rack view when you press `R`.
6. Play some audio through the looper it should be processed by the plugin.
### Test C2 Toggle bypass
1. In rack view (`R`), select the plugin using `j`/`k`.
2. Press `b` or `B` to toggle bypass.
3. The effect should stop processing (bypass mode active); pressing again reactivates.
### Test C3 Disconnect a plugin
1. In rack view, select the plugin.
2. Press `x` or `X` to disconnect all its JACK connections.
3. The plugin should no longer be connected to any looper ports; the audio should pass through unaffected.
### Test C4 Unload a plugin
1. In rack view, select the plugin.
2. Press `d` or `D` to unload (remove) the plugin.
3. The plugin disappears from the rack list.
### Test C5 Manual connection using colon commands
1. Set `from` and `to` ports as in Test C1.
2. Load a plugin without autoconnection:
- Do **not** set `from`/`to`, or set them after loading.
- Use `addplugin` with only a path.
3. Manually connect ports in colon mode:
```
connect looper:output amsynth:in
```
The connection should be established.
4. Verify in `jack_lsp` that the ports are connected.
### Test C6 Disconnect using colon commands
1. After a manual connection, disconnect using:
```
disconnect looper:output amsynth:in
```
2. The ports should be disconnected.
---
*Last updated:* 18 May 2026