-
Notifications
You must be signed in to change notification settings - Fork 0
/
SineWaveProvider.cs
139 lines (127 loc) · 4.12 KB
/
SineWaveProvider.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
132
133
134
135
136
137
138
139
using NAudio.Wave;
namespace ToneGenerator
{
/// <summary>
/// A wave provider that outputs a sine wave.
/// </summary>
public class SineWaveProvider : WaveProvider32
{
/// <summary>
/// The sample rate.
/// </summary>
public const int SAMPLE_RATE = 44100;
/// <summary>
/// Duration of the ramp in and out, in seconds.
/// </summary>
public const float RAMP_DURATION = 0.010f;
/// <summary>
/// The number of samples corresponding to the ramp duration.
/// </summary>
public const int RAMP_SAMPLES = (int)(SAMPLE_RATE * RAMP_DURATION);
// The sample that the stop command was called at.
private int stopSample = 0;
/// <summary>
/// Creates a new <see cref="SineWaveProvider"/> with default settings.
/// </summary>
public SineWaveProvider() : base(SAMPLE_RATE, 2) { }
public List<Soundtrack> Soundtracks { get; private set; } = new List<Soundtrack>();
/// <summary>
/// Gets or sets the current sample count.
/// </summary>
public int Sample { get; set; }
/// <summary>
/// Gets or sets whether the wave provider is in a playing state.
/// </summary>
public bool IsPlaying { get; set; }
/// <summary>
/// Gets or sets the calibration of the output sound.
/// </summary>
public Calibration LeftCalibration { get; set; } = new Calibration(new Dictionary<float, float> { { 1000, 0 } });
/// <summary>
/// Gets or sets the calibration of the output sound.
/// </summary>
public Calibration RightCalibration { get; set; } = new Calibration(new Dictionary<float, float> { { 1000, 0 } });
public bool UseLoudnessInCalibration { get; set; }
/// <summary>
/// Reads in a buffer.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="sampleCount">The number of samples.</param>
/// <returns>The sample count.</returns>
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
if (IsPlaying)
{
for (int i = 0; i < sampleCount; i += 2)
{
float left = 0, right = 0;
foreach (var track in Soundtracks)
{
track.CurrentPhase += 2 * Math.PI * track.Frequency / sampleRate;
float val = (float)(Math.Sin(track.CurrentPhase));
if (track.Left)
left += LeftCalibration.GetCalibratedAmplitude(track, UseLoudnessInCalibration) * val;
if (track.Right)
right += RightCalibration.GetCalibratedAmplitude(track, UseLoudnessInCalibration) * val;
}
if (Sample < RAMP_SAMPLES)
{
// Ramp in.
left *= HannIn(Sample, RAMP_SAMPLES);
right *= HannIn(Sample, RAMP_SAMPLES);
}
buffer[i + offset] = left;
buffer[i + offset + 1] = right;
++Sample;
}
}
else
{
// Not playing, but ramp out before stopping completely.
for (int i = 0; i < sampleCount; i += 2)
{
float left = 0, right = 0;
foreach (var track in Soundtracks)
{
float val = 0;
if (Sample < stopSample + RAMP_SAMPLES)
{
track.CurrentPhase += 2 * Math.PI * track.Frequency / sampleRate;
val = (float)(Math.Sin(track.CurrentPhase))
* HannOut(Sample - stopSample, RAMP_SAMPLES);
}
else
{
// Reset phase
track.CurrentPhase = 0;
}
if (track.Left)
left += LeftCalibration.GetCalibratedAmplitude(track, UseLoudnessInCalibration) * val;
if (track.Right)
right += RightCalibration.GetCalibratedAmplitude(track, UseLoudnessInCalibration) * val;
}
buffer[i + offset] = left;
buffer[i + offset + 1] = right;
++Sample;
}
}
return sampleCount;
}
/// <summary>
/// Initiates stopping, to allow the sound to ramp out.
/// </summary>
public void InitiateStop()
{
IsPlaying = false;
stopSample = Sample;
}
// Cosine ramp in: 0.5 - 0.5*cos(pi*sample/threshold)
static float HannIn(int sample, int rampSamples) =>
0.5f - 0.5f * (float)Math.Cos(Math.PI * sample / rampSamples);
// Cosine ramp out: 0.5 + 0.5*cos(pi*sample/threshold)
static float HannOut(int sample, int rampSamples) =>
0.5f + 0.5f * (float)Math.Cos(Math.PI * sample / rampSamples);
}
}