midi2sampler.git · README.md
midi2sampler.git / README.md
revision 1d264ffb818fc4e916c5e5a515f59b39ea8b8a65 · complete file
# Midi2SamplerDSP
A Swift Package providing **production-grade DSP primitives** for a **MIDI 2–native sampler**, building on
[`Fountain-Coach/midi2`](https://github.com/Fountain-Coach/midi2).
## Highlights
- **Loop seam discovery & stitching** (composite metric: amplitude + slope + spectrum)
- **Adjacent-zone crossfades** (key/velocity), equal-power windows, smoothstep mapping
- **Micro phase alignment** for seam minimization
- **DC removal, loudness matching**, slew-limited parameter changes
- **Windowed-sinc offline resampler** for pitch-matching before blending
- **Unit tests** for click-free loops and continuity
## Why this matters for MIDI 2
MIDI 2.0 (via UMP) increases resolution and enables per-note expression. This library ensures that **sample seams,
loops, and crossfades** are artifact-free so your instrument takes full advantage of MIDI 2 expressiveness without
audible clicks or comb filtering.
## Using with SwiftPM
```swift
dependencies: [
.package(url: "https://github.com/Fountain-Coach/midi2", branch: "main"),
.package(url: "https://example.com/your/Midi2SamplerDSP", branch: "main")
]
```
> If the `midi2` package product name is not `MIDI2`, adjust the dependency in `Package.swift` accordingly.
## Persistence (Fountain-Store)
This package integrates with [`Fountain-Store`](https://github.com/Fountain-Coach/Fountain-Store) via a thin adapter.
The repository reads/writes **Sampler Pack** JSON documents and defers storage backends to Fountain-Store
(local FS, cloud, or git—depending on its configuration).
```swift
import Midi2SamplerDSP
let repo = SamplerPackRepository() // uses Fountain-Store adapter by default
var pack = SamplerPack(
instrument: Instrument(
name: "GrandPiano",
sampleRate: 48000,
zones: [
Zone(id: "zp_C4_vel64",
pitchCenter: 60,
pitchRange: [57,63],
velocityRange: [0.5,0.7],
file: "Audio/piano_C4_vel64.wav",
loop: Loop(start: 123456, end: 234567, overlap: 2048))
]
)
)
try repo.save(pack, to: "Instruments/GrandPiano/Instrument.json")
let loaded = try repo.load(at: "Instruments/GrandPiano/Instrument.json")
```
> If the Fountain-Store SwiftPM **product name** is not `FountainStore`, adjust the `.product(name: "FountainStore", package: "Fountain-Store")`
in `Package.swift`, and the `import FountainStore` statements accordingly.
## FountainKit Integration (AI Reasoning)
This package exposes a **SamplerAgent** that can be wired into `FountainKit` as a Tool-capable agent.
The agent supports operations like **stitchLoop**, **savePack**, and **listPacks**. Extend it with
`renderNote`, `renderChord`, `evaluateSeam`, etc., as you formalize your tool schema.
### Wiring Example
```swift
import Midi2SamplerDSP
// import FountainKit // depending on your app target
let agent = SamplerAgent() // uses Fountain-Store by default
let req = StitchLoopRequest(audioPath: "Audio/mono.rawf32", searchStart: 48000, searchEnd: 240000, overlap: 2048)
let payload = try JSONEncoder().encode(req)
let invocation = SamplerToolInvocation(kind: .stitchLoop, payload: payload)
let resultData = try await agent.handle(invocation)
let result = try JSONDecoder().decode(StitchLoopResult.self, from: resultData)
print("Loop points:", result)
```
> Note: The included WAV loader is a stub for proof-of-concept. Replace with proper WAV I/O for production
> (e.g., `AVAudioFile` / `AudioToolbox`), or integrate your existing asset pipeline.
## OpenAPI-First (FountainKit-friendly)
This repository now ships an **OpenAPI 3.1** spec at `openapi/sampler.yaml`.
Planning/Reasoning systems (e.g., FountainKit) should **discover and invoke**
sampler capabilities by reading this spec — no direct code dependency needed.
**Primary endpoints**
- `POST /loops:stitch` — discover/write seamless loop points using the composite DSP metric
- `POST /packs:save` — persist `SamplerPack` documents (via Fountain-Store backends)
- `GET /packs?directory=...` — list packs
- `POST /render:note` — offline render for a single note (artifact URL)
**Models** are aligned with the Swift `SamplerPack` types to keep JSON and code in lockstep.
## Audio Corpus & Introspection
The package now includes an **AudioIntrospector** and corpus models to make audio files
first-class corpus citizens. Use it to compute summaries (RMS/peak/DC, spectral stats,
placeholder MFCC/chroma, optional loopability via `LoopStitcher`), generate embeddings
and simple fingerprints, then persist results via Fountain-Store.
**OpenAPI endpoints** for this flow are documented in `openapi/sampler.yaml`:
- `POST /corpus/audio:ingest`
- `POST /corpus/audio:analyze`
- `GET /corpus/audio:search`
- `GET /corpus/audio:dedupe`
## Validation
The package includes a **Validator** to enforce quality and consistency:
- `PackValidator` — checks SamplerPack structure and zone metadata (hard/soft/info).
Auto-corrects soft issues: fill `format/version`, set recommended crossfade time, suggest/raise loop overlap, set default RMS.
- `AudioValidator` — gates AudioSummary quality (DC/clipping/loopability thresholds).
```swift
let packReport = PackValidator().validate(pack)
let (corrected, fixes) = PackValidator().autoCorrect(pack)
let audioReport = AudioValidator().validate(summary)
```
## Validator Endpoints (OpenAPI)
The spec now exposes validator endpoints so planners and tools can gate changes before persisting:
- `POST /packs:validate` → returns a `ValidationReport` for a `SamplerPack`
- `POST /corpus/audio:validate` → returns a `ValidationReport` for an `AudioSummary`
Use `ValidationService` to wire these endpoints in your server quickly.
## Velocity-Timbre Morphing (Lean dynamics without many layers)
New DSP blocks:
- `VelocityTimbreModel` — maps MIDI 2 velocity to tilt/presence/transient/noise/saturation.
- `Biquad` — low-shelf & peak with smoothed coeffs.
- `TransientShaper` — attack emphasis.
- `SoftSaturator` — tanh-like soft clipper.
- `PositionEQ` — simple mic perspective EQ chain.
- `PedalResonance` — short IR convolution scaffold.
- `KeyOff` — micro-sample layer for releases.
- `TuningTables` — per-note detune/inharmonicity.
- `RealTimeNoteProcessor` — ties it all together per note.
**OpenAPI** additions for morphing:
- `POST /morph/preset:apply`
- `POST /morph/params:renderNote`
- `POST /morph/fitCurves`
Use a few velocity anchors and let DSP + MIDI 2 per-note expressivity smoothly cover the rest.
## Next Steps
- **Upgrade PedalResonance**: current IR convolution is naive FIR. Replace with partitioned FFT or GPU (Metal) for real-time efficiency.
- **Curve Fitting**: implement `/morph/fitCurves` with a small regression or ML fitter that learns velocity→timbre mapping from corpus embeddings & sparse anchors.
- **CLI Demo**: add `sampler morph-render --note 60 --vel2 12000` style command to render a WAV so developers can immediately hear the morphing results.