TablaZinc
This is an experiment to generate tablatures for fretted string instruments -- such as the guitar, bass, mandolin, banjo, etc -- using a constraint solver.
The input is a melody, represented by an array of MIDI note numbers with no duration indication. The output is a tablature with fingering annotations for the fretting hand.
The src
and data
folders contain model files and data files
written in the MiniZinc language.
Disclaimer
I have no prior experience with constraint programming in general, and MiniZinc in particular.
If you are a beginner in this field and are looking for well-written examples, this repository might not be the right place.
If you are an expert, your feedback will be welcome. Please open a new issue.
Usage
The following command outputs a tablature from two data files (an instrument definition file, and a melody file) using a set of constraints specified in a model file:
minizinc src/<model-file> data/<instrument-file> data/<melody-file>
Example
We provide the following data files:
data/guitar-std.dzn
, instrument definition parameters for the guitar in standard tuning.data/yardbird-suite.dzn
, the first eight bars of the jazz standard Yardbird Suite by Charlie Parker, where consecutive notes with the same pitch have been merged.
Three model files are currently available:
src/tablazinc-satisfy.mzn
generates a tablature with the correct notes, but with no concern for playability.
-8i--3i--5i--6i--4i--0---1i--3i--0---1i--0---1i--3i--0-----------0---1i--3i--5i--0---1i--3i-
---------------------------------------------------------1i--3i-----------------------------
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
src/tablazinc-fret-distance.mzn
attempts to minimize the distance between consecutive notes on the fretboard. There is no constraint on which finger to use for each note.
-8i-----------------------------------------------------------------------------------------
-----8i-10i-11i--9i----------8i------------------8i--5i-------------------------------------
---------------------9i-10i------9i-10i--9i-10i----------5i--7i--9i-10i-12i-14i-------------
--------------------------------------------------------------------------------14i-15i-17i-
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
src/tablazinc-finger-distance.mzn
attempts to minimize the distance between finger locations for consecutive notes. This version can take several minutes to complete with the default solver and optimization level, even for such a short melody.
-8i-----------------------------------------------------------------------------------------
-----8i-10r-11r--9i---------------------------------------------------------10m-------------
---------------------9i-10m-12p--9i-10m--9i-10m-12p--9i----------9i-10m-12p------9i-10m-12p-
--------------------------------------------------------10m-12p-----------------------------
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
The fingering annotations use the letters i
(index), m
(middle), r
(ring), and p
(pinky).
If you have a different number of fingers, or if you want to use other letters,
you can change the parameter finger_ids
in the instrument definition file guitar-std.dzn
.
Performance
The tables below show the computation time, in seconds, of the following command with each model file and each supported optimization level. We are using the default solver Gecode with one or two threads.
minizinc -p <n> -O<m> --output-time src/<model-file> data/guitar-std.dzn data/yardbird-suite.dzn
Using one thread (-p 1
):
Model file / Optimization | -O1 |
-O2 |
-O3 |
-O4 |
-O5 |
---|---|---|---|---|---|
tablazinc-satisfy.mzn |
0.05 | 0.10 | 0.09 | 0.10 | 0.09 |
tablazinc-fret-distance.mzn |
11.51 | 12.21 | 12.04 | 0.12 | 0.11 |
tablazinc-finger-distance.mzn |
269.53 | 234.25 | 251.93 | 3.36 | 3.27 |
Using two threads (-p 2
):
Model file / Optimization | -O1 |
-O2 |
-O3 |
-O4 |
-O5 |
---|---|---|---|---|---|
tablazinc-satisfy.mzn |
0.04 | 0.09 | 0.09 | 0.10 | 0.09 |
tablazinc-fret-distance.mzn |
6.64 | 7.06 | 7.05 | 0.14 | 0.13 |
tablazinc-finger-distance.mzn |
123.26 | 106.84 | 140.68 | 20.13 | 17.64 |
Platform: computer with an Intel Core i5 CPU (2.7 GHz) running Ubuntu 18.04.
Optimization levels -O4
and -O5
show a huge improvement compared to the
other levels.
Using two threads usually result in longer solving times at the highest optimization
levels, but the performance can vary a lot between runs for no obvious reasons.
For instance, I have run the model tablazinc-finger-distance.mzn
with options -p 2 -O4
three times in a row and got: 0.76sec, 6.95sec, and 38.17sec.