beeps 0.1.31 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.doc/ext/beeps/adsr.cpp +139 -0
- data/.doc/ext/beeps/analyser.cpp +128 -0
- data/.doc/ext/beeps/beeps.cpp +9 -1
- data/.doc/ext/beeps/file_in.cpp +55 -9
- data/.doc/ext/beeps/gain.cpp +64 -0
- data/.doc/ext/beeps/mic_in.cpp +83 -0
- data/.doc/ext/beeps/native.cpp +20 -8
- data/.doc/ext/beeps/oscillator.cpp +88 -0
- data/.doc/ext/beeps/pitch_shift.cpp +64 -0
- data/.doc/ext/beeps/processor.cpp +31 -2
- data/.doc/ext/beeps/sound.cpp +90 -7
- data/.doc/ext/beeps/sound_player.cpp +156 -0
- data/.doc/ext/beeps/time_stretch.cpp +64 -0
- data/.github/workflows/release-gem.yml +62 -0
- data/.github/workflows/test.yml +0 -6
- data/.github/workflows/utils.rb +13 -5
- data/ChangeLog.md +13 -0
- data/Rakefile +27 -6
- data/VERSION +1 -1
- data/beeps.gemspec +2 -2
- data/ext/beeps/adsr.cpp +150 -0
- data/ext/beeps/analyser.cpp +134 -0
- data/ext/beeps/beeps.cpp +10 -1
- data/ext/beeps/extconf.rb +1 -2
- data/ext/beeps/file_in.cpp +60 -9
- data/ext/beeps/gain.cpp +67 -0
- data/ext/beeps/mic_in.cpp +88 -0
- data/ext/beeps/native.cpp +20 -8
- data/ext/beeps/oscillator.cpp +93 -0
- data/ext/beeps/pitch_shift.cpp +67 -0
- data/ext/beeps/processor.cpp +34 -2
- data/ext/beeps/sound.cpp +99 -7
- data/ext/beeps/sound_player.cpp +169 -0
- data/ext/beeps/time_stretch.cpp +67 -0
- data/include/beeps/beeps.h +2 -1
- data/include/beeps/filter.h +179 -0
- data/include/beeps/generator.h +120 -0
- data/include/beeps/processor.h +37 -68
- data/include/beeps/ruby/filter.h +78 -0
- data/include/beeps/ruby/generator.h +60 -0
- data/include/beeps/ruby/processor.h +5 -45
- data/include/beeps/ruby/sound.h +14 -3
- data/include/beeps/signals.h +10 -4
- data/include/beeps/sound.h +67 -2
- data/lib/beeps/beeps.rb +6 -1
- data/lib/beeps/processor.rb +95 -15
- data/lib/beeps/sound.rb +29 -2
- data/src/adsr.cpp +245 -0
- data/src/analyser.cpp +254 -0
- data/src/beeps.cpp +11 -2
- data/src/file_in.cpp +94 -0
- data/src/gain.cpp +55 -0
- data/src/mic_in.cpp +262 -0
- data/src/mic_in.h +20 -0
- data/src/openal.cpp +2 -1
- data/src/oscillator.cpp +145 -0
- data/src/osx/signals.mm +83 -0
- data/src/pitch_shift.cpp +82 -0
- data/src/processor.cpp +202 -88
- data/src/processor.h +98 -0
- data/src/signals.cpp +326 -20
- data/src/signals.h +192 -2
- data/src/sound.cpp +735 -113
- data/src/sound.h +6 -1
- data/src/time_stretch.cpp +91 -0
- data/test/helper.rb +2 -1
- data/test/test_beeps.rb +10 -7
- data/test/test_beeps_init.rb +18 -0
- data/test/test_file_in.rb +15 -0
- data/test/test_processor.rb +50 -0
- data/test/test_sound.rb +87 -11
- data/test/test_sound_player.rb +134 -0
- metadata +55 -17
- data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
- data/.doc/ext/beeps/sine_wave.cpp +0 -61
- data/.doc/ext/beeps/square_wave.cpp +0 -61
- data/.github/workflows/release.yml +0 -34
- data/ext/beeps/sawtooth_wave.cpp +0 -64
- data/ext/beeps/sine_wave.cpp +0 -64
- data/ext/beeps/square_wave.cpp +0 -64
data/src/file_in.cpp
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#include "beeps/generator.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include "beeps/exception.h"
|
5
|
+
#include "signals.h"
|
6
|
+
|
7
|
+
|
8
|
+
namespace Beeps
|
9
|
+
{
|
10
|
+
|
11
|
+
|
12
|
+
struct FileIn::Data
|
13
|
+
{
|
14
|
+
|
15
|
+
String path;
|
16
|
+
|
17
|
+
Signals signals;
|
18
|
+
|
19
|
+
};// FileIn::Data
|
20
|
+
|
21
|
+
|
22
|
+
FileIn::FileIn (const char* path)
|
23
|
+
{
|
24
|
+
if (path) set_path(path);
|
25
|
+
}
|
26
|
+
|
27
|
+
FileIn::~FileIn ()
|
28
|
+
{
|
29
|
+
}
|
30
|
+
|
31
|
+
void
|
32
|
+
FileIn::reset ()
|
33
|
+
{
|
34
|
+
Super::reset();
|
35
|
+
}
|
36
|
+
|
37
|
+
void
|
38
|
+
FileIn::set_path (const char* path)
|
39
|
+
{
|
40
|
+
if (*this)
|
41
|
+
invalid_state_error(__FILE__, __LINE__, "path is already set");
|
42
|
+
|
43
|
+
self->signals = Signals_load(path);
|
44
|
+
self->path = path;
|
45
|
+
|
46
|
+
set_updated();
|
47
|
+
}
|
48
|
+
|
49
|
+
const char*
|
50
|
+
FileIn::path () const
|
51
|
+
{
|
52
|
+
return self->path;
|
53
|
+
}
|
54
|
+
|
55
|
+
double
|
56
|
+
FileIn::sample_rate () const
|
57
|
+
{
|
58
|
+
return self->signals.sample_rate();
|
59
|
+
}
|
60
|
+
|
61
|
+
uint
|
62
|
+
FileIn::nchannels () const
|
63
|
+
{
|
64
|
+
return self->signals.nchannels();
|
65
|
+
}
|
66
|
+
|
67
|
+
uint
|
68
|
+
FileIn::nsamples () const
|
69
|
+
{
|
70
|
+
return self->signals.nsamples();
|
71
|
+
}
|
72
|
+
|
73
|
+
float
|
74
|
+
FileIn::seconds () const
|
75
|
+
{
|
76
|
+
return Signals_get_seconds(self->signals);
|
77
|
+
}
|
78
|
+
|
79
|
+
void
|
80
|
+
FileIn::generate (Context* context, Signals* signals, uint* offset)
|
81
|
+
{
|
82
|
+
Super::generate(context, signals, offset);
|
83
|
+
|
84
|
+
*offset += Signals_copy(signals, self->signals, *offset);
|
85
|
+
}
|
86
|
+
|
87
|
+
FileIn::operator bool () const
|
88
|
+
{
|
89
|
+
if (!Super::operator bool()) return false;
|
90
|
+
return self->signals;
|
91
|
+
}
|
92
|
+
|
93
|
+
|
94
|
+
}// Beeps
|
data/src/gain.cpp
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#include "beeps/filter.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include "signals.h"
|
5
|
+
|
6
|
+
|
7
|
+
namespace Beeps
|
8
|
+
{
|
9
|
+
|
10
|
+
|
11
|
+
struct Gain::Data
|
12
|
+
{
|
13
|
+
|
14
|
+
float gain = 1;
|
15
|
+
|
16
|
+
};// Gain::Data
|
17
|
+
|
18
|
+
|
19
|
+
Gain::Gain (Processor* input)
|
20
|
+
: Super(input)
|
21
|
+
{
|
22
|
+
}
|
23
|
+
|
24
|
+
Gain::~Gain ()
|
25
|
+
{
|
26
|
+
}
|
27
|
+
|
28
|
+
void
|
29
|
+
Gain::set_gain (float gain)
|
30
|
+
{
|
31
|
+
self->gain = gain;
|
32
|
+
|
33
|
+
set_updated();
|
34
|
+
}
|
35
|
+
|
36
|
+
float
|
37
|
+
Gain::gain () const
|
38
|
+
{
|
39
|
+
return self->gain;
|
40
|
+
}
|
41
|
+
|
42
|
+
void
|
43
|
+
Gain::filter (Context* context, Signals* signals, uint* offset)
|
44
|
+
{
|
45
|
+
Super::filter(context, signals, offset);
|
46
|
+
|
47
|
+
Frames* frames = Signals_get_frames(signals);
|
48
|
+
if (!frames)
|
49
|
+
argument_error(__FILE__, __LINE__);
|
50
|
+
|
51
|
+
*frames *= self->gain;
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
}// Beeps
|
data/src/mic_in.cpp
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
#include "mic_in.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include <limits.h>
|
5
|
+
#include <vector>
|
6
|
+
#include <memory>
|
7
|
+
#include <algorithm>
|
8
|
+
#include "beeps/beeps.h"
|
9
|
+
#include "beeps/exception.h"
|
10
|
+
#include "openal.h"
|
11
|
+
#include "signals.h"
|
12
|
+
|
13
|
+
|
14
|
+
namespace Beeps
|
15
|
+
{
|
16
|
+
|
17
|
+
|
18
|
+
struct Mic
|
19
|
+
{
|
20
|
+
|
21
|
+
void start ()
|
22
|
+
{
|
23
|
+
if (self->started) return;
|
24
|
+
|
25
|
+
self->create();
|
26
|
+
if (!is_valid())
|
27
|
+
invalid_state_error(__FILE__, __LINE__);
|
28
|
+
|
29
|
+
alcCaptureStart(self->device);
|
30
|
+
OpenAL_check_error(__FILE__, __LINE__);
|
31
|
+
|
32
|
+
self->started = true;
|
33
|
+
}
|
34
|
+
|
35
|
+
void stop ()
|
36
|
+
{
|
37
|
+
if (!self->started) return;
|
38
|
+
|
39
|
+
if (!is_valid())
|
40
|
+
invalid_state_error(__FILE__, __LINE__);
|
41
|
+
|
42
|
+
alcCaptureStop(self->device);
|
43
|
+
OpenAL_check_error(__FILE__, __LINE__);
|
44
|
+
|
45
|
+
self->started = false;
|
46
|
+
}
|
47
|
+
|
48
|
+
bool is_started () const
|
49
|
+
{
|
50
|
+
return self->started;
|
51
|
+
}
|
52
|
+
|
53
|
+
void buffer_samples ()
|
54
|
+
{
|
55
|
+
if (!is_valid())
|
56
|
+
invalid_state_error(__FILE__, __LINE__);
|
57
|
+
|
58
|
+
ALint nsamples = 0;
|
59
|
+
alcGetIntegerv(
|
60
|
+
self->device, ALC_CAPTURE_SAMPLES, sizeof(nsamples), &nsamples);
|
61
|
+
OpenAL_check_error(__FILE__, __LINE__);
|
62
|
+
if (nsamples <= 0) return;
|
63
|
+
|
64
|
+
auto& buffer = self->buffer;
|
65
|
+
if (buffer.size() < (size_t) nsamples) buffer.resize(nsamples);
|
66
|
+
|
67
|
+
alcCaptureSamples(self->device, (ALCvoid*) &buffer[0], nsamples);
|
68
|
+
OpenAL_check_error(__FILE__, __LINE__);
|
69
|
+
|
70
|
+
auto& samples = self->samples;
|
71
|
+
samples.reserve(samples.size() + nsamples);
|
72
|
+
samples.insert(samples.end(), &buffer[0], &buffer[0] + nsamples);
|
73
|
+
|
74
|
+
if (samples.size() > samples_size_max())
|
75
|
+
{
|
76
|
+
size_t size = samples.size() - samples_size_max();
|
77
|
+
samples.erase(samples.begin(), samples.begin() + size);
|
78
|
+
self->offset += (uint) size;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
void get_signals (Signals* signals, uint* gen_offset)
|
83
|
+
{
|
84
|
+
assert(signals && signals->empty() && generate_offset);
|
85
|
+
|
86
|
+
if (!is_valid()) return;
|
87
|
+
|
88
|
+
Frames* frames = Signals_get_frames(signals);
|
89
|
+
assert(frames);
|
90
|
+
|
91
|
+
auto& samples = self->samples;
|
92
|
+
uint nchannels = self->nchannels;
|
93
|
+
uint nsamples = (uint) (samples.size() / nchannels);
|
94
|
+
uint offset = *gen_offset < self->offset ? 0 : *gen_offset - self->offset;
|
95
|
+
if (offset >= nsamples) return;
|
96
|
+
|
97
|
+
nsamples = std::min(frames->nframes(), nsamples - offset);
|
98
|
+
for (uint ch = 0; ch < frames->nchannels(); ++ch)
|
99
|
+
{
|
100
|
+
uint samples_ch = ch < nchannels ? ch : 0;
|
101
|
+
float div = 1.0f / SHRT_MAX;
|
102
|
+
for (uint i = 0; i < nsamples; ++i)
|
103
|
+
(*frames)(i, ch) = samples[(offset + i) * nchannels + samples_ch] * div;
|
104
|
+
}
|
105
|
+
Signals_set_nsamples(signals, nsamples);
|
106
|
+
|
107
|
+
*gen_offset = self->offset + offset + nsamples;
|
108
|
+
}
|
109
|
+
|
110
|
+
uint samples_size_max () const
|
111
|
+
{
|
112
|
+
assert(self->sample_rate > 0);
|
113
|
+
|
114
|
+
return self->sample_rate * 1;
|
115
|
+
}
|
116
|
+
|
117
|
+
bool is_valid () const
|
118
|
+
{
|
119
|
+
return self->sample_rate > 0 && self->nchannels > 0 && self->device;
|
120
|
+
}
|
121
|
+
|
122
|
+
struct Data
|
123
|
+
{
|
124
|
+
|
125
|
+
double sample_rate = 0;
|
126
|
+
|
127
|
+
uint nchannels = 0;
|
128
|
+
|
129
|
+
uint offset = 0;
|
130
|
+
|
131
|
+
bool started = false;
|
132
|
+
|
133
|
+
ALCdevice* device = NULL;
|
134
|
+
|
135
|
+
std::vector<ALshort> samples, buffer;
|
136
|
+
|
137
|
+
~Data ()
|
138
|
+
{
|
139
|
+
clear();
|
140
|
+
}
|
141
|
+
|
142
|
+
void create ()
|
143
|
+
{
|
144
|
+
assert(sample_rate > 0 && nchannels > 0);
|
145
|
+
|
146
|
+
if (device) return;
|
147
|
+
|
148
|
+
device = alcCaptureOpenDevice(
|
149
|
+
NULL,
|
150
|
+
sample_rate,
|
151
|
+
nchannels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
|
152
|
+
sample_rate / 10);
|
153
|
+
if (!device)
|
154
|
+
openal_error(__FILE__, __LINE__, "failed to create a mic device");
|
155
|
+
}
|
156
|
+
|
157
|
+
void clear ()
|
158
|
+
{
|
159
|
+
if (!device) return;
|
160
|
+
|
161
|
+
if (alcCaptureCloseDevice(device) == ALC_FALSE)
|
162
|
+
openal_error(__FILE__, __LINE__, "failed to close the mic device");
|
163
|
+
|
164
|
+
device = NULL;
|
165
|
+
}
|
166
|
+
|
167
|
+
};// Data
|
168
|
+
|
169
|
+
Xot::PSharedImpl<Data> self;
|
170
|
+
|
171
|
+
};// Mic
|
172
|
+
|
173
|
+
|
174
|
+
struct MicIn::Data
|
175
|
+
{
|
176
|
+
|
177
|
+
Mic mic;
|
178
|
+
|
179
|
+
};// MicIn::Data
|
180
|
+
|
181
|
+
|
182
|
+
namespace global
|
183
|
+
{
|
184
|
+
|
185
|
+
static std::vector<Mic> mics;
|
186
|
+
|
187
|
+
};// global
|
188
|
+
|
189
|
+
|
190
|
+
void
|
191
|
+
MicIn_process_streams ()
|
192
|
+
{
|
193
|
+
for (Mic& mic : global::mics)
|
194
|
+
mic.buffer_samples();
|
195
|
+
}
|
196
|
+
|
197
|
+
|
198
|
+
MicIn::MicIn (uint nchannels, double sample_rate)
|
199
|
+
{
|
200
|
+
if (nchannels > 2)
|
201
|
+
argument_error(__FILE__, __LINE__);
|
202
|
+
|
203
|
+
if (sample_rate <= 0) sample_rate = Beeps::sample_rate();
|
204
|
+
|
205
|
+
self->mic.self->sample_rate = sample_rate;
|
206
|
+
self->mic.self->nchannels = nchannels;
|
207
|
+
}
|
208
|
+
|
209
|
+
MicIn::~MicIn ()
|
210
|
+
{
|
211
|
+
}
|
212
|
+
|
213
|
+
void
|
214
|
+
MicIn::start ()
|
215
|
+
{
|
216
|
+
if (self->mic.is_started()) return;
|
217
|
+
|
218
|
+
self->mic.start();
|
219
|
+
|
220
|
+
global::mics.emplace_back(self->mic);
|
221
|
+
}
|
222
|
+
|
223
|
+
void
|
224
|
+
MicIn::stop ()
|
225
|
+
{
|
226
|
+
if (!self->mic.is_started()) return;
|
227
|
+
|
228
|
+
std::erase_if(
|
229
|
+
global::mics,
|
230
|
+
[&](auto& mic) {return mic.self.get() == self->mic.self.get();});
|
231
|
+
|
232
|
+
self->mic.stop();
|
233
|
+
}
|
234
|
+
|
235
|
+
double
|
236
|
+
MicIn::sample_rate () const
|
237
|
+
{
|
238
|
+
return self->mic.self->sample_rate;
|
239
|
+
}
|
240
|
+
|
241
|
+
uint
|
242
|
+
MicIn::nchannels () const
|
243
|
+
{
|
244
|
+
return self->mic.self->nchannels;
|
245
|
+
}
|
246
|
+
|
247
|
+
void
|
248
|
+
MicIn::generate (Context* context, Signals* signals, uint* offset)
|
249
|
+
{
|
250
|
+
Super::generate(context, signals, offset);
|
251
|
+
|
252
|
+
self->mic.get_signals(signals, offset);
|
253
|
+
}
|
254
|
+
|
255
|
+
MicIn::operator bool () const
|
256
|
+
{
|
257
|
+
if (!Super::operator bool()) return false;
|
258
|
+
return self->mic.is_valid();
|
259
|
+
}
|
260
|
+
|
261
|
+
|
262
|
+
}// Beeps
|
data/src/mic_in.h
ADDED
data/src/openal.cpp
CHANGED
@@ -23,7 +23,7 @@ namespace Beeps
|
|
23
23
|
static void
|
24
24
|
cleanup ()
|
25
25
|
{
|
26
|
-
|
26
|
+
SoundPlayer_clear_all();
|
27
27
|
|
28
28
|
alcMakeContextCurrent(NULL);
|
29
29
|
|
@@ -94,6 +94,7 @@ namespace Beeps
|
|
94
94
|
case ALC_INVALID_DEVICE: return "ALC_INVALID_DEVICE";
|
95
95
|
case ALC_INVALID_CONTEXT: return "ALC_INVALID_CONTEXT";
|
96
96
|
case ALC_INVALID_ENUM: return "ALC_INVALID_ENUM";
|
97
|
+
case ALC_INVALID_VALUE: return "ALC_INVALID_VALUE";
|
97
98
|
case ALC_OUT_OF_MEMORY: return "ALC_OUT_OF_MEMORY";
|
98
99
|
default: return "UNKNOWN ERROR";
|
99
100
|
}
|
data/src/oscillator.cpp
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#include "beeps/generator.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include "SineWave.h"
|
5
|
+
#include "BlitSquare.h"
|
6
|
+
#include "BlitSaw.h"
|
7
|
+
#include "beeps/exception.h"
|
8
|
+
#include "signals.h"
|
9
|
+
|
10
|
+
|
11
|
+
namespace Beeps
|
12
|
+
{
|
13
|
+
|
14
|
+
|
15
|
+
struct Oscillator::Data
|
16
|
+
{
|
17
|
+
|
18
|
+
Type type = NONE;
|
19
|
+
|
20
|
+
float frequency = 440;
|
21
|
+
|
22
|
+
std::unique_ptr<stk::SineWave> sine;
|
23
|
+
|
24
|
+
std::unique_ptr<stk::BlitSquare> square;
|
25
|
+
|
26
|
+
std::unique_ptr<stk::BlitSaw> saw;
|
27
|
+
|
28
|
+
};// Oscillator::Data
|
29
|
+
|
30
|
+
|
31
|
+
Oscillator::Oscillator (Type type)
|
32
|
+
{
|
33
|
+
set_type(type);
|
34
|
+
}
|
35
|
+
|
36
|
+
Oscillator::~Oscillator ()
|
37
|
+
{
|
38
|
+
}
|
39
|
+
|
40
|
+
void
|
41
|
+
Oscillator::reset ()
|
42
|
+
{
|
43
|
+
Super::reset();
|
44
|
+
|
45
|
+
if (self->sine) self->sine->reset();
|
46
|
+
if (self->square) self->square->reset();
|
47
|
+
if (self->saw) self->saw->reset();
|
48
|
+
}
|
49
|
+
|
50
|
+
void
|
51
|
+
Oscillator::set_type (Type type)
|
52
|
+
{
|
53
|
+
if (type == self->type) return;
|
54
|
+
|
55
|
+
self->type = type;
|
56
|
+
|
57
|
+
self->sine .reset();
|
58
|
+
self->square.reset();
|
59
|
+
self->saw .reset();
|
60
|
+
|
61
|
+
switch (self->type)
|
62
|
+
{
|
63
|
+
case SINE: self->sine .reset(new stk::SineWave()); break;
|
64
|
+
case SQUARE: self->square.reset(new stk::BlitSquare()); break;
|
65
|
+
case SAWTOOTH: self->saw .reset(new stk::BlitSaw()); break;
|
66
|
+
default:
|
67
|
+
argument_error(
|
68
|
+
__FILE__, __LINE__, "unknown oscilator type '%d'", self->type);
|
69
|
+
break;
|
70
|
+
}
|
71
|
+
|
72
|
+
set_updated();
|
73
|
+
}
|
74
|
+
|
75
|
+
Oscillator::Type
|
76
|
+
Oscillator::type () const
|
77
|
+
{
|
78
|
+
return self->type;
|
79
|
+
}
|
80
|
+
|
81
|
+
void
|
82
|
+
Oscillator::set_frequency (float frequency)
|
83
|
+
{
|
84
|
+
if (frequency <= 0)
|
85
|
+
argument_error(__FILE__, __LINE__);
|
86
|
+
|
87
|
+
self->frequency = frequency;
|
88
|
+
|
89
|
+
set_updated();
|
90
|
+
}
|
91
|
+
|
92
|
+
float
|
93
|
+
Oscillator::frequency () const
|
94
|
+
{
|
95
|
+
return self->frequency;
|
96
|
+
}
|
97
|
+
|
98
|
+
void
|
99
|
+
Oscillator::generate (Context* context, Signals* signals, uint* offset)
|
100
|
+
{
|
101
|
+
Super::generate(context, signals, offset);
|
102
|
+
|
103
|
+
Frames* frames = Signals_get_frames(signals);
|
104
|
+
if (!frames)
|
105
|
+
argument_error(__FILE__, __LINE__);
|
106
|
+
|
107
|
+
switch (self->type)
|
108
|
+
{
|
109
|
+
case SINE:
|
110
|
+
assert(self->sine);
|
111
|
+
self->sine->setFrequency(self->frequency);
|
112
|
+
self->sine->tick(*frames);
|
113
|
+
break;
|
114
|
+
|
115
|
+
case SQUARE:
|
116
|
+
assert(self->square);
|
117
|
+
self->square->setFrequency(self->frequency);
|
118
|
+
self->square->tick(*frames);
|
119
|
+
break;
|
120
|
+
|
121
|
+
case SAWTOOTH:
|
122
|
+
assert(self->saw);
|
123
|
+
self->saw->setFrequency(self->frequency);
|
124
|
+
self->saw->tick(*frames);
|
125
|
+
break;
|
126
|
+
|
127
|
+
default:
|
128
|
+
invalid_state_error(
|
129
|
+
__FILE__, __LINE__, "unknown oscilator type '%d'", self->type);
|
130
|
+
break;
|
131
|
+
}
|
132
|
+
|
133
|
+
Signals_set_nsamples(signals, frames->nframes());
|
134
|
+
}
|
135
|
+
|
136
|
+
Oscillator::operator bool () const
|
137
|
+
{
|
138
|
+
if (!Super::operator bool()) return false;
|
139
|
+
return
|
140
|
+
self->type != NONE && self->frequency > 0 &&
|
141
|
+
(self->sine || self->square || self->saw);
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
}// Beeps
|
data/src/osx/signals.mm
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
// -*- c++ -*-
|
2
|
+
#include "../signals.h"
|
3
|
+
|
4
|
+
|
5
|
+
#import <AVFoundation/AVFoundation.h>
|
6
|
+
#include <beeps/exception.h>
|
7
|
+
|
8
|
+
|
9
|
+
namespace Beeps
|
10
|
+
{
|
11
|
+
|
12
|
+
|
13
|
+
[[noreturn]]
|
14
|
+
void
|
15
|
+
objc_error (const char* file, int line, NSError* error, const char* format = NULL, ...)
|
16
|
+
{
|
17
|
+
if (!error)
|
18
|
+
argument_error(__FILE__, __LINE__);
|
19
|
+
|
20
|
+
XOT_STRINGF(format, s);
|
21
|
+
beeps_error(
|
22
|
+
file, line,
|
23
|
+
Xot::stringf("%s (%s)", s.c_str(), error.localizedDescription.UTF8String));
|
24
|
+
}
|
25
|
+
|
26
|
+
|
27
|
+
static AVAudioPCMBuffer*
|
28
|
+
load_buffer (const char* path)
|
29
|
+
{
|
30
|
+
if (!path)
|
31
|
+
argument_error(__FILE__, __LINE__);
|
32
|
+
|
33
|
+
NSURL* url = [NSURL fileURLWithPath: [NSString stringWithUTF8String:path]];
|
34
|
+
if (!url)
|
35
|
+
system_error(__FILE__, __LINE__, "failed to get url object for path");
|
36
|
+
|
37
|
+
NSError* error = nil;
|
38
|
+
|
39
|
+
AVAudioFile* file =
|
40
|
+
[[[AVAudioFile alloc] initForReading:url error:&error] autorelease];
|
41
|
+
if (error)
|
42
|
+
objc_error(__FILE__, __LINE__, error, "failed to open file '%s'", path);
|
43
|
+
|
44
|
+
AVAudioFormat* format = file.processingFormat;
|
45
|
+
if (format.channelCount <= 0)
|
46
|
+
beeps_error(__FILE__, __LINE__, "invalid channel count %d", format.channelCount);
|
47
|
+
|
48
|
+
AVAudioPCMBuffer* buffer = [[[AVAudioPCMBuffer alloc]
|
49
|
+
initWithPCMFormat:format frameCapacity:(AVAudioFrameCount) file.length]
|
50
|
+
autorelease];
|
51
|
+
|
52
|
+
BOOL result = [file readIntoBuffer:buffer error:&error];
|
53
|
+
if (error)
|
54
|
+
objc_error(__FILE__, __LINE__, error, "failed to read into the buffer");
|
55
|
+
if (!result)
|
56
|
+
system_error(__FILE__, __LINE__, "failed to read into the buffer");
|
57
|
+
|
58
|
+
return buffer;
|
59
|
+
}
|
60
|
+
|
61
|
+
Signals
|
62
|
+
Signals_load (const char* path)
|
63
|
+
{
|
64
|
+
AVAudioPCMBuffer* buffer = load_buffer(path);
|
65
|
+
if (!buffer) return Signals();
|
66
|
+
|
67
|
+
uint nchannels = buffer.format.channelCount;
|
68
|
+
if (nchannels <= 0)
|
69
|
+
beeps_error(__FILE__, __LINE__, "invalid nchannels %d", nchannels);
|
70
|
+
|
71
|
+
uint len = buffer.frameLength;
|
72
|
+
if (len <= 0)
|
73
|
+
beeps_error(__FILE__, __LINE__, "invalid buffer length %d", len);
|
74
|
+
|
75
|
+
const float* const* data = buffer.floatChannelData;
|
76
|
+
if (!data)
|
77
|
+
beeps_error(__FILE__, __LINE__, "failed to get channel data");
|
78
|
+
|
79
|
+
return Signals_create(data, len, nchannels, (uint) buffer.format.sampleRate);
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
}// Beeps
|