public double[] getAnalyzeDataC() { g_OffSet = 0;//初始化 //使用互相关算法分析 int len = 262144; double[] A = new double[len]; double[] B = new double[len]; for (int i = 0; i < len; i++) { A[i] = g_DataA[i]; B[i] = g_DataB[i]; } CrossCorrelation ccorr = new CrossCorrelation(); double[] R = ccorr.Rxy(A, B); int R_length = R.Length; Position p = new Position(); int location = p.max(R); int d = location - R_length / 2; g_OffSet = d;//偏移值 return R;//经过互相关计算后的数组 }
private void crossCorrelateButton_Click(object sender, RoutedEventArgs e) { List <Match> matches = new List <Match>(matchGrid.SelectedItems.Cast <Match>()); foreach (Match matchFE in matches) { Match match = matchFE; // needed as reference for async task Task.Factory.StartNew(() => { Match ccm = CrossCorrelation.Adjust(match, progressMonitor); Dispatcher.BeginInvoke((Action) delegate { multiTrackViewer.Matches.Add(ccm); matchGrid.Items.Refresh(); multiTrackViewer.RefreshAdornerLayer(); }); }); } }
private void crossCorrelateButton_Click(object sender, RoutedEventArgs e) { if (ccr == null) { Task.Factory.StartNew(() => { Match ccm = CrossCorrelation.Adjust(match, progressMonitor, out ccr); Dispatcher.BeginInvoke((Action) delegate { multiTrackViewer1.Matches.Add(ccm); multiTrackViewer1.RefreshAdornerLayer(); ShowCCResult(ccr); }); }); } else { ShowCCResult(ccr); } }
/// <summary> /// The CORE ANALYSIS METHOD. /// </summary> public static Tuple <BaseSonogram, double[, ], Plot, List <AcousticEvent>, TimeSpan> Analysis(FileInfo fiSegmentOfSourceFile, Dictionary <string, string> configDict, TimeSpan segmentStartOffset) { //set default values - int frameLength = 1024; if (configDict.ContainsKey(AnalysisKeys.FrameLength)) { frameLength = int.Parse(configDict[AnalysisKeys.FrameLength]); } double windowOverlap = 0.0; int minHz = int.Parse(configDict["MIN_HZ"]); int minFormantgap = int.Parse(configDict["MIN_FORMANT_GAP"]); int maxFormantgap = int.Parse(configDict["MAX_FORMANT_GAP"]); double decibelThreshold = double.Parse(configDict["DECIBEL_THRESHOLD"]); //dB double harmonicIntensityThreshold = double.Parse(configDict["INTENSITY_THRESHOLD"]); //in 0-1 double callDuration = double.Parse(configDict["CALL_DURATION"]); // seconds AudioRecording recording = new AudioRecording(fiSegmentOfSourceFile.FullName); //i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = frameLength, WindowOverlap = windowOverlap, NoiseReductionType = SNR.KeyToNoiseReductionType("STANDARD"), }; //default values config TimeSpan tsRecordingtDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; double framesPerSecond = freqBinWidth; //the Xcorrelation-FFT technique requires number of bins to scan to be power of 2. //assuming sr=17640 and window=1024, then 64 bins span 1100 Hz above the min Hz level. i.e. 500 to 1600 //assuming sr=17640 and window=1024, then 128 bins span 2200 Hz above the min Hz level. i.e. 500 to 2700 int numberOfBins = 64; int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; int maxbin = minBin + numberOfBins - 1; int maxHz = (int)Math.Round(minHz + (numberOfBins * freqBinWidth)); BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, rowCount - 1, maxbin); int callSpan = (int)Math.Round(callDuration * framesPerSecond); //############################################################################################################################################# //ii: DETECT HARMONICS var results = CrossCorrelation.DetectHarmonicsInSonogramMatrix(subMatrix, decibelThreshold, callSpan); double[] dBArray = results.Item1; double[] intensity = results.Item2; //an array of periodicity scores double[] periodicity = results.Item3; //intensity = DataTools.filterMovingAverage(intensity, 3); int noiseBound = (int)(100 / freqBinWidth); //ignore 0-100 hz - too much noise double[] scoreArray = new double[intensity.Length]; for (int r = 0; r < rowCount; r++) { if (intensity[r] < harmonicIntensityThreshold) { continue; } //ignore locations with incorrect formant gap double herzPeriod = periodicity[r] * freqBinWidth; if (herzPeriod < minFormantgap || herzPeriod > maxFormantgap) { continue; } //find freq having max power and use info to adjust score. //expect humans to have max < 1000 Hz double[] spectrum = MatrixTools.GetRow(sonogram.Data, r); for (int j = 0; j < noiseBound; j++) { spectrum[j] = 0.0; } int maxIndex = DataTools.GetMaxIndex(spectrum); int freqWithMaxPower = (int)Math.Round(maxIndex * freqBinWidth); double discount = 1.0; if (freqWithMaxPower < 1200) { discount = 0.0; } if (intensity[r] > harmonicIntensityThreshold) { scoreArray[r] = intensity[r] * discount; } } //transfer info to a hits matrix. var hits = new double[rowCount, colCount]; double threshold = harmonicIntensityThreshold * 0.75; //reduced threshold for display of hits for (int r = 0; r < rowCount; r++) { if (scoreArray[r] < threshold) { continue; } double herzPeriod = periodicity[r] * freqBinWidth; for (int c = minBin; c < maxbin; c++) { //hits[r, c] = herzPeriod / (double)380; //divide by 380 to get a relativePeriod; hits[r, c] = (herzPeriod - minFormantgap) / maxFormantgap; //to get a relativePeriod; } } //iii: CONVERT TO ACOUSTIC EVENTS double maxPossibleScore = 0.5; int halfCallSpan = callSpan / 2; var predictedEvents = new List <AcousticEvent>(); for (int i = 0; i < rowCount; i++) { //assume one score position per crow call if (scoreArray[i] < 0.001) { continue; } double startTime = (i - halfCallSpan) / framesPerSecond; AcousticEvent ev = new AcousticEvent(segmentStartOffset, startTime, callDuration, minHz, maxHz); ev.SetTimeAndFreqScales(framesPerSecond, freqBinWidth); ev.Score = scoreArray[i]; ev.ScoreNormalised = ev.Score / maxPossibleScore; // normalised to the user supplied threshold //ev.Score_MaxPossible = maxPossibleScore; predictedEvents.Add(ev); } //for loop Plot plot = new Plot("CROW", intensity, harmonicIntensityThreshold); return(Tuple.Create(sonogram, hits, plot, predictedEvents, tsRecordingtDuration)); } //Analysis()
private void button3_Click(object sender, EventArgs e) { EraseGraph(); CreateGraph(); panel3.Controls.Clear(); /*Label label13 = new Label(); * Label label14 = new Label(); * label13.Font = label14.Font = new System.Drawing.Font("宋体", 15.75F, System.Drawing.FontStyle.Bold); * label13.ForeColor = label14.ForeColor = Color.Red; * label13.Location = new System.Drawing.Point(100, 30); * label14.Location = new System.Drawing.Point(900, 30); * label13.AutoSize = label14.AutoSize = true; * panel3.Controls.Add(label13); * panel3.Controls.Add(label14);*/ ZedGraphControl zed3 = new ZedGraphControl(); zed3.Width = panel3.Width; zed3.Height = panel3.Height; //读取数据 int datalength = WaterLeak.get_DataLength(); double[] dataA = WaterLeak.get_DataA(); double[] dataB = WaterLeak.get_DataB(); double dataA_avg = WaterLeak.get_DataA_Avg(); double dataB_avg = WaterLeak.get_DataB_Avg(); if (比较ToolStripMenuItem.Checked == true) { byte[] A = data_A; byte[] B = data_B; int[] m = new int[2 * datalength]; int j; int k = 0; for (int i = 0; i < 2 * datalength; i++) { if (A[i] != B[i]) { j = i; m[k] = j; k++; } } int index = 0; for (int i = 0; i < 2 * datalength; i++) { if (m[i] == 0) { index = i; } } int[] n = new int[index]; for (int i = 0; i < index; i++) { n[i] = m[i]; } MessageBox.Show("" + index); } //处理数据,采用均方差算法 if (算术平均ToolStripMenuItem.Checked == true) { progressBar1.Value = 0; int j = 0; double MINC = 0; int bias = 5000; int w = 5000; double[] C = new double[2 * bias]; for (int i = -bias; i < bias; i++) { C[i + bias] = 0; for (int k = w; k < datalength - w; k++) { C[i + bias] += Math.Abs(dataA[k + i] - dataB[k]); //曼哈顿距离 } if (i == -bias) { MINC = C[0]; } else if (C[bias + i] < MINC) { j = i; MINC = C[bias + i]; } Application.DoEvents(); progressBar1.Value = (i + bias) * 50 / bias; } progressBar1.Value += 1; //进度条 double w_avg; w_avg = 0; for (int i = 0; i < 2 * bias; i++) { w_avg += C[i] / (2 * bias); } for (int i = 0; i < 2 * bias; i++) { C[i] = (C[i] - w_avg) / w_avg; } panel3.Controls.Add(zed3); GraphPane Pane = zed3.GraphPane; /*Pane.Title = "偏差结果"; * Pane.XAxis.Title = ""; * Pane.YAxis.Title = "";*/ PointPairList list1 = new PointPairList(); for (int i = 0; i < 2 * bias; i++) { list1.Add(i, C[i]); } LineItem myCurve1 = Pane.AddCurve("Porsche", list1, Color.Blue, SymbolType.None); zed3.AxisChange(); //计算漏水点位置 double positon = (WaterLeak.get_PipelineLength() + WaterLeak.get_WaveNumber() * j / (1000 * WaterLeak.get_CaptureRate())) / 2; /*label14.Text = "漏水点距离A探头" + positon.ToString() + "米!"; * * //显示标注数据 * if (j > 0) * label13.Text = "A探头滞后B探头" + j.ToString("G") + "个基点!"; * if (j < 0) * label13.Text = "A探头超前B探头" + (-j).ToString() + "个基点!"; * if (j == 0) * label13.Text = "A探头与B探头无偏移!";*/ //显示基点 textBoxOffSet.Text = j.ToString(); //保存参数 WaterLeak.set_dataC_factor(500); WaterLeak.set_dataC_shift(100); WaterLeak.set_Bias(bias); WaterLeak.set_Min_Position(j); WaterLeak.set_Min_Value(MINC); WaterLeak.set_DataC(C); } //if (LMSToolStripMenuItem.Checked == true) //{ // progressBar1.Value = 0; // MatlabClass mat = new MatlabClass(); // MWNumericArray dataA_m = new MWNumericArray(1, datalength, dataA); // MWNumericArray dataB_m = new MWNumericArray(1, datalength, dataB); // MWArray M_m = 100; // MWArray mu_m = 0.00001; // MWArray W_m = mat.my_LMS(dataA_m, dataB_m, M_m, mu_m); // Array W = ((MWNumericArray)W_m).ToArray(); // int M = ((MWNumericArray)M_m).ToScalarInteger(); // int W_length = W.Length; // int d = 0; // double temp = Convert.ToDouble(W.GetValue(0, 0)); // for (int i = 1; i < W_length; i++) // { // if (temp < Convert.ToDouble(W.GetValue(i, 0))) // { // temp = Convert.ToDouble(W.GetValue(i, 0)); // d = i - 2 * M; // } // } // panel3.Controls.Add(zed3); // GraphPane Pane = zed3.GraphPane; // Pane.Title = "偏差结果"; // Pane.XAxis.Title = ""; // Pane.YAxis.Title = ""; // PointPairList list = new PointPairList(); // for (int i = 0; i < W_length; i++) // { // list.Add(i - 2 * M, Convert.ToDouble(W.GetValue(i, 0))); // } // LineItem myCurve = Pane.AddCurve("Porsche", list, Color.Blue, SymbolType.None); // zed3.AxisChange(); // //计算漏水点位置 // double positon = (WaterLeak.get_PipelineLength() + WaterLeak.get_WaveNumber() * d / (1000 * WaterLeak.get_CaptureRate())) / 2; // label14.Text = "漏水点距离A探头" + positon.ToString() + "米!"; // //显示标注数据 // if (d > 0) // label13.Text = "A探头滞后B探头" + d.ToString("G") + "个基点!"; // if (d < 0) // label13.Text = "A探头超前B探头" + (-d).ToString() + "个基点!"; // if (d == 0) // label13.Text = "A探头与B探头无偏移!"; //} if (广义互相关ToolStripMenuItem.Checked == true) { DateTime startDT = System.DateTime.Now; progressBar1.Value = 0; int len = 262144; double[] A = new double[len]; double[] B = new double[len]; for (int i = 0; i < len; i++) { A[i] = dataA[i]; B[i] = dataB[i]; } CrossCorrelation ccorr = new CrossCorrelation(); double[] R = ccorr.Rxy(A, B); int R_length = R.Length; Position p = new Position(); int location = p.max(R); int d = location - R_length / 2; panel3.Controls.Add(zed3); GraphPane Pane = zed3.GraphPane; /*Pane.Title = "互相关系数"; * Pane.XAxis.Title = ""; * Pane.YAxis.Title = "";*/ PointPairList list = new PointPairList(); for (int i = 0; i < R_length; i++) { list.Add(i, R[i]); } LineItem myCurve = Pane.AddCurve("Porsche", list, Color.Blue, SymbolType.None); zed3.AxisChange(); //计算漏水点位置 double positon = (WaterLeak.get_PipelineLength() + WaterLeak.get_WaveNumber() * d / (1000 * WaterLeak.get_CaptureRate())) / 2; /*label14.Text = "漏水点距离A探头" + positon.ToString() + "米!"; * //显示标注数据 * if (d > 0) * label13.Text = "A探头滞后B探头" + d.ToString("G") + "个基点!"; * if (d < 0) * label13.Text = "A探头超前B探头" + (-d).ToString() + "个基点!"; * if (d == 0) * label13.Text = "A探头与B探头无偏移!";*/ //显示基点 textBoxOffSet.Text = d.ToString(); DateTime endDT = System.DateTime.Now; TimeSpan time = endDT.Subtract(startDT); //MessageBox.Show(time.TotalMilliseconds.ToString() + "ms"); } //if (互功率谱ToolStripMenuItem.Checked == true) //{ // progressBar1.Value = 0; // MatlabClass mat = new MatlabClass(); // MWNumericArray dataA_m = new MWNumericArray(1, datalength, dataA); // MWNumericArray dataB_m = new MWNumericArray(1, datalength, dataB); // MWArray datalength_m = datalength; // MWArray R_m = mat.my_xcorrPow(dataA_m, dataB_m); // Array R = ((MWNumericArray)R_m).ToArray(); // int R_length = R.Length; // int d = 0; // double temp = Convert.ToDouble(R.GetValue(0, 0)); // for (int i = 1; i < R_length; i++) // { // if (temp < Convert.ToDouble(R.GetValue(0, i))) // { // temp = Convert.ToDouble(R.GetValue(0, i)); // d = i - datalength + 1; // } // } // panel3.Controls.Add(zed3); // GraphPane Pane = zed3.GraphPane; // Pane.Title = "偏差结果"; // Pane.XAxis.Title = ""; // Pane.YAxis.Title = ""; // PointPairList list = new PointPairList(); // for (int i = 0; i < R_length; i++) // { // list.Add(i - datalength + 1, Convert.ToDouble(R.GetValue(0, i))); // } // LineItem myCurve = Pane.AddCurve("Porsche", list, Color.Blue, SymbolType.None); // zed3.AxisChange(); // //计算漏水点位置 // double positon = (WaterLeak.get_PipelineLength() + WaterLeak.get_WaveNumber() * d / (1000 * WaterLeak.get_CaptureRate())) / 2; // label14.Text = "漏水点距离A探头" + positon.ToString() + "米!"; // //显示标注数据 // if (d > 0) // label13.Text = "A探头滞后B探头" + d.ToString("G") + "个基点!"; // if (d < 0) // label13.Text = "A探头超前B探头" + (-d).ToString() + "个基点!"; // if (d == 0) // label13.Text = "A探头与B探头无偏移!"; //} //int bias = 5000; //int w = 2500; //double[] C = new double[bias]; //double minc = 0; //int d = 0; //int n = 5;//计算次数 //int sp = datalength / n;//每次计算的间隔 //int[] T = new int[n];//时延点数 //double[] S = new double[n];//离A点距离 //DataTable dt = new DataTable(); //dt.Columns.Add("A探头超前点数", typeof(int)); //dt.Columns.Add("漏水点离A探头距离", typeof(double)); //for (int m = 0; m < n; m++) //{ // for (int i = 0 + m * sp; i < bias + m * sp; i++) // { // C[i - m * sp] = 0; // for (int j = w + m * sp; j < w + bias + m * sp; j++) // { // C[i - m * sp] += Math.Abs(dataA[i + j - w - m * sp] - dataB[j]); // } // if (i == 0) minc = C[0]; // else if (C[i - m * sp] < minc) // { // d = i - m * sp; // minc = C[i - m * sp]; // } // } // T[m] = w - d; // S[m] = 500 - (double)T[m] / 10; // DataRow dr = dt.NewRow(); // dr[0] = T[m].ToString(); // dr[1] = S[m].ToString(); // dt.Rows.Add(dr); // Application.DoEvents(); // progressBar1.Value = (m - 1) * 100 / n; //} //dataGridView1.DataSource = dt; //dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; /* * * //处理数据,采用LMS算法 * //初始化 * int M=1000; * float mu = (float)0.1; * float[] en = new float[datalength]; * for (int i = 0; i < datalength; i++) * en[i] = 0; * double[] w = new double[M]; * for (int i = 0; i < M; i++) * w[i] = 0; * float y; * //迭代运算 * for (int k = M - 1; k < datalength; k++) * { * y=0; * for (int i = 0; i < M; i++) * y += (float)(w[i]) * dataB[k - i]; * if (double.IsNaN(y)) y = 0; * en[k] =dataA[k] - y; * * for (int i = 0; i < M; i++) * { * w[i] = w[i] + 2 * mu * en[k] * dataB[k - i]; * if (double.IsNaN(w[i])) w[i] = 0; * // w[i] = Math.Round(w[i], 6); * } * * progressBar1.Value = (k-M) * 100 / (datalength - M-1); * * Application.DoEvents(); * * } * * * float w_avg; * w_avg = 0; * for (int i = 0; i < M; i++) * w_avg += (float)w[i]; * * w_avg /= M; * // w_avg = Math.Round(w_avg, 6); * * // w[0] = Math.Round(w[0], 10); * textBox1.Text = w[0].ToString("G"); */ }
public void Test(double[] recorded) { // 動作テスト int N = mOrder; int P = (1 << N) - 1; var from = new double[P + 1]; { int copySize = recorded.Length; if (from.Length < copySize) { copySize = from.Length; } Array.Copy(recorded, from, copySize); } byte[] mlsSeq; { var mls = new MaximumLengthSequence(N); mlsSeq = mls.Sequence(); for (int i = 0; i < mlsSeq.Length; ++i) { Console.Write("{0} ", mlsSeq[i]); } Console.WriteLine(""); } { var mlsD = new double[mlsSeq.Length]; for (int i = 0; i < mlsD.Length; ++i) { mlsD[i] = mlsSeq[i] * 2.0 - 1.0; } var ccc = CrossCorrelation.CalcCircularCrossCorrelation(mlsD, mlsD); for (int i = 0; i < ccc.Length; ++i) { Console.Write("{0:g2} ", ccc[i]); } Console.WriteLine(""); } var mlsMat = new MatrixGF2(P, P); { for (int y = 0; y < P; ++y) { for (int x = 0; x < P; ++x) { mlsMat.Set(y, x, (0 != mlsSeq[(x + y) % P]) ? GF2.One : GF2.Zero); } } } mlsMat.Print("MLS matrix"); // σ: mlsMatの左上N*N要素 var σ = mlsMat.Subset(0, 0, N, N); σ.Print("σ"); var σInv = σ.Inverse(); σInv.Print("σ^-1"); // S: MLS行列の上N行。 var S = mlsMat.Subset(0, 0, N, P); S.Print("S"); // Sの転置S^T S.Transpose().Print("S^T"); // L: Sの転置 x σInv var L = S.Transpose().Mul(σInv); L.Print("L"); // L x S == MLS行列 var LS = L.Mul(S); LS.Print("L x S"); int diff = LS.CompareTo(mlsMat); System.Diagnostics.Debug.Assert(diff == 0); // 2進で0~P-1までの値が入っている行列 var B = new MatrixGF2(P + 1, N); var Bt = new MatrixGF2(N, P + 1); for (int r = 0; r < P + 1; ++r) { for (int c = 0; c < N; ++c) { int v = r & (1 << (N - 1 - c)); var b = (v == 0) ? GF2.Zero : GF2.One; B.Set(r, c, b); Bt.Set(c, r, b); } } B.Print("B"); Bt.Print("Bt"); // アダマール行列H8 var H8 = MatrixGF2.Mul(B, Bt); H8.Print("H8"); var vTest = new double[P + 1]; for (int i = 0; i < P + 1; ++i) { vTest[i] = i; } var r1 = H8.ToMatrix().Mul(vTest); Print(r1, "R1"); var r2 = FastWalshHadamardTransform.Transform(vTest); Print(r2, "R2"); // Ps: S行列の列の値を2進数として、順番入れ替え行列を作る var Ps = new MatrixGF2(P + 1, P + 1); var PsReorder = new List <int>(); PsReorder.Add(0); for (int c = 0; c < P; ++c) { int sum = 0; for (int r = 0; r < N; ++r) { sum += (1 << (N - 1 - r)) * S.At(r, c).Val; } Console.WriteLine("Ps: c={0} sum={1}", c, sum); Ps.Set(sum, c + 1, GF2.One); PsReorder.Add(sum); } Ps.Print("Ps"); { var testMat = new WWMatrix(P + 1, 1); for (int r = 0; r < P + 1; ++r) { testMat.Set(r, 0, PsReorder[r]); } var PsTest = Ps.ToMatrix().Mul(testMat); PsTest.Print("Ps x n"); } // Pl: L行列の列の値を2進数として、順番入れ替え行列を作る var Pl = new MatrixGF2(P + 1, P + 1); var PlReorder = new List <int>(); PlReorder.Add(0); for (int r = 0; r < P; ++r) { int sum = 0; for (int c = 0; c < N; ++c) { sum += (1 << (N - 1 - c)) * L.At(r, c).Val; } Console.WriteLine("Pl: r={0} sum={1}", r, sum); Pl.Set(r + 1, sum, GF2.One); PlReorder.Add(sum); } Pl.Print("Pl"); S.Print("S"); var BtPs = Bt.Mul(Ps); BtPs.Print("BtPs"); L.Print("L"); var PlB = Pl.Mul(B); PlB.Print("PlB"); { var test2Mat = new WWMatrix(P + 1, 1); for (int r = 0; r < P + 1; ++r) { test2Mat.Set(r, 0, r); } var PlTest = Pl.ToMatrix().Mul(test2Mat); PlTest.Print("Pl x n"); } mlsMat.Print("MLS mat"); var Mhat = Pl.Mul(H8).Mul(Ps); Mhat.Print("Mhat"); // MLS deconvolution var decon = Mhat.ToMatrix().Mul(from); Print(decon, "decon"); // 同じ処理をWalsh-Hadamard変換を使って行う。 var reorderedFrom = new double[P + 1]; for (int i = 0; i < P + 1; ++i) { reorderedFrom[PsReorder[i]] = from[i]; } #if true var hR = FastWalshHadamardTransform.Transform(reorderedFrom); #else var hR = H8.ToMatrix().Mul(reorderedFrom); #endif var hTo = new double[P + 1]; for (int i = 0; i < P + 1; ++i) { hTo[i] = hR[PlReorder[i]]; } Print(hTo, "hTo"); }
private void Window_Loaded(object sender, RoutedEventArgs e) { Title = Title + (Environment.Is64BitProcess ? " (x64)" : " (x86)"); multiTrackViewer1.ItemsSource = trackList; // INIT COMMAND BINDINGS CommandBinding playBinding = new CommandBinding(MediaCommands.Play); CommandBindings.Add(playBinding); playBinding.CanExecute += new CanExecuteRoutedEventHandler(playCommandBinding_CanExecute); playBinding.Executed += new ExecutedRoutedEventHandler(playCommandBinding_Executed); CommandBinding pauseBinding = new CommandBinding(MediaCommands.Pause); CommandBindings.Add(pauseBinding); pauseBinding.CanExecute += new CanExecuteRoutedEventHandler(pauseCommandBinding_CanExecute); pauseBinding.Executed += new ExecutedRoutedEventHandler(pauseCommandBinding_Executed); CommandBinding playToggleBinding = new CommandBinding(Commands.PlayToggle); CommandBindings.Add(playToggleBinding); playToggleBinding.Executed += new ExecutedRoutedEventHandler(playToggleBinding_Executed); //// INIT TRACKLIST STUFF //trackList.PropertyChanged += delegate(object sender2, PropertyChangedEventArgs e2) { // if (e2.PropertyName == "TotalLength") { // multiTrackViewer1.VirtualViewportMaxWidth = trackList.TotalLength.Ticks; // } //}; // INIT PLAYER player = new MultitrackPlayer(trackList); player.VolumeAnnounced += Player_VolumeAnnounced_VolumeMeter; player.CurrentTimeChanged += new EventHandler <ValueEventArgs <TimeSpan> >( delegate(object sender2, ValueEventArgs <TimeSpan> e2) { multiTrackViewer1.Dispatcher.BeginInvoke((Action) delegate { multiTrackViewer1.VirtualCaretOffset = e2.Value.Ticks; // autoscroll if (multiTrackViewer1.VirtualViewportInterval.To <= multiTrackViewer1.VirtualCaretOffset) { multiTrackViewer1.VirtualViewportOffset = multiTrackViewer1.VirtualCaretOffset; } }); }); player.PlaybackStateChanged += new EventHandler( delegate(object sender2, EventArgs e2) { multiTrackViewer1.Dispatcher.BeginInvoke((Action) delegate { // CommandManager must be called on the GUI-thread, else it won't do anything CommandManager.InvalidateRequerySuggested(); }); }); volumeSlider.ValueChanged += new RoutedPropertyChangedEventHandler <double>( delegate(object sender2, RoutedPropertyChangedEventArgs <double> e2) { player.Volume = (float)e2.NewValue; }); // INIT PROGRESSBAR progressBar1.IsEnabled = false; ProgressMonitor.GlobalInstance.ProcessingStarted += new EventHandler( delegate(object sender2, EventArgs e2) { progressBar1.Dispatcher.BeginInvoke((Action) delegate { progressBar1.IsEnabled = true; progressBar1Label.Text = ProgressMonitor.GlobalInstance.StatusMessage; win7TaskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal; win7TaskBar.ProgressValue = 0; }); }); ProgressMonitor.GlobalInstance.ProcessingProgressChanged += new EventHandler <ValueEventArgs <float> >( delegate(object sender2, ValueEventArgs <float> e2) { progressBar1.Dispatcher.BeginInvoke((Action) delegate { progressBar1.Value = e2.Value; win7TaskBar.ProgressValue = e2.Value / 100; progressBar1Label.Text = ProgressMonitor.GlobalInstance.StatusMessage; }); }); ProgressMonitor.GlobalInstance.ProcessingFinished += new EventHandler( delegate(object sender2, EventArgs e2) { progressBar1.Dispatcher.BeginInvoke((Action) delegate { progressBar1.Value = 0; progressBar1.IsEnabled = false; progressBar1Label.Text = ""; win7TaskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; }); }); // INIT RANDOM STUFF multiTrackViewer1.KeyUp += new KeyEventHandler(delegate(object sender2, KeyEventArgs e2) { if (e2.Key == Key.Delete) { // create temporary list to avoid concurrent modification exception var selectedAudioTracks = new List <AudioTrack>(multiTrackViewer1.SelectedItems.Cast <AudioTrack>()); // delete tracks foreach (AudioTrack audioTrack in selectedAudioTracks) { if (audioTrack != null) { // 1. delete all related matches List <Match> deleteList = new List <Match>(); // 1a find all related matches foreach (Match m in multiTrackViewer1.Matches) { if (m.Track1 == audioTrack || m.Track2 == audioTrack) { deleteList.Add(m); } } // 1b delete foreach (Match m in deleteList) { multiTrackViewer1.Matches.Remove(m); } // 2. delete track trackList.Remove(audioTrack); } } e2.Handled = true; } }); // INIT FFT int fftSize = 1024; double correlation = 0; fftAnalyzer = new FFTAnalyzer(fftSize); fftAnalyzerConsumer = 2; correlationConsumer = 1; WindowFunction fftWindow = WindowUtil.GetFunction(WindowType.BlackmanHarris, fftSize); fftAnalyzer.WindowFunction = fftWindow; fftAnalyzer.WindowAnalyzed += FftAnalyzer_WindowAnalyzed_FrequencyGraph; fftAnalyzer.WindowAnalyzed += FftAnalyzer_WindowAnalyzed_Spectrogram; player.SamplesMonitored += new EventHandler <StreamDataMonitorEventArgs>(delegate(object sender2, StreamDataMonitorEventArgs e2) { if (fftAnalyzerConsumer > 0 || correlationConsumer > 0) { float[][] uninterleaved = AudioUtil.Uninterleave(e2.Properties, e2.Buffer, e2.Offset, e2.Length, true); if (fftAnalyzerConsumer > 0) { fftAnalyzer.PutSamples(uninterleaved[0]); // put the summed up mono samples into the analyzer } if (correlationConsumer > 0) { correlation = CrossCorrelation.Correlate(uninterleaved[1], uninterleaved[2]); Dispatcher.BeginInvoke((Action) delegate { correlationMeter.Value = correlation; }); } } }); spectrogram.SpectrogramSize = fftSize / 2; }
} //Analysis() public static Tuple <BaseSonogram, double[, ], double[], List <AcousticEvent> > DetectHarmonics( AudioRecording recording, double intensityThreshold, int minHz, int minFormantgap, int maxFormantgap, double minDuration, int windowSize, double windowOverlap, TimeSpan segmentStartOffset) { //i: MAKE SONOGRAM int numberOfBins = 32; double binWidth = recording.SampleRate / (double)windowSize; int sr = recording.SampleRate; double frameDuration = windowSize / (double)sr; // Duration of full frame or window in seconds double frameOffset = frameDuration * (1 - windowOverlap); //seconds between starts of consecutive frames double framesPerSecond = 1 / frameOffset; //double framesPerSecond = sr / (double)windowSize; //int frameOffset = (int)(windowSize * (1 - overlap)); //int frameCount = (length - windowSize + frameOffset) / frameOffset; double epsilon = Math.Pow(0.5, recording.BitsPerSample - 1); var results2 = DSP_Frames.ExtractEnvelopeAndAmplSpectrogram( recording.WavReader.Samples, sr, epsilon, windowSize, windowOverlap); double[] avAbsolute = results2.Average; //average absolute value over the minute recording //double[] envelope = results2.Item2; double[,] matrix = results2 .AmplitudeSpectrogram; //amplitude spectrogram. Note that column zero is the DC or average energy value and can be ignored. double windowPower = results2.WindowPower; //window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins // 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz // 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz // 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz //the Xcorrelation-FFT technique requires number of bins to scan to be power of 2. //assuming sr=17640 and window=1024, then 64 bins span 1100 Hz above the min Hz level. i.e. 500 to 1600 //assuming sr=17640 and window=1024, then 128 bins span 2200 Hz above the min Hz level. i.e. 500 to 2700 int minBin = (int)Math.Round(minHz / binWidth); int maxHz = (int)Math.Round(minHz + (numberOfBins * binWidth)); int rowCount = matrix.GetLength(0); int colCount = matrix.GetLength(1); int maxbin = minBin + numberOfBins; double[,] subMatrix = MatrixTools.Submatrix(matrix, 0, minBin + 1, rowCount - 1, maxbin); //ii: DETECT HARMONICS int zeroBinCount = 5; //to remove low freq content which dominates the spectrum var results = CrossCorrelation.DetectBarsInTheRowsOfaMatrix(subMatrix, intensityThreshold, zeroBinCount); double[] intensity = results.Item1; //an array of periodicity scores double[] periodicity = results.Item2; //transfer periodicity info to a hits matrix. //intensity = DataTools.filterMovingAverage(intensity, 3); double[] scoreArray = new double[intensity.Length]; var hits = new double[rowCount, colCount]; for (int r = 0; r < rowCount; r++) { double relativePeriod = periodicity[r] / numberOfBins / 2; if (intensity[r] > intensityThreshold) { for (int c = minBin; c < maxbin; c++) { hits[r, c] = relativePeriod; } } double herzPeriod = periodicity[r] * binWidth; if (herzPeriod > minFormantgap && herzPeriod < maxFormantgap) { scoreArray[r] = 2 * intensity[r] * intensity[r]; //enhance high score wrt low score. } } scoreArray = DataTools.filterMovingAverage(scoreArray, 11); //iii: CONVERT TO ACOUSTIC EVENTS double maxDuration = 100000.0; //abitrary long number - do not want to restrict duration of machine noise List <AcousticEvent> predictedEvents = AcousticEvent.ConvertScoreArray2Events( scoreArray, minHz, maxHz, framesPerSecond, binWidth, intensityThreshold, minDuration, maxDuration, segmentStartOffset); hits = null; //set up the songogram to return. Use the existing amplitude sonogram int bitsPerSample = recording.WavReader.BitsPerSample; TimeSpan duration = recording.Duration; NoiseReductionType nrt = SNR.KeyToNoiseReductionType("STANDARD"); var sonogram = (BaseSonogram)SpectrogramStandard.GetSpectralSonogram( recording.BaseName, windowSize, windowOverlap, bitsPerSample, windowPower, sr, duration, nrt, matrix); sonogram.DecibelsNormalised = new double[rowCount]; //foreach frame or time step for (int i = 0; i < rowCount; i++) { sonogram.DecibelsNormalised[i] = 2 * Math.Log10(avAbsolute[i]); } sonogram.DecibelsNormalised = DataTools.normalise(sonogram.DecibelsNormalised); return(Tuple.Create(sonogram, hits, scoreArray, predictedEvents)); } //end Execute_HDDetect
} //Analysis() public static System.Tuple <List <double[]>, double[, ], List <AcousticEvent> > DetectKiwi(BaseSonogram sonogram, int minHz, int maxHz, /* double dctDuration, double dctThreshold, */ double minPeriod, double maxPeriod, double eventThreshold, double minDuration, double maxDuration) { int step = (int)Math.Round(sonogram.FramesPerSecond); //take one second steps int sampleLength = 32; //32 frames = 1.85 seconds. 64 frames (i.e. 3.7 seconds) is to long a sample - require stationarity. int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); double minFramePeriod = minPeriod * sonogram.FramesPerSecond; double maxFramePeriod = maxPeriod * sonogram.FramesPerSecond; int minBin = (int)(minHz / sonogram.FBinWidth); int maxBin = (int)(maxHz / sonogram.FBinWidth); //############################################################################################################################################# //window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins // 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz // 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz // 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz double[] fullArray = MatrixTools.GetRowAveragesOfSubmatrix(sonogram.Data, 0, minBin, (rowCount - 1), minBin + 130); var result1 = CrossCorrelation.DetectXcorrelationInTwoArrays(fullArray, fullArray, step, sampleLength, minFramePeriod, maxFramePeriod); double[] intensity1 = result1.Item1; double[] periodicity1 = result1.Item2; intensity1 = DataTools.filterMovingAverage(intensity1, 11); //############################################################################################################################################# //double[] lowerArray = MatrixTools.GetRowAveragesOfSubmatrix(sonogram.Data, 0, minBin, (rowCount - 1), minBin + 65); //double[] upperArray = MatrixTools.GetRowAveragesOfSubmatrix(sonogram.Data, 0, minBin+66, (rowCount - 1), minBin+130); //int actualMaxHz = (int)Math.Round((minBin+130) * sonogram.FBinWidth); //var result2 = CrossCorrelation.DetectXcorrelationInTwoArrays(lowerArray, upperArray, step, sampleLength, minFramePeriod, maxFramePeriod); //double[] intensity2 = result2.Item1; //double[] periodicity2 = result2.Item2; //intensity2 = DataTools.filterMovingAverage(intensity2, 5); //############################################################################################################################################# //minFramePeriod = 4; //maxFramePeriod = 14; //var return3 = Gratings.ScanArrayForGratingPattern(fullArray, (int)minFramePeriod, (int)maxFramePeriod, 4, step); //var return3 = Gratings.ScanArrayForGratingPattern(fullArray, step, 4, 4); //var return4 = Gratings.ScanArrayForGratingPattern(fullArray, step, 4, 5); //var return5 = Gratings.ScanArrayForGratingPattern(fullArray, step, 4, 8); //var return6 = Gratings.ScanArrayForGratingPattern(fullArray, step, 4, 10); //var return7 = Gratings.ScanArrayForGratingPattern(fullArray, step, 4, 12); //############################################################################################################################################# //bool normaliseDCT = true; //Double[,] maleHits; //predefinition of hits matrix - to superimpose on sonogram image //double[] maleScores; //predefinition of score array //double[] maleOscRate; //List<AcousticEvent> predictedMaleEvents; //double minOscilFreq = 1 / maxPeriod; //convert max period (seconds) to oscilation rate (Herz). //double maxOscilFreq = 1 / minPeriod; //convert min period (seconds) to oscilation rate (Herz). //OscillationAnalysis.Execute((SpectralSonogram)sonogram, minHz, maxHz, dctDuration, dctThreshold, normaliseDCT, // minOscilFreq, maxOscilFreq, eventThreshold, minDuration, maxDuration, // out maleScores, out predictedMaleEvents, out maleHits, out maleOscRate); //iii: CONVERT SCORES TO ACOUSTIC EVENTS List <AcousticEvent> events = AcousticEvent.ConvertScoreArray2Events(intensity1, minHz, maxHz, sonogram.FramesPerSecond, sonogram.FBinWidth, eventThreshold, minDuration, maxDuration); CropEvents(events, fullArray, minDuration); CalculateAvIntensityScore(events, intensity1); CalculateDeltaPeriodScore(events, periodicity1, minFramePeriod, maxFramePeriod); CalculateBandWidthScore(events, sonogram.Data); CalculatePeaksScore(events, fullArray); //FilterEvents(events); CalculateWeightedEventScore(events); // PREPARE HITS MATRIX var hits = new double[rowCount, colCount]; double range = maxFramePeriod - minFramePeriod; for (int r = 0; r < rowCount; r++) { if (intensity1[r] > eventThreshold) { for (int c = minBin; c < maxBin; c++) { hits[r, c] = (periodicity1[r] - minFramePeriod) / range; //normalisation } } } periodicity1 = CropArrayToEvents(events, periodicity1); //for display only var scores = new List <double[]>(); scores.Add(DataTools.normalise(fullArray)); //scores.Add(DataTools.normalise(upperArray)); //scores.Add(DataTools.normalise(lowerArray)); scores.Add(DataTools.normalise(intensity1)); scores.Add(DataTools.normalise(periodicity1)); //scores.Add(DataTools.normalise(intensity2)); //scores.Add(DataTools.normalise(return3)); //scores.Add(DataTools.normalise(return4)); //scores.Add(DataTools.normalise(return5)); //scores.Add(DataTools.normalise(return6)); //scores.Add(DataTools.normalise(return7)); //scores.Add(DataTools.normalise(maleScores)); //scores.Add(DataTools.normalise(maleOscRate)); return(System.Tuple.Create(scores, hits, events)); }
//#IntensityThreshold: 0.15 //# Event threshold - Determines FP / FN trade-off for events. //EventThreshold: 0.2 public static (List <AcousticEvent>, double[]) GetComponentsWithHarmonics( SpectrogramStandard sonogram, int minHz, int maxHz, int nyquist, double decibelThreshold, double dctThreshold, double minDuration, double maxDuration, int minFormantGap, int maxFormantGap, TimeSpan segmentStartOffset) { // Event threshold - Determines FP / FN trade-off for events. //double eventThreshold = 0.2; var sonogramData = sonogram.Data; int frameCount = sonogramData.GetLength(0); int binCount = sonogramData.GetLength(1); double freqBinWidth = nyquist / (double)binCount; int minBin = (int)Math.Round(minHz / freqBinWidth); int maxBin = (int)Math.Round(maxHz / freqBinWidth); int bandWidthBins = maxBin - minBin + 1; // extract the sub-band double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, frameCount - 1, maxBin); //ii: DETECT HARMONICS // now look for harmonics in search band using the Xcorrelation-DCT method. var results = CrossCorrelation.DetectHarmonicsInSonogramMatrix(subMatrix, decibelThreshold); // set up score arrays double[] dBArray = results.Item1; double[] harmonicIntensityScores = results.Item2; //an array of formant intesnity int[] maxIndexArray = results.Item3; for (int r = 0; r < frameCount; r++) { if (harmonicIntensityScores[r] < dctThreshold) { continue; } //ignore locations with incorrect formant gap int maxId = maxIndexArray[r]; double freqBinGap = 2 * bandWidthBins / (double)maxId; double formantGap = freqBinGap * freqBinWidth; if (formantGap < minFormantGap || formantGap > maxFormantGap) { harmonicIntensityScores[r] = 0.0; } } // smooth the harmonicIntensityScores array to allow for brief gaps. harmonicIntensityScores = DataTools.filterMovingAverageOdd(harmonicIntensityScores, 5); //extract the events based on length and threshhold. // Note: This method does NOT do prior smoothing of the score array. var acousticEvents = AcousticEvent.ConvertScoreArray2Events( harmonicIntensityScores, minHz, maxHz, sonogram.FramesPerSecond, sonogram.FBinWidth, decibelThreshold, minDuration, maxDuration, segmentStartOffset); return(acousticEvents, harmonicIntensityScores); }
private void correlationButton_Click(object sender, RoutedEventArgs e) { List <MatchGroup> trackGroups = DetermineMatchGroups(); foreach (MatchGroup trackGroup in trackGroups) { foreach (MatchPair trackPair in trackGroup.MatchPairs) { MatchPair localMP = trackPair; Task.Factory.StartNew(() => { TimeSpan t1Offset; TimeSpan t2Offset; if (localMP.Track1.Offset < localMP.Track2.Offset) { t1Offset = localMP.Track2.Offset - localMP.Track1.Offset; t2Offset = TimeSpan.Zero; } else { t1Offset = TimeSpan.Zero; t2Offset = localMP.Track1.Offset - localMP.Track2.Offset; } TimeSpan length; if (localMP.Track1.Length > localMP.Track2.Length) { length = localMP.Track2.Length; } else { length = localMP.Track1.Length; } TimeSpan interval = CorrelationIntervalSize; TimeSpan window = CorrelationWindowSize; List <Match> computedMatches = new List <Match>(); for (TimeSpan position = TimeSpan.Zero; position < length; position += interval) { Interval t1Interval = new Interval((t1Offset + position).Ticks, (t1Offset + position + window).Ticks); Interval t2Interval = new Interval((t2Offset + position).Ticks, (t2Offset + position + window).Ticks); if (t1Interval.TimeTo >= localMP.Track1.Length || t2Interval.TimeTo >= localMP.Track2.Length) { // not enough samples remaining to compute the correlation (end of track reached) break; } CrossCorrelation.Result ccr; IAudioStream s1 = localMP.Track1.CreateAudioStream(); IAudioStream s2 = localMP.Track2.CreateAudioStream(); TimeSpan offset = CrossCorrelation.Calculate(s1, t1Interval, s2, t2Interval, progressMonitor, out ccr); s1.Close(); s2.Close(); // always apply a positive offset that moves the match position inside the corelation interval, // else it can happen that a negative offset is applied to a match at the beginning of the stream // which means that the matching point would be at a negative position in the audio stream computedMatches.Add(new Match { Track1 = localMP.Track1, Track1Time = t1Offset + position + (offset < TimeSpan.Zero ? -offset : TimeSpan.Zero), Track2 = localMP.Track2, Track2Time = t2Offset + position + (offset >= TimeSpan.Zero ? offset : TimeSpan.Zero), Similarity = ccr.AbsoluteMaxValue, Source = "CC" }); } Dispatcher.BeginInvoke((Action) delegate { foreach (Match match in computedMatches) { multiTrackViewer.Matches.Add(match); } }); }); } } }
private void TimeWarp(TimeWarpType type, AudioTrack t1, TimeSpan t1From, TimeSpan t1To, AudioTrack t2, TimeSpan t2From, TimeSpan t2To, bool calculateSimilarity, bool normalizeSimilarity, bool cueIn, bool cueOut) { IAudioStream s1 = t1.CreateAudioStream(); IAudioStream s2 = t2.CreateAudioStream(); s1 = new CropStream(s1, TimeUtil.TimeSpanToBytes(t1From, s1.Properties), TimeUtil.TimeSpanToBytes(t1To, s1.Properties)); s2 = new CropStream(s2, TimeUtil.TimeSpanToBytes(t2From, s2.Properties), TimeUtil.TimeSpanToBytes(t2To, s2.Properties)); List <Tuple <TimeSpan, TimeSpan> > path = null; DTW dtw = null; // execute time warping if (type == TimeWarpType.DTW) { dtw = new DTW(TimeWarpSearchWidth, progressMonitor); } else if (type == TimeWarpType.OLTW) { dtw = new OLTW2(TimeWarpSearchWidth, progressMonitor); } if (TimeWarpDisplay) { this.Dispatcher.BeginInvoke((Action) delegate { dtwPathViewer = new DtwPathViewer(); dtwPathViewer.Show(); }); dtw.OltwInit += new DTW.OltwInitDelegate(delegate(int windowSize, IMatrix <double> cellCostMatrix, IMatrix <double> totalCostMatrix) { dtwPathViewer.Dispatcher.BeginInvoke((Action) delegate { dtwPathViewer.DtwPath.Init(windowSize, cellCostMatrix, totalCostMatrix); }); }); bool drawing = false; dtw.OltwProgress += new DTW.OltwProgressDelegate(delegate(int i, int j, int minI, int minJ, bool force) { if (!drawing || force) { dtwPathViewer.Dispatcher.BeginInvoke((Action) delegate { drawing = true; dtwPathViewer.DtwPath.Refresh(i, j, minI, minJ); drawing = false; }); } }); } path = dtw.Execute(s1, s2); if (path == null) { return; } // convert resulting path to matches and filter them int filterSize = TimeWarpFilterSize; // take every n-th match and drop the rest bool smoothing = TimeWarpSmoothing; int smoothingWidth = Math.Max(1, Math.Min(filterSize / 10, filterSize)); bool inOutCue = TimeWarpInOutCue; TimeSpan inOutCueSpan = TimeWarpSearchWidth; List <Match> matches = new List <Match>(); float maxSimilarity = 0; // needed for normalization IProgressReporter progressReporter = progressMonitor.BeginTask("post-process resulting path...", true); double totalProgress = path.Count; double progress = 0; /* Leave out matches in the in/out cue areas... * The matches in the interval at the beginning and end of the calculated time warping path with a width * equal to the search width should be left out because they might not be correct - since the time warp * path needs to start at (0,0) in the matrix and end at (m,n), they would only be correct if the path gets * calculated between two synchronization points. Paths calculated from the start of a track to the first * sync point, or from the last sync point to end of the track are probably wrong in this interval since * the start and end points don't match if there is time drift so it is better to leave them out in those * areas... in those short a few second long intervals the drict actually will never be that extreme that * someone would notice it anyway. */ if (inOutCue) { int startIndex = 0; int endIndex = path.Count; // this needs a temporally ordered mapping path (no matter if ascending or descending) foreach (Tuple <TimeSpan, TimeSpan> mapping in path) { if (cueIn && (mapping.Item1 < inOutCueSpan || mapping.Item2 < inOutCueSpan)) { startIndex++; } if (cueOut && (mapping.Item1 > (t1To - t1From - inOutCueSpan) || mapping.Item2 > (t2To - t2From - inOutCueSpan))) { endIndex--; } } path = path.GetRange(startIndex, endIndex - startIndex); } for (int i = 0; i < path.Count; i += filterSize) { //List<Tuple<TimeSpan, TimeSpan>> section = path.GetRange(i, Math.Min(path.Count - i, filterSize)); List <Tuple <TimeSpan, TimeSpan> > smoothingSection = path.GetRange( Math.Max(0, i - smoothingWidth / 2), Math.Min(path.Count - i, smoothingWidth)); Tuple <TimeSpan, TimeSpan> match = path[i]; if (smoothingSection.Count == 0) { throw new InvalidOperationException("must not happen"); } else if (smoothingSection.Count == 1 || !smoothing || i == 0) { // do nothing, match doesn't need any processing // the first and last match must not be smoothed since they must sit at the bounds } else { List <TimeSpan> offsets = new List <TimeSpan>(smoothingSection.Select(t => t.Item2 - t.Item1).OrderBy(t => t)); int middle = offsets.Count / 2; // calculate median // http://en.wikiversity.org/wiki/Primary_mathematics/Average,_median,_and_mode#Median TimeSpan smoothedDriftTime = new TimeSpan((offsets[middle - 1] + offsets[middle]).Ticks / 2); match = new Tuple <TimeSpan, TimeSpan>(match.Item1, match.Item1 + smoothedDriftTime); } float similarity = calculateSimilarity ? (float)Math.Abs(CrossCorrelation.Correlate( s1, new Interval(match.Item1.Ticks, match.Item1.Ticks + TimeUtil.SECS_TO_TICKS), s2, new Interval(match.Item2.Ticks, match.Item2.Ticks + TimeUtil.SECS_TO_TICKS))) : 1; if (similarity > maxSimilarity) { maxSimilarity = similarity; } matches.Add(new Match() { Track1 = t1, Track1Time = match.Item1 + t1From, Track2 = t2, Track2Time = match.Item2 + t2From, Similarity = similarity, Source = type.ToString() }); progressReporter.ReportProgress(progress / totalProgress * 100); progress += filterSize; } // add last match if it hasn't been added if (path.Count > 0 && path.Count % filterSize != 1) { Tuple <TimeSpan, TimeSpan> lastMatch = path[path.Count - 1]; matches.Add(new Match() { Track1 = t1, Track1Time = lastMatch.Item1 + t1From, Track2 = t2, Track2Time = lastMatch.Item2 + t2From, Similarity = 1, Source = type.ToString() }); } progressReporter.Finish(); multiTrackViewer.Dispatcher.BeginInvoke((Action) delegate { foreach (Match match in matches) { if (normalizeSimilarity) { match.Similarity /= maxSimilarity; // normalize to 1 } multiTrackViewer.Matches.Add(match); } }); s1.Close(); s2.Close(); }
private void alignTracksButton_Click(object sender, RoutedEventArgs e) { bool postProcessMatchingPoints = (bool)postProcessMatchingPointsCheckBox.IsChecked; bool removeUnusedMatchingPoints = (bool)removeUnusedMatchingPointsCheckBox.IsChecked; List <Match> matches = new List <Match>(multiTrackViewer.Matches); List <Match> newMatches = new List <Match>(); List <MatchGroup> trackGroups = DetermineMatchGroups(); try { MatchProcessor.ValidateMatches(trackGroups); } catch (Exception ex) { var message = "Invalid sequence of matches, cannot warp. " + "Please clean up the matches first (e.g. by filtering) to get rid of invalid mappings, e.g. overlapping/crossing matches."; MessageBox.Show(this, message, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error); return; } Task.Factory.StartNew(() => { Parallel.ForEach(trackGroups, trackGroup => { if (postProcessMatchingPoints) { Parallel.ForEach(trackGroup.MatchPairs, trackPair => { MatchProcessor.ValidatePairOrder(trackPair.Matches); foreach (Match match in trackPair.Matches) { newMatches.Add(CrossCorrelation.Adjust(match, progressMonitor)); } }); } }); Dispatcher.BeginInvoke((Action) delegate { newMatches.ForEach((m) => multiTrackViewer.Matches.Add(m)); if (removeUnusedMatchingPoints) { multiTrackViewer.Matches.Clear(); } TrackList <AudioTrack> alignedTracks = new TrackList <AudioTrack>(); TimeSpan componentStartTime = TimeSpan.Zero; string[] colors = { "#00aeef", "#00a651", "#8A2BE2", "#5F9EA0", "#D2691E", "#B8860B", "#483D8B", "#FF69B4", "#B0C4DE", "#6B8E23", "#F4A460" }; int colorIndex = 0; foreach (MatchGroup trackGroup in trackGroups) { if (removeUnusedMatchingPoints) { foreach (MatchPair trackPair in trackGroup.MatchPairs) { foreach (Match match in trackPair.Matches) { multiTrackViewer.Matches.Add(match); } } } MatchProcessor.FilterCoincidentMatches(trackGroup.MatchPairs); MatchProcessor.AlignTracks(trackGroup.MatchPairs); //MatchProcessor.MoveToStartTime(trackGroup.TrackList, componentStartTime); alignedTracks.Add(trackGroup.TrackList); componentStartTime = trackGroup.TrackList.End; foreach (AudioTrack t in trackGroup.TrackList) { t.Color = colors[colorIndex % colors.Length]; } colorIndex++; } // process unaligned tracks (= tracks without matching points) foreach (AudioTrack track in trackList.Except(alignedTracks)) { track.Volume = 0; } }); }); }
/// <summary> /// THIS IS THE CORE DETECTION METHOD /// Detects the human voice /// </summary> public static Tuple <BaseSonogram, double[, ], Plot, List <AcousticEvent>, TimeSpan> Analysis(FileInfo fiSegmentOfSourceFile, Dictionary <string, string> configDict, TimeSpan segmentStartOffset) { //set default values int frameLength = 1024; if (configDict.ContainsKey(AnalysisKeys.FrameLength)) { frameLength = int.Parse(configDict[AnalysisKeys.FrameLength]); } double windowOverlap = 0.0; int minHz = int.Parse(configDict["MIN_HZ"]); int minFormantgap = int.Parse(configDict["MIN_FORMANT_GAP"]); int maxFormantgap = int.Parse(configDict["MAX_FORMANT_GAP"]); double intensityThreshold = double.Parse(configDict["INTENSITY_THRESHOLD"]); //in 0-1 double minDuration = double.Parse(configDict["MIN_DURATION"]); // seconds double maxDuration = double.Parse(configDict["MAX_DURATION"]); // seconds AudioRecording recording = new AudioRecording(fiSegmentOfSourceFile.FullName); //i: MAKE SONOGRAM SonogramConfig sonoConfig = new SonogramConfig { //default values config SourceFName = recording.BaseName, WindowSize = frameLength, WindowOverlap = windowOverlap, NoiseReductionType = SNR.KeyToNoiseReductionType("STANDARD"), }; var tsRecordingtDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; //############################################################################################################################################# //window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins // 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz // 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz // 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz //the Xcorrelation-FFT technique requires number of bins to scan to be power of 2. //assuming sr=17640 and window=1024, then 64 bins span 1100 Hz above the min Hz level. i.e. 500 to 1600 //assuming sr=17640 and window=1024, then 128 bins span 2200 Hz above the min Hz level. i.e. 500 to 2700 int numberOfBins = 64; int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; int maxbin = minBin + numberOfBins - 1; int maxHz = (int)Math.Round(minHz + (numberOfBins * freqBinWidth)); BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, rowCount - 1, maxbin); //ii: DETECT HARMONICS int zeroBinCount = 4; //to remove low freq content which dominates the spectrum var results = CrossCorrelation.DetectBarsInTheRowsOfaMatrix(subMatrix, intensityThreshold, zeroBinCount); double[] intensity = results.Item1; double[] periodicity = results.Item2; //an array of periodicity scores //intensity = DataTools.filterMovingAverage(intensity, 3); //expect humans to have max power >100 and < 1000 Hz. Set these bounds int lowerHumanMaxBound = (int)(100 / freqBinWidth); //ignore 0-100 hz - too much noise int upperHumanMaxBound = (int)(3000 / freqBinWidth); //ignore above 2500 hz double[] scoreArray = new double[intensity.Length]; for (int r = 0; r < rowCount; r++) { if (intensity[r] < intensityThreshold) { continue; } //ignore locations with incorrect formant gap double herzPeriod = periodicity[r] * freqBinWidth; if (herzPeriod < minFormantgap || herzPeriod > maxFormantgap) { continue; } //find freq having max power and use info to adjust score. double[] spectrum = MatrixTools.GetRow(sonogram.Data, r); for (int j = 0; j < lowerHumanMaxBound; j++) { spectrum[j] = 0.0; } for (int j = upperHumanMaxBound; j < spectrum.Length; j++) { spectrum[j] = 0.0; } double[] peakvalues = DataTools.GetPeakValues(spectrum); int maxIndex1 = DataTools.GetMaxIndex(peakvalues); peakvalues[maxIndex1] = 0.0; int maxIndex2 = DataTools.GetMaxIndex(peakvalues); int avMaxBin = (maxIndex1 + maxIndex2) / 2; //int freqWithMaxPower = (int)Math.Round(maxIndex * freqBinWidth); int freqWithMaxPower = (int)Math.Round(avMaxBin * freqBinWidth); double discount = 1.0; if (freqWithMaxPower > 1000) { discount = 0.0; } else if (freqWithMaxPower < 500) { discount = 0.0; } //set scoreArray[r] - ignore locations with low intensity if (intensity[r] > intensityThreshold) { scoreArray[r] = intensity[r] * discount; } } //transfer info to a hits matrix. var hits = new double[rowCount, colCount]; double threshold = intensityThreshold * 0.75; //reduced threshold for display of hits for (int r = 0; r < rowCount; r++) { if (scoreArray[r] < threshold) { continue; } double herzPeriod = periodicity[r] * freqBinWidth; for (int c = minBin; c < maxbin; c++) { //hits[r, c] = herzPeriod / (double)380; //divide by 380 to get a relativePeriod; hits[r, c] = (herzPeriod - minFormantgap) / maxFormantgap; //to get a relativePeriod; } } //iii: CONVERT TO ACOUSTIC EVENTS List <AcousticEvent> predictedEvents = AcousticEvent.ConvertScoreArray2Events( scoreArray, minHz, maxHz, sonogram.FramesPerSecond, freqBinWidth, intensityThreshold, minDuration, maxDuration, segmentStartOffset); //remove isolated speech events - expect humans to talk like politicians //predictedEvents = Human2.FilterHumanSpeechEvents(predictedEvents); Plot plot = new Plot(AnalysisName, intensity, intensityThreshold); return(Tuple.Create(sonogram, hits, plot, predictedEvents, tsRecordingtDuration)); } //Analysis()