3
* Created on 31-dic-2005
5
* Redesigned on 05-March-2007
11
package org.herac.tuxguitar.gui.editors.chord;
13
import java.util.ArrayList;
14
import java.util.Collections;
16
import java.util.Comparator;
18
import java.util.Iterator;
20
import java.util.List;
22
import org.herac.tuxguitar.gui.TuxGuitar;
24
import org.herac.tuxguitar.song.models.TGChord;
28
* Class that helps to create a chord from information put in ChordSelector
31
* Also contains ChordDatabase static field.
33
* @author Nikola Kolarovic
38
public class ChordCreatorUtil {
41
* Maximum number of strings variable - has twin in TrackPropertiesAction
44
public static final int MAX_STRINGS = 7;
46
/** Maximum fret distance for a chord */
48
public static final int MAX_FRET_SPAN = 5;
50
/** mark for bass note type **/
51
private final int BASS_INDEX = -1;
53
/** mark for essential note in a chord - MUST be in */
54
private final int ESSENTIAL_INDEX = -2;
56
/** mark for essential note in a chord - PENALTY if not in */
57
private final int NOT_ESSENTIAL_INDEX = -3;
59
/** Keep the Thread control */
60
private static long runningProcess;
62
// ------ attributes ------
64
//protected ChordInfo info;
65
private long processId;
67
private ChordCreatorListener listener;
69
/** the alteration List selectionIndex */
70
private int alteration;
72
private int chordIndex;
74
/** essential notes for the chord (from ChordInfo) */
75
private int[] requiredNotes;
77
/** notes that expand the chord (add+-) */
78
private int[] expandingNotes;
80
/** is the fifth altered */
83
/** name of a chord */
84
private String chordName = null;
86
private int bassTonic;
88
private int chordTonic;
90
/** current tunning */
93
private ChordCreatorUtil(long processId,ChordCreatorListener listener){
94
this.processId = processId;
95
this.listener = listener;
98
public boolean isValidProcess(){
99
return (this.processId == runningProcess);
102
public static long getNewProcess(){
103
return (++ runningProcess);
106
public static void getChords(final ChordCreatorListener listener,
108
final int chordIndex,
109
final int alteration,
116
final int chordTonic,
117
final boolean sharp){
119
final ChordCreatorUtil chordCreator = new ChordCreatorUtil(getNewProcess(), listener );
120
new Thread(new Runnable() {
122
chordCreator.getChords( tuning, chordIndex, alteration, plusMinus, add, add5, add9, add11, bassTonic, chordTonic, sharp);
127
protected void getChords(int[] tuning,
139
if(!isValidProcess()){
145
this.tuning = tuning;
147
this.chordIndex = chordIndex;
149
this.chordTonic = chordTonic;
151
this.bassTonic = bassTonic;
153
this.alteration = alteration;
155
this.chordName = new ChordNamingConvention().createChordName(this.chordTonic,
167
// find the notes that expand the chord
168
if (this.alteration!=0) {
170
this.expandingNotes = new int[1];
171
this.expandingNotes[0]= getAddNote(this.alteration-1,plusMinus);
173
else { // not just add...
174
// 9+- = 7b !9(+-) (index=1)
175
// 11+- = 7b !11(+-) 9(+-) (index=2)
176
// 13+- = 7b !13(+-) 9(+-) 11(+-) (index=3)
177
this.expandingNotes = new int[1+this.alteration];
178
this.expandingNotes[0] = 11; //7b
179
this.expandingNotes[1] = getAddNote(this.alteration-1,plusMinus); //this.alteration+-
182
for (int i=2; i<=this.alteration; i++)
183
this.expandingNotes[i]=getAddNote(i-2, i==2 ? add9 : add11); // @2=add9+-, @3=add11+- tone
187
else this.expandingNotes=new int[0];
192
//this.requiredNotes = ((ChordDatabase.ChordInfo)new ChordDatabase().getChords().get(chordIndex)).cloneRequireds();
193
this.requiredNotes = ChordDatabase.get(chordIndex).cloneRequireds();
194
//IT DON'T BUILD UNDER JRE1.4
195
//this.requiredNotes = ((ChordDatabase.ChordInfo) ChordCreatorUtil.getChordData().getChords().get(chordIndex)).getRequiredNotes().clone();
198
// adjust the subdominant if needed
200
for (int i=0; i<this.requiredNotes.length; i++)
201
if (this.requiredNotes[i]==8) // alternate the subdominant
202
this.requiredNotes[i]+=(add5==1 ? 1 : -1);
205
// first count different from -1
207
for (int i=0; i<this.requiredNotes.length; i++) {
208
this.requiredNotes[i]=checkForOverlapping(this.requiredNotes[i]);
209
if (this.requiredNotes[i]!=-1)
212
// then fill in the new array
213
int[] tempNotes = new int[count];
215
for (int i=0; i<this.requiredNotes.length; i++)
216
if (this.requiredNotes[i]!=-1) {
217
tempNotes[count]=this.requiredNotes[i];
220
// and substitute them
221
this.requiredNotes = tempNotes;
223
//return getChords();
224
if(isValidProcess()){
225
List chords = getChords();
226
if(chords != null && isValidProcess()){
227
this.listener.notifyChords(this, chords);
232
/** We have to make sure that if required note is already inside
233
* expanding notes array so we don't put it twice...
235
protected int checkForOverlapping(int checkIt) {
236
for (int i=0; i<this.expandingNotes.length; i++)
237
if (this.expandingNotes[i]==checkIt)
244
* Creates the chord combinations out of given data and then uses some kind
247
* heuristics to check the most suitable formations.
249
* @return the list of TGChord structures that are most suitable
252
private java.util.List getChords() {
253
if(!isValidProcess()){
256
ArrayList potentialNotes = makePotentialNotes();
258
ArrayList combinations = makeCombinations( potentialNotes);
260
ArrayList priorities = determinePriority( combinations);
262
ArrayList theBestOnes = takeBest( priorities);
264
return createChords( theBestOnes);
268
* Creates the TGChord ArrayList out of StringValue's ArrayLists
270
* @param Highest rated StringValues
271
* @return TGChord collection
273
private ArrayList createChords(ArrayList top) {
274
if(!isValidProcess()){
278
ArrayList chords = new ArrayList(top.size());
280
Iterator it = top.iterator();
282
while (it.hasNext()) {
283
TGChord chord = TuxGuitar.instance().getSongManager().getFactory()
284
.newChord(this.tuning.length);
285
Iterator it2 = ((ArrayList) it.next()).iterator();
287
while (it2.hasNext()) {
288
StringValue stringValue = (StringValue) it2.next();
289
int string = ((chord.getStrings().length - 1) - (stringValue.getString()));
290
int fret = stringValue.getFret();
291
chord.addFretValue(string, fret);
292
chord.setName(this.chordName);
302
* If string/fret combination is needed for the chord formation, add it into
305
* @return true if the note is needed for chord formation
308
private void find(int stringTone, int stringIndex, int fret,List stringList){
309
if(!isValidProcess()){
312
boolean bassAlreadyIn=false;
314
for (int i = 0; i < this.requiredNotes.length; i++)
315
if ((stringTone + fret) % 12 == (this.chordTonic+this.requiredNotes[i] - 1) % 12) {
316
if (!bassAlreadyIn && (stringTone + fret) % 12 == this.bassTonic)
318
stringList.add(new StringValue(stringIndex, fret, i));
323
if (this.expandingNotes.length!=0) {
324
for (int i=0; i<this.expandingNotes.length; i++) {
325
if ((stringTone+fret)%12==(this.chordTonic+this.expandingNotes[i]-1)%12) {
326
if (!bassAlreadyIn && (stringTone + fret) % 12 == this.bassTonic)
328
stringList.add(new StringValue(stringIndex,fret,(i<2 ? this.ESSENTIAL_INDEX : this.NOT_ESSENTIAL_INDEX)));
335
if ((stringTone + fret) % 12 == this.bassTonic) {
336
stringList.add(new StringValue(stringIndex, fret, this.BASS_INDEX));
343
* Returns the wanted note for ADD chord
346
* 0==add9; 1==add11; 2==add13;
347
* @param selectionIndex
348
* index of selected item in the List combo
349
* @return wanted note, or -1 if nothing was selected
352
private int getAddNote(int type, int selectionIndex) {
358
wantedNote = 3; // add9
361
wantedNote = 6; // add11
364
wantedNote = 10; // add13
368
switch (selectionIndex) {
382
private ArrayList makePotentialNotes(){
383
if(!isValidProcess()){
386
ArrayList potentialNotes = new ArrayList(this.tuning.length);
388
for (int string = 0; string < this.tuning.length; string++) {
390
ArrayList currentStringList = new ArrayList(10);
392
// search all the frets
394
if (ChordSettings.instance().getFindChordsMin()>0 && ChordSettings.instance().isEmptyStringChords())
395
find(this.tuning[string], string, 0, currentStringList); // if it's open chord but wanted to search from different minimal fret
398
for (int fret = ChordSettings.instance().getFindChordsMin(); fret <= ChordSettings.instance().getFindChordsMax(); fret++) {
399
// put in all the needed notes
400
find(this.tuning[string], string, fret, currentStringList);
403
potentialNotes.add(currentStringList);
406
return potentialNotes;
411
* Makes the all-possible combinations of found notes that can be reached by
414
* @param potentialNotes
415
* list consisted of found notes for each string
417
* @return list of list of StringValues, with tones that can form a chord
420
private ArrayList makeCombinations(ArrayList potentialNotes) {
421
if(!isValidProcess()){
425
// COMBINATIONS of strings used in a chord
426
ArrayList stringCombination = new ArrayList(60);
427
ArrayList lastLevelCombination = null;
429
for (int i = 0; i < this.tuning.length - 1; i++)
431
lastLevelCombination = makeStringCombination(lastLevelCombination);
433
// lastLevelCombination after 3rd round: [[0, 1, 2, 3], [0, 1, 2,
434
// 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4], [0, 1, 2, 5], [0,
435
// 1, 3, 5], [0, 2, 3, 5], [1, 2, 3, 5], [0, 1, 4, 5], [0, 2, 4, 5],
436
// [1, 2, 4, 5], [0, 3, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]]
438
stringCombination.addAll(lastLevelCombination);
441
ArrayList combinations = new ArrayList(800);
443
// --- combine the StringValues according to strings combination
444
// ----------------------=======
446
Iterator iterator = stringCombination.iterator();
448
while (iterator.hasNext()) { // go through all string combinations list
449
// take a string combinations
450
ArrayList currentStringCombination = (ArrayList) iterator.next();
451
lastLevelCombination = null;
453
// go through all the strings in one combination
454
for (int level = 0; level < currentStringCombination.size(); level++) {
456
// take the string index
457
int currentString = ((Integer) currentStringCombination.get(level)).intValue();
459
// take all the potential notes from currentString and combine
460
// them with potential notes from other strings
462
lastLevelCombination = makeStringValueCombination(lastLevelCombination,(ArrayList)potentialNotes.get(currentString));
464
// the structure of combinations is AL { AL(StringValue,SV,SV),
465
// AL(SV), AL(SV,SV),AL(SV,SV,SV,SV,SV,SV) }
469
if(lastLevelCombination != null){
470
combinations.addAll(lastLevelCombination);
478
* Makes a combination of string indices
480
* @param lastLevelCombination
481
* structure to be expanded by current level
483
* @return structure of stringCombination is AL { AL(0), AL(0,1),
484
* AL(0,2),AL(0,1,3,4),AL(0,1,2,3,4,5) }
486
private ArrayList makeStringCombination(ArrayList lastLevelCombinationRef){
487
if(!isValidProcess()){
491
List lastLevelCombination = lastLevelCombinationRef;
493
if (lastLevelCombination == null) {
494
// first combination is AL { AL(0), AL(1), AL(2), AL(3), AL(4),
495
// ...AL(tuning.length) }
496
lastLevelCombination = new ArrayList();
498
for (int i = 0; i < this.tuning.length; i++) {
499
lastLevelCombination.add(new ArrayList());
500
((ArrayList) lastLevelCombination.get(i)).add(new Integer(i));
504
ArrayList thisLevelCombination = new ArrayList();
505
for (int current = 1; current < this.tuning.length; current++)
507
Iterator it = lastLevelCombination.iterator();
509
while (it.hasNext()) {
510
ArrayList combination = (ArrayList) it.next();
511
Integer currentInteger = new Integer(current);
512
if (((Integer) combination.get(combination.size() - 1))
513
.intValue() < current
514
&& !combination.contains(currentInteger)) {
516
// check if the string is already in combination
517
ArrayList newCombination = (ArrayList) combination.clone();
518
newCombination.add(currentInteger);
519
thisLevelCombination.add(newCombination);
526
return thisLevelCombination;
531
* Makes a combination of notes by multiplying last combination and current
536
* @param lastLevelCombination
537
* structure to be expanded by current level
540
* notes that can be considered into making a chord
542
* @return structure of StringValue combinations : AL {
543
* AL(StringValue,SV,SV), AL(SV), AL(SV,SV),AL(SV,SV,SV,SV,SV,SV) }
546
private ArrayList makeStringValueCombination(ArrayList lastLevelCombination, ArrayList notes) {
547
if(!isValidProcess()){
550
ArrayList thisLevelCombination = null;
552
if (lastLevelCombination == null) { // initial combination
554
thisLevelCombination = new ArrayList(notes.size());
556
for (int i = 0; i < notes.size(); i++) {
558
thisLevelCombination.add(new ArrayList(6));
560
((ArrayList) thisLevelCombination.get(i)).add(notes.get(i));
564
// first combination is AL { AL(firstOne), AL(anotherFret) }
570
thisLevelCombination = new ArrayList();
572
for (int i = 0; i < notes.size(); i++)
573
for (int j = 0; j < lastLevelCombination.size(); j++) { // cartesian multiplication
574
ArrayList currentCombination = (ArrayList) ((ArrayList) lastLevelCombination.get(j)).clone();
575
currentCombination.add(notes.get(i));
577
// if the distance maximum between the existing frets
578
// is less than wanted, add it into potential list
580
if (checkCombination(currentCombination))
581
thisLevelCombination.add(currentCombination);
586
return thisLevelCombination;
590
* Checks if the combination can be reached by fingers. It is reachable
592
* if the distance between lowest and highest fret is less than
594
* <i>ChordCreatorUtil.MAX_FRET_SPAN</i>.
596
* Also note that this method eliminates or includes the chords with empty
599
* which is controlled with <i>boolean ChordCreatorUtil.EMPTY_STRING_CHORDS</i>
602
* current combination to be examined
604
* @return true if it can be reached
607
private boolean checkCombination(ArrayList combination) {
609
Iterator it = combination.iterator();
610
int maxLeft, maxRight;
612
maxLeft = maxRight = ((StringValue) combination.get(0)).getFret();
614
while (it.hasNext()) {
616
int fret = ((StringValue) it.next()).getFret();
618
//chords with empty-string are welcome
619
if (fret != 0 || !ChordSettings.instance().isEmptyStringChords()) {
631
if (Math.abs(maxLeft - maxRight) >= MAX_FRET_SPAN)
640
* orders the StringValue ArrayList by their priority, calculated here
642
* for every single chord combination.<br>
644
* Priority is higher if:<br>
645
* - tone combination has all notes required for the chord basis<br>
646
* - has good chord semantics uses many basic tones, and all necessary
647
* tones in their place<br>
648
* - tone combination has all subsequent strings (no string skipping)<br>
649
* - has a chord bass tone as lowest tone<br>
650
* - uses more strings<br>
651
* - uses good fingering positions<br>
653
* @param allCombinations
654
* all the StringValue combinations that make some sense
656
* @return Treemap of the StringValue ArrayLists, in which the key is
658
* <i>float priority</i>.
661
private ArrayList determinePriority(ArrayList allCombinations) {
662
if(!isValidProcess()){
665
ArrayList ordered = new ArrayList();
667
Iterator it = allCombinations.iterator();
669
while (it.hasNext() && isValidProcess()) {
673
ArrayList stringValueCombination = (ArrayList) it.next();
675
// tone combination has all notes required for the chord basis
677
priority += combinationHasAllRequiredNotes(stringValueCombination);
679
// uses good chord semantics
681
priority += combinationChordSemantics(stringValueCombination);
683
// tone combination has all subsequent strings (no string skipping)
685
priority += combinationHasSubsequentStrings(stringValueCombination);
687
// has a chord bass tone as lowest tone
689
priority += combinationBassInBass(stringValueCombination);
692
// 4 and less strings will be more praised in case of negative grade
693
// 4 and more strings will be more praised in case of positive grade
694
priority += ChordSettings.instance().getManyStringsGrade() / 3
695
* (stringValueCombination.size()-this.tuning.length /
696
(ChordSettings.instance().getManyStringsGrade()>0 ? 2 : 1.2) );
698
// uses good fingering positions
700
priority += combinationHasGoodFingering(stringValueCombination);
702
// System.out.println("OVERALL:
703
// "+priority+"----------------------------");
705
PriorityItem item = new PriorityItem();
707
item.priority = priority;
709
item.stringValues = stringValueCombination;
721
* Takes the StringValue ArrayLists that has the best priority rating
724
private ArrayList takeBest(ArrayList priorityItems) {
725
if(!isValidProcess()){
729
int maximum = ChordSettings.instance().getChordsToDisplay();
731
ArrayList bestOnes = new ArrayList(maximum);
733
Collections.sort(priorityItems, new PriorityComparator());
734
for(int i = 0; i < priorityItems.size() && isValidProcess(); i ++){
735
PriorityItem item = (PriorityItem)priorityItems.get(i);
736
if (!checkIfSubset(item.stringValues, bestOnes) ){
737
bestOnes.add(item.stringValues);
739
if( bestOnes.size() >= maximum ){
749
/** adds points if the combination has all the notes in the basis of chord */
750
private float combinationHasAllRequiredNotes(ArrayList stringValueCombination) {
751
if(!isValidProcess()){
754
Iterator it = stringValueCombination.iterator();
755
int[] values = new int[this.requiredNotes.length];
756
int currentIndex = 0;
758
while (it.hasNext()) {
759
StringValue sv = (StringValue) it.next();
761
if (sv.requiredNoteIndex >= 0) { // only basis tones
762
boolean insert = true;
764
for (int i = 0; i < currentIndex; i++)
765
if (values[i] == sv.requiredNoteIndex + 1)
768
// sv.requiredNoteIndex+1, because we have index 0 and we don't
772
values[currentIndex] = sv.requiredNoteIndex + 1;
779
if (currentIndex == this.requiredNotes.length) {
780
return ChordSettings.instance().getRequiredBasicsGrade();
783
if (currentIndex == this.requiredNotes.length - 1) {
785
boolean existsSubdominant = false;
787
Iterator it2 = stringValueCombination.iterator();
788
while (it2.hasNext()) {
789
StringValue current = (StringValue)it2.next();
790
if ((this.tuning[current.string] + current.fret) % 12 == (this.chordTonic + 7) %12)
791
existsSubdominant = true;
794
if (!existsSubdominant && currentIndex == this.requiredNotes.length-1) {
795
// if not riff. "sus" chord, or chord with altered fifth allow chord without JUST subdominant (fifth) with small penalty
797
//if ( !((ChordInfo)new ChordDatabase().getChords().get(this.chordIndex)).getName().contains("sus") && this.requiredNotes.length!=2 && this.add5==0) {
798
//String.contains(String) is not available at JRE1.4
799
//Replaced by "String.indexOf(String) >= 0"
800
if ( ChordDatabase.get(this.chordIndex).getName().indexOf("sus") >= 0 && this.requiredNotes.length != 2 && this.add5 == 0) {
801
return ( ChordSettings.instance().getRequiredBasicsGrade() * 4 / 5 );
807
// required notes count should decrease the penalty
808
int noteCount = (this.alteration == 0 ? 0 : 1+ this.alteration)+currentIndex+ (this.bassTonic == this.chordTonic ? 0 : 1);
810
// sometimes, when noteCount is bigger then tunning length, this pennalty will become positive, which may help
811
return -ChordSettings.instance().getRequiredBasicsGrade()
812
* (this.tuning.length - noteCount) / this.tuning.length * 2;
816
/** adds points if the combination has strings in a row */
817
private float combinationHasSubsequentStrings(ArrayList stringValueCombination) {
818
if(!isValidProcess()){
821
boolean stumbled = false, noMore = false, penalty = false;
823
for (int i = 0; i < this.tuning.length; i++) {
824
boolean found = false;
825
Iterator it = stringValueCombination.iterator();
827
if (((StringValue) it.next()).string == i)
834
if (penalty) // penalty for skipped strings
835
return -ChordSettings.instance().getSubsequentGrade();
845
return ChordSettings.instance().getSubsequentGrade();
848
/** checks if the bass tone is the lowest tone in chord */
849
private float combinationBassInBass(ArrayList stringValueCombination) {
850
if(!isValidProcess()){
853
for (int i = 0; i < this.tuning.length; i++) {
855
Iterator it = stringValueCombination.iterator();
857
while (it.hasNext()) {
858
StringValue sv = (StringValue) it.next();
860
if (sv.string == i) { // stumbled upon lowest tone
861
if ( (this.tuning[sv.string]+sv.fret) % 12 == this.bassTonic )
862
return ChordSettings.instance().getBassGrade();
864
return -ChordSettings.instance().getBassGrade();
874
* grades the fingering in a chord.
876
* fingering is good if:<br>
877
* - uses as little as possible fret positions<br>
878
* - uses less than 3 fret positions<br>
879
* - distributes good among fingers<br>
880
* - can be placed capo <br>
883
private float combinationHasGoodFingering(ArrayList stringValueCombination) {
884
if(!isValidProcess()){
887
// init: copy into simple array
888
float finalGrade = 0;
889
int[] positions = new int[this.tuning.length];
890
for (int i = 0; i < this.tuning.length; i++)
893
Iterator it = stringValueCombination.iterator();
895
while (it.hasNext()) {
896
StringValue sv = (StringValue) it.next();
897
positions[sv.string] = sv.fret;
902
// distance between fingers
903
int min = ChordSettings.instance().getFindChordsMax()+2, max = 0, maxCount=0;
904
boolean openChord = false, zeroString = false;
906
for (int i = 0; i < this.tuning.length; i++) {
908
openChord|= ChordSettings.instance().isEmptyStringChords() && positions[i] == 0;
909
zeroString |= positions[i]==0;
911
if (positions[i] < min && positions[i] != 0 && positions[i]!=-1)
914
if (positions[i] > max) {
919
if (positions[i]==max)
928
for (int i = 0; i < this.tuning.length; i++)
929
if (positions[i] == min)
934
finalGrade += ChordSettings.instance().getFingeringGrade()/8;
937
finalGrade += ChordSettings.instance().getFingeringGrade()/8;
941
finalGrade += ChordSettings.instance().getFingeringGrade()/8;
943
// position distance: 1-2 nice 3 good 4 bad 5 disaster
946
switch(Math.abs(max-min)) {
947
case 0 : distanceGrade=ChordSettings.instance().getFingeringGrade()/5;
949
case 1 : distanceGrade=ChordSettings.instance().getFingeringGrade()/(5+maxCount);
951
case 2 : distanceGrade=ChordSettings.instance().getFingeringGrade()/(6+maxCount);
952
if (min<5) distanceGrade*=0.9;
954
case 3 : distanceGrade=-ChordSettings.instance().getFingeringGrade()/10*maxCount;
955
// I emphasize the penalty if big difference is on some
956
// lower frets (it is greater distance then)
957
if (min<5) distanceGrade*=1.3;
959
case 4 : distanceGrade=-ChordSettings.instance().getFingeringGrade()/4*maxCount;
960
if (min<=5) distanceGrade*=1.8;
962
default : distanceGrade=-ChordSettings.instance().getFingeringGrade()*maxCount;
965
finalGrade += distanceGrade;
967
// ============== finger position abstraction ==================
968
// TODO: what to do with e.g. chord -35556 (C7)
969
// ... it can be held with capo on 5th fret, but very hard :)
970
// ... This is the same as with "capo after", I didn't consider that (e.g. chord -35555)
971
ArrayList[] fingers={new ArrayList(2),new ArrayList(2),new ArrayList(2),new ArrayList(2)};
972
// TODO: still no thumb, sorry :)
974
// STRUCTURE: ArrayList consists of Integers - first is fret
975
// - others are strings
977
for (int i=0; i<this.tuning.length; i++)
978
System.out.print(" "+positions[i]);
979
System.out.println("");
982
// if chord is open, then we can have capo only in strings before open string
983
int lastZeroIndex = 0;
986
for (int i=0; i<positions.length; i++)
987
if (positions[i]==0) lastZeroIndex=i;
989
// open or not not open chord,
990
// index finger is always on lowest fret possible
991
fingers[0].add(new Integer(min));
993
for (int i=lastZeroIndex; i<positions.length; i++)
994
if (positions[i]==min) {
995
fingers[0].add(new Integer(i));
1000
// if not zero-fret, occupy fingers respectivly
1002
for (int i=0; i<positions.length; i++) {
1003
if (positions[i]!=0 && positions[i]!=-1) {
1005
fingers[finger].add(new Integer(positions[i]));
1006
fingers[finger].add(new Integer(i));
1013
/* System.out.println("Positions:");
1014
for (int i=0; i<4; i++) {
1015
if (fingers[i].size()>1)
1016
System.out.print("G"+(i+1)+"R"+((Integer)fingers[i].get(0)).intValue()+"S"+((Integer)fingers[i].get(1)).intValue()+" ");
1021
finalGrade-=ChordSettings.instance().getFingeringGrade();
1023
finalGrade+=ChordSettings.instance().getFingeringGrade()*0.1*(15-2*finger);
1025
// TODO: maybe to put each finger's distance from the minimum
1031
* grades the chord semantics, based on theory.
1033
* Tone semantics is good if:<br>
1034
* - there appear tones from chord basis or bass tone<br>
1035
* - there appear alteration tones on their specific places<br><br>
1038
* - search for chord tonic. If some note is found before (and it's not bass) do penalty<br>
1039
* - make penalty if the bass tone is not in bass<br>
1040
* - check if all the expanding notes are here. If some are not, do penalty<br>
1041
* - if expanding note isn't higher than tonic octave, then priority should be less<br>
1042
* - If there are not some with NON_ESSENTIAL_INDEX are not here, penalty should be less<br>
1044
private float combinationChordSemantics(ArrayList stringValueCombination) {
1045
if(!isValidProcess()){
1048
float finalGrade = 0;
1050
int foundTonic = -1;
1052
int[] foundExpanding = new int[this.expandingNotes.length];
1055
for (int string = 0; string < this.tuning.length; string++) {
1056
// we have to go string-by-string because of the octave
1057
Iterator it = stringValueCombination.iterator();
1058
StringValue current = null;
1059
boolean found=false;
1061
while (it.hasNext() && !found) {
1062
StringValue sv = (StringValue) it.next();
1063
if (sv.string == string &&!found && sv.fret!=-1) { // stumbled upon next string
1070
// grade algorithms----
1071
if (current != null) {
1073
if (foundTonic==-1 && current.requiredNoteIndex==0)
1074
foundTonic=this.tuning[current.string]+current.fret;
1076
// specific bass not in bass?
1077
if (stringDepth>1) {
1078
if (current.requiredNoteIndex==this.BASS_INDEX)
1079
finalGrade -= ChordSettings.instance().getGoodChordSemanticsGrade();
1081
if (current.requiredNoteIndex<0) { // expanding tones
1082
// expanding tone found before the tonic
1084
finalGrade -= ChordSettings.instance().getGoodChordSemanticsGrade()/2;
1086
// if expanding note isn't higher than tonic's octave
1087
if (foundTonic+11 > this.tuning[current.string]+current.fret)
1088
finalGrade -= ChordSettings.instance().getGoodChordSemanticsGrade()/3;
1091
// search for distinct expanding notes
1092
for (int i=0; i<this.expandingNotes.length; i++)
1093
if ((this.tuning[string]+current.fret)%12==(this.chordTonic+this.expandingNotes[i]-1)%12)
1094
if (foundExpanding[i]==0)
1095
foundExpanding[i]=current.requiredNoteIndex;
1102
// penalties for not founding an expanding note
1103
if (this.alteration!=0) {
1104
int essentials=0, nonEssentials=0;
1105
for (int i=0; i<foundExpanding.length; i++) {
1106
if (foundExpanding[i]==this.ESSENTIAL_INDEX)
1109
if (foundExpanding[i]!=0)
1113
if (essentials+nonEssentials==this.expandingNotes.length)
1114
finalGrade+=ChordSettings.instance().getGoodChordSemanticsGrade();
1116
if (essentials==2) // if all essentials are there, it's good enough
1117
finalGrade+=ChordSettings.instance().getGoodChordSemanticsGrade()/2;
1119
// but if some are missing, that's BAD:
1121
finalGrade+= (essentials+nonEssentials-this.expandingNotes.length)*ChordSettings.instance().getGoodChordSemanticsGrade();
1122
// half of the penalty for non-essential notes
1123
finalGrade+= nonEssentials*ChordSettings.instance().getGoodChordSemanticsGrade()/2;
1133
* If current StringValue is a subset or superset of already better ranked
1134
* chords, it shouldn't be put inside, because it is duplicate.
1135
* @param stringValues current StringValue to be examined
1136
* @param betterOnes ArrayList of already stored StringList chords
1137
* @return true if it is duplicate, false if it is unique
1139
private boolean checkIfSubset(List stringValues, List betterOnes) {
1140
if(!isValidProcess()){
1143
Iterator it = betterOnes.iterator();
1144
while (it.hasNext()) {
1145
List currentStringValue = (List)it.next();
1146
boolean foundDifferentFret = false;
1147
// repeat until gone through all strings, or found something different
1148
for (int i=0; i<currentStringValue.size(); i++) {
1149
int currentString = ((ChordCreatorUtil.StringValue)currentStringValue.get(i)).string ;
1150
// search for the same string - if not found do nothing
1151
for (int j=0; j<stringValues.size(); j++)
1152
if ( ((ChordCreatorUtil.StringValue)stringValues.get(j)).string == currentString) {
1153
// if the frets on the same string differ, then chords are not subset/superset of each other
1154
if (((ChordCreatorUtil.StringValue)stringValues.get(j)).fret != ((ChordCreatorUtil.StringValue)currentStringValue.get(i)).fret)
1155
foundDifferentFret=true;
1159
if (!foundDifferentFret)// nothing is different
1167
* contains information about the note: string, fret and tone function in a
1174
private class StringValue {
1176
protected int string;
1178
protected int requiredNoteIndex;
1180
public StringValue(int string, int fret, int requiredNoteIndex) {
1181
this.string = string;
1183
this.requiredNoteIndex = requiredNoteIndex;
1187
public int getString() {
1191
public void setString(int string) {
1192
this.string = string;
1195
public int getFret() {
1199
public void setFret(int fret) {
1203
public int getRequiredNoteIndex() {
1204
return this.requiredNoteIndex;
1207
public void setRequiredNoteIndex(int requiredNoteIndex) {
1208
this.requiredNoteIndex = requiredNoteIndex;
1213
/** used just to sort StringValue ArrayLists by priorities */
1214
protected class PriorityItem {
1216
ArrayList stringValues;
1221
/** used to sort the array */
1222
protected class PriorityComparator implements Comparator {
1224
public int compare(Object o1, Object o2) {
1225
return Math.round(((PriorityItem) o2).priority - ((PriorityItem) o1).priority);
b'\\ No newline at end of file'