/
MicRecorder.cs
131 lines (108 loc) · 4.16 KB
/
MicRecorder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using System;
using System.IO;
using System.ComponentModel;
using Microsoft.Xna.Framework.Audio;
using System.Windows.Media;
using Pitch;
namespace Pitch
{
public enum DetectionAlgorithm
{
YIN,
AUTOCORRELATION
}
public class MicRecorder
{
private Microphone _microphone;
private byte[] _buffer;
private int _bufferSize;
private TimeSpan _duration;
private Yin yin;
private PitchTracker ptracker;
private BackgroundWorker worker = new BackgroundWorker();
public String Note = String.Empty;
public float MidiNote = 0;
public float Pitch = 0.0f;
public MicRecorder()
{
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
_microphone = Microphone.Default;
/*
* The duration of the capture buffer must be between 100ms and 1000ms. Additionally, the capture buffer must be 10ms aligned (BufferDuration % 10 == 0).
* Silverlight applications must ensure that FrameworkDispatcher.Update is called regularly in order for "fire and forget" sounds to work correctly.
* BufferDuration throws an InvalidOperationException if FrameworkDispatcher.Update has not been called at least once before making this call.
* For more information, see Enable XNA Framework Events in Windows Phone Applications.
*/
_microphone.BufferDuration = TimeSpan.FromMilliseconds(100);
_duration = _microphone.BufferDuration;
_bufferSize = _microphone.GetSampleSizeInBytes(_microphone.BufferDuration);
_microphone.BufferReady += new EventHandler<EventArgs>(MicrophoneBufferReady);
}
public void Start(DetectionAlgorithm algorithm)
{
if (algorithm == DetectionAlgorithm.AUTOCORRELATION)
{
ptracker = new PitchTracker();
ptracker.PitchDetected += ptracker_PitchDetected;
ptracker.SampleRate = _microphone.SampleRate;
}
else if (algorithm == DetectionAlgorithm.YIN)
{
yin = new Yin(_microphone.SampleRate, _bufferSize/4);
}
_microphone.Start();
}
public void Stop()
{
ptracker.PitchDetected-=ptracker_PitchDetected;
yin = null;
_microphone.Stop();
}
private void MicrophoneBufferReady(object sender, EventArgs e)
{
_buffer = new byte[_bufferSize];
_microphone.GetData(_buffer);
if (!worker.IsBusy) worker.RunWorkerAsync(_buffer);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
float[] floatBuffer = bytesToFloats((byte[])e.Argument);
if(yin != null)
setNumbers(yin.getPitch(floatBuffer).getPitch());
else if(ptracker != null)
ptracker.ProcessBuffer(floatBuffer);
}
void ptracker_PitchDetected(PitchTracker sender, PitchTracker.PitchRecord pitchRecord)
{
setNumbers(pitchRecord.Pitch);
}
private void setNumbers(float pitch)
{
if (pitch < 0) pitch = 0;
// Set pitch
Pitch = pitch;
MidiNote = PitchDsp.PitchToMidiNote(pitch);
Note = PitchDsp.GetNoteName((int)MidiNote, true, true);
}
private static float[] bytesToFloats(byte[] bytes)
{
float[] floats = new float[bytes.Length / 2];
for (int i = 0; i < bytes.Length; i += 2)
{
floats[i / 2] = bytes[i] | (bytes[i + 1] << 8);
}
return floats;
}
// For FFT(not implemented yet)
public static double[] floatsToDoubles(float[] input)
{
double[] output = new double[input.Length];
for (int i = 0; i < input.Length; i++)
{
output[i] = input[i];
}
return output;
}
}
}