Adaptive guitar fret finder algorithm implementation
Project description
Guitar fret finder implementation in Python
This repository has a re-implementation of the algorithm proposed in the following research article (written in Brazilian Portuguese), available at http://www.lta.poli.usp.br/lta/publicacoes/artigos/2008:
Bellini, D.J.S.; Tavella, A.C.B.; Conversão de partituras para tablaturas usando algoritmo baseado em autômato adaptativo. In: Segundo Workshop de Tecnologia Adaptativa - WTA 2008. EPUSP, pages 39-42, 2008.
The algorithm can be regarded as a depth-first search implemented through an "adaptive Turing Machine" model (a custom alternative to the adaptive automatons proposed by João José Neto) with synchronized input/output "tapes" that can go back and forth. One of the tapes has the data input (musical score/staff as a list of fret numbers for each string, one list for each note) while the other has the output (the string index where one should play the notes from the input).
Installation
That can be done directly from PyPI:
pip install fretfinder
Example
It's possible to run the Greensleaves excerpt example from the paper
with the following terminal command
(it also works with python -m fretfinder
):
fretfinder -rt Bass4 -M14 'A3 C4 D4 E4 F4 E4 D4'
Or, from Python:
>>> from fretfinder import Tablature, Guitar, Staff
>>> tab = Tablature(
... staff=Staff("A3 C4 D4 E4 F4 E4 D4"),
... guitar=Guitar("Bass4", max_fret=14),
... reverse=True,
... )
>>> tab.strings # Result of fretfinder.find_strings(...)
[[2], [1], [1], [0], [0], [0], [1]]
>>> print(tab.ascii_tab())
G3|----------9-10-9----||
D3|----10-12--------12-||
A2|-12-----------------||
E2|--------------------||
The guitar tuning is configured using the same syntax of a melody.
For more complex staves, use "R
" for rests
and parentheses to group simultaneous notes.
For more details, use:
python -m fretfinder --help
Differences between the paper and this implementation
Most of the content in this repository tried to emphasize the way the algorithm was proposed in the cited research article, but there are a few differences between this implementation and the original specification, as follows:
-
Most configuration defaults are the same of the paper, but one: use
--disallow-open
explicitly to get the original algorithm behavior. -
The following names were internally modified:
MinX
andMaxX
were renamed tomin_x
andmax_x
, not a big issue, that's just to follow the PEP8 convention;- The
X(i)
function was renamed to theis_valid_range
method; - The
Ux
andBx
adaptive actions were implemented asupdate_x
andbackup_x
, which are also their descriptive names in the paper; fretStack
was renamed tofret_history
, it really behaves as a stack for most of the time, but the new name is more descriptive for what it really stores, and it's easier to access its contents without requiring to pop-and-push its contents back and forth just to update themin_x
andmax_x
.
-
The
Ux
(update_x
) adaptive action wasn't explicitly seen as a parametrized function in the original article, unlike the "string" states and theX(i)
(is_valid_range
) function. In some sense, that's not really a difference, but an implementation detail, as the behavior is the same as the one described in the article. That parameter is required in order to get the fret number regarding the next string state the "automaton" is going to be in, something that can be interpreted as a distinctUx
for each possible target string (a parametrized implementation), or a singleUx
that "knows" the target state (the article description). One can store the fret number when it's required to evaluateX(i)
, that's what the article assumes that had been done since it's an optimization to avoid calculating the same fret number twice. Here the fret number is calculated twice because of a strict "software engineering" constraint: neither the state handlers norX(i)
should have any collateral effect as they're neither adaptive actions nor state transitions, and the adaptive actions in this implementation doesn't "know" the next target state, henceUx
becomes a parametrized "Ux(i)
". -
The
--reverse
option (thereverse
keyword argument) was created to let the evaluation begin from the last string to the first to solve the indeterminacy when more than one string state is a possible next state. The article proposes that the string order should be from the first string (the highest pitched one) to the last string (the lowest pitched one), and that's the default behavior of this implementation. On the other hand, the example from the article results from applying the algorithm in the reverse direction for an output whose last fret is 14 (e.g. to play on an acoustic guitar), that's why this option had been included. -
The original paper tells us that the output tape storing action was linked with the tape transitions to left/right. The same happens in this implementation, but the code doesn't enforce any link between the tape transition and the output tape storing action.
-
There's no "open string" for the algorithm proposed in the paper. Here it's implemented as the
--allow-open/--disallow-open
options (theallow_open
keyword argument in Python), which makes the notes bypass the valid fret range. These open string notes are also filtered out from the fret history window, but they count as part of the window size. -
The
--distinct-only
option (thedistinct_only
argument) has nothing to do with the paper, it's another way to avoid the issue regarding the tremolo picking, which was the reason underlying the choice of the default window size of 7.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file fretfinder-1.0.0.tar.gz
.
File metadata
- Download URL: fretfinder-1.0.0.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.54.1 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | e0738898e9ed183a1585762994d97cb8a6a27829bc865dd328e180b54558783c |
|
MD5 | 046c2b8e1e0c2e0d98311f0b31272abf |
|
BLAKE2b-256 | 864806b0e8fc6adcac21e0c349a894324ece4529eb1f01b46f46b3e173908461 |