Skip to main content

Make music with Mac's say command

Project description

Make music with Mac's say command.


🔊 Demos

saysynth sounds like this:

<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/1519081741%3Fsecret_token%3Ds-VO1a2l6AiiT&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>

🙋 About


saysynth is a a synthesizer built on top of Apple's built-in Speech Synthesis framework, first introduced nearly 30 years ago, when Steve Jobs demoed "Fred". saysynth provides utilities for synthesizing notes, chords, arpeggiated melodies, multi-track sequences and more!

☞ how it works

At some point in Fred's development, Apple decided they needed to give developers the ability to control the pitch and speaking rate of his voice. These capabilities were provided via a domain-specific language (DSL) Apple created to allow users to control the duration and pitch contours of individual phonemes. Eventually, this DSL was expanded to support "Alex" and "Victoria", two other built-in voices. The syntax for this DSL looks like this:

AA {D 120; P 176.9:0 171.4:22 161.7:61}

Where AA is a valid phoneme, D 120 is the duration of the phoneme in milliseconds, and P 176.9:0 171.4:22 161.7:61 represents the pitch contour for the phoneme in colon-separated pairs of frequency and percentage duration.

saysynth works by harnessing this DSL to create musical passages with the say command, mapping notes onto their associated frequencies via midi-utils, generating phonemes with pitch contours (as described in Apple's Speech Synthesis Programming Guide), and spawning multiple subprocesses in Python to create polyphonic, mostly drone-oriented music. Rudimentary text-to-speech capabilities are provided by g2p-en, a library for extracting phonemes from words, though, as of now, some trial and error is necessary to get this sounding intelligible.

🛠️ Installation


saysynth only works on Mac OS X machines with a working say installation. By default, the path to the executable is set to /usr/bin/say. You can override that path by setting the environment variable SAYSYNTH_SAY_EXECUTABLE.

☞ via pypi

First, install python via homebrew (eg: brew install python)

Next, run:

pip install --user --upgrade saysynth

You should now be able to run sy --help. This command will also update a currently-installed instance of saysynth.

💻 Command-Line Interface (sy)


saysynth is primarily designed to be used via it's command-line interface (sy for short).

You can view all commands (and their corresponding docs) by runnings sy --help:

Usage: sy [OPTIONS] COMMAND [ARGS]...

  Make music with the `say` command.

Options:
  --help  Show this message and exit.

Commands:
  chord    Generate a polyphonic chord.
  version  Print the current version of saysynth to the console.
  list     List all currently running saysynth processes.
  midi     Synthesize a melody from a fully-monophonic midi file.
  stop     Stop currently running `say` processes by `sequence`, `track`,...
  font     Given a scale and other parameters, generate a soundfont of...
  arp      Generate an arpeggiated melody.
  demo     Play a built-in demo.
  note     Generate an individual note.
  seq      Play a sequence of `chord`, `midi`, `note`, and/or `arp`...

Below are basic details on each command's functionality. For a detailed overview on how to make music with saysynth, refer to this blog post.

☞ sy note


sy note accepts a note name (eg: C3) or midi note number (eg: 69) and generates input to the say command which makes a monophonic note.

examples

Play the note D#2 randomizing the phoneme each segment by choosing from the drone-like phonemes for Freds voice.

sy note D#2 --randomize-phoneme Fred:drone --randomize-segments phoneme

You can see the full list of options for this command via sy note --help.

☞ sy arp


sy arp accepts a chord root (eg: C3), chord name, and list of styles to generate a melodic, arpeggiated sequence of speech synthesis.

example

Play an acid-like sequence:

sy arp E0 `# set the root of the arpeggiator to E-1` \
  --chord-notes 0,3,5,7,9,12,14,25,31 `# set the notes of the arpeggiator` \
  --text '. TEE BEE THREE OH THREE  .' `# text to sing` \
  --styles 'down,random_shuffle,random_octaves' `# arpeggiator style names come from the midi-utils module.` \
  --beat-bpm 160 `# the bpm to use when applying the note-count ` \
  --beat-count 1/16 `# the duration of each beat in the arpeggiator` \
  --note-bpm 160 `# the bpm to use when applying the note-count` \
  --note-count 1/16 `# the duration of each note` \
  --segment-bpm 160 `# the bpm to use when applying the segment-count` \
  --segment-count 1/32 `# the duration of each phoneme segment` \
  --velocities 60,90,127 `# a list of velocities to apply in order to the outputted notes` \
  --duration 15000 `# the total duration of the arpeggiator in milliseconds` \
  --render-volume-level-per-note 5 `# see docs` \
  --render-volume-level-per-segment 5 `# see docs`

You can see the full list of options for this command via sy arp --help.

☞ sy chord


sy chord accepts a chord root (eg: C3) or midi note number (eg: 69), a chord name (eg: min6), and other parameters to spawn multiple say commands that generate a polyphonic chord.

example

Play a slowly-evolving minor 6th chord:

sy chord C2 `# the root of the chord` \
  --chord min6 `# the name of the chord (comes from midi-utils)` \
  --duration 60000 `# the duration in ms` \
  --segment-bpm 155 `# the bpm to use when using --segment-count` \
  --segment-count 1/16 `# the duration of each segment in the note` \
  --attack 0.5 --decay 0 --sustain 0.5 --release 0.5 `# ADSR settings` \
  --randomize-segments phoneme `# phoneme-level randomization settings` \
  --voice Alex `# the voice to use, either Fred, Victoria, or Alex` \
  --phoneme m,OW,EW `# list of phonemes to randomly pick from` \
  --volume-range 0.03 0.33 `# min and mix of volume range`

You can see the full list of options for this command via sy chord --help.

☞ sy font


sy font enables the generation of "soundfonts" or directories of individual sound files, which can be used in a sampler or DAW to create custom instruments. All synthesis parameters from sy note can be modified in sy font.

example

Create a directory of audio files, one per pitch in a specified scale. These can be used to create instruments in a DAW / livecoding environment of your choice:

mkdir -p tmp `# create an output directory`
sy font \
  --scale-start-at C2 `# the lowest note of the scale to generate` \
  --scale-end-at C5 `# the highest note of the scale to generate` \
  --key C `# the key of the --scale` \
  --scale=octatonic_whole `# the scale to use when selecting the notes to generate. (from midi_utils)` \
  --output-dir tmp/ `# the directory to write each file to` \
  --format aiff `# the format of each file` \
  --duration 1000 `# the duration of each file`

You can see the full list of options for this command via sy font --help.

☞ sy midi


sy midi accepts a midi file and generates pitched phonemes. The midi files must be fully monophonic. (In other words there must not be any overlapping notes. Eventually I'll figure out this issue, but for now there is a helpful error message which indicates the name of an overlapping note and the time at which it occurs. You can then use this information to edit your midi file in whatever DAW you use. There is also no support for multi-track midi files, though that will be less challenging to implement.) sy midi then maps the notes in the midi file onto pitched phonemes

example

To run this example, clone this repository and execute the following command from the root directory. Alternatively, generate your own midi file and replace it's path with examples/arp.mid.

Play a high-pitched sequence from a a midi file.

sy midi examples/arp.mid --phoneme m

You can see the full list of options for this command via sy midi --help.

☞ sy seq


sy seq accepts a yaml filepath specifying multiple saysynth commands to be concurrently executed.

The yaml file might look something like this:

name: my-sequence # the name of the sequence
globals: # configurations shared between all tracks
  duration_bpm: 80
tracks: # list of tracks / configurations
  chord1:
    type: chord
    options:
      root: E3
      chord: min6
      duration_count: 128
      segment_bpm: 80
  note1:
    type: note
    options:
      start_bpm: 80
      start_count: 2
      root: F#3

Where globals define options shared between all tracks, each of which have a type which corresponds to a saysynth command (chord, midi, note, and/or arp) and a set of options.

subcommands

sy seq provides multiple subcommands to control the behavior of your sequence. These include:

  • play: Play the sequence as-is, from beginning to end, respecting any start_* configurations.
  • start: Launch all tracks in the sequence immediately, irregardless of any start_* configurations.
  • stop: Stop one or more tracks currently playing from the sequence.
  • echo: Print the sequence to the console.
  • render: Render all tracks in the sequence as separate, monophonic audio-files.

Each of these subcommands accepts command line flags, as well. For instance, --tracks allows you to play, start, stop, or render only certain tracks in the sequence. Similarly --audio-devices allows you to filter tracks which are configured to play on certain audio outputs. --config-overrides provides the ability to override global and track-level configurations at runtime by passing in yaml-formatted configurations, eg: -c '{"foo":"bar"}'. These configurations can be specified at the track-level by nesting them under the track name, eg: -c '{"track":{"foo":"bar"}}'. You can also override configurations by providing extra command line arguments available to midi, note, chord, rand/or arp tracks, eg: -sd 10 or --segment- duration 10. These can be similarly nested by using a __ separator, eg: --track__segment-duration 10. Parameters specified via the --config-overrides option will take precedence over any extra CLI arguments. Finally, --output-dir allows you to specify the directory to write audio files into as a part of the render command.

example

To run this example, clone this repository and execute the following command from the root directory. Alternatively, generate your own yaml file and replace it's path with examples/hello-world.yml.

Launch a multi-track sequence from a yaml file and stop it after 10 seconds:

sy seq play examples/hello-world.yml
sleep 10
sy seq stop examples/hello-world.yml -t hello_world

You can also see an archive of my past saysynth performances for examples of sequences.

You can see the full list of options for this command via sy seq --help.

☞ sy stop


sy stop allows you to stop currently running saysynth processes by sequences, tracks, audio_devices, and/or parent_pids. Omit all the flags to stop all running processes.

example

Launch a couple notes, wait 10 seconds, and then stop them:

sy note D#3 -rp Fred:drone
sy note G#3 -rp Fred:drone
sleep 10
echo "stopping all notes now!"
sy stop -t note

☞ sy demo


sy demo is a wrapper for sy seq and allows you to play built-in demo sequences. Live recordings of these demos are also for sale on basecamp.

example

Play the built-in demo fire:

sy demo play fire

You can see the full list of built-in demos. for this command via sy demo --help.

☞ sy version


sy version prints the current version of saysynth

example

Print the currently-installed version of saysynth:

sy version

🤝🏽 Development / Contributing


If you're interested in contributing to saysynth or would like to report an issue, all development is done on gitlab. You can also reach out to me via hey [at] gltd [dot] email. I'm particularly interested in working with interface designers to turn this into a free VST, or something similar.

To install via git for local development:

git clone https://gitlab.com/gltd/saysynth.git # clone this repo
cd saysynth && python -m venv .venv            # create a virtualenv with Python 3.9 or higher
source .venv/bin/activate                      # activate it
make install                                   # install the library
saysynth --help                                # check if it worked
make test                                      # run the tests
make docs-html && make docs-view               # compile and view the docs (via: pdoc)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

saysynth-1.0.3a0.tar.gz (50.9 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page