beeps 0.1.32 → 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 +4 -1
- data/ChangeLog.md +8 -0
- data/Rakefile +26 -4
- 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 +54 -16
- 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/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/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
|