feat: add script module for note-to-command mapping with FIFO support
This commit is contained in:
committed by
Loic Coenen (aider)
parent
16a800209f
commit
f776b8a361
301
docs/manual_test_protocols.md
Normal file
301
docs/manual_test_protocols.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# Manual Test Protocols – Guitar / Audio Looper
|
||||
|
||||
This document provides step‑by‑step manual testing procedures using a real guitar (or any line‑level 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 interface’s input
|
||||
- `qjackctl` (optional, for visual wiring) or knowledge of `jack_connect` commands
|
||||
|
||||
## Test 1 – Basic Audio Pass‑Through (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 interface’s capture port to the looper’s input:
|
||||
```sh
|
||||
jack_connect system:capture_1 looper:input
|
||||
```
|
||||
|
||||
4. Connect the looper’s 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 looper’s 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 2‑second 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 control‑key (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 16‑bit 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) control‑key + 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 looper’s 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 one‑second 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 engine’s 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 auto‑connection:
|
||||
- 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
|
||||
Reference in New Issue
Block a user