sobota 28. prosince 2013

Detekce hudebních akordů regulárním výrazem

V posledních několika měsících jsem programoval aplikaci pro zobrazování písniček včetně akordů. Akordy byly buď označeny zvláštní značkou (například v závorkách) nebo jen jako součást textu a mým úkolem bylo akord rozeznat pro potenciální transpozici. Transpozice je posunutí všech tónů v písničce o stejný interval. Tím způsobem se pro písničku změní tónina. Pro to je potřeba napřed každý akord správně detekovat a všechny jeho části.

Struktura zápisu akordu

Akordy se zapisůjí různě a pro jeden akord existuje často mnoho zápisů. Následující odkazy obsahují možný výčet akordů:

http://www.supermusic.sk/akordy.php http://www.akordytexty.cz/

Ze zápisů jsem vybral několik akordů:

Cmi6add9 Cmimaj7 C7maj3/A

Není ani tak nutné vědět co akord znamená, ale poznat co je akord a co obsahuje za tón (tóny).

Z příkladů akordů jsem rozdělil akord na několik částí - Tón akordu (C) + modifikace (mi6add9, mimaj7, 7maj3) + / + 2. tón akordu (A)

Tón akordu

Tón akordu je písmeno, které reprezentuje tón. Písmeno může následovat ještě posunutí (béčko, křížek, dvojté béčko, dvojitý křížek). Tón akordu může být písmeno A-H. Křížek - zvednutí i půltón se reprezentuje znakem sharp (#), béčko se reprezentuje zpravidla malým b, případně příponou "es" (u tónů A a E je to jen přípona "s"). V českém zápise po tónu A následuje tón H a jeho snížení o půltón je B, neexistuje snížený tón "Hes". V západním zápise neexistuje tón H a nahrazuje ho tón B, jeho snížení je potom Bb. Je tedy faktický půltonový rozdíl mezi západním B a českým B! U dvojitého snížení je to potom přípona "eses", případně "sas" pro A a "ses" pro E. Regulární výraz pro tón je potom následující:

[ABCDEFGHabcdefgh](\#|b|ses|sas|es){0,2}

Tento výraz má v sobě určitou volnost, protože detekuje i akordy jako třeba "Asassas", detekuje ale všechny možné validní varitanty. Pro přesnější zápis by bylo třeba pro A a E vytvořit výjimky. V případě, že se používá pro zápis jen křížek, béčko a dvojité snížení a zvýšení nehrozí (protože se v praxi na zápis u písniček nezapisují - nemají smysl), regulární výraz se dá zjednodušit na:

[ABCDEFGHabcdefgh](\#|b)?

Modifikace akordu

Modifikace obsahují zpravidla trojpísmenové heslo nebo číslo (1-2 ciferné). Někdy obojí. Některé akordy mají obojí a dvakrát za sebou (mi6add9). Pro pojmutí takové mofikace je nutné mít výčet možných hesel (z odkazů výše) a pak 1-2číslí tyto hesla s číslicemi se mohou opakovat 0-2krát. Regulární výraz pro modifikaci potom vypadá následovně:

((maj|mi|sus|add|m)[1-9]{0,2}){0,2}

Druhý tón

Některé akordy poté ještě obsahují lomítko a další tón. Tento tón má stejná pravidla jako první tón, akorát je následován lomítkem, opět mohou následovat modifikace akordu, i když ne v takovém množství:

(\/[ABCDEFGHabcdefgh](\#|b)?)?

Výsledný výraz

Z předchozích úvah se dá vytvořit složitější nebo jednodušší verze výrazu. Liší se počtem možných zápisů, které výraz přijme, ale také počtem chybných, které výraz přijme. Já osobně používám následující, protože akordy jsou výhradně české, neobsahují dvojité snížení aní zvýšení:

([A-Habcdefgh]\#?b?)(?:sus|maj|mimaj|add|aug|dim|min|mi|m|b|\+)?[1-9]{0,2}(?:(\/)
([A-Habcdefgh]\#?b?)(?:sus|maj|mimaj|add|aug|dim|min|mi|m|b|\+)?[1-9]{0,2})?

Tento výraz mam rozdělený na několik částí, sestavení v jazyce java vypadá následovně:

 private static final String SINGLE_TONE = "[A-Habcdefgh]\\#?b?"; // b

 private static final String TONE_ADDITIONS = "(?:sus|maj|mimaj|add|aug|dim|min|mi|m|b|\\+)?[1-9]{0,2}";

 public static final String FULL_CHORD = "(" + SINGLE_TONE + ")" + TONE_ADDITIONS + "(?:(\\/)(" +
SINGLE_TONE + ")" + TONE_ADDITIONS + ")?";
Edit:

Výsledný výraz používám v aplikaci Zpěvník pro android, více o aplikaci na http://karel-hovorka.eu/zpevnik/.