double evaluateFingerObstacleCost(FingerState state, float minPreparation, NoteChord obsNote, float startPosition, int startHeight, int pitch) { float deltaX = startPosition - Piano.KeyPositions[pitch]; float deltaY = startHeight - Piano.getKeyHeight(pitch); double distance = Math.Min(Math.Sqrt(deltaX * deltaX + deltaY * deltaY), 5) + 1; float prepareTime = startTime - minPreparation; float importance = NotationUtils.getNoteImportanceInChord(obsNote, state.Pitch); debug += string.Format("O{0},{1},{2},{3};", state.Pitch, prepareTime, state.Release, state.Press); if (prepareTime >= state.Release) { double coeff = CostCoeff.FINGER_MOVE_SPEED_PUNISH * Math.Pow(0.1, (startTime - state.Release) / minPreparation); return(coeff * distance); } else if (prepareTime >= state.Press) { double speedCost = CostCoeff.FINGER_MOVE_SPEED_PUNISH * distance; double cutoffCost = CostCoeff.NOTE_CUTOFF_PUNISH * importance * (state.Release - prepareTime) / (state.Release - state.Press); return(speedCost + cutoffCost); } else { return(CostCoeff.OMIT_KEY_PUNISH * importance); } }
double evaluateSingleArmCost(HandConfig.Range lastWrist, HandConfig.Range currentWrist, FingerState[] lastFs, FingerState[] currentFs, int hand) { double cost = 0; // wrist offset punish // by middle cost += Math.Abs(currentWrist.middle - lastWrist.middle) * CostCoeff.WRIST_OFFSET_MIDDLE_PUNISH / timeUnit; debug += string.Format("FM: {0}\\n", Math.Abs(currentWrist.middle - lastWrist.middle) / timeUnit); // by range if (!(lastWrist.low < currentWrist.high && lastWrist.high > currentWrist.low)) { cost += Math.Min(Math.Abs(currentWrist.low - lastWrist.high), Math.Abs(lastWrist.low - currentWrist.high)) * CostCoeff.WRIST_OFFSET_RANGE_PUNISH / timeUnit; debug += string.Format("FR: {0}\\n", Math.Min(Math.Abs(currentWrist.low - lastWrist.high), Math.Abs(lastWrist.low - currentWrist.high)) / timeUnit); } // finger speed punish if (lastFs != null) { List <int> fingers = new List <int>(); List <int> pitches = new List <int>(); foreach (var pair in fingerChord) { int finger = (int)pair.Value * hand; if (finger > 0) { int first = (int)Math.Floor(finger / 10f) - 1; int second = finger % 10 - 1; if (first > 0) { fingers.Add(first); pitches.Add(pair.Key); } fingers.Add(second); pitches.Add(pair.Key); } } for (int i = 0; i < fingers.Count; ++i) { int finger = fingers[i]; int pitch = pitches[i]; //float position = Piano.KeyPositions[pitch]; FingerState state = lastFs[finger]; float minimumPreparationTime = s_BenchmarkDuration * 2 * HandConfig.MinimumPreparationRate; // self obstacle if (state.Index >= 0) { NoteChord obsNote = s_NoteSeq[state.Index]; cost += evaluateFingerObstacleCost(state, minimumPreparationTime * 2, obsNote, state.Position, state.Height, pitch); } // other obstacles if (finger > 0) { for (int of = 1; of < 5; ++of) { // ignore self finger if (of == finger) { continue; } FingerState obsState = lastFs[of]; if (obsState.Index >= 0) { int height = Piano.getKeyHeight(pitch); // allow ring finger on black cross pinky finger on white if (finger == 3 && of == 4 && height > obsState.Height) { continue; } float startPosition = obsState.Position + (finger - of) * hand; int startHeight = obsState.Height; float targetPosition = currentFs[finger].Position; if (state.Index >= 0) { startHeight = state.Height; if (finger * hand < of * hand) { startPosition = Math.Min(startPosition, state.Position); } else { startPosition = Math.Max(startPosition, state.Position); } } //debug += string.Format("of: {0}, {1}, {2}, {3}\\n", of, startPosition, obsState.Position, targetPosition); if ((targetPosition - obsState.Position) * (obsState.Position - startPosition) <= 0) { continue; } NoteChord obsNote = s_NoteSeq[obsState.Index]; cost += evaluateFingerObstacleCost(obsState, minimumPreparationTime, obsNote, startPosition, startHeight, pitch); //debug += "O" + ((of + 1) * hand).ToString() + ","/* + (startTime - minimumPreparationTime - obsState.Release).ToString() + ";"*/; } } } } } // TODO: return(cost); }
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); }