Music compiler and other tools
How to add new music and create instruments
Technical, minimalist page
This tools documentation is currently the bare minimum to get started.
Also, the tools do not have a friendly user interface: some skill with the command line, including executable paths and such, is required. If you use them often enough, you may want to define .bat files or shell scripts to avoid repetitive typing.
Linux / OS X note: the example commands are for Windows; for Linux and OS X use forward slashes ('/') instead of ('\').
Lua interpreter required
All play-v6 tools are written in the Lua language and are system-independent: I tested them under Windows, Linux and OS X. Please let me know if you encounter problems.
You need a Lua 5.2 interpreter to run the tools. You can get it here (just download the executables).
The examples in this page assume that the Lua 5.2 interpreter is named lua52
and is in the executable path.
Tools included in the package
The following tools are in the distribution:
The music compiler
The music compiler takes a music source file (a text file) and compiles it into a music code file, a compact block of binary data ready to be sent to play-v6, as described in the playing music page.
To use the compiler, go into the music
directory and type:
lua52 ..\tool\musicomp\musicomp.lua Greensleeves.txt Greensleeves.code
This will produce a greensleeves.code
music code file (as noted above, lua52
should execute the Lua 5.2 interpreter; also remember to use '/' instead of '\' for Linux and OS X).
The source music syntax is quite versatile... and presently quite lightly documented. Please refer to the music samples, i.e. the .txt files in the music
directory. Here are a few pointers to get started (have a look at greensleeves.txt
):
# Greensleeves (traditional) [main]
[main]
section name. The main
section is compiled by default (you may choose to compile other sections).These are global commands that must be present at the start of a section (they can also be used in the middle of a music piece); they act on all voices:
time:3/4 tempo:4=140 volume:f
time:3/4
generates musical accents for a 3/4 musical time (automatic accents are currently always enabled).tempo:4=140
means that the execution speed will be 140 quarter notes per minute. Dotted durations are allowed (such as tempo:8.=120).
volume:f
sets the global volume control to f
; valid values range from ppp
to fff
. To use full dynamic range, the compiler will normalize the level to the highest volume actually used.The following lines are instaed per-voice settings, one line for each used voice; they too can be changed in mid-music:
instrument:violin mix:80% instrument:voice transpose:-12 mix:100% instrument:guitar transpose:-12 mix:65% release:off instrument:guitar transpose:-12 mix:65% release:off instrument:guitar transpose:-12 mix:65% release:off instrument:piano mix:100%
instrument:violin
is self-explanatory; refer to the instrument generator below for valid names, or for redefining them.transpose:-12
means to play the notes of the corresponding voice 12 semitones lower (i.e. one octave lower, in this case).mix:65%
sets the voice mixer control to the given level (0% = silent, 100% = full).release:off
disables the fast decay when a note terminates, making it decay naturally instead; its opposite is release:on
. It has no effect on instruments where notes have a natural decay, such as drums and, often, guitar.do5:2 re5:4 do5:2 re5:4 p:4 do4:4 do4:4 p:4 la4:4 la4:4 p:4 mi4:4 mi4:4 la2:2.
do5:2
or its equivalent notation c5:2
means a C note (DO in Latin notation), octave 5, for a duration of 1/2. Dotted notation is allowed, e.g. c5:4..
(note the two dots) has a duration of 1/4 + 1/8 + 1/16. For a pause use p
; for example p:4
is a 1/4 pause.do3:4_ _do3:8_ _do3:8.
will play as a single note, for a total duration of 1/2.t1
, t2
, t3
; for example do3:4t1 p:4t2 mi3:4t3
means that the two notes and the pause, together, have a duration of 1/4 (the total time is right, but the first note in a triplet will be slightly longer, due to timing limitations).You may ask: why not just use a MIDI file and avoid all this trouble?
There are at least two good reasons: first, MIDI files are relatively large, while the RAM available for the current music is severely limited (1800 bytes). Second, interpreting a MIDI file would require much more CPU time, a very scarce resource in the tight playing loop (interpreting a MIDI bus stream could be more feasible). Of course, if you want to write a converter from MIDI files or from LilyPond files to play-v6 music source or music code, you are welcome :-)
Once you have successfully compiled some music, you can play it via remote control. To add it to the on-board Flash memory, see the music packer below.
The instrument generator
The instrument generator has many functions. To just list the available instruments, enter the tool
directory and execute the program without any arguments:
lua52 instruments.lua
The first instrument (sine
) is a pure sinusoidal sound that can be useful for tests, measurements and educational applications.
To modify an existing instrument or create a new one, open the source file instruments.lua
and search for "instrument data"
. You will see a series of descriptive blocks, such as:
{ name = 'accordion', -21.5, -12.2, -33.2, -34.0, -31.8, -19.0, -31.1, -30.7, -44.0, -33.6, -47.7, -44.1, -47.8, -37.7, -46.6, -42.0, -74.3, -42.7, -52.5, -39.6, -54.9, -46.8, -61.5, -50.1, -59.6, attackDuration = 0.120, decayDuration = 0.8, sustainLevel = 0.5, releaseDuration = 0.120, testReleaseTime = 0.7, },
It is a good idea to copy an existing instrument block, paste it at the end of the list and change its name to something such as test
, to avoid messing up existing instruments (even if you are working to replace one of those).
sustainLevel
; it is a bit shortened in the image below for illustrative purposes (the last part is almost constant).sustainLevel
, or a very low value if a minimal residual sound is more realistic (e.g. for a guitar).A simple trick to choose harmonics levels for a given instrument is to record the sound of an actual instrument, perform a spectrum analysis (e.g. using Audacity), copy the measured values of the harmonics into the table and then listen to the test sound.
To generate a test sound for the new instrument, the SoX
utility must be installed and must be in the executable path. It can be downloaded from the SoX site.
The test sound can then be produced by adding the instrument name by going into the tool
directory and typing the command:
lua52 instruments.lua accordion
(you may get some warnings from SoX. I preferred to preserve compatibility with previous versions)
The resulting .wav
file will contain five G / SOL note samples for the desired instrument, at different octaves; try changing testReleaseTime
to see and hear the effect of different note durations (I know, this is a bit primitive, the instruments tool would deserve a real-time GUI).
Do not expect too much: an actual musical instrument has a very complex and rich sound that changes with time and is different for each note, while play-v6 can only reproduce a single waveform.
When you are satified with your instrument, it is time to add it to play-v6 in Flash memory. First, try this to be sure there are no errors in the instrument tables:
lua52 instruments.lua c
It should print out on the console a lot of C code, without stopping with an error message. If all is OK, it is time to update the instruments table of play-v6 source code with these two commands (be sure you are in the tool
directory):
lua52 instruments.lua c >..\player\instrum.c
lua52 instruments.lua list >musicomp\instruments.list
If you have changed the name of existing instruments without altering their position in the list inside instruments.lua
, any music source file using those instruments will have to be changed accordingly (music already in Flash memory will play correctly anyway, with no need to recompile play-v6).
If you have changed the position of existing instruments in the list inside instruments.lua
, music files using those instruments will have to be recompiled and their new music code files must replace those already in Flash memory, using the music packer.
Lastly (after using the music packer, if needed) recompile play-v6 and send the program to the Arduino.
The music packer
The music packer is needed to store music into the Flash memory of the Arduino microcontroller, so that it could be played with no USB connection to a computer.
Go into the music
directory and open the builtin.list
file with a text editor. You will find something like this:
Greensleeves.code Yankee_Doodle.code Sor_op31n2.code
Change it to the list of music code files you would like have in the Flash memory, in the desired order, one file per line. To check that there are no problems (e.g. the name of a non-existing file) write:
lua52 ..\tool\musipack.lua builtin.list
It should print out on the console a lot of C code, without stopping with an error message. If all is OK, type this command to update the music code table of play-v6 source code (be sure you are in the music
directory):
lua52 ..\tool\musipack.lua builtin.list >..\player\music.c
Then recompile play-v6 and send the program to the Arduino. If you run out of available Flash memory space, the compiler (actually, the linker) should complain; in this case, remove one or more music files from the list and reissue both the above commands.
The table generator
The table generator produces the note table that play-v6 uses to generate the correct pitch (frequency) for a given note; for example an A4 (LA4) note should have a frequency of 440 Hz. It also prouces the tempo table that controls, for each musical tempo setting (e.g. 120 quarter notes per minute), how long an 1/64 interval will last.
Usually there is no need to change these tables; I used this tool to produce the C tables and it is documented here for completeness. In any case, the command to update the note and tempo tables in play-v6 source code is, from the tool
directory:
lua52 tables.lua >..\player\tables.c
A note about tuning: the table generator adds comments in player\tables.c
indicating the pitch error for each note, assuming an accurate microcontroller clock. Unfortunately the Arduino Uno uses a cheap and imprecise ceramic resonator instead of the usual crystal. This could add a pitch error up to about 20 cents, but it does not affect the relative pitch between different notes, so unless you are a trained musician with an 'absolute ear' you will probably be unable to tell the difference.
Updated 03/01/19 by Enrico Colombini (erix@erix.it)