@@ -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
105111make smoke # one bank per target (CI)
106112make build TARGET=targets/akai-mpc.yml BANK=AKWF_0001 # single target
107113make build # full library (slow)
108- make validate BANK=AKWF_0001
114+ make validate # validate built outputs
109115make package # zip to releases/
116+ make clean # remove dist/ and releases/
110117```
111118
112119Or 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
116123python3 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
132134name : " Akai MPC"
133135output_dir : " AKWF--Akai-MPC"
134136description : " Format compatible with Akai MPC samplers"
135137
136138audio :
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+
143149normalize :
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+
147156naming :
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
151161structure :
152- mirror_source : true # replicate src/AKWF bank/file structure
162+ mirror_source : true
153163
154164metadata :
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
0 commit comments