public static string getFingerChordJsonDump(FingerChord fc) { string list = ""; foreach (Finger f in fc.Values) { list += (list.Length > 0 ? "," : "") + ((int)f).ToString(); } return("\"" + list + "\""); }
void tryFingerChoice(int[] pitches, ref List <FingerChord> choices, FingerChord constrait, FingerChord fc, int currentNoteIndex, int emptyQuota) { if (currentNoteIndex >= pitches.Length) { choices.Add(new FingerChord(fc)); /*Finger[] fa = new Finger[fc.Values.Count]; * fc.Values.CopyTo(fa, 0); * UnityEngine.Debug.Log("fc: " + String.Join(",", Array.ConvertAll(fa, x => ((int)x).ToString())));*/ } else { int currentPitch = pitches[currentNoteIndex]; if (constrait != null && constrait.ContainsKey(currentPitch) && constrait[currentPitch] != Finger.EMPTY) { fc[currentPitch] = constrait[currentPitch]; tryFingerChoice(pitches, ref choices, constrait, fc, currentNoteIndex + 1, emptyQuota); } else { foreach (Finger f in FingerConstants.SolveTypeFingers[HandType]) { bool pass = true; for (int index = currentNoteIndex - 1; index >= 0; --index) { int pitch = pitches[index]; Finger finger = fc[pitch]; float distance = Piano.pitchPairDistance(pitch, currentPitch); pass = FingerConstants.testFingerDistance(finger, f, Config, distance); if (!pass) { break; } } if (pass) { fc[currentPitch] = f; tryFingerChoice(pitches, ref choices, constrait, fc, currentNoteIndex + 1, emptyQuota); } } if (emptyQuota > 0) { fc[currentPitch] = Finger.EMPTY; tryFingerChoice(pitches, ref choices, constrait, fc, currentNoteIndex + 1, emptyQuota - 1); } } } }
FingerMap getTreeNodeFingerMap(TreeNode node) { FingerMap map = node.parent != null?getTreeNodeFingerMap(node.parent) : new FingerMap(); if (node.Choice >= 0) { FingerChord fc = ChoiceSequence[node.Index][node.Choice].chord; NoteChord nc = NoteSeq[node.Index]; map[nc.tick] = fc; } return(map); }
TreeNode(TreeNode parent_, int choiceIndex_) { parent = parent_; choiceIndex = choiceIndex_; Choice choice = s_ChoiceSequence[Index][choiceIndex]; fingerChord = choice.chord; staticCost = choice.staticCost; wrists = choice.wrists; note = s_NoteSeq[Index]; startTime = note.start; timeUnit = choice.deltaTime / s_BenchmarkDuration; leftFingers = generateFingerStates(parent.leftFingers, -1, fingerChord, note, Index); rightFingers = generateFingerStates(parent.rightFingers, 1, fingerChord, note, Index); }
Choice[] getFingerChoices(NoteChord nc, int index) { if (nc.notes.Count == 0) { return(new Choice[0]); } List <FingerChord> choices = new List <FingerChord>(); int[] pitches = new int[nc.notes.Count]; nc.notes.Keys.CopyTo(pitches, 0); FingerChord constrait = null; if (KeepConstraints) { constrait = new FingerChord(); foreach (var pair in nc.notes) { if (pair.Value.finger != Finger.EMPTY) { constrait[pair.Key] = pair.Value.finger; UnityEngine.Debug.Log("pair.Value.finger: " + pair.Value.finger.ToString()); } } } int fingerCount = HandType == SolveHandType.MIX ? 10 : 5; int emptyQuota = Math.Max(pitches.Length - fingerCount, 0); FingerChord fc = new FingerChord(); while (choices.Count == 0) { tryFingerChoice(pitches, ref choices, constrait, fc, 0, emptyQuota++); } Choice[] choiceArray = new Choice[choices.Count]; for (int i = 0; i < choices.Count; ++i) { FingerChord chord = choices[i]; choiceArray[i] = evaluateChordChoice(chord, index); } return(choiceArray); }
public RangePair getFingerChordWristRange(FingerChord fc) { RangePair rp = new RangePair(); foreach (var pair in fc) { if (pair.Value != Finger.EMPTY) { float keyPosition = Piano.KeyPositions[pair.Key]; Range range = getFingerRange(pair.Value); range = new Range { low = keyPosition + range.low, high = keyPosition + range.high }; if (pair.Value > Finger.EMPTY) { if (rp.right == null) { rp.right = range; } else { rp.right.low = Math.Max(rp.right.low, keyPosition + range.low); rp.right.high = Math.Min(rp.right.high, keyPosition + range.high); } } else { if (rp.left == null) { rp.left = range; } else { rp.left.low = Math.Max(rp.left.low, keyPosition + range.low); rp.left.high = Math.Min(rp.left.high, keyPosition + range.high); } } } } return(rp); }
Choice evaluateChordChoice(FingerChord chord, int index) { float deltaTime = index > 0 ? NoteSeq[index].start - NoteSeq[index - 1].start : 0; NoteChord note = NoteSeq[index]; HandConfig.RangePair wrists = Config.getFingerChordWristRange(chord); double cost = 0; // wrist position naturality reward if (wrists.left != null) { float distance = Math.Abs(wrists.left.middle - -HandConfig.WristNaturePosition); cost = Math.Pow(distance / 14, 4) * CostCoeff.WRIST_POSITION_NATURALITY_REWARD; } if (wrists.right != null) { float distance = Math.Abs(wrists.right.middle - HandConfig.WristNaturePosition); cost = Math.Pow(distance / 14, 4) * CostCoeff.WRIST_POSITION_NATURALITY_REWARD; } // wrist crowd punish if (wrists.left != null && wrists.right != null) { float distance = Math.Max(Math.Abs(wrists.left.high - wrists.right.low), Math.Abs(wrists.right.high - wrists.left.low)); if (distance < 5) { cost += CostCoeff.WRIST_CROWD_PUNISH * (5f - distance) / 5f; } } foreach (Finger f in chord.Values) { // shift fingers punish if (Math.Abs((int)f) > 10) { cost += CostCoeff.SHIFT_FINGERS_PUNISH; } } int leftFingerCount = 0; int rightFingerCount = 0; foreach (var pair in chord) { if (pair.Value != Finger.EMPTY) { // black key short punish if (Piano.isBlackKey(pair.Key)) { int finger = Math.Abs((int)pair.Value); int first = (int)Math.Floor(finger / 10f) - 1; int second = finger % 10 - 1; float sh = HandConfig.BlackKeyShort[second]; if (first >= 0) { sh = Math.Max(HandConfig.BlackKeyShort[first], sh); } cost += sh * CostCoeff.BLACK_KEY_SHORT_PUNISH; } if (pair.Value > Finger.EMPTY) { ++rightFingerCount; } else if (pair.Value < Finger.EMPTY) { ++leftFingerCount; } } else { // omit key punish float importance = NotationUtils.getNoteImportanceInChord(note, pair.Key); cost += CostCoeff.OMIT_KEY_PUNISH * importance; } } // multiple fingers punish if (leftFingerCount > 0) { float value = leftFingerCount / 5f; cost += CostCoeff.MULTIPLE_FINGERS_PUNISH * value * value; } if (rightFingerCount > 0) { float value = rightFingerCount / 5f; cost += CostCoeff.MULTIPLE_FINGERS_PUNISH * value * value; } return(new Choice { chord = chord, staticCost = cost, wrists = wrists, deltaTime = deltaTime, node = new TreeNodeChoice() }); }
static FingerState[] generateFingerStates(FingerState[] parentStates, int hand, FingerChord chord, NoteChord nc, int index) { FingerState[] states = null; if (parentStates != null) { states = new FingerState[parentStates.Length]; Array.Copy(parentStates, states, states.Length); } foreach (var pair in chord) { int finger = (int)pair.Value * hand; if (finger > 0) { if (states == null) { states = Enumerable.Repeat(new FingerState { Press = -10000f, Release = -10000f, Index = -1, Height = 0 }, 5).ToArray(); for (int i = 0; i < states.Length; ++i) { states[i].Position = (HandConfig.WristNaturePosition + i - 2) * hand; } } Note note = nc.notes[pair.Key]; UnityEngine.Debug.Assert(note != null, "note and finger chord mismatch"); float position = Piano.KeyPositions[pair.Key]; int height = Piano.getKeyHeight(pair.Key); int first = (int)Math.Floor(finger / 10f) - 1; int second = finger % 10 - 1; if (first >= 0) { states[first].Press = note.start; states[first].Release = note.start; states[first].Pitch = pair.Key; states[first].Position = position; states[first].Height = height; states[first].Index = index; } UnityEngine.Debug.Assert(second >= 0 && second < 5, "invalid finger value"); { states[second].Press = note.start; states[second].Release = note.start + note.duration; states[second].Pitch = pair.Key; states[second].Position = position; states[second].Height = height; states[second].Index = index; //fixFingerStatesObstacle(ref states, hand, second); } } } return(states); }