Skip to content

Commit 971860d

Browse files
Update CI and target configuration files
1 parent b4381df commit 971860d

20 files changed

Lines changed: 1177 additions & 778 deletions

.github/workflows/ci.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ jobs:
2525
sudo apt-get install -y --no-install-recommends \
2626
sox libsox-fmt-all libsndfile1 gnuplot ffmpeg
2727
28-
- name: Install Python dependencies
29-
run: pip install -r requirements.txt
28+
- name: Install pre-commit
29+
run: pip install pre-commit
3030

31-
- name: Check formatting
32-
run: bash scripts/check-format.sh
31+
32+
- name: Run pre-commit formatting checks
33+
run: pre-commit run --all-files
3334

3435
- name: Smoke build (one bank per target)
3536
run: bash scripts/ci-smoke.sh

AGENT.md

Lines changed: 98 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -63,32 +63,38 @@ AKWF-FREE/
6363
│ ├── AKWF_cello/
6464
│ └── ... # ~4358 waveforms across 65 banks
6565
66-
├── targets/ # Target definitions (one file per target)
67-
│ ├── akai-mpc.yml
68-
│ ├── surge-1024.yml
69-
│ ├── teensy-c.yml
70-
│ └── ...
66+
├── targets/ # Target definitions
67+
│ ├── README.md # Target definition schema & docs
68+
│ ├── akwf-standard.yml # Matrix: all power-of-2 × bit depth × SR combos
69+
│ ├── akai-mpc.yml # Akai MPC sampler format
70+
│ ├── surge-1024.yml # Surge synth 1024-sample wavetables
71+
│ ├── surge-512.yml # Surge synth 512-sample wavetables
72+
│ ├── teensy-c.yml # Teensy C header arrays
73+
│ ├── web-json.yml # JSON for web players
74+
│ └── png-plots.yml # PNG waveform visualizations
7175
7276
├── scripts/ # Build/conversion tooling
73-
│ ├── convert.py # Main conversion engine (WAV, JSON, C, PNG)
74-
│ ├── validate.py # Single-directory WAV validation
77+
│ ├── convert.py # Main conversion engine
78+
│ ├── targets.py # Shared target-YAML loading/matrix expansion
79+
│ ├── validate.py # Output quality validation
7580
│ ├── validate-built.py # Validate all dist/ outputs
7681
│ ├── package.py # Zip packaging for releases
7782
│ ├── check-format.sh # Line-ending and config checks
7883
│ └── ci-smoke.sh # CI one-bank-per-target build
7984
85+
├── Makefile # check, smoke, build, validate, package, clean
8086
├── requirements.txt # Pinned Python dependencies
81-
├── Makefile # check, smoke, build, validate, package
8287
├── .github/workflows/ci.yml # GitHub Actions CI
8388
8489
├── dist/ # Built outputs (gitignored, generated)
90+
│ ├── AKWF--64smp-16bit-44_1k/
91+
│ ├── AKWF--1024smp-32bit-48k/
8592
│ ├── AKWF--Akai-MPC/
86-
│ ├── AKWF--Surge/
8793
│ └── ...
8894
89-
├── releases/ # Zip archives (gitignored, for GitHub releases)
95+
├── releases/ # Zip archives (gitignored)
9096
91-
├── AKWF--*/ # Legacy pre-built outputs (being migrated to dist/)
97+
├── AKWF--*/ # Legacy pre-built outputs (to be migrated to dist/)
9298
├── AKWF-c/ # Legacy C headers (regenerate via targets/teensy-c.yml)
9399
├── AKWF-js/ # Legacy JSON (regenerate via targets/web-json.yml)
94100
└── AKWF-png/ # Legacy PNG plots (regenerate via targets/png-plots.yml)
@@ -105,88 +111,120 @@ make check # format checks + list t
105111
make smoke # one bank per target (CI)
106112
make build TARGET=targets/akai-mpc.yml BANK=AKWF_0001 # single target
107113
make build # full library (slow)
108-
make validate BANK=AKWF_0001
114+
make validate # validate built outputs
109115
make package # zip to releases/
116+
make clean # remove dist/ and releases/
110117
```
111118

112119
Or directly:
113120

114121
```bash
115-
python3 scripts/convert.py --target targets/akai-mpc.yml
122+
python3 scripts/convert.py --target targets/akwf-standard.yml --bank AKWF_0001
116123
python3 scripts/convert.py --all
117-
python3 scripts/convert.py --target targets/akai-mpc.yml --dry-run
124+
python3 scripts/convert.py --list
118125
```
119126

120-
**Dependencies:** `pip install -r requirements.txt` plus system tools SoX, gnuplot, FFmpeg.
121-
The dev container installs everything automatically.
127+
**Dependencies:** `pip install -r requirements.txt`
122128

123129
### Adding a New Target
124130

125-
1. Create a YAML definition in `targets/` (see existing targets for schema)
126-
2. The definition specifies: output format, sample length, bit depth, sample rate, normalization, naming conventions, folder structure
127-
3. Run the conversion script to generate the output in `dist/`
128-
129-
### Target Definition Schema
131+
**Single target:** Create a YAML file in `targets/`:
130132

131133
```yaml
132134
name: "Akai MPC"
133135
output_dir: "AKWF--Akai-MPC"
134136
description: "Format compatible with Akai MPC samplers"
135137

136138
audio:
137-
format: wav
138-
bit_depth: 16
139+
format: wav # wav | raw | c-header | json | png | custom
140+
bit_depth: 16 # 8 | 16 | 24 | 32 (32 = float)
139141
sample_rate: 44100
140-
channels: mono
141-
length_samples: 600 # target cycle length
142-
142+
channels: mono # mono | stereo (stereo duplicates the mono signal to both channels)
143+
length_samples: 600
144+
145+
resample:
146+
method: none # none | sinc | linear | sox-speed | sox-rate
147+
quality: high # low | medium | high | very-high — only affects sox-rate
148+
143149
normalize:
144150
enabled: true
145-
target_dbfs: 0.0 # peak normalize to 0 dBFS
146-
151+
method: peak # peak | gain
152+
target_dbfs: -0.1
153+
gain_db: -6.4 # used when method is gain (legacy SoX `gain` parity, e.g. Surge targets)
154+
remove_dc: true
155+
147156
naming:
148-
extension: ".WAV" # uppercase for MPC compatibility
149-
filename_case: preserve # preserve original filename case
157+
extension: ".WAV"
158+
filename_case: preserve # preserve | upper | lower
159+
extension_case: upper # lower | upper
150160

151161
structure:
152-
mirror_source: true # replicate src/AKWF bank/file structure
162+
mirror_source: true
153163

154164
metadata:
155-
add_loop_points: true # WAV smpl chunk: loop 0..length-1
165+
add_loop_points: true
166+
167+
packaging:
168+
zip: true
169+
zip_name: "AKWF--Akai-MPC.zip"
170+
171+
skip:
172+
banks: ["AKWF_stereo"]
156173
```
157174
158-
### Normalization
175+
**Matrix target:** Use `type: matrix` for combinations:
159176

160-
- All targets should specify normalization settings
161-
- Peak normalization to 0 dBFS means the loudest sample hits exactly ±1.0
162-
- Some targets may want headroom (e.g., -1 dBFS, -3 dBFS)
163-
- Normalization is per-file (each waveform independently)
177+
```yaml
178+
type: matrix
164179
165-
### Resampling (Changing Cycle Length)
180+
matrix:
181+
length_samples: [64, 128, 256, 512, 1024, 2048]
182+
bit_depth: [16, 32]
183+
sample_rate: [44100, 48000]
166184
167-
When converting 600 samples to a different length (e.g., 1024):
168-
- Use high-quality sinc interpolation (band-limited)
169-
- Single-cycle waveforms MUST remain seamlessly loopable
170-
- The resampled waveform must have the same spectral content
171-
- Power-of-2 lengths preferred for synthesizer compatibility: 64, 128, 256, 512, 1024, 2048
185+
defaults:
186+
# Same fields as single target — applied to all combinations
187+
188+
output_template:
189+
dir: "AKWF--{length}smp-{bits}bit-{rate_k}k"
190+
zip: "AKWF--{length}smp-{bits}bit-{rate_k}k.zip"
191+
name: "AKWF {length}smp {bits}-bit {rate_k}kHz"
192+
```
172193

173-
### Tools Available
194+
### Resampling
174195

175-
- **Makefile** — `check`, `smoke`, `build`, `validate`, `package`, `clean`
176-
- **SoX** (`sox`) — resampling for C-header targets (`sox-speed` method)
177-
- **gnuplot** — ASCII art in C headers and PNG waveform plots
178-
- **FFmpeg** (`ffmpeg`) — format probing, edge cases
179-
- **Python 3.11+** with `requirements.txt` — numpy, scipy, soundfile, PyYAML, tqdm
196+
- **FFT-based sinc** (`scipy.signal.resample`) — default, mathematically optimal for periodic signals
197+
- Treats signal as periodic (exactly what single-cycle waveforms are)
198+
- Preserves all harmonics below Nyquist with zero aliasing
199+
- Output is perfectly loopable
200+
- All processing in 64-bit float internally
201+
- **sox-rate** (legacy Teensy AKWF-c approach) resamples via SoX's `rate` effect; `resample.quality` maps to SoX's quality presets here (no-op for `sinc`/`linear`/`sox-speed`)
202+
- **sox-speed** (legacy AKWF--Surge approach) resamples via SoX's `speed` factor for bit-exact legacy parity
180203

181-
### Resampling Math
204+
### Dithering
182205

183-
To change from 600 samples to N samples using SoX `speed`:
184-
- `speed_factor = 600 / N`
185-
- Example: 600 → 1024: `speed 0.5859375` (= 600/1024)
186-
- Example: 600 → 512: `speed 1.171875` (= 600/512)
187-
- Example: 600 → 2048: `speed 0.29296875` (= 600/2048)
206+
- **TPDF** (Triangular Probability Density Function) applied when quantizing to 8-bit, 16-bit, or 24-bit — `wav`/`raw` formats only
207+
- Decorrelates quantization error, replacing distortion with a tiny noise floor
208+
- 32-bit float output skips dithering
209+
- `c-header` output is never dithered — it quantizes straight to `uint16` via `float_to_uint16()`, not through the wav/raw dither path
188210

189-
Note: SoX `speed` changes both length and effective sample rate. The output file keeps 44100 Hz in the header but the cycle is now N samples long.
211+
### Validation
212+
213+
`scripts/validate.py` dispatches by output format (wav/raw/json/c-header/png), auto-detected from the directory contents or read from `--target`:
214+
215+
```bash
216+
python3 scripts/validate.py dist/AKWF--1024smp-16bit-44_1k/
217+
python3 scripts/validate.py dist/AKWF--1024smp-16bit-44_1k/ --target targets/akwf-standard.yml
218+
```
219+
220+
`scripts/validate-built.py` (what `make validate` runs) does the same across every target in `dist/` at once.
221+
222+
### Packaging
223+
224+
```bash
225+
python3 scripts/package.py --target targets/akwf-standard.yml
226+
python3 scripts/package.py --all
227+
```
190228

191229
### Release Process
192230

@@ -199,16 +237,17 @@ Note: SoX `speed` changes both length and effective sample rate. The output file
199237
## Conventions
200238

201239
- Source files in `src/AKWF/` are NEVER modified by build scripts
202-
- All generated/converted output goes to `dist/` or target-specific folders
240+
- All generated output goes to `dist/`
203241
- File names preserve the `AKWF_` prefix
204242
- Bank folder names preserve the `AKWF_` prefix
205243
- One waveform = one file = one single cycle
206244
- The `AKWF_stereo` bank is special (stereo files) — skip or handle per target
245+
- Output naming: `AKWF--{length}smp-{bits}bit-{rate_k}k` (double-dash prefix)
246+
- Rate uses underscore: `44_1k` not `44.1k` (dots in filenames cause issues)
207247

208248
## Important Notes
209249

210-
- The source is 600 samples — this is an unusual, non-power-of-2 length by historical design
211-
- Some waveforms are already peak-normalized to 0 dBFS, some are not
250+
- The source is 600 samples — an unusual, non-power-of-2 length by historical design
212251
- The collection has ~4358 mono waveforms across 65 banks
213252
- License is CC0 — completely public domain, no attribution required
214253
- The project currently lives on the `updates` branch; `master` is the default/release branch

AKWF--Surge/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# AKWF for Surge
22

3-
The [free and open source synth Surge](https://surge-synthesizer.github.io/) can load single cycle waveforms directly. The Surge team also provides a python tool (wt-tool.py) for creating Wavetables using single cycle waveforms. You can download those tools or read about how to use them here:
3+
The [free and open source synth Surge](https://surge-synthesizer.github.io/) can load single cycle waveforms directly. The Surge team also provides a python tool (wt-tool.py) for creating Wavetables using single cycle waveforms. You can download those tools or read about how to use them here:
44
---> [Surge Wavetable Wiki](https://github.com/surge-synthesizer/surge-synthesizer.github.io/wiki/Creating-Wavetables-for-Surge)<---
55

6-
Adventure Kid waveforms are traditionally 600 samples long but Surge requires Wavetables to be power-of-2 cycle lengths (.... 64, 128, 256, 512, etc).
6+
Adventure Kid waveforms are traditionally 600 samples long but Surge requires Wavetables to be power-of-2 cycle lengths (.... 64, 128, 256, 512, etc).
77
This folder contains versions of the AKWF library converted to 64 (kinda silly), 512(reasonable), and 1024(higher quality) samples.
88
Also included is a set of Wavetables in WT format made from each folder inside of AKWF-512.
99

@@ -25,23 +25,23 @@ for d in ./*/;
2525
for file in $(find *.wav);
2626
do sox "$file" ./"$d"/"$file" speed 0.5859 gain -6.4;
2727
cp -r "$d" ~/Desktop/AKWF-1024/"$d";
28-
done;
28+
done;
2929

30-
#512 version DOWNSCALE
31-
# mkdir ~/Desktop/AKWF-512
30+
#512 version DOWNSCALE
31+
# mkdir ~/Desktop/AKWF-512
3232
# for file in $(find *.wav);
3333
# do sox "$file" "$d"/"$file" speed 1.1718 gain -6.4;
3434
# cp -r "$d" ~/Desktop/AKWF-512/"$d";
3535
# done;
3636

3737
#64 version SUPER DOWNSCALE
38-
#Kinda silly TBH
38+
#Kinda silly TBH
3939

4040
# mkdir ~/Desktop/AKWF-64
4141
# for file in $(find *.wav);
4242
# do sox "$file" ./"$d"/"$file" speed 9.4 gain -6.4;
4343
# cp -r "$d" ~/Desktop/AKWF-64/"$d";
44-
# done;
44+
# done;
4545

4646

4747
cd ..;

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# make smoke # CI-style: one bank per target YAML
66
# make build # full library build (slow)
77
# make build BANK=AKWF_0001 TARGET=targets/akai-mpc.yml
8-
# make validate # validate built WAV outputs
8+
# make validate # validate built outputs
99
# make package # zip dist/ targets to releases/
1010
# make clean # remove dist/ and releases/
1111

@@ -21,7 +21,7 @@ help:
2121
@echo " make smoke - build one bank per target YAML (CI default)"
2222
@echo " make dry-run - preview build without writing files"
2323
@echo " make build - build targets (all, or TARGET=..., BANK=...)"
24-
@echo " make validate - validate built WAV outputs in dist/"
24+
@echo " make validate - validate built outputs in dist/"
2525
@echo " make package - create zip archives in releases/"
2626
@echo " make clean - remove dist/ and releases/"
2727
@echo " make list - list available target definitions"
@@ -60,3 +60,6 @@ clean:
6060

6161
list:
6262
$(PYTHON) scripts/convert.py --list
63+
64+
format:
65+
pre-commit run --all-files

0 commit comments

Comments
 (0)