Привет, столкнулся с задачей, слать по wi-fi данные о спектре воспроизводимого аудио на компе.
Сразу скажу, чтоб сильно не пинали, на C# первый раз что-то делаю.
Нагуглил библиотеку NAudio, при помощи неё захватываю сигнал, который выводится со звуковухи.
Дальше этот сигнал нужно разложить по частотам, при помощи FFT и получить максимальные значения для частот. Мне нужно разделение на 3, низкие, средние и высокие. Как именно это сделать - так и не понял. Нашёл примеры, попытался сделать по примерам, но результат получился удручающим - показывает плюс-минус одну и ту же величину.
Вот мой код:
namespace AudioSpectr
{
public partial class Form1 : Form
{
private Socket client;
private IPEndPoint remote_point;
private WasapiLoopbackCapture _capture;
private object _lock;
private int _fftPos;
private int _fftLength;
private Complex[] _fftBuffer;
private float[] _lastFftBuffer;
private bool _fftBufferAvailable;
private int _m;
private float[] colorMusic;
private Dictionary<int, MMDevice> devices;
private Button button1;
private Button button2;
private Label label1;
private Label label2;
private Label label3;
private ComboBox comboBox1;
private TextBox textBox1;
private Label label4;
private TextBox textBox2;
private BackgroundWorker backgroundWorker1;
private Button button3;
private Dictionary<int, string> comboBoxDev;
public Form1()
{
InitializeComponent();
button2.Enabled = false;
label3.Visible = false;
devices = new Dictionary<int, MMDevice>();
comboBoxDev = new Dictionary<int, string>();
int count = 0;
foreach (var wasapi in new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active))
{
comboBoxDev.Add(count, wasapi.FriendlyName);
devices.Add(count, wasapi);
count += 1;
}
comboBox1.DataSource = new BindingSource(comboBoxDev, null);
comboBox1.DisplayMember = "Value";
comboBox1.ValueMember = "Key";
textBox1.Text = Properties.Settings.Default.ip;
textBox2.Text = Properties.Settings.Default.token;
this._lock = new object();
this._fftLength = 9728;
this._m = (int)Math.Log(this._fftLength, 2.0);
this._fftBuffer = new Complex[this._fftLength];
this._lastFftBuffer = new float[this._fftLength];
this.colorMusic = new float[3] { 0, 0, 0};
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox cmb = (ComboBox)sender;
int selectedIndex = cmb.SelectedIndex;
_capture = new WasapiLoopbackCapture(devices[selectedIndex]);
}
private void button1_Click(object sender, EventArgs e)
{
Start();
button1.Enabled = false;
button2.Enabled = true;
button3.Enabled = false;
label3.Visible = true;
textBox1.ReadOnly = true;
textBox2.ReadOnly = true;
comboBox1.Enabled = false;
try
{
this.client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
this.remote_point = new IPEndPoint(IPAddress.Parse(textBox1.Text), 5545);
this._capture.DataAvailable += this.DataAvailable;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
this.button2_Click(sender, e);
}
}
private void button2_Click(object sender, EventArgs e)
{
Stop();
button1.Enabled = true;
button2.Enabled = false;
button3.Enabled = true;
label3.Visible = false;
textBox1.ReadOnly = false;
textBox2.ReadOnly = false;
comboBox1.Enabled = true;
}
private void button3_Click(object sender, EventArgs e)
{
Properties.Settings.Default.ip = textBox1.Text;
Properties.Settings.Default.token = textBox2.Text;
Properties.Settings.Default.Save();
}
private float[] ConvertByteToFloat(byte[] array, int length)
{
int samplesNeeded = length / 4;
float[] floatArr = new float[samplesNeeded];
for (int i = 0; i < samplesNeeded; i++)
{
floatArr[i] = (float)BitConverter.ToInt32(array, i * 4);
}
return floatArr;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
}
private void DataAvailable(object sender, WaveInEventArgs e)
{
// Convert byte[] to float[].
float[] data = ConvertByteToFloat(e.Buffer, e.BytesRecorded);
// For all data. Skip right channel on stereo (i += this.Format.Channels).
for (int i = 0; i < data.Length; i += this.Format.Channels)
{
this._fftBuffer[_fftPos].X = (float)(data[i] * FastFourierTransform.HannWindow(_fftPos, _fftLength));
this._fftBuffer[_fftPos].Y = 0;
this._fftPos++;
if (this._fftPos >= this._fftLength)
{
this._fftPos = 0;
// NAudio FFT implementation.
FastFourierTransform.FFT(true, this._m, this._fftBuffer);
// Copy to buffer.
lock (this._lock)
{
for (int c = 0; c < this._fftLength; c++)
{
float amplitude = this.GetPower(this._fftBuffer[c]);
this._lastFftBuffer[c] = amplitude;
}
for (byte n = 2; n < 6; n++)
{
if (_lastFftBuffer[n] > this.colorMusic[0]) this.colorMusic[0] = _lastFftBuffer[n];
}
for (byte n = 6; n < 11; n++)
{
if (_lastFftBuffer[n] > this.colorMusic[1]) this.colorMusic[1] = _lastFftBuffer[n];
}
for (byte n = 11; n < 32; n++)
{
if (_lastFftBuffer[n] > this.colorMusic[2]) this.colorMusic[2] = _lastFftBuffer[n];
}
string json = JsonConvert.SerializeObject(new Packet(textBox2.Text, this.colorMusic));
Console.WriteLine(json);
byte[] send_buffer = Encoding.ASCII.GetBytes(json);
this.client.SendTo(send_buffer, remote_point);
this._fftBufferAvailable = true;
}
}
}
}
private float GetPower(Complex c)
{
// not entirely sure whether the multiplier should be 10 or 20 in this case.
// going with 10 from here http://stackoverflow.com/a/10636698/7532
float intensityDB = (float)(10 * Math.Log10(Math.Sqrt(c.X * c.X + c.Y * c.Y)));
return intensityDB;
}
public WaveFormat Format
{
get
{
return this._capture.WaveFormat;
}
}
public void Start()
{
this._capture.StartRecording();
}
public void Stop()
{
this._capture.StopRecording();
}
public bool GetFFTData(float[] fftDataBuffer)
{
lock (this._lock)
{
if (this._fftBufferAvailable)
{
this._lastFftBuffer.CopyTo(fftDataBuffer, 0);
this._fftBufferAvailable = false;
return true;
}
else
{
return false;
}
}
}
public int GetFFTFrequencyIndex(int frequency)
{
int index = (int)(frequency / (this.Format.SampleRate / this._fftLength / this.Format.Channels));
return index;
}
}
public class Packet
{
public string token { get; set; }
public float[] colorMusic { get; set; }
public Packet(string token, float[] colorMusic)
{
this.token = token;
this.colorMusic = colorMusic;
}
}
}