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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/beeps/adsr.cpp +139 -0
  3. data/.doc/ext/beeps/analyser.cpp +128 -0
  4. data/.doc/ext/beeps/beeps.cpp +9 -1
  5. data/.doc/ext/beeps/file_in.cpp +55 -9
  6. data/.doc/ext/beeps/gain.cpp +64 -0
  7. data/.doc/ext/beeps/mic_in.cpp +83 -0
  8. data/.doc/ext/beeps/native.cpp +20 -8
  9. data/.doc/ext/beeps/oscillator.cpp +88 -0
  10. data/.doc/ext/beeps/pitch_shift.cpp +64 -0
  11. data/.doc/ext/beeps/processor.cpp +31 -2
  12. data/.doc/ext/beeps/sound.cpp +90 -7
  13. data/.doc/ext/beeps/sound_player.cpp +156 -0
  14. data/.doc/ext/beeps/time_stretch.cpp +64 -0
  15. data/.github/workflows/release-gem.yml +62 -0
  16. data/.github/workflows/test.yml +0 -6
  17. data/.github/workflows/utils.rb +13 -5
  18. data/ChangeLog.md +13 -0
  19. data/Rakefile +27 -6
  20. data/VERSION +1 -1
  21. data/beeps.gemspec +2 -2
  22. data/ext/beeps/adsr.cpp +150 -0
  23. data/ext/beeps/analyser.cpp +134 -0
  24. data/ext/beeps/beeps.cpp +10 -1
  25. data/ext/beeps/extconf.rb +1 -2
  26. data/ext/beeps/file_in.cpp +60 -9
  27. data/ext/beeps/gain.cpp +67 -0
  28. data/ext/beeps/mic_in.cpp +88 -0
  29. data/ext/beeps/native.cpp +20 -8
  30. data/ext/beeps/oscillator.cpp +93 -0
  31. data/ext/beeps/pitch_shift.cpp +67 -0
  32. data/ext/beeps/processor.cpp +34 -2
  33. data/ext/beeps/sound.cpp +99 -7
  34. data/ext/beeps/sound_player.cpp +169 -0
  35. data/ext/beeps/time_stretch.cpp +67 -0
  36. data/include/beeps/beeps.h +2 -1
  37. data/include/beeps/filter.h +179 -0
  38. data/include/beeps/generator.h +120 -0
  39. data/include/beeps/processor.h +37 -68
  40. data/include/beeps/ruby/filter.h +78 -0
  41. data/include/beeps/ruby/generator.h +60 -0
  42. data/include/beeps/ruby/processor.h +5 -45
  43. data/include/beeps/ruby/sound.h +14 -3
  44. data/include/beeps/signals.h +10 -4
  45. data/include/beeps/sound.h +67 -2
  46. data/lib/beeps/beeps.rb +6 -1
  47. data/lib/beeps/processor.rb +95 -15
  48. data/lib/beeps/sound.rb +29 -2
  49. data/src/adsr.cpp +245 -0
  50. data/src/analyser.cpp +254 -0
  51. data/src/beeps.cpp +11 -2
  52. data/src/file_in.cpp +94 -0
  53. data/src/gain.cpp +55 -0
  54. data/src/mic_in.cpp +262 -0
  55. data/src/mic_in.h +20 -0
  56. data/src/openal.cpp +2 -1
  57. data/src/oscillator.cpp +145 -0
  58. data/src/osx/signals.mm +83 -0
  59. data/src/pitch_shift.cpp +82 -0
  60. data/src/processor.cpp +202 -88
  61. data/src/processor.h +98 -0
  62. data/src/signals.cpp +326 -20
  63. data/src/signals.h +192 -2
  64. data/src/sound.cpp +735 -113
  65. data/src/sound.h +6 -1
  66. data/src/time_stretch.cpp +91 -0
  67. data/test/helper.rb +2 -1
  68. data/test/test_beeps.rb +10 -7
  69. data/test/test_beeps_init.rb +18 -0
  70. data/test/test_file_in.rb +15 -0
  71. data/test/test_processor.rb +50 -0
  72. data/test/test_sound.rb +87 -11
  73. data/test/test_sound_player.rb +134 -0
  74. metadata +55 -17
  75. data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
  76. data/.doc/ext/beeps/sine_wave.cpp +0 -61
  77. data/.doc/ext/beeps/square_wave.cpp +0 -61
  78. data/.github/workflows/release.yml +0 -34
  79. data/ext/beeps/sawtooth_wave.cpp +0 -64
  80. data/ext/beeps/sine_wave.cpp +0 -64
  81. data/ext/beeps/square_wave.cpp +0 -64
@@ -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 "beeps/processor.h"
1
+ #include "processor.h"
2
2
 
3
3
 
4
- #include "SineWave.h"
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
- Processor::Processor ()
12
+ static ProcessorContext*
13
+ get_context(Processor::Context* context)
17
14
  {
15
+ return (ProcessorContext*) context;
18
16
  }
19
17
 
20
- Processor::~Processor ()
18
+
19
+ struct Processor::Data
21
20
  {
22
- }
23
21
 
24
- void
25
- Processor::process (Signals* signals)
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
- if (!signals || !*signals)
28
- argument_error(__FILE__, __LINE__);
41
+ assert(processor);
29
42
 
30
- if (!*this)
31
- invalid_state_error(__FILE__, __LINE__);
43
+ return processor->self->buffering_seconds;
32
44
  }
33
45
 
34
- Processor::operator bool () const
46
+
47
+ Processor::Processor (bool generator)
35
48
  {
36
- return true;
49
+ self->generator = generator;
37
50
  }
38
51
 
39
- bool
40
- Processor::operator ! () const
52
+ Processor::~Processor ()
41
53
  {
42
- return !operator bool();
43
54
  }
44
55
 
45
-
46
- struct SineWave::Data
56
+ void
57
+ Processor::reset ()
47
58
  {
59
+ if (self->input) self->input->reset();
48
60
 
49
- stk::SineWave oscillator;
61
+ set_updated();
62
+ }
50
63
 
51
- float frequency;
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
- };// SineWave::Data
70
+ self->input = input;
54
71
 
72
+ set_updated();
73
+ }
55
74
 
56
- SineWave::SineWave ()
75
+ Processor*
76
+ Processor::input ()
57
77
  {
58
- set_frequency(440);
78
+ return self->input;
59
79
  }
60
80
 
61
- SineWave::~SineWave ()
81
+ const Processor*
82
+ Processor::input () const
62
83
  {
84
+ return const_cast<Processor*>(this)->input();
63
85
  }
64
86
 
65
- void
66
- SineWave::set_frequency (float frequency)
87
+ Processor::operator bool () const
67
88
  {
68
- self->frequency = frequency;
69
- self->oscillator.setFrequency(frequency);
89
+ return self->has_generator();
70
90
  }
71
91
 
72
- float
73
- SineWave::frequency () const
92
+ bool
93
+ Processor::operator ! () const
74
94
  {
75
- return self->frequency;
95
+ return !operator bool();
76
96
  }
77
97
 
78
98
  void
79
- SineWave::process (Signals* signals)
99
+ Processor::process (Context* context, Signals* signals, uint* offset)
80
100
  {
81
- Super::process(signals);
82
-
83
- self->oscillator.tick(*Signals_get_frames(signals));
101
+ if (self->generator)
102
+ generate(context, signals, offset);
103
+ else
104
+ filter(context, signals, offset);
84
105
  }
85
106
 
86
-
87
- struct SquareWave::Data
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
- stk::BlitSquare oscillator;
113
+ if (!*this || self->input)
114
+ invalid_state_error(__FILE__, __LINE__);
115
+ }
91
116
 
92
- float frequency;
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
- };// SquareWave::Data
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
- SquareWave::SquareWave ()
130
+ void
131
+ Processor::set_updated ()
98
132
  {
99
- set_frequency(440);
133
+ self->last_update_time = Xot::time();
100
134
  }
101
135
 
102
- SquareWave::~SquareWave ()
136
+
137
+ Generator::Generator ()
138
+ : Super(true)
103
139
  {
104
140
  }
105
141
 
106
142
  void
107
- SquareWave::set_frequency (float frequency)
143
+ Generator::filter (Context* context, Signals* signals, uint* offset)
108
144
  {
109
- self->frequency = frequency;
110
- self->oscillator.setFrequency(frequency);
145
+ beeps_error(__FILE__, __LINE__);
111
146
  }
112
147
 
113
- float
114
- SquareWave::frequency () const
148
+
149
+ Filter::Filter (Processor* input)
150
+ : Super(false)
115
151
  {
116
- return self->frequency;
152
+ if (input) set_input(input);
117
153
  }
118
154
 
119
155
  void
120
- SquareWave::process (Signals* signals)
156
+ Filter::set_buffering_seconds (float seconds)
121
157
  {
122
- Super::process(signals);
158
+ Super::self->buffering_seconds = seconds;
159
+
160
+ set_updated();
161
+ }
123
162
 
124
- self->oscillator.tick(*Signals_get_frames(signals));
163
+ void
164
+ Filter::generate (Context* context, Signals* signals, uint* offset)
165
+ {
166
+ beeps_error(__FILE__, __LINE__);
125
167
  }
126
168
 
127
169
 
128
- struct SawtoothWave::Data
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
- stk::BlitSaw oscillator;
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
- float frequency;
184
+ if (
185
+ last_update_time < processor->self->last_update_time ||
186
+ *offset < buffer_offset)
187
+ {
188
+ clear();
189
+ }
134
190
 
135
- };// SawtoothWave::Data
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
- SawtoothWave::SawtoothWave ()
139
- {
140
- set_frequency(440);
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
- SawtoothWave::~SawtoothWave ()
207
+ SignalsBuffer::operator bool () const
144
208
  {
209
+ return buffer;
145
210
  }
146
211
 
147
- void
148
- SawtoothWave::set_frequency (float frequency)
212
+ bool
213
+ SignalsBuffer::operator ! () const
149
214
  {
150
- self->frequency = frequency;
151
- self->oscillator.setFrequency(frequency);
215
+ return !operator bool();
152
216
  }
153
217
 
154
- float
155
- SawtoothWave::frequency () const
218
+ void
219
+ SignalsBuffer::buffer_next (
220
+ ProcessorContext* context, Processor* processor, uint offset)
156
221
  {
157
- return self->frequency;
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
- SawtoothWave::process (Signals* signals)
230
+ SignalsBuffer::clear ()
162
231
  {
163
- Super::process(signals);
164
-
165
- self->oscillator.tick(*Signals_get_frames(signals));
232
+ Signals_clear(&buffer);
233
+ buffer_offset = 0;
166
234
  }
167
235
 
168
236
 
169
- struct FileIn::Data
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
- stk::FileWvIn input;
249
+ Signals_clear(&signals);
250
+ process(processor, &signals, &offset);
173
251
 
174
- };// FileIn::Data
252
+ if (signals.nsamples() < signals.capacity())
253
+ finished = true;
175
254
 
255
+ return signals.nsamples() > 0 ? signals : Signals();
256
+ }
176
257
 
177
- FileIn::FileIn (const char* path)
258
+ void
259
+ ProcessorContext::process (
260
+ Processor* processor, Signals* signals, uint* offset, bool ignore_buffer)
178
261
  {
179
- if (path)
180
- self->input.openFile(path);
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
- FileIn::~FileIn ()
271
+ bool
272
+ ProcessorContext::is_finished () const
184
273
  {
274
+ return finished;
185
275
  }
186
276
 
187
- void
188
- FileIn::process (Signals* signals)
277
+ ProcessorContext::operator bool () const
189
278
  {
190
- Super::process(signals);
279
+ return signals && !finished;
280
+ }
191
281
 
192
- self->input.tick(*Signals_get_frames(signals));
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
- FileIn::operator bool () const
296
+ SignalsBuffer*
297
+ ProcessorContext::get_buffer (Processor* processor)
196
298
  {
197
- return self->input.isOpen();
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