private static Func <PitchHmmEmission, double> NoteProbability(UtauNote note) { if (note == null) { throw new ArgumentNullException(nameof(note)); } if (note.IsRestNote) { throw new ArgumentException(); } // 正規分布 const double stdDev = 0.6; var mean = note.NoteNumber % 12; return(e => { if (e.IsSilent) { return double.NegativeInfinity; } var p = Math.Max( Math.Max( NormalDistribution(mean, stdDev, e.NormalizedPitch), NormalDistribution(mean - 12, stdDev, e.NormalizedPitch) ), NormalDistribution(mean + 12, stdDev, e.NormalizedPitch) ); return Math.Log(p); }); }
/// <summary> /// <paramref name="restNote"/> を通過するときに無音状態になる確率 /// </summary> private static double ProbabilityOfNoSoundWhenRestNote(UtauNote restNote) { if (!restNote.IsRestNote) { throw new ArgumentException(); } // 長さ 720 程度で 0.65 に到達するくらいの確率 const int maxLength = 720; const double minProbability = 0.3; const double maxProbability = 0.65; return(Math.Min( minProbability + ((maxProbability - minProbability) / maxLength) * restNote.Length, maxProbability )); }
/// <summary> /// <paramref name="prevNote"/> のあとに無音状態になる確率 /// </summary> private static double ProbabilityOfNoSoundAfter(UtauNote prevNote) { // ノートが長いほど、そのあとは休みがち const int minLength = 480; const double minProbability = 0.05; const double maxProbability = 0.2; // 短いときはほとんど休まない if (prevNote.Length <= minLength) { return(minProbability); } // 長いほど確率が上がり、全音符なら 0.2 return(Math.Min( minProbability + (prevNote.Length - minLength) * ((maxProbability - minProbability) / (1920 - minLength)), maxProbability )); }
public static PitchHmmState CreateNoSoundState(UtauNote reportingNote) { return(new PitchHmmState(reportingNote, true)); }
public static PitchHmmState CreateEmittingSoundState(UtauNote reportingNote) { return(new PitchHmmState(reportingNote, false)); }
protected PitchHmmState(UtauNote reportingNote, bool isSilentState) { this.ReportingNote = reportingNote; this.IsSilentState = isSilentState; }
public void InputObservation(PitchHmmEmission observation) { var states = this._model.States; var maxLogProbabilities = new double[states.Count]; for (var i = 0; i < maxLogProbabilities.Length; i++) { maxLogProbabilities[i] = double.NegativeInfinity; } // 初期状態確率 * 遷移確率 * 生成確率 を計算する foreach (var(fromStateIndex, stateLogProbability) in this._stateLogProbabilities) { foreach (var(toStateIndex, transitionLogProbability) in states[fromStateIndex].LogProbabilitiesByOutgoingStateIndexes) { var lp = stateLogProbability + transitionLogProbability + states[toStateIndex].EmissionLogProbability(observation); if (!(lp <= 0.0)) { throw new Exception("確率が不正な値になりました。"); // NaN チェックも含めるため not <= } if (lp > maxLogProbabilities[toStateIndex]) { maxLogProbabilities[toStateIndex] = lp; } } } var stateLogProbabilities = this._stateLogProbabilities; stateLogProbabilities.Clear(); var logSum = double.NegativeInfinity; var maxP = double.NegativeInfinity; // 最大の確率 var argmaxP = 0; // 最大の確率のときの状態 for (var stateIndex = 0; stateIndex < maxLogProbabilities.Length; stateIndex++) { var lp = maxLogProbabilities[stateIndex]; if (double.IsNegativeInfinity(lp)) { continue; } logSum = Special.LogSum(logSum, lp); stateLogProbabilities.Add(new KeyValuePair <int, double>(stateIndex, lp)); if (lp > maxP) { maxP = lp; argmaxP = stateIndex; } } if (stateLogProbabilities.Count == 0) { throw new Exception("すべての状態の確率が 0 になりました。"); } // 確率が最も高い状態を現在のノートとして認識 this.CurrentNote = states[argmaxP].Value.ReportingNote; // 次の計算のために確率を均しておく // 新しい確率 = 確率 / 確率の合計 for (var i = 0; i < stateLogProbabilities.Count; i++) { var(stateIndex, lp) = stateLogProbabilities[i]; stateLogProbabilities[i] = new KeyValuePair <int, double>(stateIndex, lp - logSum); } }