/// <summary> /// Evaluate harmonic and percussive mag-phase spectrograms from given signal. /// Both spectrogram objects share the same phase array. /// </summary> /// <param name="signal"></param> /// <returns></returns> public (MagnitudePhaseList, MagnitudePhaseList) EvaluateSpectrograms(DiscreteSignal signal) { // spectrogram memory will be reused for harmonic magnitudes var harmonicSpectrogram = _stft.MagnitudePhaseSpectrogram(signal); var harmonicMagnitudes = harmonicSpectrogram.Magnitudes; // median filtering along frequency axis: var percussiveMagnitudes = new List <float[]>(harmonicMagnitudes.Count); for (var i = 0; i < harmonicMagnitudes.Count; i++) { var mag = new DiscreteSignal(1, harmonicMagnitudes[i]); percussiveMagnitudes.Add(_medianPercussive.ApplyTo(mag).Samples); _medianPercussive.Reset(); } // median filtering along time axis: for (var j = 0; j <= _stft.Size / 2; j++) { int i = 0, k = 0; for (; k < _medianHarmonic.Size / 2; k++) // feed first Size/2 samples { _medianHarmonic.Process(harmonicMagnitudes[k][j]); } for (; i < harmonicMagnitudes.Count - _medianHarmonic.Size / 2; i++, k++) { var h = _medianHarmonic.Process(harmonicMagnitudes[k][j]); harmonicMagnitudes[i][j] *= _mask(h, percussiveMagnitudes[k][j]); percussiveMagnitudes[i][j] *= _mask(percussiveMagnitudes[k][j], h); } for (k = 0; k < _medianHarmonic.Size / 2; i++, k++) // don't forget last samples { var h = _medianHarmonic.Process(0); harmonicMagnitudes[i][j] *= _mask(h, percussiveMagnitudes[i][j]); percussiveMagnitudes[i][j] *= _mask(percussiveMagnitudes[i][j], h); } _medianHarmonic.Reset(); } var percussiveSpectrogram = new MagnitudePhaseList { Magnitudes = percussiveMagnitudes, Phases = harmonicSpectrogram.Phases }; return(harmonicSpectrogram, percussiveSpectrogram); }