Skip to content

Commit 82c14fe

Browse files
committed
📦 NEW: audio clips, instrument automation and five more Bitwig devices in the DAWproject import
Audio clips become builtin:audio tracks: warp markers give the file's tempo, play start trims, loops repeat, fades and repitch carry over. Automation of the instrument plugin's parameters (normalized, via the new "scale": "normalized" curve option) and of the master volume is imported. From the Bitwig project, Filter, EQ-5, Reverb, Delay-2 and Distortion map to built-in effects; EQ-5 settled the shared EQ band types. Bus volume automation was counted on top of the fader twice; now it adds to it like a track's.
1 parent d1c1889 commit 82c14fe

7 files changed

Lines changed: 295 additions & 48 deletions

File tree

‎AGENTS.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ A DAW's own devices are only names in a DAWproject. For Bitwig exports the impor
3434
project itself (`<name>.bwproject`, found next to the export or in `~/Documents/Bitwig Studio/Projects/
3535
<name>/`, or given with `--bitwig`): Drum Machine pads become one track each (plugins with their
3636
states, Sampler pads as `builtin:sampler`) into a bus named after the drum track, and Chain, EQ+,
37-
Compressor, Multiband FX-3, Peak Limiter and Tool become built-in effects, with the plugins inside
38-
them. Other Bitwig devices (Polymer, Filter, Reverb, ...) are listed as left out. Without the Bitwig
39-
project a Drum Machine becomes `builtin:drums`. Audio tracks, launcher clips and device-parameter
40-
automation aren't imported. `wavelength import song.bwproject` lists a Bitwig project's tracks,
37+
EQ-5, Filter, Reverb, Delay-2, Distortion, Compressor, Multiband FX-3, Peak Limiter and Tool become
38+
built-in effects, with the plugins inside them. Other Bitwig devices (Polymer, Mid-Side Split, ...)
39+
are listed as left out. Without the Bitwig project a Drum Machine becomes `builtin:drums`. Audio
40+
clips become `builtin:audio` tracks (warped clips follow the song tempo); automation of volume, pan
41+
and the instrument plugin's parameters comes across. Automation of plugin effects and of Bitwig's own
42+
devices, and launcher clips, don't. `wavelength import song.bwproject` lists a Bitwig project's tracks,
4143
devices, pads and plugin states. Then edit the job like any other and render it; `render
4244
song.dawproject` does both steps.
4345

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ All notable changes to Wavelength. Versions follow semantic versioning.
55
## [Unreleased]
66

77
### Fixed
8+
- DAWproject import: volume automation on effect and group buses was added on top of the fader
9+
twice (bus automation adds to the fader, like a track's).
810
- `"stems": "none"` no longer creates an empty `stems/` folder.
911
- `lint` compared every pair at every voice's note starts, so a third voice's rhythm split a pair's
1012
moves and hid real parallels (four parallel octaves in Fugue for the Night Shift's exposition went
@@ -24,6 +26,13 @@ measured before their fader), a render that loses a track exits 1 with `"ok": fa
2426
command rejects options it doesn't know.
2527

2628
### Added
29+
- DAWproject import, more of the song: audio clips become `builtin:audio` tracks (warp markers give
30+
the file's tempo so clips follow the song, play start trims, looped clips repeat, fades, "repitch"
31+
plays tape-style), automation of the instrument plugin's parameters and of the master volume comes
32+
across, and from the Bitwig project Filter, EQ-5, Reverb, Delay-2 and Distortion become built-in
33+
effects (read from how 745 projects store them; EQ-5 confirmed the EQ band types, which also fixes
34+
one EQ+ type). Effects parked at 0% mix (factory drum kits' pad effects) are skipped.
35+
- Automation curves take `"scale": "normalized"`: values 0..1 of a plugin parameter's range.
2736
- MIDI files: `wavelength import song.mid` (and `render song.mid`) makes a job from a Standard MIDI
2837
File (types 0, 1 and 2): tempo map, time signature, markers, one track per MIDI track and channel,
2938
sustain pedal folded into note lengths, CC7 / CC10 / CC11 as fader, pan and rides, other CCs,

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ A minimal job:
162162
| `analyze <file.wav \| render-dir> [--start S] [--end S] [--json]` | Measures pitch, brightness, band balance, stereo width, onsets and envelope of a WAV, or of a render's mix, stems and sections. |
163163
| `params <plugin> [--preset N] [--state F] [--all] [--json]` | Shows parameters with ranges, current values and display text, optionally after loading a preset or state. |
164164
| `render <job.json> [--out DIR] [--stems float\|24\|16\|none] [--jobs N] [--json]` | Renders stems, `mix.wav` and `report.json`. Plugin tracks render in worker processes, several at once; a crashing plugin costs its track, not the song. A failed render leaves `{"ok": false}` in `report.json`, never a stale report. |
165-
| `import <song.dawproject> [--out DIR] [--bitwig FILE \| none] [--json]` | Turns a DAWproject export (Bitwig, Studio One, Cubase) into a job: notes, tracks with their plugins and saved states, mixer, sends, groups, tempo map, markers, volume and pan automation. For Bitwig it also reads the `.bwproject` behind the export for Bitwig's own devices: Drum Machine pads (plugins and samples), Chain, EQ+, Compressor, Multiband FX-3, Peak Limiter, Tool. Lists what didn't come across. `import song.bwproject` lists a Bitwig project's devices; `render song.dawproject` imports and renders in one step. |
165+
| `import <song.dawproject> [--out DIR] [--bitwig FILE \| none] [--json]` | Turns a DAWproject export (Bitwig, Studio One, Cubase) into a job: notes, tracks with their plugins and saved states, mixer, sends, groups, tempo map, markers, volume and pan automation. Audio clips (warped ones follow the song tempo) and automation of the instrument plugin's parameters come across too. For Bitwig it also reads the `.bwproject` behind the export for Bitwig's own devices: Drum Machine pads (plugins and samples), Chain, EQ+, EQ-5, Filter, Reverb, Delay-2, Distortion, Compressor, Multiband FX-3, Peak Limiter, Tool. Lists what didn't come across. `import song.bwproject` lists a Bitwig project's devices; `render song.dawproject` imports and renders in one step. |
166166
| `import <song.mid> [--out DIR] [--instrument PLUGIN] [--json]` | Turns a Standard MIDI File into a job: tempo map, time signature, markers, and a track per MIDI track and channel with its notes (sustain pedal folded into note lengths), volume, pan and expression, other controllers, pitch bend (with the file's bend range) and pressure. Channel 10 plays `builtin:drums`; other channels a General MIDI-family sound from the installed sample library, or `--instrument` for all of them. `render song.mid` imports and renders. |
167167
| `export <job.json> [--out song.mid] [--json]` | Writes the job's parts as a type 1 MIDI file to open in a DAW or notation program: tempo map (ramps stepped every 1/16 beat), time signature, markers, and a track per job track with its notes, fader, pan, rides and CC, pitch bend and pressure automation. Export, import and export again gives the same file byte for byte. |
168168
| `master <mix.wav> --chain <chain \| job> [--loudness L] [--lead-in S] [--out DIR] [--json]` | Masters a finished mix: plays it through a master chain (an effect list, a master object, or a song's job with its markers; a file or inline JSON) and reports loudness and true peak before and after, per section. |
@@ -210,8 +210,8 @@ render's stem loudness (`<song>/out` by default).
210210

211211
1. Per-note pitch and level tracking in `analyze`; gain-reduction readouts for compressors and limiters.
212212
2. Auditions inside each instrument's range, and an envelope-depth measure for rhythmic patches.
213-
3. The rest of Bitwig's own devices (Filter, Reverb, Delay, EQ-5, Distortion) and device-parameter
214-
automation in the DAWproject import; audio clips.
213+
3. DAWproject import: automation of plugin effects and of Bitwig's own devices, launcher clips, more
214+
Bitwig devices (Mid-Side Split, Polymer, Phase-4).
215215
4. FLAC and AIFF samples, SFZ instruments, and a General MIDI SoundFont fallback.
216216
5. A local service with a web UI and live playback through the speakers; Audio Unit hosting.
217217

‎docs/job-format.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ A job is one JSON object. Unknown fields are ignored.
7474
| `articulations` | none | Articulation name → keyswitch key (`{"long": 0, "spiccato": 1, "tremolo": 3}`). Notes pick one with `"art"`; the keyswitch note is sent 30 ms before the first note of every change. |
7575
| `range` | none | `[lowest, highest]` playable keys (`["G3", "C#7"]`): notes outside it get a warning in the report (sample libraries are silent there). |
7676
| `velocityTo` | none | Drive a controller from note velocities, one point per onset, ramping between them: `{"param": "Dynamics", "min": 0.1, "max": 1}` or `{"cc": 1, "min": 10, "max": 127}`. For libraries whose long notes take loudness from a controller instead of velocity. Explicit automation of the same target wins. |
77-
| `automation` | none | `gain` (dB), `rides` (dB added on top of `gain`: one curve, or named curves `{"sections": curve, "fills": curve}` that all add up, so section rides never overwrite the written fader curve), `pan` (-1..1), `params` (`{"Name": curve}`, plain values), `cc` (`{"1": curve, "64": curve}`, MIDI CC values 0-127), `pitchbend` (semitones, see `bendRange`), `pressure` (0-127). CC, pitch bend and pressure reach CLAP plugins as MIDI (or note expressions) and VST3 plugins through the parameters they map those controllers to (a warning names any they don't map). Curves are described in `effects.md` (points, steps, LFOs). |
77+
| `automation` | none | `gain` (dB), `rides` (dB added on top of `gain`: one curve, or named curves `{"sections": curve, "fills": curve}` that all add up, so section rides never overwrite the written fader curve), `pan` (-1..1), `params` (`{"Name": curve}` or `{"#id": curve}`, plain values; a curve object with `"scale": "normalized"` gives 0..1 of the parameter's range, as DAWs store automation), `cc` (`{"1": curve, "64": curve}`, MIDI CC values 0-127), `pitchbend` (semitones, see `bendRange`), `pressure` (0-127). CC, pitch bend and pressure reach CLAP plugins as MIDI (or note expressions) and VST3 plugins through the parameters they map those controllers to (a warning names any they don't map). Curves are described in `effects.md` (points, steps, LFOs). |
7878

7979
`plugin` may also be `builtin:drums` or `builtin:fx` (see `effects.md`), `builtin:sampler`, or `builtin:audio`.
8080

‎src/automation.hpp‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ class Envelope {
113113
else if (curve == "step") allStep = true;
114114
else throw std::runtime_error("automation curve must be linear, exp or step");
115115
if (j.contains("lfo")) e.lfo_ = std::make_shared<Lfo>(Lfo::parse(j["lfo"], tempo));
116+
const std::string scale = j.value("scale", "plain");
117+
if (scale == "normalized") e.normalized_ = true;
118+
else if (scale != "plain") throw std::runtime_error("automation \"scale\" must be plain or normalized");
116119
if (j.contains("points")) points = &j["points"];
117120
else if (j.contains("value")) { e.pts_.push_back({0.0, j["value"].get<double>()}); e.step_.push_back(false); return e; }
118121
else throw std::runtime_error("automation object needs \"points\" or \"value\"");
@@ -139,6 +142,15 @@ class Envelope {
139142
}
140143

141144
bool empty() const { return pts_.empty(); }
145+
// "scale": "normalized": values are 0..1 of a plugin parameter's range (as DAWs store automation)
146+
bool normalized() const { return normalized_; }
147+
Envelope scaled(double lo, double hi) const { // normalized -> the parameter's own range
148+
Envelope e = *this;
149+
for (auto &p : e.pts_) p.second = lo + std::clamp(p.second, 0.0, 1.0) * (hi - lo);
150+
if (e.lfo_) { auto l = std::make_shared<Lfo>(*e.lfo_); l->depth *= hi - lo; for (auto &d : l->depthPts) d.second *= hi - lo; e.lfo_ = l; }
151+
e.normalized_ = false;
152+
return e;
153+
}
142154
bool constant() const { return pts_.size() <= 1 && !lfo_; }
143155

144156
double at(double sec) const {
@@ -167,6 +179,7 @@ class Envelope {
167179
std::vector<std::pair<double, double>> pts_; // (seconds, value)
168180
std::vector<bool> step_; // jump (hold, then step) into this point
169181
bool exp_ = false;
182+
bool normalized_ = false;
170183
std::shared_ptr<Lfo> lfo_;
171184
};
172185

0 commit comments

Comments
 (0)