1 /*
2 
3  * Created on 31-dic-2005
4 
5  * Redesigned on 05-March-2007
6 
7  *
8 
9  */
10 
11 package org.herac.tuxguitar.gui.editors.chord;
12 
13 import java.util.ArrayList;
14 import java.util.Collections;
15 
prepare_cdb(name, target_dir)16 import java.util.Comparator;
17 
18 import java.util.Iterator;
19 
20 import java.util.List;
21 
22 import org.herac.tuxguitar.gui.TuxGuitar;
23 
24 import org.herac.tuxguitar.song.models.TGChord;
25 
26 /**
27  *
28  * Class that helps to create a chord from information put in ChordSelector
29  * dialog.
30  *
run_analyzer(directory, cdb, args)31  * Also contains ChordDatabase static field.
32  *
33  * @author Nikola Kolarovic
34  *
35  * @author julian
36  *
37  */
38 public class ChordCreatorUtil {
39 
40 	/**
41 	 * Maximum number of strings variable - has twin in TrackPropertiesAction
42 	 * class
43 	 */
44 	public static final int MAX_STRINGS = 7;
45 
46 	/** Maximum fret distance for a chord */
47 
48 	public static final int MAX_FRET_SPAN = 5;
49 
50 	/** mark for bass note type **/
51 	private final int BASS_INDEX = -1;
52 
53 	/** mark for essential note in a chord - MUST be in */
54 	private final int ESSENTIAL_INDEX = -2;
55 
56 	/** mark for essential note in a chord - PENALTY if not in */
57 	private final int NOT_ESSENTIAL_INDEX = -3;
58 
59 	/** Keep the Thread control */
60 	private static long runningProcess;
61 
62 	// ------ attributes ------
63 
64 	//protected ChordInfo info;
65 	private long processId;
66 
67 	private ChordCreatorListener listener;
68 
69 	/** the alteration List selectionIndex */
70 	private int alteration;
71 
72 	private int chordIndex;
73 
74 	/** essential notes for the chord (from ChordInfo) */
75 	private int[] requiredNotes;
76 
77 	/** notes that expand the chord (add+-) */
78 	private int[] expandingNotes;
79 
80 	/** is the fifth altered */
81 	private int add5 = 0;
82 
83 	/** name of a chord */
84 	private String chordName = null;
85 
86 	private int bassTonic;
87 
88 	private int chordTonic;
89 
90 	/** current tunning */
91 	private int[] tuning;
92 
93 	private ChordCreatorUtil(long processId,ChordCreatorListener listener){
94 		this.processId = processId;
95 		this.listener = listener;
96 	}
97 
98 	public boolean isValidProcess(){
99 		return (this.processId == runningProcess);
100 	}
101 
102 	public static long getNewProcess(){
103 		return (++ runningProcess);
104 	}
105 
106 	public static void getChords(final ChordCreatorListener listener,
107 	                             final int[] tuning,
108 	                             final int chordIndex,
109 	                             final int alteration,
110 	                             final int plusMinus,
111 	                             final boolean add,
112 	                             final int add5,
113 	                             final int add9,
114 	                             final int add11,
115 	                             final int bassTonic,
116 	                             final int chordTonic,
117 	                             final boolean sharp){
118 
119 		final ChordCreatorUtil chordCreator = new ChordCreatorUtil(getNewProcess(), listener );
120 		new Thread(new Runnable() {
121 			public void run() {
122 				chordCreator.getChords( tuning, chordIndex, alteration, plusMinus, add, add5, add9, add11, bassTonic, chordTonic, sharp);
123 			}
124 		}).start();
125 	}
126 
127 	protected void getChords(int[] tuning,
128 	                         int chordIndex,
129 	                         int alteration,
130 	                         int plusMinus,
131 	                         boolean add,
132 	                         int add5,
133 	                         int add9,
134 	                         int add11,
135 	                         int bassTonic,
136 	                         int chordTonic,
137 	                         boolean sharp) {
138 
139 		if(!isValidProcess()){
140 			return;
141 		}
142 
143 		this.add5 = add5;
144 
145 		this.tuning = tuning;
146 
147 		this.chordIndex = chordIndex;
148 
149 		this.chordTonic = chordTonic;
150 
151 		this.bassTonic = bassTonic;
152 
153 		this.alteration = alteration;
154 
155 		this.chordName = new ChordNamingConvention().createChordName(this.chordTonic,
156 		                                                             this.chordIndex,
157 		                                                             this.alteration,
158 		                                                             plusMinus,
159 		                                                             add,
160 		                                                             add5,
161 		                                                             add9,
162 		                                                             add11,
163 		                                                             this.bassTonic,
164 		                                                             sharp);
165 
166 
167 		// find the notes that expand the chord
168 		if (this.alteration!=0) {
169 			if (add) {
170 				this.expandingNotes = new int[1];
171 				this.expandingNotes[0]= getAddNote(this.alteration-1,plusMinus);
172 			}
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+-
180 
181 				// rest
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
184 			}
185 
186 		}
187 		else this.expandingNotes=new int[0];
188 
189 
190 
191 		// Required notes
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();
196 
197 
198 		// adjust the subdominant if needed
199 		if (add5!=0) {
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);
203 		}
204 
205 		// first count different from -1
206 		int count = 0;
207 		for (int i=0; i<this.requiredNotes.length; i++) {
208 			this.requiredNotes[i]=checkForOverlapping(this.requiredNotes[i]);
209 			if (this.requiredNotes[i]!=-1)
210 				count++;
211 		}
212 		// then fill in the new array
213 		int[] tempNotes = new int[count];
214 		count = 0;
215 		for (int i=0; i<this.requiredNotes.length; i++)
216 			if (this.requiredNotes[i]!=-1) {
217 				tempNotes[count]=this.requiredNotes[i];
218 				count++;
219 			}
220 		// and substitute them
221 		this.requiredNotes = tempNotes;
222 
223 		//return getChords();
224 		if(isValidProcess()){
225 			List chords = getChords();
226 			if(chords != null && isValidProcess()){
227 				this.listener.notifyChords(this, chords);
228 			}
229 		}
230 	}
231 
232 	/** We have to make sure that if required note is already inside
233 	 * expanding notes array so we don't put it twice...
234 	 */
235 	protected int checkForOverlapping(int checkIt) {
236 		for (int i=0; i<this.expandingNotes.length; i++)
237 			if (this.expandingNotes[i]==checkIt)
238 				return -1;
239 		return checkIt;
240 	}
241 
242 	/**
243 	 *
244 	 * Creates the chord combinations out of given data and then uses some kind
245 	 * of
246 	 *
247 	 * heuristics to check the most suitable formations.
248 	 *
249 	 * @return the list of TGChord structures that are most suitable
250 	 *
251 	 */
252 	private java.util.List getChords() {
253 		if(!isValidProcess()){
254 			return null;
255 		}
256 		ArrayList potentialNotes = makePotentialNotes();
257 
258 		ArrayList combinations = makeCombinations( potentialNotes);
259 
260 		ArrayList priorities = determinePriority( combinations);
261 
262 		ArrayList theBestOnes = takeBest( priorities);
263 
264 		return createChords( theBestOnes);
265 	}
266 
267 	/**
268 	 * Creates the TGChord ArrayList out of StringValue's ArrayLists
269 	 *
270 	 * @param Highest rated StringValues
271 	 * @return TGChord collection
272 	 */
273 	private ArrayList createChords(ArrayList top) {
274 		if(!isValidProcess()){
275 			return null;
276 		}
277 
278 		ArrayList chords = new ArrayList(top.size());
279 
280 		Iterator it = top.iterator();
281 
282 		while (it.hasNext()) {
283 			TGChord chord = TuxGuitar.instance().getSongManager().getFactory()
284 					.newChord(this.tuning.length);
285 			Iterator it2 = ((ArrayList) it.next()).iterator();
286 
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);
293 			}
294 
295 			chords.add(chord);
296 		}
297 		return chords;
298 	}
299 
300 	/**
301 	 *
302 	 * If string/fret combination is needed for the chord formation, add it into
303 	 * List
304 	 *
305 	 * @return true if the note is needed for chord formation
306 	 *
307 	 */
308 	private void find(int stringTone, int stringIndex, int fret,List stringList){
309 		if(!isValidProcess()){
310 			return;
311 		}
312 		boolean bassAlreadyIn=false;
313 		// chord base notes
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)
317 					bassAlreadyIn=true;
318 				stringList.add(new StringValue(stringIndex, fret, i));
319 				return;
320 			}
321 
322 		// alterations
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)
327 						bassAlreadyIn=true;
328 					stringList.add(new StringValue(stringIndex,fret,(i<2 ? this.ESSENTIAL_INDEX : this.NOT_ESSENTIAL_INDEX)));
329 				}
330 			}
331 		}
332 
333 		// bass tone
334 		if (!bassAlreadyIn)
335 			if ((stringTone + fret) % 12 == this.bassTonic) {
336 				stringList.add(new StringValue(stringIndex, fret, this.BASS_INDEX));
337 				return;
338 			}
339 
340 	}
341 
342 	/**
343 	 * Returns the wanted note for ADD chord
344 	 *
345 	 * @param type
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
350 	 *
351 	 */
352 	private int getAddNote(int type, int selectionIndex) {
353 
354 		int wantedNote = 0;
355 
356 		switch (type) {
357 			case 0:
358 				wantedNote = 3; // add9
359 				break;
360 			case 1:
361 				wantedNote = 6; // add11
362 				break;
363 			case 2:
364 				wantedNote = 10; // add13
365 				break;
366 		}
367 
368 		switch (selectionIndex) {
369 			case 1:
370 				wantedNote++;
371 				break;
372 			case 2:
373 				wantedNote--;
374 				break;
375 			default:
376 				break;
377 		}
378 
379 		return wantedNote;
380 	}
381 
382 	private ArrayList makePotentialNotes(){
383 		if(!isValidProcess()){
384 			return null;
385 		}
386 		ArrayList potentialNotes = new ArrayList(this.tuning.length);
387 
388 		for (int string = 0; string < this.tuning.length; string++) {
389 
390 			ArrayList currentStringList = new ArrayList(10);
391 
392 			// search all the frets
393 
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
396 
397 
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);
401 			}
402 
403 			potentialNotes.add(currentStringList);
404 
405 		}
406 		return potentialNotes;
407 	}
408 
409 	/**
410 	 *
411 	 * Makes the all-possible combinations of found notes that can be reached by
412 	 * fingers
413 	 *
414 	 * @param potentialNotes
415 	 *            list consisted of found notes for each string
416 	 *
417 	 * @return list of list of StringValues, with tones that can form a chord
418 	 *
419 	 */
420 	private ArrayList makeCombinations(ArrayList potentialNotes) {
421 		if(!isValidProcess()){
422 			return null;
423 		}
424 
425 		// COMBINATIONS of strings used in a chord
426 		ArrayList stringCombination = new ArrayList(60);
427 		ArrayList lastLevelCombination = null;
428 
429 		for (int i = 0; i < this.tuning.length - 1; i++)
430 		{
431 			lastLevelCombination = makeStringCombination(lastLevelCombination);
432 
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]]
437 
438 			stringCombination.addAll(lastLevelCombination);
439 		}
440 
441 		ArrayList combinations = new ArrayList(800);
442 
443 		// --- combine the StringValues according to strings combination
444 		// ----------------------=======
445 
446 		Iterator iterator = stringCombination.iterator();
447 
448 		while (iterator.hasNext()) { // go through all string combinations list
449 			// take a string combinations
450 			ArrayList currentStringCombination = (ArrayList) iterator.next();
451 			lastLevelCombination = null;
452 
453 			// go through all the strings in one combination
454 			for (int level = 0; level < currentStringCombination.size(); level++) {
455 
456 				// take the string index
457 				int currentString = ((Integer) currentStringCombination.get(level)).intValue();
458 
459 				// take all the potential notes from currentString and combine
460 				// them with potential notes from other strings
461 
462 				lastLevelCombination = makeStringValueCombination(lastLevelCombination,(ArrayList)potentialNotes.get(currentString));
463 
464 				// the structure of combinations is AL { AL(StringValue,SV,SV),
465 				// AL(SV), AL(SV,SV),AL(SV,SV,SV,SV,SV,SV) }
466 
467 			}
468 
469 			if(lastLevelCombination != null){
470 				combinations.addAll(lastLevelCombination);
471 			}
472 		}
473 
474 		return combinations;
475 	}
476 
477 	/**
478 	 * Makes a combination of string indices
479 	 *
480 	 * @param lastLevelCombination
481 	 *            structure to be expanded by current level
482 	 *
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) }
485 	 */
486 	private ArrayList makeStringCombination(ArrayList lastLevelCombinationRef){
487 		if(!isValidProcess()){
488 			return null;
489 		}
490 
491 		List lastLevelCombination = lastLevelCombinationRef;
492 
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();
497 
498 			for (int i = 0; i < this.tuning.length; i++) {
499 				lastLevelCombination.add(new ArrayList());
500 				((ArrayList) lastLevelCombination.get(i)).add(new Integer(i));
501 			}
502 		}
503 
504 		ArrayList thisLevelCombination = new ArrayList();
505 		for (int current = 1; current < this.tuning.length; current++)
506 		{
507 			Iterator it = lastLevelCombination.iterator();
508 
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)) {
515 
516 					// check if the string is already in combination
517 					ArrayList newCombination = (ArrayList) combination.clone();
518 					newCombination.add(currentInteger);
519 					thisLevelCombination.add(newCombination);
520 				}
521 
522 			}
523 
524 		}
525 
526 		return thisLevelCombination;
527 
528 	}
529 
530 	/**
531 	 * Makes a combination of notes by multiplying last combination and current
532 	 * note arrays
533 	 *
534 	 *
535 	 *
536 	 * @param lastLevelCombination
537 	 *            structure to be expanded by current level
538 	 *
539 	 * @param notes
540 	 *            notes that can be considered into making a chord
541 	 *
542 	 * @return structure of StringValue combinations : AL {
543 	 *         AL(StringValue,SV,SV), AL(SV), AL(SV,SV),AL(SV,SV,SV,SV,SV,SV) }
544 	 *
545 	 */
546 	private ArrayList makeStringValueCombination(ArrayList lastLevelCombination, ArrayList notes) {
547 		if(!isValidProcess()){
548 			return null;
549 		}
550 		ArrayList thisLevelCombination = null;
551 
552 		if (lastLevelCombination == null) { // initial combination
553 
554 			thisLevelCombination = new ArrayList(notes.size());
555 
556 			for (int i = 0; i < notes.size(); i++) {
557 
558 				thisLevelCombination.add(new ArrayList(6));
559 
560 				((ArrayList) thisLevelCombination.get(i)).add(notes.get(i));
561 
562 			}
563 
564 			// first combination is AL { AL(firstOne), AL(anotherFret) }
565 
566 		}
567 
568 		else {
569 
570 			thisLevelCombination = new ArrayList();
571 
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));
576 
577 					// if the distance maximum between the existing frets
578 					// is less than wanted, add it into potential list
579 
580 					if (checkCombination(currentCombination))
581 						thisLevelCombination.add(currentCombination);
582 
583 				}
584 		}
585 
586 		return thisLevelCombination;
587 	}
588 
589 	/**
590 	 * Checks if the combination can be reached by fingers. It is reachable
591 	 *
592 	 * if the distance between lowest and highest fret is less than
593 	 *
594 	 * <i>ChordCreatorUtil.MAX_FRET_SPAN</i>.
595 	 *
596 	 * Also note that this method eliminates or includes the chords with empty
597 	 * strings,
598 	 *
599 	 * which is controlled with <i>boolean ChordCreatorUtil.EMPTY_STRING_CHORDS</i>
600 	 *
601 	 * @param combination
602 	 *            current combination to be examined
603 	 *
604 	 * @return true if it can be reached
605 	 *
606 	 */
607 	private boolean checkCombination(ArrayList combination) {
608 
609 		Iterator it = combination.iterator();
610 		int maxLeft, maxRight;
611 
612 		maxLeft = maxRight = ((StringValue) combination.get(0)).getFret();
613 
614 		while (it.hasNext()) {
615 
616 			int fret = ((StringValue) it.next()).getFret();
617 
618 			//chords with empty-string are welcome
619 			if (fret != 0 || !ChordSettings.instance().isEmptyStringChords()) {
620 
621 				if (fret < maxLeft)
622 					maxLeft = fret;
623 
624 				if (fret > maxRight)
625 					maxRight = fret;
626 
627 			}
628 
629 		}
630 
631 		if (Math.abs(maxLeft - maxRight) >= MAX_FRET_SPAN)
632 
633 			return false;
634 
635 		return true;
636 
637 	}
638 
639 	/**
640 	 * orders the StringValue ArrayList by their priority, calculated here
641 	 *
642 	 * for every single chord combination.<br>
643 	 *
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>
652 	 *
653 	 * @param allCombinations
654 	 *            all the StringValue combinations that make some sense
655 	 *
656 	 * @return Treemap of the StringValue ArrayLists, in which the key is
657 	 *
658 	 * <i>float priority</i>.
659 	 *
660 	 */
661 	private ArrayList determinePriority(ArrayList allCombinations) {
662 		if(!isValidProcess()){
663 			return null;
664 		}
665 		ArrayList ordered = new ArrayList();
666 
667 		Iterator it = allCombinations.iterator();
668 
669 		while (it.hasNext() && isValidProcess()) {
670 
671 			float priority = 0;
672 
673 			ArrayList stringValueCombination = (ArrayList) it.next();
674 
675 			// tone combination has all notes required for the chord basis
676 
677 			priority += combinationHasAllRequiredNotes(stringValueCombination);
678 
679 			// uses good chord semantics
680 
681 			priority += combinationChordSemantics(stringValueCombination);
682 
683 			// tone combination has all subsequent strings (no string skipping)
684 
685 			priority += combinationHasSubsequentStrings(stringValueCombination);
686 
687 			// has a chord bass tone as lowest tone
688 
689 			priority += combinationBassInBass(stringValueCombination);
690 
691 			// uses many strings
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) );
697 
698 			// uses good fingering positions
699 
700 			priority += combinationHasGoodFingering(stringValueCombination);
701 
702 			// System.out.println("OVERALL:
703 			// "+priority+"----------------------------");
704 
705 			PriorityItem item = new PriorityItem();
706 
707 			item.priority = priority;
708 
709 			item.stringValues = stringValueCombination;
710 
711 			ordered.add(item);
712 
713 		}
714 
715 		return ordered;
716 
717 	}
718 
719 	/**
720 	 *
721 	 * Takes the StringValue ArrayLists that has the best priority rating
722 	 *
723 	 */
724 	private ArrayList takeBest(ArrayList priorityItems) {
725 		if(!isValidProcess()){
726 			return null;
727 		}
728 
729 		int maximum = ChordSettings.instance().getChordsToDisplay();
730 
731 		ArrayList bestOnes = new ArrayList(maximum);
732 
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);
738 
739 				if( bestOnes.size() >= maximum ){
740 					break;
741 				}
742 			}
743 		}
744 
745 		return bestOnes;
746 
747 	}
748 
749 	/** adds points if the combination has all the notes in the basis of chord */
750 	private float combinationHasAllRequiredNotes(ArrayList stringValueCombination) {
751 		if(!isValidProcess()){
752 			return 0;
753 		}
754 		Iterator it = stringValueCombination.iterator();
755 		int[] values = new int[this.requiredNotes.length];
756 		int currentIndex = 0;
757 
758 		while (it.hasNext()) {
759 			StringValue sv = (StringValue) it.next();
760 
761 			if (sv.getRequiredNoteIndex() >= 0) { // only basis tones
762 				boolean insert = true;
763 
764 				for (int i = 0; i < currentIndex; i++)
765 					if (values[i] == sv.getRequiredNoteIndex() + 1)
766 						insert = false;
767 
768 				// sv.requiredNoteIndex+1, because we have index 0 and we don't
769 				// want it inside
770 
771 				if (insert) {
772 					values[currentIndex] = sv.getRequiredNoteIndex() + 1;
773 					currentIndex++;
774 				}
775 
776 			}
777 		}
778 
779 		if (currentIndex == this.requiredNotes.length) {
780 			return ChordSettings.instance().getRequiredBasicsGrade();
781 		}
782 
783 		if (currentIndex == this.requiredNotes.length - 1) {
784 
785 			boolean existsSubdominant = false;
786 
787 			Iterator it2 = stringValueCombination.iterator();
788 			while (it2.hasNext()) {
789 				StringValue current = (StringValue)it2.next();
790 				if ((this.tuning[current.getString()] + current.getFret()) % 12 == (this.chordTonic + 7) %12)
791 					existsSubdominant = true;
792 			}
793 
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
796 
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 );
802 				}
803 			}
804 
805 		}
806 
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);
809 
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;
813 
814 	}
815 
816 	/** adds points if the combination has strings in a row */
817 	private float combinationHasSubsequentStrings(ArrayList stringValueCombination) {
818 		if(!isValidProcess()){
819 			return 0;
820 		}
821 		boolean stumbled = false, noMore = false, penalty = false;
822 
823 		for (int i = 0; i < this.tuning.length; i++) {
824 			boolean found = false;
825 			Iterator it = stringValueCombination.iterator();
826 			while (it.hasNext())
827 				if (((StringValue) it.next()).getString() == i)
828 					found = true;
829 			if (found) {
830 				if (!stumbled)
831 					stumbled = true;
832 				if (noMore)
833 					penalty = true;
834 				if (penalty) // penalty for skipped strings
835 					return -ChordSettings.instance().getSubsequentGrade();
836 			}
837 			else
838 			if (stumbled)
839 				noMore = true;
840 		}
841 
842 		if (penalty)
843 			return 0.0f;
844 
845 		return ChordSettings.instance().getSubsequentGrade();
846 	}
847 
848 	/** checks if the bass tone is the lowest tone in chord */
849 	private float combinationBassInBass(ArrayList stringValueCombination) {
850 		if(!isValidProcess()){
851 			return 0;
852 		}
853 		for (int i = 0; i < this.tuning.length; i++) {
854 
855 			Iterator it = stringValueCombination.iterator();
856 
857 			while (it.hasNext()) {
858 				StringValue sv = (StringValue) it.next();
859 
860 				if (sv.getString() == i) { // stumbled upon lowest tone
861 					if ( (this.tuning[sv.getString()]+sv.getFret()) % 12 == this.bassTonic  )
862 					  return ChordSettings.instance().getBassGrade();
863 					// else
864 					return -ChordSettings.instance().getBassGrade();
865 				}
866 			}
867 
868 		}
869 
870 		return 0;
871 	}
872 
873 	/**
874 	 * grades the fingering in a chord.
875 	 *
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>
881 	 *
882 	 */
883 	private float combinationHasGoodFingering(ArrayList stringValueCombination) {
884 		if(!isValidProcess()){
885 			return 0;
886 		}
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++)
891 			positions[i] = -1;
892 		{
893 			Iterator it = stringValueCombination.iterator();
894 
895 			while (it.hasNext()) {
896 				StringValue sv = (StringValue) it.next();
897 				positions[sv.getString()] = sv.getFret();
898 			}
899 		}
900 		// algorithm
901 
902 		// distance between fingers
903 		int min = ChordSettings.instance().getFindChordsMax()+2, max = 0, maxCount=0;
904 		boolean openChord = false, zeroString = false;
905 
906 		for (int i = 0; i < this.tuning.length; i++) {
907 
908 			openChord|= ChordSettings.instance().isEmptyStringChords() && positions[i] == 0;
909 			zeroString |= positions[i]==0;
910 
911 			if (positions[i] < min && positions[i] != 0 && positions[i]!=-1)
912 				min = positions[i];
913 
914 			if (positions[i] > max) {
915 				max = positions[i];
916 				maxCount=1;
917 			}
918 			else
919 				if (positions[i]==max)
920 					maxCount++;
921 
922 		}
923 
924 		// finger as capo
925 
926 		int count = 0;
927 
928 		for (int i = 0; i < this.tuning.length; i++)
929 			if (positions[i] == min)
930 				count++;
931 
932 		if (!openChord) {
933 			if (zeroString)
934 				finalGrade += ChordSettings.instance().getFingeringGrade()/8;
935 			else
936 				if (count >= 2)
937 					finalGrade += ChordSettings.instance().getFingeringGrade()/8;
938 		}
939 		else
940 			if (openChord)
941 				finalGrade += ChordSettings.instance().getFingeringGrade()/8;
942 
943 		// position distance: 1-2 nice 3 good 4 bad 5 disaster
944 		float distanceGrade;
945 
946 		switch(Math.abs(max-min)) {
947 			case 0 : distanceGrade=ChordSettings.instance().getFingeringGrade()/5;
948 					break;
949 			case 1 : distanceGrade=ChordSettings.instance().getFingeringGrade()/(5+maxCount);
950 					break;
951 			case 2 : distanceGrade=ChordSettings.instance().getFingeringGrade()/(6+maxCount);
952 					 if (min<5) distanceGrade*=0.9;
953 					break;
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;
958 					break;
959 			case 4 : distanceGrade=-ChordSettings.instance().getFingeringGrade()/4*maxCount;
960 					if (min<=5) distanceGrade*=1.8;
961 					break;
962 			default : distanceGrade=-ChordSettings.instance().getFingeringGrade()*maxCount;
963 					break;
964 		}
965 		finalGrade += distanceGrade;
966 
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 :)
973 
974 		// STRUCTURE: ArrayList consists of Integers - first is fret
975 		//                                           - others are strings
976 /*
977 		for (int i=0; i<this.tuning.length; i++)
978 			System.out.print(" "+positions[i]);
979 		System.out.println("");
980 */
981 
982 		// if chord is open, then we can have capo only in strings before open string
983 		int lastZeroIndex = 0;
984 
985 		if (zeroString)
986 			for (int i=0; i<positions.length; i++)
987 				if (positions[i]==0) lastZeroIndex=i;
988 
989 		// open or not not open chord,
990 		// index finger is always on lowest fret possible
991 		fingers[0].add(new Integer(min));
992 
993 		for (int i=lastZeroIndex; i<positions.length; i++)
994 				if (positions[i]==min) {
995 					fingers[0].add(new Integer(i));
996 					positions[i]=-1;
997 				}
998 
999 		// other fingers
1000 		// if not zero-fret, occupy fingers respectivly
1001 		int finger=1;
1002 		for (int i=0; i<positions.length; i++) {
1003 			if (positions[i]!=0 && positions[i]!=-1) {
1004 				if (finger<4) {
1005 					fingers[finger].add(new Integer(positions[i]));
1006 					fingers[finger].add(new Integer(i));
1007 					positions[i]=-1;
1008 				}
1009 				finger++;
1010 			}
1011 		}
1012 
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()+" ");
1017 		}
1018 */
1019 
1020 		if (finger>4)
1021 			finalGrade-=ChordSettings.instance().getFingeringGrade();
1022 		 else
1023 			finalGrade+=ChordSettings.instance().getFingeringGrade()*0.1*(15-2*finger);
1024 
1025 		// TODO: maybe to put each finger's distance from the minimum
1026 		return finalGrade;
1027 
1028 	}
1029 
1030 	/**
1031 	 * grades the chord semantics, based on theory.
1032 	 *
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>
1036 	 *
1037 	 * Algorithm:<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>
1043 	 */
1044 	private float combinationChordSemantics(ArrayList stringValueCombination) {
1045 		if(!isValidProcess()){
1046 			return 0;
1047 		}
1048 		float finalGrade = 0;
1049 
1050 		int foundTonic = -1;
1051 
1052 		int[] foundExpanding = new int[this.expandingNotes.length];
1053 		int stringDepth=0;
1054 
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;
1060 
1061 			while (it.hasNext() && !found) {
1062 				StringValue sv = (StringValue) it.next();
1063 				if (sv.getString() == string &&!found && sv.getFret()!=-1) { // stumbled upon next string
1064 					current = sv;
1065 					found=true;
1066 					stringDepth++;
1067 				}
1068 			}
1069 
1070 			// grade algorithms----
1071 			if (current != null) {
1072 				// search for tonic
1073 				if (foundTonic==-1 && current.getRequiredNoteIndex()==0)
1074 					foundTonic=this.tuning[current.getString()]+current.getFret();
1075 
1076 				// specific bass not in bass?
1077 				if (stringDepth>1) {
1078 					if (current.getRequiredNoteIndex()==this.BASS_INDEX)
1079 						finalGrade -= ChordSettings.instance().getGoodChordSemanticsGrade();
1080 
1081 					if (current.getRequiredNoteIndex()<0) { // expanding tones
1082 						// expanding tone found before the tonic
1083 						if (foundTonic==-1)
1084 							finalGrade -= ChordSettings.instance().getGoodChordSemanticsGrade()/2;
1085 						else {
1086 							// if expanding note isn't higher than tonic's octave
1087 							if (foundTonic+11 > this.tuning[current.getString()]+current.getFret())
1088 								finalGrade -= ChordSettings.instance().getGoodChordSemanticsGrade()/3;
1089 						}
1090 
1091 						// search for distinct expanding notes
1092 						for (int i=0; i<this.expandingNotes.length; i++)
1093 							if ((this.tuning[string]+current.getFret())%12==(this.chordTonic+this.expandingNotes[i]-1)%12)
1094 								if (foundExpanding[i]==0)
1095 									foundExpanding[i]=current.getRequiredNoteIndex();
1096 
1097 					}
1098 				}
1099 			}
1100 		}
1101 
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)
1107 					essentials++;
1108 				else
1109 					if (foundExpanding[i]!=0)
1110 						nonEssentials++;
1111 			}
1112 
1113 			if (essentials+nonEssentials==this.expandingNotes.length)
1114 				finalGrade+=ChordSettings.instance().getGoodChordSemanticsGrade();
1115 			else {
1116 				if (essentials==2) // if all essentials are there, it's good enough
1117 					finalGrade+=ChordSettings.instance().getGoodChordSemanticsGrade()/2;
1118 
1119 				// but if some are missing, that's BAD:
1120 				else {
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;
1124 				}
1125 			}
1126 		}
1127 
1128 		return finalGrade;
1129 
1130 	}
1131 
1132 	/**
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
1138 	 */
1139 	private boolean checkIfSubset(List stringValues, List betterOnes) {
1140 		if(!isValidProcess()){
1141 			return false;
1142 		}
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)).getString() ;
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)).getString() == 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)).getFret() != ((ChordCreatorUtil.StringValue)currentStringValue.get(i)).getFret())
1155 						foundDifferentFret=true;
1156 				}
1157 
1158 			}
1159 			if (!foundDifferentFret)// nothing is different
1160 				return true;
1161 		}
1162 
1163 		return false;
1164 	}
1165 
1166 	/**
1167 	 * contains information about the note: string, fret and tone function in a
1168 	 * chord
1169 	 *
1170 	 * @author julian
1171 	 *
1172 	 */
1173 
1174 	private class StringValue {
1175 
1176 		protected int string;
1177 		protected int fret;
1178 		protected int requiredNoteIndex;
1179 
1180 		public StringValue(int string, int fret, int requiredNoteIndex) {
1181 			this.string = string;
1182 			this.fret = fret;
1183 			this.requiredNoteIndex = requiredNoteIndex;
1184 
1185 		}
1186 
1187 		public int getString() {
1188 			return this.string;
1189 		}
1190 
1191 		public int getFret() {
1192 			return this.fret;
1193 		}
1194 
1195 		public int getRequiredNoteIndex() {
1196 			return this.requiredNoteIndex;
1197 		}
1198 	}
1199 
1200 	/** used just to sort StringValue ArrayLists by priorities */
1201 	protected class PriorityItem {
1202 
1203 		ArrayList stringValues;
1204 		float priority;
1205 
1206 	}
1207 
1208 	/** used to sort the array */
1209 	protected class PriorityComparator implements Comparator {
1210 
1211 		public int compare(Object o1, Object o2) {
1212 			return Math.round(((PriorityItem) o2).priority - ((PriorityItem) o1).priority);
1213 		}
1214 
1215 	}
1216 }