Docs / Guides

Write the music

Read the episode's timing, write a score as a song file, check it, and play it into a music track on your computer.

Your agent writes the music as a short Python script. The script saves a song file, which is data: instrument names, notes and times in seconds. MiniCut checks the song file and plays it with the instruments it brings. MiniCut never runs your script.

Before you start, sign in and make a project with a shot sheet for the episode, episodes/ep01/shots.md. See Shot sheet. The script needs Python 3.9 or newer. To play a song, run minicut setup once. It installs the music tools and the instrument samples. The samples are about 396 MB. Run the commands in the project folder, ~/.minicut/projects/rain-shop here.

Read the timing

  1. List the shots, the cuts and the spoken lines of the episode, in seconds:
Terminal
minicut music cuts episodes/ep01
Output
~/.minicut/projects/rain-shop/episodes/ep01: 15.0 s, 3 shot(s), 2 cut(s)
  C01      0.00 to    5.00 s  street         sound: rain
  C02      5.00 to   11.00 s  shop           sound: 
  C03     11.00 to   15.00 s  shop           SILENT sound: silence
  cut    5.00 s into C02  scene change
  cut   11.00 s into C03  into silence
  line 5.00 to 11.00 s  Lin: Are you open?
  lines from: shots.md Line (speaker) cells (whole row, rough)

The spoken lines come from the Line (speaker) column of shots.md, rough to the whole shot. When the episode has a transcript.json, they come from its word times. Add --json to get the same facts as JSON.

  1. List the instruments:
Terminal
minicut music instruments

It prints one line for each instrument with its note range, then the knobs and the reverb presets. A drum instrument lists its pieces instead of notes.

Write the score

  1. Print the import lines for your script:
Terminal
minicut music api
  1. Make the file episodes/ep01/music/score.py. Paste the two lines the command printed at the top. Then write the score. Every time is in seconds.
score.py
# Paste the two lines that minicut music api printed here.

ep = read_episode("episodes/ep01")
song = Song(length_s=ep["length_s"], title="ep01 score", tempo=72)
strings = song.track("violins", volume_db=-8)
strings.chord(chord("D3", "minor"), at=0, length=4, velocity=60)
drum = song.track("timpani")
for cut in ep["cuts"]:
    if cut["scene_change"]:
        drum.hit("low", at=cut["t"], velocity=110)
song.duck_under(ep["lines"], db=-8)
song.save("episodes/ep01/music/score.song.json")

duck_under turns the score down under each spoken line. save checks the song and writes it. On a problem it writes nothing, prints each problem and exits with code 5.

  1. Run the script with your own Python:
Terminal
python3 episodes/ep01/music/score.py
Output
Wrote episodes/ep01/music/score.song.json: 15.0 s, 2 track(s), 4 note(s). Next: minicut music render episodes/ep01/music/score.song.json

A song is up to 15 minutes long and has up to 64 tracks.

Check and play the song

  1. Check that the song file can be played:
Terminal
minicut music check episodes/ep01/music/score.song.json
Output
PASS: 15.0 s, 2 track(s), 4 note(s). Next: minicut music render ~/.minicut/projects/rain-shop/episodes/ep01/music/score.song.json

If it fails, it prints each problem and FAIL, and exits with code 5. Fix the script, run it again and check again.

  1. Play the song into audio files:
Terminal
minicut music render episodes/ep01/music/score.song.json

It writes score.wav, score.mp3, score.beats.json, score-waveform.png and score.report.json beside the song file.

  1. Optional: to play one stretch while you work on it, add --from 9 --to 12. To also write each instrument as its own file, add --stems. To skip the .mp3, add --no-mp3.
  2. Measure the result against the episode:
Terminal
minicut music measure episodes/ep01/music/score.wav --episode episodes/ep01

It reports loudness, peaks, hits on the cuts and the music level under the spoken lines. Change the script and play again until the numbers fit the shot sheet.

Optional: start from a MIDI file

  1. Write a map file that says which instrument plays each MIDI track. A key is TRACK:CHANNEL or the track's name. A drums key maps a MIDI note to INSTRUMENT/PIECE.
map.json
{
  "tracks": { "0:0": { "instrument": "piano", "volume_db": -6 } }
}
  1. Turn the MIDI file into a song file:
Terminal
minicut music from-midi episodes/ep01/music/theme.mid --map episodes/ep01/music/map.json

It prints Wrote with the path of the new .song.json file, its length, tracks and notes, and the next command to run.

A track or drum note that is not in the map is left out, and the command prints left out: with its name. Then check and play the song file as above.

Check it worked

score.wav and score.mp3 are in episodes/ep01/music, and the measure numbers match what the shot sheet asks for. The waveform picture score-waveform.png shows where the music is loud and quiet.

If a step fails

MessageWhat to do
shots.md is missingWrite the shot sheet first. See Shot sheet
no Python found. Run: minicut setupRun minicut setup, then run the command again
the music tools are not set up (...). Run: minicut setupRun minicut setup, then run the command again
no instrument samples in .... Run: minicut setup (about 396 MB, once). Nothing was rendered.Run minicut setup, then render again
FAIL: N problem(s).Fix the problems printed above it in your script, then run it again
the song file is over 32 MBMake the song smaller
Stopped: it ran past the time limit.Render a shorter part with --from and --to

Next