我正在使用C#编写一个Windows窗体应用程序,它将用户输入的频率输入到设备的麦克风中,我想获得以一定间隔计算的频率,
我希望通过将最后计算的频率分配给全局变量来实现这一点,并且当提出timer.tick事件时,将值赋给数组的索引(userFrequencies),
以下是代码:
private void getFreqTimer_Tick(object sender, EventArgs e)
{
if (cIndex < userFrequencies.Length)
{
userFrequencies[cIndex] = currentFreq;
cIndex++; // Also tried ++cIndex;
}
}
我遇到的问题是cIndex有时增加1,有时增加2,因此没有正确地为数组赋值,
我知道计时器可以关闭大约15毫秒并且表格背景中有很多事情发生 - 这可能是一个因素,在编译过程中事件会不同吗?
非常感谢任何帮助!
编辑:要求的附加代码:
using NAudio.Dsp;
using NAudio.Wave;
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
namespace VocalWarmUpFYP
{
public partial class Form1 : Form
{
private WaveIn waveIn;
public WaveFileWriter waveFile = null;
string timeHour = (DateTime.Now.Hour).ToString();
string timeSecond = (DateTime.Now.Second).ToString();
string timeMinute = (DateTime.Now.Minute).ToString();
string timeDay = (DateTime.Now.Day).ToString();
string chartImagePath;
int callStopBtnClick;
int timeForExercise;
int timerSecs;
private static int fftLength = 2048;
private static int sampleRate = 44100;
private SampleAggregator sampleAggregator = new SampleAggregator(fftLength);
bool CompFFT = false;
int userId = HelpData.UserID;
double[] userFrequencies = new double[20];
double[] desiredFrequencies;
double currentFreq = 0;
public int cIndex = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
StartBtn.BackColor = HelpData.formBackColour;
StopBtn.BackColor = HelpData.formBackColour;
BackBtn.BackColor = HelpData.formBackColour;
BackColor = HelpData.formBackColour;
timer1.Tick += timer1_Tick;
getFreqTimer.Tick += getFreqTimer_Tick;
raiseStopTimer.Tick += raiseStopTimer_Tick;
chart1.Series.Clear();
chart1.BackColor = HelpData.formBackColour;
switch (HelpData.selectedWarmUp)
{
case 1:
warmupImg.Image = Image.FromFile(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\Visual Studio 2015\Projects\VocalWarmUpFYP\Images\BumbleBee.png");
getFreqTimer.Interval = 750;
timeForExercise = 13;
break;
case 2:
warmupImg.Image = Image.FromFile(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\Visual Studio 2015\Projects\VocalWarmUpFYP\Images\Lips.png");
getFreqTimer.Interval = 450;
timeForExercise = 7;
break;
case 3:
warmupImg.Image = Image.FromFile(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\Visual Studio 2015\Projects\VocalWarmUpFYP\Images\Siren.png");
getFreqTimer.Interval = 700;
timeForExercise = 12;
break;
case 4:
warmupImg.Image = Image.FromFile(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\Visual Studio 2015\Projects\VocalWarmUpFYP\Images\Mum.png");
getFreqTimer.Interval = 800;
timeForExercise = 14;
break;
case 5:
warmupImg.Image = Image.FromFile(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\Visual Studio 2015\Projects\VocalWarmUpFYP\Images\HummingBird.png");
getFreqTimer.Interval = 450;
timeForExercise = 7;
break;
}
if (chart1.Series.Count == 0) // Check to make sure Series 0 exists
{
var series = chart1.Series.Add("Frequency"); // If it doesn't exist then create it.
series.ChartType = SeriesChartType.FastLine;
series.ChartArea = "ChartArea1";
series.IsVisibleInLegend = false;
series.LabelBackColor = HelpData.formBackColour;
series.Color = Color.White;
series.BorderWidth = 5;
}
chart1.ChartAreas["ChartArea1"].BackColor = HelpData.formBackColour;
chart1.ChartAreas["ChartArea1"].AxisX.MinorGrid.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisX.MajorGrid.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisY.MajorGrid.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisX.MajorTickMark.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisX.MinorTickMark.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisX.Interval = 0;
chart1.ChartAreas["ChartArea1"].AxisY.LabelStyle.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisY.MajorTickMark.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisY.MinorTickMark.Enabled = false;
chart1.ChartAreas["ChartArea1"].AxisX.LineWidth = 0;
chart1.ChartAreas["ChartArea1"].AxisY.LineWidth = 0;
chart1.Visible = false;
}
private void StopBtn_Click(object sender, EventArgs e)
{
timer1.Stop();
getFreqTimer.Stop();
raiseStopTimer.Stop();
string currentTimeStamp = timeDay + timeHour + timeMinute + timeSecond;
waveIn.StopRecording();
waveFile.Dispose();
waveIn.Dispose();
StartBtn.Visible = true;
StopBtn.Visible = false;
chart1.SaveImage(@"C:\Temp\TestChart" + currentTimeStamp, ChartImageFormat.Png);
chartImagePath = @"C:\Temp\TestChart" + currentTimeStamp + ".Png";
NameRecording NameRecording = new NameRecording();
NameRecording.Tag = this;
NameRecording.Show(this);
for (int i = 0; i < userFrequencies.Length; i++)
{
Debug.WriteLine("User frequency at index " + i + ": " + userFrequencies[i]);
}
/*
var con = new SqlConnection("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=C:\\USERS\\OWNER\\DOCUMENTS\\VISUAL STUDIO 2015\\PROJECTS\\VOCALWARMUPFYP\\VOCALWARMUPFYP\\NEWDATABASE.MDF;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False");
using (con)
{
string oString = "SELECT COUNT (*) FROM VocalDetails";
string oString2 = "INSERT INTO VocalDetails (VocalID, FreqImg, Name, Accuracy, Id, TimeStamp, RecordingRef) VALUES (@vocalid, @freqimg, @name, @accuracy, @userid, @timestamp, @filereference)";
con.Open();
SqlCommand oCmd = new SqlCommand(oString, con);
int newVocalID = (Convert.ToInt32(oCmd.ExecuteScalar()) + 1);
string recordingName = HelpData.userRecordingName;
string timeStamp = DateTime.Now.ToShortDateString();
string freqImg = chartImagePath;
SqlCommand oCmd2 = new SqlCommand(oString2, con);
oCmd2.Parameters.AddWithValue("@vocalid", newVocalID);
oCmd2.Parameters.AddWithValue("@freqimg", freqImg);
oCmd2.Parameters.AddWithValue("@name", recordingName);
oCmd2.Parameters.AddWithValue("@accuracy", Accuracy);
oCmd2.Parameters.AddWithValue("@userid", userId);
oCmd2.Parameters.AddWithValue("@timestamp", timeStamp);
oCmd2.Parameters.AddWithValue("@filereference", fileReference);
}
con.Close();
con.Dispose();
*/
}
private void StartBtn_Click(object sender, EventArgs e)
{
StartBtn.Visible = false;
StopBtn.Visible = true;
warmupImg.Visible = false;
chart1.Visible = true;
string currentTimeStamp = timeDay + timeHour + timeMinute + timeSecond;
string fileToProcess = @"C:\Temp\Test" + currentTimeStamp + ".wav";
sampleAggregator.PerformFFT = true;
sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
BufferedWaveProvider wavprov = new BufferedWaveProvider(new WaveFormat(sampleRate, 2));
waveIn = new WaveIn();
waveIn.WaveFormat = new WaveFormat(44100, WaveIn.GetCapabilities(0).Channels);
waveIn.DeviceNumber = 0;
waveFile = new WaveFileWriter(fileToProcess, waveIn.WaveFormat);
timerSecs = DateTime.Now.AddSeconds(timeForExercise).Second;
waveIn.DataAvailable += OnDataAvailable;
waveIn.StartRecording();
timer1.Start();
getFreqTimer.Start();
raiseStopTimer.Start();
}
void OnDataAvailable(object sender, WaveInEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
}
else
{
byte[] buffer = e.Buffer;
int bytesRecorded = e.BytesRecorded;
int bufferIncrement = waveIn.WaveFormat.BlockAlign;
for (int index = 0; index < bytesRecorded; index += bufferIncrement)
{
float sample32 = BitConverter.ToInt16(buffer, index);
sampleAggregator.Add(sample32);
}
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
}
class SampleAggregator
{
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
public bool PerformFFT { get; set; }
private Complex[] fftBuffer;
private FftEventArgs fftArgs;
private int fftPos;
private int fftLength;
private int m;
public SampleAggregator(int fftLength)
{
if (!IsPowerOfTwo(fftLength))
{
throw new ArgumentException("FFT Length must be a power of two");
}
m = (int)Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
fftBuffer = new Complex[fftLength];
fftArgs = new FftEventArgs(fftBuffer);
}
bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Add(float value)
{
if (PerformFFT && FftCalculated != null)
{
fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HannWindow(fftPos, fftLength));
fftBuffer[fftPos].Y = 0; // This is always zero with audio.
fftPos++;
if (fftPos >= fftLength)
{
fftPos = 0;
FastFourierTransform.FFT(true, m, fftBuffer);
FftCalculated(this, fftArgs);
}
}
}
}
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
this.Result = result;
}
public Complex[] Result { get; private set; }
}
private void FftCalculated(object sender, FftEventArgs e)
{
double[] Magnitude = new double[(e.Result.Length / 2) - 1];
double Freq;
double Power;
int Index = 0;
if (CompFFT == true)
{
for (int i = 1; i < (e.Result.Length / 2) - 1; i++)
{
if (chart1.Series.Count <= fftLength) //<<======Now it wont give exception**
{
if (InvokeRequired == false) //<<====== Invoke method used to allow access to the chart control from custom event**
{
this.Invoke(new MethodInvoker(delegate
{
var series = 0;
double Max_Magnitude = 0;
double Max_Index = -1;
for (int j = 0; j < (e.Result.Length / 2) - 1; j++)
{
e.Result[j].Y = 0;
Power = (e.Result[j].X * e.Result[j].X) + (e.Result[j].Y * e.Result[j].Y);
Magnitude[j] = Math.Sqrt(Power);
}
for (int j = 0; j < Magnitude.Length; j++)
{
if (Magnitude[j] > Max_Magnitude && j != 0 && Magnitude[j] > 1)
{
Index = j;
Max_Magnitude = Magnitude[Index];
Max_Index = Index;
}
}
Freq = ((Max_Index * sampleRate) / fftLength);
if (Freq > 0)
{
currentFreq = Freq;
}
chart1.DataSource = Freq;
chart1.Series[series].Points.Add(Freq);
CompFFT = false;
}));
return;
}
}
chart1.Update();
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
CompFFT = true;
}
private void BackBtn_Click(object sender, EventArgs e)
{
SelectWarmUp SelectWarmUp = new SelectWarmUp();
SelectWarmUp.Tag = this;
SelectWarmUp.Show(this);
Hide();
}
private void getFreqTimer_Tick(object sender, EventArgs e)
{
if (cIndex < userFrequencies.Length)
{
userFrequencies[cIndex] = currentFreq;
cIndex++;
}
Debug.WriteLine("cIndex: " + cIndex);
}
private void raiseStopTimer_Tick(object sender, EventArgs e)
{
callStopBtnClick = DateTime.Now.Second;
if (callStopBtnClick == timerSecs + 2)
{
StopBtn.PerformClick();
}
}
}
}
timer1的间隔:25ms getFreqTimer的间隔:750毫秒
应用程序在自动调用StopBtn_Click事件之前运行15秒 - 这应该最多提供20个值
来自@Eminem建议的输出:
cIndex: 1
x: 1
cIndex: 2
x: 2
cIndex: 3
x: 3
cIndex: 4
x: 4
cIndex: 5
x: 5
cIndex: 6
x: 6
cIndex: 7
x: 7
cIndex: 8
x: 8
cIndex: 9
x: 9
cIndex: 10
x: 10
cIndex: 11
x: 11
cIndex: 12
x: 12
cIndex: 13
x: 13
cIndex: 14
x: 14
cIndex: 15
x: 15
cIndex: 16
x: 16
cIndex: 17
x: 17
cIndex: 18
x: 18
cIndex: 19
x: 19
cIndex: 20
x: 20
cIndex: 20
x: 21
cIndex: 20
x: 22
cIndex: 20
x: 23
cIndex: 20
x: 24
cIndex: 20
x: 25
cIndex: 20
x: 26
cIndex: 20
x: 27
cIndex: 20
x: 28
cIndex: 20
x: 29
cIndex: 20
x: 30
cIndex: 20
x: 31
cIndex: 20
x: 32
cIndex: 20
x: 33
cIndex: 20
x: 34
cIndex: 20
x: 35
cIndex: 20
x: 36
cIndex: 20
x: 37
cIndex: 20
x: 38
User frequency at index 0: 172.265625
User frequency at index 1: 172.265625
User frequency at index 2: 279.931640625
User frequency at index 3: 279.931640625
User frequency at index 4: 193.798828125
User frequency at index 5: 193.798828125
User frequency at index 6: 279.931640625
User frequency at index 7: 279.931640625
User frequency at index 8: 387.59765625
User frequency at index 9: 387.59765625
User frequency at index 10: 236.865234375
User frequency at index 11: 236.865234375
User frequency at index 12: 1098.193359375
User frequency at index 13: 1098.193359375
User frequency at index 14: 559.86328125
User frequency at index 15: 559.86328125
User frequency at index 16: 258.3984375
User frequency at index 17: 258.3984375
User frequency at index 18: 279.931640625
User frequency at index 19: 279.931640625
答案 0 :(得分:1)
我输入了一个类似的应用。问题是以下两行:
getFreqTimer.Tick += getFreqTimer_Tick;
raiseStopTimer.Tick += raiseStopTimer_Tick;
事件被提升两次。因此删除这两行,只保留通过属性检查器添加的行(仅在.Designer.cs文件中检查它)