Singing voice synthesizer using LPC for classic robot-sounding singing
README.md

Savvas #

A small LPC singing-voice synthesizer. Uses a source-filter model, Rosenberg glottal pulse train/band-limited noise driving LPC. This implementation is a test to see how closely this style of LPC voice synthesis matches the Teenage Engineering Choir for later use in a project with the same algorithm running on an ESP32.

Vowels and melody #

cargo run --release -- sing --melody "A4:0.45:a C5:0.45:e E5:0.9:i" --out demo.wav
cargo run --release -- sing --voice take.wav --melody "C4:0.6:o E4:0.6:o G4:0.9:o" --out out.wav

Melody notes are NOTE:DUR[:VOWEL]; r:DUR is a rest, #/b for accidentals.

Consonants and text #

--phonemes takes explicit X-SAMPA phones; --text runs a crude English letter-to-sound front end. Both split into syllables and take one melody note per syllable; without --melody the syllables get a plain chant.

cargo run --release -- sing --text "hello world" --out hello.wav
cargo run --release -- phonemes --text "the quick brown fox"
cargo run --release -- sing --phonemes "h V l oU | w 3: l d" --melody "A3:0.5 A3:0.4 E3:0.9" --out out.wav

Pieces #

song renders a score file, which is made of tracks with explicitly defined musical parts. A vocal track entry is TIME NOTE DUR PHONES (phones dash separated), and a piano track entry is TIME NOTES DUR AMP where NOTES is a chord joined by +. A vocal track header can cast a singer: preset=bass|baritone|tenor|alto|mezzo|soprano|child sets the whole vocal quality, and individual settings (formant=, cluster=, tilt=, vibrato=, vibrato_rate=, glide=, breath=) override it. The singer's formant cluster moves with the tract length, so a bass and a soprano differ above 2 kHz as well as below; cluster= scales it further. Score-level lines reverb <0..1> and humanize <ms> set the wet mix and the timing drift, so a piece renders the same way without flags; the command line overrides them.

cargo run --release -- song --file songs/daisy.song --out daisy.wav
cargo run --release -- song --file songs/chorale.song --out chorale.wav
cargo run --release -- song --file songs/chorale_four.song --out chorale_four.wav

songs/daisy.song is the chorus of Daisy Bell (Harry Dacre, 1892) with a simple piano part. songs/chorale.song is Bach's O Haupt voll Blut und Wunden (BWV 244/74) in four voices. songs/chorale_four.song is Bach's Ach Gott, vom Himmel sieh darein (BWV 153), also in four voices.

install #

flake.nix packages the binary and exports an overlay, so it can be a system command rather than something you build yourself.

nix profile install .                     # or: nix run . -- song --file songs/daisy.song --out x.wav

On NixOS, take it as an input and apply the overlay:

{
  inputs.savvas.url = "path:/mnt/hdd0/Code/savvas";
  outputs = { self, nixpkgs, savvas }: {
    nixosConfigurations.host = nixpkgs.lib.nixosSystem {
      modules = [{
        nixpkgs.overlays = [ savvas.overlays.default ];
        environment.systemPackages = [ pkgs.savvas ];
      }];
    };
  };
}

The build runs the test suite in the sandbox.

The phone inventory is X-SAMPA and covers the IPA chart: 112 phones, including the German set (x, C, y, Y, 2, 9, 6, R, p_f, t_s), the pulmonic consonants, the cardinal vowels, and ejectives, implosives and clicks. savvas phonemes --all lists them.

A track may set pan=-1..1; a song with any pan set renders stereo.