灵符lingfu

ling-station

↓ 0 downloads

LingStationX: a DAW in Ling with a piano roll, arranger, performance view, theory-aware score fixing, Qwen instrument choice, Prism / Growl / Synth X / Membrane instruments and Ling-design-system effect windows

Windows · x86-64Linux · x86-64LingOS · x86-64LingOS · Pi 5 ARM64Web · WASM
Keywords daw, music, midi, synth, sequencer, ling
lingfu install ling-station
LicenseApache-2.0 OR MIT
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (907)

README.md
# LingStationX

A digital audio workstation **written in [Ling](../ling)** — the arranger, piano roll, mixer, node editor,
performance launcher, file browser, dialogs and every instrument are `.ling` programs. It is a port of
[LingStation](../[LingStation]) (same layout, same ROYGBIV palette, same panels) that swaps the egui/VST host
for Ling's own runtime and audio crates.

```
LingStationX/
  ling/            the application (main.ling + modules)          <- the DAW itself
  instruments/     instrument "plugins": plain .ling patch files    <- edit / write your own
  assets/          font, project templates (Ambient Pads, EDM Starter, Hip Hop)
  tools/           patch/template generators, screenshot test harness
  docs/            DEV_NOTES.md (language gotchas, API, testing)
```

The audio engine is the native `ling-daw` crate in the Ling repo (`../ling/crates/ling-daw`), exposed to
scripts as `daw_*` builtins; a handful of `px_*` / `st_*` / `fs_*` / `dlg_*` builtins give the UI fast pixel
drawing, a shared state store, file browsing and native dialogs.

## Build & run

Needs a checkout of the Ling repo next to this folder (`../ling`, branch `lingstationx`) and Rust.

```powershell
cd ..\ling
cargo build --release --bin ling      # once (add --bin ling only; ~10 min)
cd ..\LingStationX
.\run.bat                             # or: ../ling/target/release/ling run --interp ling/main.ling
```

`run.bat` prefers the release build and falls back to the debug build.

### Distributables

`tools/build.py` builds every edition from one command (`python tools/build.py --help` lists the options):

```
python tools/build.py win          # Windows x64 portable folder + zip (add --installer for the Inno Setup installer)
python tools/build.py lin          # Linux x86_64 folder + tar.gz (native on Linux; in Docker on Windows / macOS)
python tools/build.py mac          # macOS .app + zip (on a Mac)
python tools/build.py web          # WebAssembly bundle in dist/web
python tools/build.py lingos-x86   # LingOS x86_64 live ISO with LingStationX in the desktop (needs ../LingOS)
python tools/build.py lingos-pi5   # LingOS for the Raspberry Pi 5: kernel8.img + FAT32 boot image (needs ../LingOS)
python tools/build.py all          # everything this machine can build
```

The desktop editions are a native launcher (`ling build --platform ...`) that runs `ling/main.ling` on the interpreter from the
folder it sits in; `--features llm` (the default) bakes the in-process Qwen helper into it, `--features none` is the quick build.
The LingOS editions are built by LingOS's own kernels (`kernel/x86_64-wm`, `kernel/rpi-lingstation`), which compile this checkout
into the image as a built-in app, and packaged with its `live/` scripts (in a WSL distro that has grub-mkrescue / mtools, natively on
Linux, or in `tools/docker/lingos-pack.Dockerfile`). The web bundle builds, but the wasm runtime has no DAW engine yet, so it is
the UI program only. `.github/workflows/build.yml` runs the same script on Windows, Linux and macOS runners.

## What is in it

* **Arranger** — timeline with ruler, loop region, tracks, clips (MIDI note previews, audio waveforms), Draw / Select
  (marquee) / Move / Slice tools, grid snap, zoom, copy-drag, drag files in from the sidebar, expandable
  **automation lanes** (level, pan, any instrument/effect parameter) with point editing and automation recording.
* **Piano roll & Parameters** — draw/select/move/resize notes, snap and note-length, velocity / pan / cutoff /
  resonance lanes per note, quantize / humanize / transpose; the Parameters tab adds the instrument's knobs,
  presets, patch load/save and (for drums) a 4x4 pad grid.
* **Instruments** — *Ling Synth X* (subtractive + additive + FM in one voice), *Ling Membrane* (physical-modeling
  drums), **Ling Growl** (monophonic dubstep / electro bass and lead: wavetable stack, FM, vowel filter, four drawable
  16-point modulation lanes and a 7x10 mod matrix, 8 presets in `instruments/Growl/`) and **Ling Voice** (a formant singer
  driven by IPA lyrics, 7 singers in `instruments/Voice/`). Patches are text files in `instruments/`. The instrument
  panels are drawn in the Ling design system (outline + crosshatch, tile lettering, ROYGBIV meanings; `ling/ds.ling`).
* **Ling Prism** - a 16-voice analog-style synth for big electronic sounds (`ling-daw/src/dsp/prism.rs`): two morphing wavetable oscillators
  (sine / tri / saw / square / pulse / organ / bell / spectral) with 1-7 unison voices and phase warp, six ways for A and B to interact (add, ring,
  phase-mod, hard sync, xor, min), sub, noise, an analog *hum* drone and voice drift, a stereo multimode filter plus a note-tuned comb, eight
  multi-point envelopes (draw / drag / add / remove breakpoints) and two LFOs that reach 20 destinations through a 13 x 20 modulation matrix,
  and a harmonizer, phaser, ensemble chorus, ping-pong delay and reverb. **Microtonal**: 19 / 22 / 24 / 31 / 17 / 53 EDO, just intonation,
  Pythagorean, meantone, Bohlen-Pierce, slendro, pelog, harmonic series, whole tone, octave stretch, or twelve hand-tuned notes. The dice
  randomize the sound, the modulation, the effects or all of it, and *mut* wanders away from the current patch. 281 presets in `instruments/Prism/`
  (trance, deep house, disco, witch house, hyperpop, microtonal, ambient, plus a bank of sounds named after and imitating FL Studio's Sytrus and Harmor
  factory presets - pads, hoovers, donk and wobble basses, arps, keys, bells, kicks, FX) from `tools/gen_prism.py` and `tools/gen_prism_fl.py`.
* **Electronic GM** - `instruments/Electronic GM/` is a second 128-patch General MIDI bank (e-pianos, house organs, acid / sub /
  wobble / reese / neuro basses, supersaw pads and strings, stabs, sync / laser / vowel leads, risers and FX).
  `tools/gen_gm_electronic.py` + `cargo run --release -p ling-daw --example gm_electronic`.
* **Sidechain duck** - Edit > Sidechain Duck... (or the Duck button in the arranger toolbar): pick a track, say the drum kit, choose what triggers it (the kick only, the snare, every hit...) and the tracks that should get out of its way. Each gets a Ducker effect (depth, attack, hold, release, curve; the dip is drawn as you set it) triggered from the kick's notes, so only the kick ducks, it is exact to the sample, and it works even with the drums muted. Ducks show in the Node Editor and the effect list.
* **Tracker import** - File > Import MIDI / FL Studio / Tracker... reads ProTracker `.mod`, ScreamTracker `.s3m`, FastTracker `.xm`, Impulse Tracker `.it` and OpenMPT `.mptm` modules (compressed IT samples too; `.mo3` is not supported - save as `.it`/`.xm` in OpenMPT). Every module instrument becomes a track playing the module's real samples (written as a SoundFont with loops, panning, fine tune and fade-out), the patterns are played through in song order (jumps and breaks followed) into notes with their volumes, pans, lengths and note cuts, and speed/tempo changes become the tempo map. Vibrato, slides, arpeggios and sample offsets are not modelled.
* **Vocal editor** (Piano Roll on an audio clip, or Edit > Edit Vocal) - like Melodyne: Analyze finds the sung notes and shows them as blobs on the pitch grid (shape = loudness, line = the pitch as sung). Drag a note up / down for pitch (semitones, Alt = free), sideways to move it (grid snap, Alt = free), its edges to stretch it (time-slice stretch: the breaths and consonants around it stretch along); double-click puts its centre on the nearest note; Split / Drift / Gain tools; Up / Down, Delete (mute), Ctrl+A, box select. The panel corrects pitch to a scale (centre and drift amounts, five temperaments), quantizes timing, aligns pitch and timing to a guide (a MIDI part, or another edited vocal to tighten a double), adds vibrato, merges / resets notes and writes them out as MIDI. Every edit re-renders the clip with pitch-synchronous resynthesis (formants kept); the recording is kept, so Compare plays the original. It replaces Vocal to MIDI, the Vocal Tuner and Tune to the score.
* **Ling Membrane drums** - 28 knobs per piece and a kit bus. Character: sub (a sine at the fundamental that follows the pitch drop), snap (a tonal chirp on the attack), ring (ring-modulated clang), fold (wavefolder), vary (every hit a little different). Kit bus, the same on every piece: duck (the kick pumps the rest of the kit), room (one shared room), glue (bus compression), smash (parallel compression), tape (saturation with a soft top), boom and air (low / high shelves). 20 kits in `instruments/Kit_*.ling` (new: Trap, Techno Hard, Drum and Bass, Boom Bap, Industrial, UK Garage, Stadium Rock, Brushes, Afro Percussion, Hyperpop, Dembow, Dub), written by `tools/gen_kits.py`.
* **FL Studio automation on import** - automation clips come in with the project: each one is mapped onto the parameter it moves (the clip's FL name, "SubSynth - Filter Cutoff", picks the part and the knob: cutoff, resonance, envelope, drive, chorus, delay, reverb, LFO, volume, pan, pitch ...), kept as a lane you point at a parameter later, or skipped - for all clips or one by one in the import dialog. An unmapped lane shows "FL: <name>" in the Automation lanes; click its name to choose what it moves (any lane can be re-pointed the same way).
* **New AI Track** (File menu) - a whole song from a description, lyrics and an optional reference song. Describe it ("uplifting trance at 138 bpm in F# minor over i VI III VII, female vocal, intro verse chorus ..."), paste lyrics ([Verse] / [Chorus] labels or stanzas), and optionally pick a reference WAV / MP3: its tempo, key, chord loop and form (from its energy, bar by bar) are borrowed and its sound guides the mix. Qwen can fill in what the description leaves open. Plan shows everything as one editable line (genre, bpm, key, chords, structure, instruments, voice, seed); Generate builds it: instruments chosen for one of 27 genres (Prism / Growl / Synth / GM patches, a Membrane kit), a phrase per section for every part (repeated sections repeat, drums fill into the next section, parts enter as the energy rises), a Ling Voice singing the lyrics, filter sweeps through the builds and sidechain-style pumping in club drops, articulation on the bass and lead, section tags on the ruler; then Mix AI levels, pans and masters it to the genre's loudness, and Voice Skin can have an RVC model sing the vocal. Qwen proposes verse and chorus chords, which are checked against the key (out-of-key chords that are not a usual borrowing are corrected). The lead and the singer develop one motif through the song (quoted in the chorus, inverted in the bridge, varied when a verse returns, lifted in the last chorus); the bass locks to the kick; the whole band shares one human feel (`human=` in the plan line, 0 = on the grid); the arrangement changes section by section (a small intro, a part dropping out in the bridge, a fuller second verse, everything in the last chorus). LENGTH: Short / Normal / Long, Fit lyrics (each sung section as long as its words need, about 2.2 syllables a second with room to hold notes), Fit MIDI (the guide MIDI's form, bars and tempo) or a Duration (m:ss); the sung line is shaped to the words: one note per syllable (two only on a long note when a line has no room left), nothing shorter than a sixteenth, the last word of every line held with a breath before the next line - the same SING mode Generate Vocal Track uses by default. Same description + seed = same song.
  * **Guide MIDI + Follow it**: give it a MIDI file and a slider. The song takes the file's tempo, key, chord loop and form (energy per bar) - the description still wins - and the slider says how much of the file's NOTES it keeps: 0 ignores the file, low borrows only tempo/key/chords/form, in the middle that share of the bars play the file's notes (in the song's key and each part's register, picked the same way for one seed) and the rest is written new over the same chords, 100 plays the file's notes everywhere in new instruments. Instruments the file has and the genre lacks (a bass line, a melody) are added once the slider passes 40 %.
  * **Instruments are measured, not assumed** (ling-daw/src/soundcat.rs): every synth patch is played offline (a handful of notes: pitch against twelve-tone equal temperament, partials off the harmonic series, attack, sustain, brightness, low end; results cached in cache/soundcat.tsv). Patches that play out of tune or clash (19-EDO / slendro / Bohlen-Pierce / shruti / quarter-tone patches, stretched-partial bells, a tape-wobble pad...) are never picked for a song or an import - the role tables are filtered by what was measured. Qwen is told what each plugin is for (Prism, Growl, Synth X, Pluck, Voice, Membrane, SoundFont) and, for every role, the instrument that will play it in the song's genre with how it measured (in tune, soft attack, sustained, warm ...), in the import chooser and the song planner alike.
* **Export Tracker** (File menu) - tick MOD, S3M, XM, IT, MPTM and/or PDF. The plugins are sampled from the project: several pitches per instrument where the format keymaps instruments (XM, IT, MPTM), one sample where it cannot (MOD, S3M), a sample per drum for kits, loops on whole periods of sustained sounds (IT keeps the release after a sustain loop). Audio tracks are cut into tempo-tagged segments ("Vox 128bpm 03") triggered on their rows, so they stay in time through tempo changes. Tempo changes become tempo commands; when a format runs out of samples or channels the result says what was simplified.
* **Register** (Help > Register LingStationX) - nothing is locked. Open (or paste) the license file from your account or download page on studio.linglin.art: a LingStationX Lifetime License, or a Studio subscription while it runs. The file is checked on this computer (two signatures, ML-DSA-87 and Ed25519, nothing is sent anywhere); a registered copy shows your name in the title bar and About, and can set a wallpaper behind the editors (choose an image, set its strength).
* **Save to Cloud** (File menu) - uploads the saved project folder to studio.linglin.art with its details filled in from the project (title, artist, genre, tags, a description from the tempo / key / instruments or File > Metadata, the lyrics typed on the clips) for you to edit first, as private / unlisted / public. Sign in once with your username or e-mail and password (only the account's API token is kept), or paste the API token from your Account page. Later saves add a new version to the same project. Uploading needs a Studio subscription.
* **Text size** - A- / A+ at the top right (click the % to reset), Ctrl+Shift+= / Ctrl+Shift+-, Ctrl+0, or Settings > General.
* **Voice Skin (RVC)** (Edit menu, the vocal editor, or "Sing with an RVC voice..." on a Ling Voice part) - sings a vocal with an RVC voice model. The NATIVE engine (default) runs RVC inside LingStation (HuBERT, the voice model and its vocoder on the GPU with CUDA, or the CPU) with no Python: it only needs `hubert_base.pt` in `models/rvc` or the RVC folder. The PYTHON engine uses an RVC WebUI install on this machine (RVC folder in the dialog, default `E:/SannyChat/talk`) and adds RMVPE pitch, the .index accent and breath protection. An audio clip is converted in place (Restore brings the recording back); a Ling Voice part - lyrics fitted to a MIDI clip - is rendered alone and sung by the model on a new audio track, which turns pasted lyrics + a melody into a real voice. Voices: `models/rvc/` (Name.pth + its .index, or a folder per voice) and the install's `weights/` with `logs/<Name>/*.index`. Pitch shift, pitch tracking (RMVPE needs `rmvpe.pt` in the RVC folder, else harvest), index amount, breath protection, loudness, GPU. Runs in the background; the Python engine is driven by `tools/rvc_skin.py`. **Generate Vocal Track** (Edit menu) turns a MIDI melody + pasted lyrics into a Ling Voice take for one of 16 voice types (pop / jazzy smoky / breathy / R&B / opera female, choir soprano / alto / tenor / bass, pop / soul / rock / crooner / opera male, whisper, robot) with knobs for gender, tone, breath, grit, vibrato, scoop, ring and ensemble, then optionally has an RVC voice sing it.
* **Tracker tab** (F4, between Node Editor and Performance) - the arrangement as patterns and pages like OpenMPT: rows down, channels across (a track gets one channel per note that sounds at once, up to 8, so chords read across), an order list of pages, note / instrument / volume cells, hold and note-off marks, the playhead row (Follow keeps it in view), rows per beat 1-8, page size 16-128, hex or decimal row numbers. Click a cell and type notes on the keyboard (Z S X D C V G B H N J M / Q 2 W 3 E R 5 T 6 Y 7 U I), Delete clears, arrows move, [ ] shorten / lengthen the note, Esc leaves the grid. The Font switch draws the grid in the normal English font, Daemon, Chesilin or Konnarian (Chesilin has no - # = so those use the normal font). Konnarian is Daemon stacked into syllable blocks: a note is a solfège block (도 레 미 파 솔 라 시) with ♯ and the octave raised (C#5 = 도♯⁵), channel names are romanized into blocks, and the numbers use the font's own numerals.
* **Sampler** (a new plugin; Parameters tab) - zones of audio files with root note, key / velocity range, fine tune, volume, pan, pitch tracking, forward / ping-pong loops (draggable markers, Auto loop), trim, reverse, one-shot; amplitude ADSR or a full multi-point envelope with sustain / loop points and fade-out, plus panning, pitch and filter envelopes, a resonant low-pass (cutoff, resonance) and random variation per note (volume, pan, cutoff, resonance, like OpenMPT's swings); WAV / FLAC / OGG / MP3, WavPack (via ffmpeg) and Ogg-in-WAV files. Saved with the project (samples are copied into `<project>/samples`).
* **Tracker import** now plays the module's own samples through Samplers: arpeggios become notes, pitch slides / portamento / vibrato become pitch-bend lanes, volume slides / tremolo / retrigger become volume lanes and notes, Zxx filter changes become cutoff / resonance lanes, pattern loops and delays are followed, XM / IT volume, panning and pitch / filter envelopes, fade-outs, instrument cutoff / resonance and global volumes come across. Channels that use these effects get their own track with their lanes. Checked against libopenmpt renders: pitch correlation 0.96-0.99, durations within 0.05 %, level within 1.5 dB. In the import dialog each part can use its original samples, go to a shared percussion kit (one Sampler track), or use a drum sample found in your sample folders by name ("Splash Cymbal" -> a splash sample); **Ask Qwen** gives percussion parts search words and the importer falls back to the module's own sample when nothing is found. Sample folders searched: `[LingStation]` next to the programs folder, `library/samples`, `assets/samples` and Settings > sample_roots.
* **Export Karaoke** (File menu) - subtitles `.ass` (per-syllable karaoke, count-in dots), `.srt` (lines and a progressive-colour version), `.lrc` (word level), a self-contained **HTML player** (cursor: sweep / pop / bouncing ball / underline / block / arrow; pitch ribbon of the melody; every look option live in a panel) and an **MP4** through ffmpeg, with the backing audio (instrumental = lyric tracks muted). Look options: font (default, Daemon, Chesilin or any font file), size, text / sung / outline / cursor colours, outline thickness and opacity, text opacity, shadow, background, lines on screen, colour by pitch, resolution.
* **Transcribe Vocals** (File menu) - speech to text with faster-whisper running on this computer (GPU when available): picks a model from your Hugging Face cache, writes `.srt` (cues from the word timings, broken at sentences and pauses) and `.txt`, and can fit the words to a MIDI clip's notes. ling-lang itself has the same thing as `stt_*` builtins (`stt_start`, `stt_poll`, `stt_segments`, `stt_words`, `stt_srt` ...; see `ling/stt/README.md` and `ling/examples/stt_demo.ling`). Setup once with `ling/stt/setup_windows.ps1` (an isolated venv, nothing goes into your normal Python).
* **Export Sheet Music** (File menu) - the notes as a colour-coded schematic instead of a staff: time on a bar / beat grid, height = pitch (black-key rows shaded), tile colour = pitch class (twelve colours on a circle of fifths), letters in English, Daemon or Chesilin, width = length, brightness = loudness, octave numbers, chord stems, drum symbols, lyrics under the notes, tempo marks, a legend page. SVG pages and a printable HTML (fonts embedded).
* **Vocal to MIDI + tuning** - now part of the vocal editor above: Notes to MIDI writes the edited notes to a MIDI track, Correct pitch / Quantize / Align replace the Vocal Tuner and Tune to the score.
* **Scripts** (Edit > Scripts) - Ling files in `scripts/` that the program loads while it runs (save a file and it reloads): note scripts (`arp`, `humanize`, `reverse`, `euclid`) and **custom plugins written in LingDSP**, a small sample-by-sample language returned by `<name>_dsp()`: effects (`tubedrive`, `bitcrush`, `ringmod`, `pingecho`) and instruments (`fmbell`, `supersaw`, `karplus`) with their own knobs, filters, delays, oscillators and envelopes; every copy of a plugin updates when its script is saved. See `scripts/*.ling`.
* **Vocoder** (effect) - the track's own sound is the carrier; the modulator is another track routed to it (Node Editor kind MODULATOR, or the picker in its window) or the built-in talker (vowels, robot, whisper, gate). 8-32 bands, formant shift, hiss for consonants, 14 presets.
* **Ringstorm** (effect) - modular pedals, amps and racks you wire up in a pedalboard window: 20 pedal kinds (gate, compressor, boost, overdrive, distortion, fuzz, amp, cab, EQ, wah, chorus, flanger, phaser, tremolo, vibrato, delay, reverb, octaver, volume, slicer), serial chains, splits and parallel lanes, 20 rig presets.
* **Ling Pluck** (instrument) - physical-modelling strings: guitar, harp, koto, shamisen, sitar (jawari buzz and sympathetic strings), oud, banjo, kalimba and more; 26 presets.
* **Prism genre banks** - 104 new presets (HyperPop, Deep House, Disco, Dance, EDM, Industrial, Atmos & Space, Synthwave, Lo-Fi) made by `tools/gen_prism_genre.py`; every instrument's preset chooser has [<] [>] to step, [@] for a random one, and opens a categorized browser that highlights (and remembers) the current preset.
* **AI Mix & Master** (Edit menu) - every track is rendered alone and measured (loudness, peaks, dynamics, spectrum, width); a plan names each track's role, sets levels, pans crowded instruments, cuts low-end and mud, compresses what is too dynamic and aims the master at a loudness (-14 LUFS by default), each move with its reason and a tick to leave it out; Qwen can correct roles and pick the style. One Undo.
* **MusicXML** - Export Sheet Music can also write a MusicXML score for MuseScore, Sibelius, Dorico or Finale (notation on screen is the Sheet tab; the piano roll's old Score face was the same thing twice and is gone).
* **Glue phrases** (Arranger button, or Edit > Glue Clips Into Phrases) - the reverse of Slice: clips that touch (or are at most a chosen rest apart) on the same instrument join into one phrase clip, up to a phrase limit of 4 / 8 / 16 bars or none; a run of identical bars folds into one clip that loops its bar. Per instrument or just the selected track, with a preview of every clip count before anything changes, and one Undo.
* **Export Tracker (PDF)** (File menu) - the Tracker tab as printed pages, like OpenMPT: one page per pattern (rows down, channels across; a wider song continues on more pages of the same pattern), cells `C-5 01 v32` (note, instrument = track number in hex, volume 0-64), `|` held, `===` note ends, bar and beat rows tinted. English, Daemon or Chesilin font, 1-8 rows per beat, 16-128 rows per page, hex or decimal row numbers, light or dark page, portrait or landscape, a range of patterns. Writes an HTML and a PDF (printed by Edge / Chrome / Chromium).
* The **Ling Design System** now also covers the Mixer, Sidebar, Arranger and Piano Roll through the shared buttons, chips, tabs and checkboxes (line work, brackets, crosshatch for "on"), hatched clip headers and outlined notes.
* **Node Editor** is now a round signal table (Reactable style): the master in the middle, every track a puck on the ring with its effects on the spoke toward the centre, master effects orbiting the master; dots travel along the lines faster with the level, each track puck shows its level arc and the notes of the current bar with a hand sweeping at the playhead. Sends / sidechains / ducks are made by dragging from a puck's out port. **Performance** and both views use the Ling design system (line work, crosshatch, brackets, tile lettering, colour keys).
* **Export stems bundle** - File > Render Audio... > tick *Bundle for other DAWs*: a folder with one WAV stem per track (all starting together, so drop them at bar 1), `midi/` (project.mid with the tempo map + one file per track), `instruments/` (soundfonts and Ling patches), `params.json` (levels, pans, instrument state, effect chains and parameters, automation, routing), `project.dawproject` (the open DAWproject format for Bitwig, Studio One, Cubase 14: stems as audio tracks plus muted note tracks) and a README with steps for FL Studio / Ableton / Bitwig.
* **Match key** - `Match key` in the arranger toolbar (or Edit > Match Tracks To Song Key) finds the song's key from the notes and the audio, then matches the ticked tracks to it: MIDI clips in another key are transposed and stray notes snapped onto the scale; melodic audio (a voice, a lead) is tuned onto the scale with Bezier-eased glides from note to note (strength, glide time and curve are yours); loops and mixes are moved to the key.
* **Fit lyrics** - in the Piano Roll's lyrics strip, `Fit...` takes a song's words (paste them in any language; labels, chords and (x2) are removed), splits them into syllables (Qwen can do it, or the built-in splitter) and spreads them over the notes: syllables land on strong beats and long notes, extra notes sustain, short notes hold the syllable before them, a chord carries one sustained syllable, and the lyrics can loop. Ling Voice follows chords legato.
* **Colour-coded icons** for every instrument and effect (Add Effect is now a card picker with a short description of each), and two arranger ticks: **Follow playhead** and **Loop at last clip** (the song loops at the end of its last clip).
* **Automate parameters** - under a track's automation lanes, `Automate...` writes lanes for **this track's parameters, a random handful, or every parameter of every melodic track**, with an optional prompt ("tense build into a drop", "slow ambient drift", "glitchy and wild"). Qwen knows what each parameter does and picks common articulations (wobble, pump, stutter, sweep, riser, per-note plucks and swells ...) that lock to the bars and the notes; without a model the built-in chooser reads your words instead. Lanes you drew yourself are never replaced, and Ctrl+Z undoes it all.
* **Singing** - give a Ling Voice track notes, open the Piano Roll and type lyrics into the strip under the grid:
  `[so-fa]-[rei][to][mi-do]` (`[..]` = one note, `-` inside = several syllables on one note, `-` between notes = slur / sustain the
  vowel, `[-]` = hold). Syllables can be IPA, plain letters read phonetically, pinyin, Hanzi, Hangul, kana or Thai script and are
  converted to IPA (`ling-daw/src/lyrics.rs`). Native script is pasted (Ctrl+V / Paste), IPA symbols have a palette.
* **SoundFonts** - `assets/soundfonts/Ling.sf2` (the Majora's Mask N64 set) is used by MIDI import when present: bank select +
  program change pick the preset, unmatched parts fall back to the built-in GM sounds. Add a soundfont instrument from the Plugin
  Picker (browse / audition with a search box); Settings > MIDI chooses the import soundfont. Loops, envelopes, filter, LFOs,
  layers, velocity/key ranges and exclusive classes are supported; SF2 modulators and effect sends are not.
* **MIDI import extras** - tempo changes become a tempo map (the toolbar shows "N changes", click to clear), pitch bend becomes a
  `bend` automation lane (RPN 0 sets the range), bank select, volume/expression/pan lanes and the sustain pedal are honoured.
* **Mastering & safety** - master limiter (mixer checkbox, on by default), audio test tone + limiter meter in Settings > Audio,
  stem export (Render > one file per track), A/B compare for instrument settings, left-edge clip trimming, double-click slider
  reset, and crash/close recovery (an unsaved session is offered back on the next start).
* **Sheet tab (notation editor) + MuseScore** — the *Sheet* tab (next to Performance, F3) shows the whole project as notation on dark A4 pages, two at a time,
  with a moving **playhead** (it turns the pages while playing; Ctrl+click anywhere on a staff moves it) and **editable notes**: click a note to select it,
  Up / Down transposes (Shift = octave), Left / Right moves it, `,` `.` shorten / lengthen, Delete removes it; with the Add tool a click on a staff puts a
  note there (length and grid are in the toolbar). The **Parts** list on the left works like MuseScore's Instruments panel: click selects a part, Ctrl-click
  adds to the selection, Shift-click selects a range, the eye shows / hides the part (or every selected part), *Show only selected* solos them.
  *Export for MuseScore* writes MusicXML (`.musicxml`) and compressed MusicXML (`.mxl`); *Open in MuseScore* starts MuseScore on it (checked with MuseScore 4).
  File > Export Sheet Music keeps the SVG / HTML / PDF options. **Small screens**: at 1024x768 the toolbars switch from text buttons to icons with
  tooltips, the side panels narrow and the kit pads shrink (`ling run --interp ling/main.ling --size 1024x768` previews it).
* **Drum kits** — Ling Membrane has 23 parameters per piece (tune, decay, tension, hardness, body, click, tone, damp, purity, sweep,
  drive, gate, analog, swell, pan, level, plus strike position, shell resonance, wires, noise tail, humanize, velocity-to-pitch and dynamics)
  and every GM key has its own voicing (six tom pitches, side stick, cowbell, congas, ...). **Per-sample effects**: every Membrane piece and every
  Sampler zone has its own insert rack (filter, drive, bit/rate crusher, transient shaper, compressor, ping-pong echo, room, stereo width);
  in the Sampler open a zone and switch SETTINGS / EFFECTS.
  Stock kits: Kit_Jazz, Kit_Rock, Kit_Metal, Kit_Funk, Kit_Electro_808, Kit_House_909, Kit_New_Wave, Kit_LoFi (`tools/gen_kits.py`).
* **GM sounds** — `instruments/GM/` holds 128 General MIDI patches (`tools/gen_gm.py` + the `gm_normalize` example).
* **Mixer** — strips with level/pan/mute/solo, meters, per-track FX chains (EQ, Compressor, Drive, Delay, Reverb),
  master strip with compressor, sends and sidechain routing.
* **Node Editor** — graph of tracks → effects → master with draggable send and sidechain routes.
* **Performance** — session/clip launcher: scenes × tracks, quantized launch, Song Play / Session Only, auto-slicing
  the arrangement into scenes, record launched clips back into the arrangement.
* **Audio** — import WAV/FLAC/OGG/MP3, warp clips to the project tempo and transpose them independently
  (WSOLA time-stretch), record audio from an input device, MIDI keyboard input and recording, MIDI export.
* **FL Studio import** - File > Import MIDI / FL Studio... (or drop an `.flp` in the browser) reads the channel rack, pattern notes, playlist
  arrangement, tempo and audio clips of an FL Studio 20/21 project (`ling-daw/src/flp_import.rs`, layout verified against real projects).
  Each channel becomes a part in the same dialog as MIDI import, so any plugin / patch can be chosen per channel; drum channels merge into
  one Drums part on GM keys, sample channels become audio clips, plugins are matched to a suitable sound by name. Automation clips, mixer
  effects, plugin settings and presets are not converted.
* **Ask Qwen to choose instruments** - the import dialog's *Ask Qwen to choose* button describes every part (register, polyphony, note length, GM program) to a
  small Qwen3 model that runs inside the program through ling's own `ling-ai` crate (GGUF via candle, no server, nothing sent anywhere). The model only
  names a *role* for each part ("rolling bass", "warm pad", ...); `ling-daw/src/ai_import.rs` maps roles to real plugins and patches, repairs or ignores bad
  JSON, caps any one role at a third of the parts, and fills in anything the model skipped, so every part always gets a valid choice. Build ling with
  `--features llm,llm-cuda` (or just `llm` for CPU), put a Qwen3 GGUF and its `tokenizer.json` in `assets/models/` (Qwen3-1.7B-Q8_0 is the default, about
  2 GB and ~5-10 s per file; Qwen3-4B-Q4_K_M is a bit better) or pick one in Settings > AI. `tools/ai_eval.ling` compares models on real files.
* **Saving** - Save / Save As ask for a name, create `projects/<name>/` first and write `<name>/<name>.json` there (a name that is already taken becomes `<name> 2`);
  the project is renamed to match and its Metadata (title, genre, a short description of the music) is filled in. The Render dialog always starts from the
  current project's name in `<project folder>/renders/` with a free file name and asks before replacing an existing file. Piano roll: click or drag the ruler
  to set the playhead (double-click plays from there); wheel = keys, Shift+wheel / wheel over the ruler or lanes / a tilt wheel = along the beats, Ctrl+wheel = zoom.
  The **+** next to every preset chooser saves the current sound as a user preset (`instruments/User/`), listed as "User: name" for that instrument.
* **Fix all scores** (button at the top of the Arranger) - opens a sidebar where you choose what to fix, with live counts: pull notes into the key (the tuning of
  every melodic MIDI note), resolve clashes with the chord, straighten passing notes, snap timing, remove doubled notes - and for the arrangement: line clip
  starts up on the grid, round clip durations (never cutting notes), trim overlaps, close gaps. *Fix everything* repeats over every clip until a pass finds
  nothing left (up to 8 passes) and lists what changed per clip; Ctrl+Z undoes all of it. *Suggest sections* reads how busy each bar is and proposes intro /
  verse / build / chorus or drop / bridge or breakdown / fill / outro; click a tag to change it, *Apply tags* draws them on the ruler.
* **Order vs chaos, and repetition** (Fix all scores) - the *Chaos <-> Order* slider (presets *Jazz / Breakcore*, *Balanced*, *Strict*) sets how forgiving the fixer is: low
  leaves colour tones, tension and off-grid swing alone, high pulls everything in. The key and chords for a region come from a **majority vote across the instruments**
  (a loud single-note line counts for more, harmony supports it). Two repetition options conform the arrangement "page by page, book by book": *Backing parts: one
  repeating page* turns a long chord / pad / bass clip into one 1-8 bar page that repeats, with a small turn every 4th and 8th time; *Loud riffs: stated twice, up to 32
  bars* trims or extends a prominent riff to whole 8-bar phrases and states it a second time in the next chorus / drop.
* **How the fixer decides a note is wrong** - it reads the key and chords once, from the score as it was (not again after each pass), smooths away key sections shorter than 5 bars,
  and only changes a note when there is evidence: a long note outside the key that no triad of its neighbours explains, or a note that grinds (half step / major seventh within
  about an octave) against another instrument. Short ornaments, trills, leading tones, arpeggiated chromatic chords and ordinary suspensions are left alone; "resolve clashes with the
  chord" and "straighten passing notes" only act at Strict order, and a fix is never allowed to create a new clash. Fixes are limited to pitches in the key.
* **Slice long clips into patterns** (Fix all scores, before the arrangement is lined up) - a clip of 8 bars or more that is one solid block (an imported 128-bar channel) is cut into
  patterns: silence is dropped, a run of identical 1 / 2 / 4 / 8-bar pages becomes ONE clip holding a single page with its loop length set, material that does not repeat becomes 4-bar
  phrases, and the same pattern coming back later gets the same letter ([A], [B] ...).
* **Rainbow Hypergrowl** (+ Add FX) - the digital harmonic growl of a resampled-wavetable bass (the Au5 "HyperGrowl" family), as a deep chain with 44 knobs and 8 presets
  (Hypergrowl, Yoi Bass, Digital Chop, Granular Cloud, Octave Shatter, Comb Scream, Vowel Melt, Sub Rattler): **drive** (gain, bias, tanh -> sine fold, ring modulator, bit and rate
  crusher) -> a 24-band **wavetable filter** that crossfades two of 12 spectral tables (vowel, comb, harmonic, hollow, rainbow, digital, tilt, bell, yoi, ripple, notches, stairs),
  with position, resonance, shift in octaves, rainbow spread (detunes the bands and runs the right channel's motion late), tilt and *body* (keeps the clean low end) -> a tuned
  feedback **comb** -> a **grain delay** (up to 32 grains: delay, size, density, spray, pitch +-24 st, random pitch, reverse chance, feedback, stereo spread) -> a 2048-point **spectral**
  stage (compress every harmonic toward the local average, bin shifter, weak-bin gate, blur, tilt) -> width, level, soft limiter, mix. **Motion** drives it: an LFO (sine / tri / saw /
  square / sample & hold) with optional stepping, an envelope follower, a slow random drift and a second LFO that wobbles the shift, the comb and the grain pitch. Every knob can be
  automated. It has 2048 samples (about 43 ms) of latency; the dry signal is delayed to match.
* **Hypergrowl sample section** - load a WAV / FLAC / OGG (or use the built-in sung-vowel sweep) as a spectral source: *sample table* (`smix`) lets its spectrum, band by band, replace the
  wavetable in the filter bank, *spos* / *sscan* choose and scan the reading position, *xsyn* lays its spectral envelope over your sound (a vocoder: the bass speaks in the sample's
  voice), *gsrc* makes grains read the sample (a granular sampler), and *freeze* holds the spectrum as a pad. The file is saved with the project. New presets: Vocoder Bass, Sample Cloud,
  Sample Scan, Freeze Pad.
* **Ling Growl: Talk and Sync, and 34 new presets** - *Talk* is a 16-step, tempo-synced vowel sequencer (draw A..U on the bars) feeding a formant filter that blends over the sound, with
  step rate, glide, step count, formant resonance and shift: the bass talks. *Sync* hard-syncs oscillator A (the neuro screech); lane 3 sweeps the ratio. Presets (Parameters tab):
  talkbox, robot vowels, formant bass, yoi talk, alien talk, vox pad, choir stack, glitch talk, screech, neuro sync, hard lead, sync sweep, sync pluck, tear out, dub wobble, half-time wob,
  triplet wob, riddim, riddim 2, minimal wub, mid growl, rasp, gnarl, future bass, supersaw, detune lead, fm bell, pluck bass, sub drop, 808 growl, acid squeal, chirp, swarm, wide stab.
* **Effect windows** (double-click an effect in the mixer) are in the Ling design system for every effect: framed sections, hatched knobs with readouts in real units, tile selectors,
  on / init / rand switches, presets where an effect has them, and for the growl a live display of the filter bank, the motion and the grains.
* **Right-click any knob** (synth or effect) - *Automate* (creates the lane), *Program shape* (writes a sine / triangle / ramp / pulse / staircase / random-hold LFO into the lane
  for the whole song, around the knob or over the full range), *Reset to default*, *Set exact value*, minimum / centre / maximum / randomize, copy / paste value. Effects on the
  master can be automated too (their lanes are programmed from the knob menu; the Arranger shows lanes of tracks).
* **Add harmonized part** (Piano Roll > Compose > Harmony) - select notes (or none for the whole clip), optionally say how ("thirds above", "choir", "jazz stack", "counter melody",
  "soft", "held"), pick the track whose chords to build it off (or all the other tracks) and where to put it (a new track with the same sound, or an existing one). Voices
  stay in key, follow the chords, and avoid a minor second / major seventh against the melody.
* **Vocal tuning** (Piano Roll > Compose with an audio clip selected) - tunes a recorded vocal to the notes of a MIDI track under it: the pitch is tracked, each
  moment is pulled toward the score's note (octave-folded, with strength and retune speed), sung starts are moved onto the beat (within a window you set), and the
  long notes can get vibrato (depth and rate). It re-synthesises with PSOLA so the voice keeps its formants, and writes the tuned take next to the project.
* **Vocal effects** (add them from a track's + Add FX): **Vox Filter** - a 12 / 24 / 48 dB high-pass and low-pass with a resonant corner, a spectral tilt and a *dynamic*
  high-pass that climbs when the voice is quiet (cleans breaths and room noise between phrases); **Vox Reverb** - an 8-line modulated FDN with pre-delay, early
  reflections, a low cut and high cut on the wet signal only, width, and a **duck** that lowers the reverb while the voice sings and blooms it back in the gaps;
  **Vocal Halo** - makes a voice pop in stereo: a mono-safe decorrelated "halo" of the top end (it cancels exactly in mono), a wandering artificial double, a harmonic
  presence exciter, an air shelf, a de-esser, a transient shaper, bass-centred width and a ducked bloom.
* **Batch import** (`tools/batch_list.py`, `tools/batch_run.py`, `tools/batch_import.ling`) - turns a pile of FL Studio projects and MIDI files into LingStation projects:
  for each file Qwen first names the genre, then chooses an instrument for every part knowing the genre, the FL plugin that played it (Sytrus and Harmor suit Prism,
  3x Osc suits Ling Synth X ...) and what each LingStation plugin is; the import is saved to `originals/`, the score and arrangement are fixed and tagged and saved
  to `production/`, and a MIDI file plus a 24-bit stereo 48 kHz WAV go to `renders/` under short names. It runs in small chunks, skips what is already done and
  carries on after a bad file. Every new or imported track starts with a gentle compressor and the master with a glue compressor.
* **Menus** taller than the window scroll (the automation parameter list); in the piano roll, dragging the end of one of several selected notes stretches the whole
  selection (hold Alt to lengthen every note by the same amount instead) and Shift+drag copies the notes to follow the cursor.
* **Solo** - any number of tracks can be soloed together; clicking a soloed track's S clears every solo.
* **Timeline and ranges** - the ruler of the Arranger and the Piano Roll share one set of controls: click or drag = playhead; **double-click then drag** (or Shift+drag)
  = select a time range (the clips or notes inside it get selected too); Ctrl+drag, or the **Loop** chip that appears on a selected range (Ctrl+L), = loop it;
  drag a loop edge to resize the loop; Esc clears the range. Bar / beat numbers thin out as you zoom out and fade away when they would not fit. In the roll,
  Shift- or Ctrl-drag on a selected note drags a copy of the selection; Ctrl+B duplicates it; right-click deletes; arrows nudge (Shift = bar / octave).
* **Pattern tab** (piano roll > Compose) - for a drum track: a rhythm generator (four on the floor, house, techno, trance, disco, breakbeat, trap, drum and bass,
  half-time, boom bap, one drop, euclidean) with density, swing and fills. For a melodic track: *turn chords into arpeggios* (order, rate, octaves, gate and a
  rhythm mask such as off-beat, gallop or euclidean hits/steps/turn) or write an arpeggio over the project's chords. Both write into the selected range
  (replacing only that stretch), at the playhead, or over the whole clip; press again for a variation.
* **Derive** - the **~** next to every preset chooser (and *derive* in Prism's header, *Derive a similar sound* in the Parameters panel) describes up to 24 of the
  sound's parameters to Qwen with a request ("darker and more movement") and applies its "similar but different" version; Ctrl+Z gives the old sound back. Without
  a model it drifts the parameters randomly instead.
* **Theory assistant (piano roll)** - the roll shows the key (and key changes), a chord lane with Roman numerals, and a phrase lane (repeated progressions,
  cadences, motifs, sequences, runs, arpeggios). Notes are coloured by what they are against the key and the chords: chord tone, scale tone, chromatic
  passing, clash, out of key (hover a wrong note for the fix). The detection is deterministic code (`ling-daw/src/theory.rs`), so it is instant and
  never guesses. Chords come from the other tracks when the clip is a melody, else from the clip itself.
* **Compose tab** (piano roll, next to Tools) - *Write*: type "8 bars sparse trance melody in F# minor, i VI III VII" and press Write it (keywords) or
  Ask Qwen to write (the model only fills a small spec: style, density, contour, feel; the notes come from `compose.rs`, so they are always in key and on
  the chords - by default over the chords already in your project). *Fix*: **Auto-correct** pulls out-of-key notes into the key, resolves clashes with the
  chord, snaps slightly-off timing and removes doubled notes in one undo step, with a preview count; *Move to another key* retunes a melody keeping its scale
  degrees. Also shows chords that work next (click one to add it).
* **Style thumbprint** - the project is described as a short story ("A driving trance piece in A minor at 128 BPM, over the progression i VI VII v. Bass plays
  rolling eighth notes...") plus a field list, saved in the project and refreshed on save. Tick *In my style* in Write to compose with it, or scan
  `library/midi/` (drop your MIDI / FLP files there) and borrow another file's style: its feel in your key over your chords.

* **MIDI import** — a dialog lets you pick the instrument for every part (soundfont / General MIDI, the Electronic GM bank, Growl, Voice
  or any patch file); every MIDI track/channel becomes its own arranger track; the GM program picks a matching Ling Synth X
  patch (128 GM names, one voice per instrument family), channel 10 becomes the Ling Membrane drum kit (GM percussion map).
  Imports set the project tempo when the project is still empty; tempo changes inside the file are not followed.
* **Audio/MIDI settings** — Settings > Audio: output device, device buffer size, safety buffer (render-ahead, the main dropout
  control), engine sample rate, live CPU load / latency / dropout counter. MIDI: input device (remembered), channel filter. Audio In:
  recording device. Audio is rendered on its own thread into a ring buffer; the device callback only copies frames.
* **Parallel rendering** - tracks that do not feed each other (sends / sidechains) render side by side on a pool of DSP threads,
  and stem export renders several stems at once. The audio is bit-identical to rendering one track after another.
* **Window** — resizable and maximizable (title-bar button / double-click); the UI re-lays out to the new size.
* **Project** — *Save* puts a new project in `./projects/<name>/` next to the program (no dialog; *Save As* picks another
  folder). The folder is self-contained: `project.json` (notes, lyrics, automation, effects, routing, scenes, instrument
  state), `audio/` (recordings and imported audio, copied in), `soundfonts/`, plus `midi/` (one standard MIDI file per
  track and `project.mid`) and `patches/` (each track's instrument as a `.ling` patch) for other programs. Writes are atomic.
  Undo/redo, autosave, recent projects, templates, render to WAV / FLAC / OGG (in the background, with a progress bar and cancel).

### Keys (FL Studio style - Help > Shortcuts shows them all)
Space play/pause · Enter play from start · Home/End rewind / jump to end · R record · L loop · F5-F8 Arranger / Parameters /
Piano Roll / Node Editor · F9 mixer · F10 settings · F11 Performance · F12 sidebar · Tab next view · F2 rename · Esc deselect ·
+/- zoom · , . grid snap · Ctrl+Z/Y undo/redo · Ctrl+S/O/N · Ctrl+R render · Ctrl+I import · Ctrl+C/X/V · Ctrl+A · Ctrl+B duplicate
selection · Ctrl+D duplicate track · Shift+M / Shift+S mute / solo track.
Arranger: P draw · E select · Y move · S slice · Up/Down track · Left/Right nudge. Piano roll: P/E tools · Q quantize ·
Shift+Q humanize · [ ] note length · Up/Down semitone (Shift = octave). Parameters: PageUp/PageDown presets.
**Caps Lock** turns the typing keyboard into a piano (A W S E D F T G Y H U J K, Z/X octave); with it off the letters are tools.

## Instrument patches

```text
# Ling Synth X patch
name = "Sub Bass"
type = synth                     # synth | drums
wave=0.66 sub=1.0 cutoff=0.3 reso=0.4 detune=0.1 attack=0.05 release=0.16
fx Delay time=0.2 feedback=0.35 mix=0.25
```

Values are normalised 0..1. Drum kits use `<piece>.<param>` keys (`snare.tune=0.4`). Load one from the
Parameters tab, from *Mixer > + > Plugin Picker*, or write a new one and drop it into `instruments/`.
`tools/make_patches.ling` regenerates the stock set.

## Development

`docs/DEV_NOTES.md` documents Ling's quirks and the full `daw_*` API. UI changes can be checked headlessly:

```powershell
python tools/shot.py out.png -- --shot 30 --tab 3 --drive "10,400,300,0;11,400,300,1;12,400,300,0"
```

## UI toolkit

The interface is drawn by Ling itself: `px_*` primitives and vector text (fast, clip-aware, alpha-blended) for the
egui-style chrome, plus **ling-ui's `ui_*` vector controls** (rings, sliders, toggles, checkboxes, tabs) where they fit
- knob arcs, the master level slider, Settings tabs/toggles and the Record-mode checkboxes. ling-ui's Rust-only types
(`MenuBar`, `ShortcutRegistry`...) are not reachable from scripts, so menus and shortcuts are implemented in Ling.

## Known limitations

* VST3 / CLAP hosting and the online licence/activation are not part of this port (Ling instruments replace plug-ins).
* Run with `--interp` (the launcher does): Ling's default JIT backend currently panics compiling this program.
  The tree-walking interpreter runs the UI at roughly 30-60 fps depending on the project.
* The window close button cannot be intercepted by scripts, so there is no "unsaved changes" prompt on exit
  (File > Quit has one; an autosave is written next to the project, or to `autosave_untitled.json`).
* The engine renders at 44.1/48 kHz and resamples (linear) to the device rate, so 96/192 kHz interfaces cost no extra DSP.
* Warp / transpose use a WSOLA stretcher (good for rhythmic and simple material, not Rubber Band quality).
* MIDI hardware input and audio-input recording use the OS default devices and were not exercised on real hardware here.
* The Performance launcher copies clips into scenes; there is no drag-and-drop from the arranger (use a pad's right-click
  menu > *Copy Selected Arrangement Clip Here*).

## Tests

```powershell
cd ..\ling ; cargo test -p ling-daw      # audio engine: DSP, sequencing, routing, launcher, export, hostile-input robustness
cd ..\LingStationX ; python tools\smoke.py   # opens every tab / dialog / menu in the real app
```

← Back to index