-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsweep.cpp
121 lines (88 loc) · 2.88 KB
/
sweep.cpp
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
#include "AudioDevice.h"
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace consts {
constexpr audio::Metadata metadata;
// sample duration
static const float dT = 1 / static_cast<float>(metadata.sampleRate);
// sweep 20Hz ... 20kHz
static const float fMin = 20.f;
static const float fMax = 20000.f;
static const float sweepFactor = 1.1f;
} // namespace consts
audio::Sequence<float> sweepNaive()
{
audio::Sequence<float> seq{consts::metadata};
// time accumulator
float t = 0.f;
float values[consts::metadata.sampleCount];
for(float freq = consts::fMin; freq < consts::fMax; freq *= consts::sweepFactor) {
for(auto&& value : values) {
value = std::sin(2 * static_cast<float>(M_PI) * freq * t);
t += consts::dT;
}
seq.push(std::begin(values), std::end(values));
}
return seq;
}
audio::Sequence<float> sweepTimeAccumulator()
{
audio::Sequence<float> seq{consts::metadata};
// time accumulator
float t = 0.f;
// phase continuity offset
// recalculated on each frequency update to guarantee continuous phase and smooth audio transition
// inspired by https://dsp.stackexchange.com/q/971
float phiOffset = 0.f;
float values[consts::metadata.sampleCount];
float freq = consts::fMin;
while (freq < consts::fMax)
{
// cache the last phi of this frequency
float phi = 0.f;
for(auto&& value : values) {
phi = 2 * static_cast<float>(M_PI) * freq * t + phiOffset;
value = std::sin(phi);
t += consts::dT;
}
seq.push(std::begin(values), std::end(values));
const auto nextFreq = freq * consts::sweepFactor;
// recalculate phase continuity offset
phiOffset = phi - nextFreq * (t - consts::dT);
freq = nextFreq;
}
return seq;
}
audio::Sequence<float> sweepPhaseAccumulator()
{
audio::Sequence<float> seq{consts::metadata};
// phase accumulator
// to guarantee continuous phase and smooth audio transition
// note: mind numeric inaccuracies depending on duration and frequency
float phi = 0.f;
float values[consts::metadata.sampleCount];
for(float freq = consts::fMin; freq < consts::fMax; freq *= consts::sweepFactor) {
const auto deltaPhi = 2 * static_cast<float>(M_PI) * freq * consts::dT;
for(auto&& value : values) {
value = std::sin(phi);
phi += deltaPhi;
}
seq.push(std::begin(values), std::end(values));
}
return seq;
}
int main(int, char**)
try {
audio::DevicePlayback<float> playback(consts::metadata);
std::cout << "simple sweep..." << std::endl;
playback.play(sweepNaive());
std::cout << "sweep generated using time accumulator..." << std::endl;
playback.play(sweepTimeAccumulator());
std::cout << "sweep generated using phase accumulator..." << std::endl;
playback.play(sweepPhaseAccumulator());
return EXIT_SUCCESS;
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}