beeps 0.1.31 → 0.1.33
Sign up to get free protection for your applications and to get access to all the features.
- 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/pitch_shift.cpp
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#include "beeps/filter.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include "PitShift.h"
|
5
|
+
#include "signalsmith-stretch.h"
|
6
|
+
#include "signals.h"
|
7
|
+
|
8
|
+
|
9
|
+
namespace Beeps
|
10
|
+
{
|
11
|
+
|
12
|
+
|
13
|
+
struct PitchShift::Data
|
14
|
+
{
|
15
|
+
|
16
|
+
signalsmith::stretch::SignalsmithStretch<float> stretch;
|
17
|
+
|
18
|
+
float shift = 1;
|
19
|
+
|
20
|
+
};// PitchShift::Data
|
21
|
+
|
22
|
+
|
23
|
+
PitchShift::PitchShift (Processor* input)
|
24
|
+
: Super(input)
|
25
|
+
{
|
26
|
+
set_buffering_seconds(1);
|
27
|
+
}
|
28
|
+
|
29
|
+
PitchShift::~PitchShift ()
|
30
|
+
{
|
31
|
+
}
|
32
|
+
|
33
|
+
void
|
34
|
+
PitchShift::reset ()
|
35
|
+
{
|
36
|
+
Super::reset();
|
37
|
+
|
38
|
+
self->stretch.reset();
|
39
|
+
}
|
40
|
+
|
41
|
+
void
|
42
|
+
PitchShift::set_shift (float shift)
|
43
|
+
{
|
44
|
+
self->shift = shift;
|
45
|
+
|
46
|
+
set_updated();
|
47
|
+
}
|
48
|
+
|
49
|
+
float
|
50
|
+
PitchShift::shift () const
|
51
|
+
{
|
52
|
+
return self->shift;
|
53
|
+
}
|
54
|
+
|
55
|
+
void
|
56
|
+
PitchShift::filter (Context* context, Signals* signals, uint* offset)
|
57
|
+
{
|
58
|
+
Super::filter(context, signals, offset);
|
59
|
+
|
60
|
+
if (self->shift == 1 || signals->nsamples() <= 0)
|
61
|
+
return;
|
62
|
+
|
63
|
+
SignalSamples<float> input(*signals);
|
64
|
+
SignalSamples<float> output(input.nsamples(), signals->nchannels());
|
65
|
+
|
66
|
+
self->stretch.presetDefault(signals->nchannels(), signals->sample_rate());
|
67
|
+
self->stretch.setTransposeFactor(self->shift);
|
68
|
+
self->stretch.process(
|
69
|
+
input.channels(), input.nsamples(),
|
70
|
+
output.channels(), output.nsamples());
|
71
|
+
|
72
|
+
Signals_write_samples(signals, output, signals->nsamples());
|
73
|
+
}
|
74
|
+
|
75
|
+
PitchShift::operator bool () const
|
76
|
+
{
|
77
|
+
if (!Super::operator bool()) return false;
|
78
|
+
return self->shift > 0;
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
}// Beeps
|
data/src/processor.cpp
CHANGED
@@ -1,200 +1,314 @@
|
|
1
|
-
#include "
|
1
|
+
#include "processor.h"
|
2
2
|
|
3
3
|
|
4
|
-
#include "
|
5
|
-
#include "BlitSaw.h"
|
6
|
-
#include "BlitSquare.h"
|
7
|
-
#include "FileWvIn.h"
|
4
|
+
#include "xot/time.h"
|
8
5
|
#include "beeps/exception.h"
|
9
|
-
#include "signals.h"
|
10
6
|
|
11
7
|
|
12
8
|
namespace Beeps
|
13
9
|
{
|
14
10
|
|
15
11
|
|
16
|
-
|
12
|
+
static ProcessorContext*
|
13
|
+
get_context(Processor::Context* context)
|
17
14
|
{
|
15
|
+
return (ProcessorContext*) context;
|
18
16
|
}
|
19
17
|
|
20
|
-
|
18
|
+
|
19
|
+
struct Processor::Data
|
21
20
|
{
|
22
|
-
}
|
23
21
|
|
24
|
-
|
25
|
-
|
22
|
+
bool generator = false;
|
23
|
+
|
24
|
+
float buffering_seconds = 0;
|
25
|
+
|
26
|
+
double last_update_time = 0;
|
27
|
+
|
28
|
+
Processor::Ref input;
|
29
|
+
|
30
|
+
bool has_generator () const
|
31
|
+
{
|
32
|
+
return generator || (input && input->self->has_generator());
|
33
|
+
}
|
34
|
+
|
35
|
+
};// Processor::Data
|
36
|
+
|
37
|
+
|
38
|
+
float
|
39
|
+
Processor_get_buffering_seconds (Processor* processor)
|
26
40
|
{
|
27
|
-
|
28
|
-
argument_error(__FILE__, __LINE__);
|
41
|
+
assert(processor);
|
29
42
|
|
30
|
-
|
31
|
-
invalid_state_error(__FILE__, __LINE__);
|
43
|
+
return processor->self->buffering_seconds;
|
32
44
|
}
|
33
45
|
|
34
|
-
|
46
|
+
|
47
|
+
Processor::Processor (bool generator)
|
35
48
|
{
|
36
|
-
|
49
|
+
self->generator = generator;
|
37
50
|
}
|
38
51
|
|
39
|
-
|
40
|
-
Processor::operator ! () const
|
52
|
+
Processor::~Processor ()
|
41
53
|
{
|
42
|
-
return !operator bool();
|
43
54
|
}
|
44
55
|
|
45
|
-
|
46
|
-
|
56
|
+
void
|
57
|
+
Processor::reset ()
|
47
58
|
{
|
59
|
+
if (self->input) self->input->reset();
|
48
60
|
|
49
|
-
|
61
|
+
set_updated();
|
62
|
+
}
|
50
63
|
|
51
|
-
|
64
|
+
void
|
65
|
+
Processor::set_input (Processor* input)
|
66
|
+
{
|
67
|
+
if (input && self->generator)
|
68
|
+
invalid_state_error(__FILE__, __LINE__, "generator cannot have inputs");
|
52
69
|
|
53
|
-
|
70
|
+
self->input = input;
|
54
71
|
|
72
|
+
set_updated();
|
73
|
+
}
|
55
74
|
|
56
|
-
|
75
|
+
Processor*
|
76
|
+
Processor::input ()
|
57
77
|
{
|
58
|
-
|
78
|
+
return self->input;
|
59
79
|
}
|
60
80
|
|
61
|
-
|
81
|
+
const Processor*
|
82
|
+
Processor::input () const
|
62
83
|
{
|
84
|
+
return const_cast<Processor*>(this)->input();
|
63
85
|
}
|
64
86
|
|
65
|
-
|
66
|
-
SineWave::set_frequency (float frequency)
|
87
|
+
Processor::operator bool () const
|
67
88
|
{
|
68
|
-
self->
|
69
|
-
self->oscillator.setFrequency(frequency);
|
89
|
+
return self->has_generator();
|
70
90
|
}
|
71
91
|
|
72
|
-
|
73
|
-
|
92
|
+
bool
|
93
|
+
Processor::operator ! () const
|
74
94
|
{
|
75
|
-
return
|
95
|
+
return !operator bool();
|
76
96
|
}
|
77
97
|
|
78
98
|
void
|
79
|
-
|
99
|
+
Processor::process (Context* context, Signals* signals, uint* offset)
|
80
100
|
{
|
81
|
-
|
82
|
-
|
83
|
-
|
101
|
+
if (self->generator)
|
102
|
+
generate(context, signals, offset);
|
103
|
+
else
|
104
|
+
filter(context, signals, offset);
|
84
105
|
}
|
85
106
|
|
86
|
-
|
87
|
-
|
107
|
+
void
|
108
|
+
Processor::generate (Context* context, Signals* signals, uint* offset)
|
88
109
|
{
|
110
|
+
if (!context || !signals || !*signals || signals->nsamples() > 0 || !offset)
|
111
|
+
argument_error(__FILE__, __LINE__);
|
89
112
|
|
90
|
-
|
113
|
+
if (!*this || self->input)
|
114
|
+
invalid_state_error(__FILE__, __LINE__);
|
115
|
+
}
|
91
116
|
|
92
|
-
|
117
|
+
void
|
118
|
+
Processor::filter (Context* context, Signals* signals, uint* offset)
|
119
|
+
{
|
120
|
+
if (!context || !signals || !*signals || !offset)
|
121
|
+
argument_error(__FILE__, __LINE__);
|
93
122
|
|
94
|
-
|
123
|
+
if (!*this)
|
124
|
+
invalid_state_error(__FILE__, __LINE__);
|
95
125
|
|
126
|
+
if (self->input)
|
127
|
+
get_context(context)->process(self->input, signals, offset);
|
128
|
+
}
|
96
129
|
|
97
|
-
|
130
|
+
void
|
131
|
+
Processor::set_updated ()
|
98
132
|
{
|
99
|
-
|
133
|
+
self->last_update_time = Xot::time();
|
100
134
|
}
|
101
135
|
|
102
|
-
|
136
|
+
|
137
|
+
Generator::Generator ()
|
138
|
+
: Super(true)
|
103
139
|
{
|
104
140
|
}
|
105
141
|
|
106
142
|
void
|
107
|
-
|
143
|
+
Generator::filter (Context* context, Signals* signals, uint* offset)
|
108
144
|
{
|
109
|
-
|
110
|
-
self->oscillator.setFrequency(frequency);
|
145
|
+
beeps_error(__FILE__, __LINE__);
|
111
146
|
}
|
112
147
|
|
113
|
-
|
114
|
-
|
148
|
+
|
149
|
+
Filter::Filter (Processor* input)
|
150
|
+
: Super(false)
|
115
151
|
{
|
116
|
-
|
152
|
+
if (input) set_input(input);
|
117
153
|
}
|
118
154
|
|
119
155
|
void
|
120
|
-
|
156
|
+
Filter::set_buffering_seconds (float seconds)
|
121
157
|
{
|
122
|
-
Super::
|
158
|
+
Super::self->buffering_seconds = seconds;
|
159
|
+
|
160
|
+
set_updated();
|
161
|
+
}
|
123
162
|
|
124
|
-
|
163
|
+
void
|
164
|
+
Filter::generate (Context* context, Signals* signals, uint* offset)
|
165
|
+
{
|
166
|
+
beeps_error(__FILE__, __LINE__);
|
125
167
|
}
|
126
168
|
|
127
169
|
|
128
|
-
|
170
|
+
SignalsBuffer::SignalsBuffer (
|
171
|
+
uint nsamples_per_block, uint nchannels, double sample_rate)
|
129
172
|
{
|
173
|
+
buffer = Signals_create(nsamples_per_block, nchannels, sample_rate);
|
174
|
+
assert(*this);
|
175
|
+
}
|
130
176
|
|
131
|
-
|
177
|
+
void
|
178
|
+
SignalsBuffer::process (
|
179
|
+
ProcessorContext* context,
|
180
|
+
Processor* processor, Signals* signals, uint* offset)
|
181
|
+
{
|
182
|
+
assert(processor && context && signals && offset);
|
132
183
|
|
133
|
-
|
184
|
+
if (
|
185
|
+
last_update_time < processor->self->last_update_time ||
|
186
|
+
*offset < buffer_offset)
|
187
|
+
{
|
188
|
+
clear();
|
189
|
+
}
|
134
190
|
|
135
|
-
|
191
|
+
if (buffer.nsamples() == 0)
|
192
|
+
buffer_next(context, processor, *offset);
|
136
193
|
|
194
|
+
while (true)
|
195
|
+
{
|
196
|
+
*offset += Signals_copy(signals, buffer, *offset - buffer_offset);
|
137
197
|
|
138
|
-
|
139
|
-
|
140
|
-
|
198
|
+
bool signals_full = signals->nsamples() == signals->capacity();
|
199
|
+
bool buffer_full = buffer.nsamples() == buffer.capacity();
|
200
|
+
if (signals_full || !buffer_full)
|
201
|
+
break;
|
202
|
+
|
203
|
+
buffer_next(context, processor, buffer_offset + buffer.nsamples());
|
204
|
+
}
|
141
205
|
}
|
142
206
|
|
143
|
-
|
207
|
+
SignalsBuffer::operator bool () const
|
144
208
|
{
|
209
|
+
return buffer;
|
145
210
|
}
|
146
211
|
|
147
|
-
|
148
|
-
|
212
|
+
bool
|
213
|
+
SignalsBuffer::operator ! () const
|
149
214
|
{
|
150
|
-
|
151
|
-
self->oscillator.setFrequency(frequency);
|
215
|
+
return !operator bool();
|
152
216
|
}
|
153
217
|
|
154
|
-
|
155
|
-
|
218
|
+
void
|
219
|
+
SignalsBuffer::buffer_next (
|
220
|
+
ProcessorContext* context, Processor* processor, uint offset)
|
156
221
|
{
|
157
|
-
|
222
|
+
Signals_clear(&buffer);
|
223
|
+
buffer_offset = offset;
|
224
|
+
context->process(processor, &buffer, &offset, true);
|
225
|
+
|
226
|
+
last_update_time = Xot::time();
|
158
227
|
}
|
159
228
|
|
160
229
|
void
|
161
|
-
|
230
|
+
SignalsBuffer::clear ()
|
162
231
|
{
|
163
|
-
|
164
|
-
|
165
|
-
self->oscillator.tick(*Signals_get_frames(signals));
|
232
|
+
Signals_clear(&buffer);
|
233
|
+
buffer_offset = 0;
|
166
234
|
}
|
167
235
|
|
168
236
|
|
169
|
-
|
237
|
+
ProcessorContext::ProcessorContext (
|
238
|
+
uint nsamples_per_process, uint nchannels, double sample_rate)
|
239
|
+
: signals(Signals_create(nsamples_per_process, nchannels, sample_rate))
|
240
|
+
{
|
241
|
+
assert(*this);
|
242
|
+
}
|
243
|
+
|
244
|
+
Signals
|
245
|
+
ProcessorContext::process_signals (Processor* processor)
|
170
246
|
{
|
247
|
+
assert(result && processor);
|
171
248
|
|
172
|
-
|
249
|
+
Signals_clear(&signals);
|
250
|
+
process(processor, &signals, &offset);
|
173
251
|
|
174
|
-
|
252
|
+
if (signals.nsamples() < signals.capacity())
|
253
|
+
finished = true;
|
175
254
|
|
255
|
+
return signals.nsamples() > 0 ? signals : Signals();
|
256
|
+
}
|
176
257
|
|
177
|
-
|
258
|
+
void
|
259
|
+
ProcessorContext::process (
|
260
|
+
Processor* processor, Signals* signals, uint* offset, bool ignore_buffer)
|
178
261
|
{
|
179
|
-
|
180
|
-
|
262
|
+
assert(processor);
|
263
|
+
|
264
|
+
SignalsBuffer* buffer = NULL;
|
265
|
+
if (!ignore_buffer && (buffer = get_buffer(processor)))
|
266
|
+
buffer->process(this, processor, signals, offset);
|
267
|
+
else
|
268
|
+
processor->process(this, signals, offset);
|
181
269
|
}
|
182
270
|
|
183
|
-
|
271
|
+
bool
|
272
|
+
ProcessorContext::is_finished () const
|
184
273
|
{
|
274
|
+
return finished;
|
185
275
|
}
|
186
276
|
|
187
|
-
|
188
|
-
FileIn::process (Signals* signals)
|
277
|
+
ProcessorContext::operator bool () const
|
189
278
|
{
|
190
|
-
|
279
|
+
return signals && !finished;
|
280
|
+
}
|
191
281
|
|
192
|
-
|
282
|
+
bool
|
283
|
+
ProcessorContext::operator ! () const
|
284
|
+
{
|
285
|
+
return !operator bool();
|
286
|
+
}
|
287
|
+
|
288
|
+
static uintptr_t
|
289
|
+
get_buffer_key (Processor* processor)
|
290
|
+
{
|
291
|
+
assert(processor);
|
292
|
+
|
293
|
+
return (uintptr_t) processor->self.get();
|
193
294
|
}
|
194
295
|
|
195
|
-
|
296
|
+
SignalsBuffer*
|
297
|
+
ProcessorContext::get_buffer (Processor* processor)
|
196
298
|
{
|
197
|
-
|
299
|
+
float buffering_sec = Processor_get_buffering_seconds(processor);
|
300
|
+
if (buffering_sec <= 0) return NULL;
|
301
|
+
|
302
|
+
uintptr_t key = get_buffer_key(processor);
|
303
|
+
auto it = buffers.find(key);
|
304
|
+
if (it != buffers.end()) return it->second.get();
|
305
|
+
|
306
|
+
SignalsBuffer* buffer = new SignalsBuffer(
|
307
|
+
buffering_sec * signals.sample_rate(),
|
308
|
+
signals.nchannels(), signals.sample_rate());
|
309
|
+
|
310
|
+
buffers.emplace(key, buffer);
|
311
|
+
return buffer;
|
198
312
|
}
|
199
313
|
|
200
314
|
|
data/src/processor.h
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
// -*- c++ -*-
|
2
|
+
#pragma once
|
3
|
+
#ifndef __BEEPS_SRC_PROCESSOR_H__
|
4
|
+
#define __BEEPS_SRC_PROCESSOR_H__
|
5
|
+
|
6
|
+
|
7
|
+
#include <map>
|
8
|
+
#include <beeps/processor.h>
|
9
|
+
#include "signals.h"
|
10
|
+
|
11
|
+
|
12
|
+
namespace Beeps
|
13
|
+
{
|
14
|
+
|
15
|
+
|
16
|
+
class ProcessorContext;
|
17
|
+
|
18
|
+
|
19
|
+
float Processor_get_buffering_seconds (Processor* processor);
|
20
|
+
|
21
|
+
|
22
|
+
class SignalsBuffer
|
23
|
+
{
|
24
|
+
|
25
|
+
public:
|
26
|
+
|
27
|
+
SignalsBuffer (
|
28
|
+
uint nsamples_per_block, uint nchannels, double sample_rate);
|
29
|
+
|
30
|
+
void process (
|
31
|
+
ProcessorContext* context,
|
32
|
+
Processor* processor, Signals* signals, uint* offset);
|
33
|
+
|
34
|
+
operator bool () const;
|
35
|
+
|
36
|
+
bool operator ! () const;
|
37
|
+
|
38
|
+
private:
|
39
|
+
|
40
|
+
Signals buffer;
|
41
|
+
|
42
|
+
uint buffer_offset = 0;
|
43
|
+
|
44
|
+
double last_update_time = 0;
|
45
|
+
|
46
|
+
void buffer_next (
|
47
|
+
ProcessorContext* context, Processor* processor, uint offset);
|
48
|
+
|
49
|
+
void clear ();
|
50
|
+
|
51
|
+
};// SignalsBuffer
|
52
|
+
|
53
|
+
|
54
|
+
class ProcessorContext : public Processor::Context
|
55
|
+
{
|
56
|
+
|
57
|
+
public:
|
58
|
+
|
59
|
+
ProcessorContext (
|
60
|
+
uint nsamples_per_process, uint nchannels, double sample_rate);
|
61
|
+
|
62
|
+
Signals process_signals (Processor* processor);
|
63
|
+
|
64
|
+
void process (
|
65
|
+
Processor* processor, Signals* signals, uint* offset,
|
66
|
+
bool ignore_buffer = false);
|
67
|
+
|
68
|
+
//void push_offset (uint offset);
|
69
|
+
|
70
|
+
//uint pop_offset ();
|
71
|
+
|
72
|
+
bool is_finished () const;
|
73
|
+
|
74
|
+
operator bool () const;
|
75
|
+
|
76
|
+
bool operator ! () const;
|
77
|
+
|
78
|
+
private:
|
79
|
+
|
80
|
+
Signals signals;
|
81
|
+
|
82
|
+
uint offset = 0;
|
83
|
+
|
84
|
+
bool finished = false;
|
85
|
+
|
86
|
+
//std::vector<uint> offset_stack;
|
87
|
+
|
88
|
+
std::map<uintptr_t, std::unique_ptr<SignalsBuffer>> buffers;
|
89
|
+
|
90
|
+
SignalsBuffer* get_buffer (Processor* processor);
|
91
|
+
|
92
|
+
};// ProcessorContext
|
93
|
+
|
94
|
+
|
95
|
+
}// Beeps
|
96
|
+
|
97
|
+
|
98
|
+
#endif//EOH
|