/// <summary> /// Initializes a new instance of the <see cref="GazeSelectionService" /> class. /// </summary> /// <param name="eventAggregator">Provides pub/sub events (obtained through DI).</param> public GazeSelectionService(IEventAggregator eventAggregator) { this.timedPoints = new TimedPoints(Configuration.PointKeepAliveTimeSpan); this.averagingFilter = new AveragingLastNpointsWithinTimeSpanFilter(3, TimeSpan.FromMilliseconds(75)); this.dataPerControl = new ConcurrentDictionary <SelectableControl, ISelectableControlViewModel>(); this.KnownWindows.Add(Application.Current.MainWindow); eventAggregator.GetEvent <Events.NewCoordinateEvent>().Subscribe(this.ProcessPoint); Application.Current.MainWindow.Closing += (sender, args) => eventAggregator.GetEvent <Events.NewCoordinateEvent>().Unsubscribe(this.ProcessPoint); }
public void AdjustAndInterpolate(SpeechTypeSpecification speechTypeSpecification) // , double deltaTime, Boolean setUnvoicedPitch) { // Carry out median filtering to remove single errors List <double> correctedPitchValues = new List <double>(); correctedPitchValues.Add(pitchPeriodSpecification.TimePitchPeriodTupleList[0].Item2); for (int ii = 1; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count - 1; ii++) { List <double> rawPitchValues = new List <double>() { pitchPeriodSpecification.TimePitchPeriodTupleList[ii - 1].Item2, pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item2, pitchPeriodSpecification.TimePitchPeriodTupleList[ii + 1].Item2 }; rawPitchValues.Sort(); correctedPitchValues.Add(rawPitchValues[1]); // Median } // Finally adjust the end points (which are not touched by the initial median filtering) if (pitchPeriodSpecification.TimePitchPeriodTupleList.Count > 2) { List <double> rawPitchValues = new List <double>() { pitchPeriodSpecification.TimePitchPeriodTupleList[0].Item2, pitchPeriodSpecification.TimePitchPeriodTupleList[1].Item2, pitchPeriodSpecification.TimePitchPeriodTupleList[2].Item2 }; rawPitchValues.Sort(); correctedPitchValues[0] = rawPitchValues[1]; int lastIndex = pitchPeriodSpecification.TimePitchPeriodTupleList.Count - 1; rawPitchValues = new List <double>() { pitchPeriodSpecification.TimePitchPeriodTupleList[lastIndex].Item2, pitchPeriodSpecification.TimePitchPeriodTupleList[lastIndex - 1].Item2, pitchPeriodSpecification.TimePitchPeriodTupleList[lastIndex - 2].Item2 }; rawPitchValues.Sort(); correctedPitchValues.Add(rawPitchValues[1]); } for (int ii = 0; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count - 1; ii++) { pitchPeriodSpecification.TimePitchPeriodTupleList[ii] = new Tuple <double, double>(pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item1, correctedPitchValues[ii]); } // Extend (extrapolate) the pitch period specification so that it runs to the end of the sound: double lastTime = speechTypeSpecification.TimeSpeechTypeTupleList.Last().Item1; int lastPitchIndex = pitchPeriodSpecification.TimePitchPeriodTupleList.Count - 1; double lastPitchTime = pitchPeriodSpecification.TimePitchPeriodTupleList[lastPitchIndex].Item1; if (lastTime > lastPitchTime) // Should always be the case, but just to be sure ... { double lastPitch = pitchPeriodSpecification.TimePitchPeriodTupleList[lastPitchIndex].Item2; pitchPeriodSpecification.TimePitchPeriodTupleList.Add(new Tuple <double, double>(lastTime, lastPitch)); } // Next, resample (upsample) the pitch period specification List <double> timeList = new List <double>(); List <double> pitchList = new List <double>(); for (int ii = 0; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; ii++) { double time = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item1; double pitch = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item2; timeList.Add(time); pitchList.Add(pitch); } List <List <double> > timePitchList = new List <List <double> >() { timeList, pitchList }; int numberOfPoints = (int)Math.Round(lastTime / deltaTime); List <List <double> > interpolatedTimePitchList = LinearInterpolation.Interpolate(timePitchList, numberOfPoints); pitchPeriodSpecification = new PitchPeriodSpecification(); for (int ii = 0; ii < interpolatedTimePitchList[0].Count; ii++) { double time = interpolatedTimePitchList[0][ii]; double pitch = interpolatedTimePitchList[1][ii]; pitchPeriodSpecification.TimePitchPeriodTupleList.Add(new Tuple <double, double>(time, pitch)); } // Optionally (usually true) hard-set the (anyway rather arbitrary) pitch period for // unvoiced parts of the sound, by extending the pitch period from surrounding // voiced parts. This might cause occasional jumps (in the middle of an unvoiced // section), but those jumps are reoved in the subsequent lowpass filtering if (setUnvoicedPitch) { double previousTime = pitchPeriodSpecification.TimePitchPeriodTupleList[0].Item1; SpeechType previousSpeechType = speechTypeSpecification.GetSpeechType(previousTime); int firstChangeIndex = 0; // Will be changed later - must initialize here. int lastChangeIndex = -1; double previousPitch; // Must define here for use after the loop as well. for (int ii = 1; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; ii++) { double time = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item1; SpeechType speechType = speechTypeSpecification.GetSpeechType(time); if ((previousSpeechType == SpeechType.Voiced) && (speechType != SpeechType.Voiced)) { firstChangeIndex = ii; lastChangeIndex = -1; // Not yet assigned. The value -1 is used for handling cases where the // sound remains not voiced until the end (see below). } else if ((previousSpeechType != SpeechType.Voiced) && (speechType == SpeechType.Voiced)) { lastChangeIndex = ii - 1; int middlexIndex = (firstChangeIndex + lastChangeIndex) / 2; // integer division // assign the preceding pitch to the first half of the interval (unless firstChangeIndex = 0, meaning // that the sound started with an unvoiced segment), and the subsequent pitch to the second half of // the interval: double subsequentPitch = pitchPeriodSpecification.TimePitchPeriodTupleList[lastChangeIndex].Item2; previousPitch = subsequentPitch; if (firstChangeIndex > 0) { previousPitch = pitchPeriodSpecification.TimePitchPeriodTupleList[firstChangeIndex - 1].Item2; } for (int jj = firstChangeIndex; jj < middlexIndex; jj++) { time = pitchPeriodSpecification.TimePitchPeriodTupleList[jj].Item1; pitchPeriodSpecification.TimePitchPeriodTupleList[jj] = new Tuple <double, double>(time, previousPitch); } for (int jj = middlexIndex; jj <= lastChangeIndex; jj++) { time = pitchPeriodSpecification.TimePitchPeriodTupleList[jj].Item1; pitchPeriodSpecification.TimePitchPeriodTupleList[jj] = new Tuple <double, double>(time, subsequentPitch); } } previousTime = time; previousSpeechType = speechType; } // At the end, if lastChangeIndex = -1, then the sound remained not voiced from the latest // change until the end. Thus: if ((lastChangeIndex == -1) && (firstChangeIndex > 0)) { previousPitch = pitchPeriodSpecification.TimePitchPeriodTupleList[firstChangeIndex - 1].Item2; for (int jj = firstChangeIndex; jj < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; jj++) { double time = pitchPeriodSpecification.TimePitchPeriodTupleList[jj].Item1; pitchPeriodSpecification.TimePitchPeriodTupleList[jj] = new Tuple <double, double>(time, previousPitch); } } // Then, finally, low-pass filter the interpolated list, and assign the result: AveragingFilter averagingFilter = new AveragingFilter(); List <double> inputList = new List <double>(); for (int ii = 0; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; ii++) { double input = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item2; inputList.Add(input); } List <double> outputList = averagingFilter.Run(inputList); for (int ii = 0; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; ii++) { double filteredPitch = outputList[ii]; double time = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item1; pitchPeriodSpecification.TimePitchPeriodTupleList[ii] = new Tuple <double, double>(time, filteredPitch); } /* FirstOrderLowPassFilter lowPassFilter = new FirstOrderLowPassFilter(); * lowPassFilter.SetAlpha(0.9); // To do: Parameterize. * for (int ii = 0; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; ii++) * { * double input = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item2; * lowPassFilter.Step(input); * } * for (int ii = 0; ii < pitchPeriodSpecification.TimePitchPeriodTupleList.Count; ii++) * { * double filteredPitch = lowPassFilter.OutputList[ii]; * double time = pitchPeriodSpecification.TimePitchPeriodTupleList[ii].Item1; * pitchPeriodSpecification.TimePitchPeriodTupleList[ii] = new Tuple<double, double>(time, filteredPitch); * } */ } }