Add harmony Automatyczna harmonizacja melodii DOKUMENTACJA Filip Łakomik
Spis treści Wizard...2 Odpowiedzialności...2 Sposób realizacji odpowiedzialności...3 Pola...3 Okna...3 Metody...4 Publiczne...4 Prywatne...4 Kody metod...4 Harmonizer...8 Odpowiedzialności...8 Sposób realizacji odpowiedzialności...8 Pola...9 Metody...9 Publiczne...9 Prywatne...9 Kody metod...10 RuleChecker...13 Odpowiedzialności...13 Sposób realizacji odpowiedzialności...13 Pola...13 Metody...14 Publiczne...14 Prywatne...14 Kody metod...15
Wizard Odpowiedzialności komunikacja z użytkownikiem konfiguracja algorytmu harmonizacji poprzez wyświetlenie kolejnych okien konfiguracyjnych; sprawdzenie poprawności wpisanych danych uruchomienie algorytmu harmonizacji Sposób realizacji odpowiedzialności Wyświetlanie kolejnych okien dialogowych informujących o znaczeniu poszczególnych parametrów algorytmu, umożliwiających zmianę ich wartości Nieumożliwianie przejścia do kolejnego okna jeśli wprowadzono błędne dane Pola ActDialog : string nazwa aktualnie wyświetlanego okna dialogowego. ChordsPathDlgMessage : string komunikat okna ChordsPathDlg FinishDlgMessage : string komunikat okna FinishDlg KeyDlgMessage : string komunikat okna KeyDlg RulesPathDlgMessage : string komunikat okna RulesPathDlg StartDlgMessage : string komunikat okna StartDlg _ChordsPath : string ścieżka do pliku z bazą akordów _RulesPath : string ścieżka do pliku z bazą reguł _KeyItems : array of integer ilości półtonów interwału o jaki mają być przetransponowane akordy do wyboru w ComboBoxie _KeyValue : integer interwał o jaki mają być przetransponowane akordy _r1, _r2, _r3, _r4, _r5, _r6, _r7 : real parametry kolejnych reguł, zmienne związane z TextBoxami w RulesDlg _RulesParameters : array of real tablica przechowująca parametry kolejnych reguł
Okna StartDlg okno powitalne; ChordsPathDlg okno wyboru ścieżki do pliku z akordami; KeyDlg okno wyboru interwału o jaki mają być transponowane akordy; RulesPathDlg okno wybory ścieżki do pliku z dodatkowymi regułami; RulesDlg okno ustawień parametrów poszczególnych reguł; FinishDlg okno z komunikatem końcowym. Metody Publiczne Run() - Sprawdza czy została zaznaczona melodia do harmonizacji, jeśli nie informuje użytkownika o tym żeby ją zaznaczył, jeśli tak: wczytuje komunikaty z plików, wyświetla kolejne okna dialogowe z wczytanymi komunikatami oraz konfiguracją do modyfikacji. Prywatne LoadMessages(path : string) wczytuje komunikaty okien dialogowych z plików tekstowych umieszczonych w katalogu path. AllDlgCancel() kończy pracę wizarda. StartDlgNext() ustawia kolejne okno dialogowe to wyświetlenia. ChordsPathDlgBack() ustawia poprzednie okno do wyświetlenia. ChordsPathDlgBrowse() wyświetla okno wyboru ścieżki pliku. ChordsPathDlgNext() sprawdza poprawność podanej ścieżki do pliku z akordami i jeśli ścieżka jest poprawna wyświetla kolejne okno, jeśli nie informuje o tym użytkownika i ponownie wyświetla aktualne okno. KeyDlgBack() - ustawia poprzednie okno do wyświetlenia. KeyDlgNext() - ustawia kolejne okno do wyświetlenia. RulesPathDlgBack() - ustawia poprzednie okno do wyświetlenia. RulesPathDlgBrowse() - wyświetla okno wyboru ścieżki pliku. RulesPathDlgNext() - sprawdza poprawność podanej ścieżki do pliku z dodatkowymi regułami i jeśli ścieżka jest poprawna ustawia kolejne okno do wyświetlenia, jeśli nie informuje o tym użytkownika i ponownie wyświetlane jest aktualne okno. RulesDlgBack() - ustawia poprzednie okno do wyświetlenia.
RulesDlgNext() - ustawia kolejne okno do wyświetlenia. FinishDlgBack() - ustawia poprzednie okno do wyświetlenia. FinishDlgHarmonize() - kończy pracę wtyczki i uruchamia algorytm harmonizacji. Inicialize() - metoda uruchamiana podczas ładowania wtyczki przez program Sibelius dodaje wtyczkę do podmenu Plug-Ins w menu głównym programu. Kody metod Run() path = Sibelius.GetPluginsFolder() & "\\Add harmony\\wizardmessages"; CanIStart = true; if (Sibelius.ScoreCount = 0) CanIStart = false; i=0; for each NoteRest nr in Sibelius.ActiveScore.Selection i=i+1; if (i=0) CanIStart = false; if(canistart = true) LoadMessages(path); ActDialog = "StartDlg"; while(actdialog!="end") Sibelius.ShowDialog(@ActDialog,Self); Sibelius.MessageBox("Please select melody and run this Wizard again"); AnyDlgCancel() ActDialog= "End";
ChordsPathDlgBack() ActDialog = "StartDlg"; ChordsPathDlgBrowse() p = Sibelius.GetPluginsFolder()&"\\Add harmony\\samples"; file = Sibelius.SelectFileToOpen("Select chords path", "*.sib", p); if(file!= null) _ChordsPath = file.namewithext; ActDialog = "ChordsPathDlg"; ChordsPathDlgNext() if(sibelius.fileexists(_chordspath)) ActDialog = "KeyDlg"; Sibelius.MessageBox("Wrong Path!"); FinishDlgBack() ActDialog = "RulesDlg"; FinishDlgHarmonize() ActDialog = "End"; Harmonizer.Run(_ChordsPath,_KeyValue,_RulesParameters,_RulesPath); Initialize() AddToPluginsMenu("Wizard","Run"); KeyDlgBack() ActDialog = "ChordsPathDlg";
KeyDlgNext() ActDialog = "RulesPathDlg"; LoadMessages(p:string) ChPDM = Sibelius.ReadTextFile(p & "\\ChordsPathDlgMessage.txt"); FDM = Sibelius.ReadTextFile(p & "\\FinishDlgMessage.txt"); KDM = Sibelius.ReadTextFile(p & "\\KeyDlgMessage.txt"); RPDM = Sibelius.ReadTextFile(p & "\\RulesPathDlgMessage.txt"); SDM = Sibelius.ReadTextFile(p & "\\StartDlgMessage.txt"); ChordsPathDlgMessage = ""; for i=0 to ChPDM.NumChildren ChordsPathDlgMessage = ChordsPathDlgMessage & ChPDM[i] & "\n"; FinishDlgMessage = ""; for i=0 to FDM.NumChildren FinishDlgMessage = FinishDlgMessage & FDM[i] & "\n"; KeyDlgMessage = ""; for i=0 to KDM.NumChildren KeyDlgMessage = KeyDlgMessage & KDM[i] & "\n"; RulesPathDlgMessage = ""; for i=0 to RPDM.NumChildren RulesPathDlgMessage = RulesPathDlgMessage & RPDM[i] & "\n"; StartDlgMessage = ""; for i=0 to SDM.NumChildren StartDlgMessage = StartDlgMessage & SDM[i] & "\n"; RulesDlgBack() ActDialog = "RulesPathDlg"; RulesDlgNext() ActDialog = "FinishDlg"; _RulesParameters = CreateArray(); _RulesParameters[1] = _r1 + 0; _RulesParameters[2] = _r2 + 0; _RulesParameters[3] = _r3 + 0; _RulesParameters[4] = _r4 + 0; _RulesParameters[5] = _r5 + 0; _RulesParameters[6] = _r6 + 0; _RulesParameters[7] = _r7 + 0; RulesPathDlgBack() ActDialog = "KeyDlg";
RulesPathDlgBrowse() p = Sibelius.GetPluginsFolder()&"\\Add harmony\\samples"; file = Sibelius.SelectFileToOpen("Select rules path", "*.sib", p); if(file!= null) _RulesPath = file.namewithext; ActDialog = "RulesPathDlg"; StartDlgNext() ActDialog = "ChordsPathDlg";
Harmonizer Odpowiedzialności wykonanie algorytmu harmonizacji wyświetlanie paska postępu modyfikacja zadanej melodii Sposób realizacji odpowiedzialności Dla każdego z zaznaczonych taktów T wykonuj: Begin Dla każdego obiektu NR reprezentującego pojedynczy dźwięk, lub współbrzmienie w takcie T wykonuj: Begin Jeżeli NR jest pojedynczym dźwiękiem: begin Znajdź możliwe do zastosowania akordy MA Wybierz najbardziej odpowiedni akord A2 z MA przez sprawdzenie połączeń MA[i] z poprzednio zastosowanym akordem A1 Dopisz dźwięki akordu do NR i podpisz NR nazwą akordu. A1 := A2; end; Jeżeli NR jest akordem: Begin Określ nazwę NR korzystając z bazy danych. A1 := NR; end; Aktualizuj okno postępu; end; End. Pola _BestChord : array tablica reprezentująca najwyżej oceniony akord _BestValue : real ocena najwyżej ocenionego akordu _Chords : array tablica przechowująca reprezentację wszystkich akordów _RulesParameters : array of real tablica przechowująca parametry reguł
Metody Publiczne Run(ChP:string,kv:int, _RulesParameters:array, path:string) wykonuje algorytm harmonizacji, parametrami są kolejno: ścieżka do pliku z akordami, tablica z parametrami reguł, ścieżka do pliku z bazą reguł. SetBestChord(x:real, ch:array) parametrem jest akord ch i jego ocena x, jeżeli x>_bestvalue, ch jest ustawiany jako _BestChord, a x jako _BestValue Prywatne string _FindName(ch:array, kv:int) szuka akordu o budowie takiej jak ch w tablicy _Chords i jeśli akord istnieje to zwraca jego nazwę, jeśli nie istnieje zwraca ciąg UNKNOWN CHORD, drugi parametr (kv) to ustawiony przez użytkownika interwał o jaki mają być transponowane akordy. _FindPossibleChords(pitch:int, kv:int) znajduje akordy, które w najwyższym głosie mają dźwięk o wysokości pitch i zapisuje je w tablicy _PossibleChords, drugi parametr (kv) to ustawiony przez użytkownika interwał o jaki mają być transponowane akordy. _LoadChords(ChP:string) wczytuje akordy z pliku o podanej ścieżce (ChP) do tablicy _Chords Kody metod Run(ChP:string,kv:int, _RulesParameters:array, path:string) kv = -kv; _LoadChords(ChP); Sibelius.RandomSeedTime(); thisscore = Sibelius.ActiveScore; thisscore.redraw = false; selection = thisscore.selection; length=0; for each NoteRest nr in selection length = length+1; Sibelius.CreateProgressDialog("Harmonizing melody (please wait)", 0,length); ch1 = CreateArray(); ch1[0] = 0; progres = 0; which_line = true; for each Bar b in selection bn = 0; for each NoteRest nr in b nc = nr.notecount; if(nc = 1) _FindPossibleChords(nr.Highest.Pitch, kv); PChNumber = _PossibleChords[0]; ActChord = 1;
_BestValue = -999999999; for i=0 to PChNumber ch2 = CreateArray(); ch2[0] = _PossibleChords[ActChord]+0; for j=1 to _PossibleChords[ActChord]+2 ch2[j] = _PossibleChords[ActChord+j] + 0; ch2[j] = _PossibleChords[ActChord + _PossibleChords[ActChord]+1] & ""; if(bn = 0)NB=true;NB=false; RuleChecker.Run(ch1,ch2,_RulesParameters,NB,path); ActChord = ActChord + _PossibleChords[ActChord]+2; ch1[0] = _BestChord[0]+0; for i=1 to _BestChord[0] nr.addnote(_bestchord[i]); ch1[i] = _BestChord[i] + 0; ch1[_bestchord[0]] = _BestChord[_BestChord[0]]+0; ch1[_bestchord[0]+1] = _BestChord[_BestChord[0]+1]&""; if(which_line) b.addtext(bn,_bestchord[_bestchord[0]+1],"text.staff.space.hypen.lyric s.verse2"); which_line = false; b.addtext(bn,_bestchord[_bestchord[0]+1],"text.staff.space.hypen.lyric s.verse3"); which_line = true; if(nc > 1) ch1[0] = nr.notecount + 0; for k = 0 to nr.notecount ch1[k+1] = nr[k].pitch; ch1[nr.notecount+1] = _FindName(ch1,kv); progres = progres+1; percent = (progres * 100)/length; Sibelius.UpdateProgressDialog(progres,"Harmonizing melody (" & percent & "%)"); bn = bn+nr.duration; thisscore.redraw = true; SetBestChord(x:real, ch:array) if(x>_bestvalue) _BestValue = x; _BestChord = ch;
string _FindName(ch:array, kv:int) tkv = kv + 0; while(ch[ch[0]]+tkv<60) tkv = tkv+12; while(ch[ch[0]]+tkv>71) tkv = tkv-12; i = 1; k = 1; NameFound = false; while((k<_chords[0]) and (NameFound = false)) if(_chords[i]=ch[0]) NameFound = true; for j = 1 to ch[0]+1 if(_chords[i+j]!=(ch[j]+tkv)) NameFound = false; i = i + _Chords[i]+1; k = k + 1; if(namefound = true) return _Chords[i]&""; return "UNKNOWN CHORD"; _FindPossibleChords(pitch:int, kv:int) tkv = kv + 0; while(pitch+tkv<60) tkv = tkv+12; while(pitch+tkv>71) tkv = tkv-12; liczba_akordow = _Chords[0]; nr_ak = 0; pocz_ak = 1; pocz_wstawianego = 1; _PossibleChords[0]=0; while(nr_ak<liczba_akordow) if(_chords[pocz_ak+_chords[pocz_ak]] - tkv = pitch) _PossibleChords[0] = _PossibleChords[0]+1; _PossibleChords[pocz_wstawianego] = _Chords[pocz_ak]+0; pocz_wstawianego = pocz_wstawianego+1; for i = pocz_ak+1 to pocz_ak + _Chords[pocz_ak]+1 _PossibleChords[pocz_wstawianego] = _Chords[i]-tkv; pocz_wstawianego = pocz_wstawianego+1; _PossibleChords[pocz_wstawianego] = _Chords[pocz_ak + _Chords[pocz_ak]+1]&""; pocz_wstawianego = pocz_wstawianego+1; nr_ak = nr_ak+1; pocz_ak = pocz_ak + _Chords[pocz_ak]+2;
_LoadChords(ChP:string) _Chords = Sibelius.ReadTextFile(ChP);
RuleChecker Odpowiedzialności ocena połączenia dwóch akordów Sposób realizacji odpowiedzialności sprawdzenie zgodności połączenia z regułami zaimplementowanymi w tej wtyczce oraz zapisanymi w pliku przez użytkownika Pola _IsNewBar : bool zmienna informująca o tym czy połączenie jest pomiędzy dwoma taktami, czy w ramach jednego taktu. _Value : real przechowuje ocenę połaczenia Metody Publiczne Run(ch1, ch2, RulesParameters, IsNewBar,path) wywołuje wszystkie metody sprawdzające reguły. Parametrami są: dwa akordy pomiędzy którymi połączenie jest sprawdzane, tablica z parametrami reguł, zmienna logiczna informująca o tym czy połączenie jest pomiędzy dwoma taktami, czy w ramach jednego taktu oraz ścieżka do pliku z regułami użytkownika. Wywołuje metodą SetBestChord Harmonizera. Prywatne _CheckFileRules(ch1,ch2,path) ocenia połączenie ch1 z ch2 według reguł zapisanych w pliku o ścieżce path, modyfikuje zmienną _Value bool _CompareNames(n1,n2) porównuje 2 nazwy akordów, jeżeli n2 jest wzorcem nazwy (zawiera znak X ) to porównywanych jest tylko 6 pierwszych znaków (istotnych w przyjętej konwencji nazewnictwa) a X oznacza dowolny znak, jeśli n2 jest nazwą bez znaków X to wykonywane jest normalne porównanie stringów, zwraca prawdę jeśli n1 pasuje do wzorca n2. bool _IsDifferentFunction(ch1,ch2) zwraca prawdę jeśli oba akordy w nazwie mają ten sam znak na pierwszej pozycji oznaczającej nazwę funkcji. _Rule1(ch1, ch2, imp) zmienia _Value o losową wartość z zakresu 0..100 wymnożoną przez imp. _Rule2(ch1, ch2, imp) wylicza sumę odległości o jaką przesuwają się dźwięki w 3 pierwszych głosach, mnoży ją przez imp i odejmuje do _Value.
_Rule3(ch1, ch2, imp) jeżeli ch1 i ch2 są różnymi funkcjami i w którymś głosie został zachowany wspólny dźwięk zwiększa _Value o imp. _Rule4(ch1, ch2, imp) zlicza wystąpienia równoległych prym kwint i oktaw, ich ilość mnoży przez imp i odejmuje od _Value. _Rule5(ch1, ch2, imp) jeżeli ch1 i ch2 to różne funkcje, a dźwięk w sopranie stoi, lub ch2 jest powtórzeniem funkcji ch1 a dźwięk w sopranie się porusza to zwiększa _Value o imp. _Rule6(ch1, ch2, imp) zlicza skoki o septymę oraz większe niż oktawa w poszczególnych głosach, mnoży przez imp i odejmuje od _Value. _Rule7(ch1, ch2, imp) jeśli ch2 jest powtórzeniem funkcji ch1 przez kreskę taktową to zmniejsza _Value o imp. Kody metod Run(ch1, ch2, RulesParameters, IsNewBar,path) _Value = 0; _IsNewBar = IsNewBar; rc = 8; i = 1; while(i<rc) x = RulesParameters[i]+0; myrule = "_Rule" & i; @myrule(ch1,ch2,x); i=i+1; if(path!="none") _CheckFileRules(ch1,ch2,path); Harmonizer.SetBestChord(_Value, ch2); _CheckFileRules(ch1,ch2,path) Rules = Sibelius.ReadTextFile(path); i = 1; while ((i/3)<rules[0]) if(_comparenames(ch1[ch1[0]+1],rules[i]) and _CompareNames(ch2[ch2[0]+1],Rules[i+1])) _Value = _Value + Rules[i+2]; i = i + 3;
bool _CompareNames(n1,n2) result = true; czybylx = false; ni = 6; for i=0 to ni if((substring(n2,i,1)!= "X") and (Substring(n2,i,1)!= Substring(n1,i,1))) result = false; if(substring(n2,i,1) = "X")czybylX = true; if(czybylx = false) if(n1=n2)result = true; result = false; return result; bool _IsDifferentFunction(ch1,ch2) f1 = ch1[ch1[0]+1]; f2 = ch2[ch2[0]+1]; f1 = Substring(f1,0,1); f2 = Substring(f2,0,1); if((f1!=f2) and (Length(f1)>0)) return true; return false; _Rule1(ch1, ch2, imp) result = (Sibelius.RandomNumber() % 100) * imp; _Value = _Value + result; _Rule2(ch1, ch2, imp) if((ch1[0]=ch2[0]) and _IsDifferentFunction(ch1,ch2)) result = 0; for i=2 to ch1[0]+1 if(ch1[i]>ch2[i]) result = result + ch1[i] - ch2[i]+0; result = result + ch2[i] - ch1[i]+0; _Value = _Value - (result * imp);
_Rule3(ch1, ch2, imp) if(_isdifferentfunction(ch1,ch2)) t = false; for i=1 to ch1[0]+1 if(ch1[i]=ch2[i])t = true; if(t = true) _Value = _Value + imp; _Rule4(ch1, ch2, imp) if(ch1[0]>0) result = 0; for i = 1 to ch1[0] for j = i+1 to ch1[0]+1 if(ch1[i]>ch1[j]) interval = ch1[i]-ch1[j]; interval = ch1[j]-ch1[i]; if((interval=0)or(interval=7)or(interval=12)) if(ch2[i]>ch2[j]) interval2 = ch2[i]-ch2[j]; interval2 = ch2[j]-ch2[i]; if(interval2=interval) result = result+1; _Value = _Value - (result * imp);
_Rule5(ch1, ch2, imp) if(_isdifferentfunction(ch1,ch2)) if(ch1[ch1[0]] = ch2[ch2[0]]) _Value = _Value + imp; _Value = _Value - imp; if(ch1[ch1[0]] = ch2[ch2[0]]) _Value = _Value - imp; _Value = _Value + imp; _Rule6(ch1, ch2, imp) if(ch1[0] = ch2[0]) for i = 1 to ch1[0] if(ch1[i]>ch2[i]) interval = ch1[i]-ch2[i]; interval = ch2[i]-ch1[i]; if((interval=10) or (interval=11) or (interval>12)) _Value = _Value - imp; _Rule7(ch1, ch2, imp) if(_isnewbar and (_IsDifferentFunction(ch1,ch2)=false)) _Value = _Value - imp;