beeps 0.2.1 → 0.3.1

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/beeps/analyser.cpp +1 -1
  3. data/.doc/ext/beeps/{adsr.cpp → envelope.cpp} +20 -20
  4. data/.doc/ext/beeps/file_in.cpp +1 -1
  5. data/.doc/ext/beeps/gain.cpp +1 -1
  6. data/.doc/ext/beeps/mic_in.cpp +1 -1
  7. data/.doc/ext/beeps/native.cpp +4 -2
  8. data/.doc/ext/beeps/oscillator.cpp +1 -1
  9. data/.doc/ext/beeps/pitch_shift.cpp +1 -1
  10. data/.doc/ext/beeps/processor.cpp +1 -1
  11. data/.doc/ext/beeps/sequencer.cpp +87 -0
  12. data/.doc/ext/beeps/sound.cpp +1 -1
  13. data/.doc/ext/beeps/sound_player.cpp +1 -1
  14. data/.doc/ext/beeps/time_stretch.cpp +1 -1
  15. data/.github/workflows/release-gem.yml +1 -1
  16. data/.github/workflows/test.yml +3 -0
  17. data/ChangeLog.md +13 -0
  18. data/Gemfile.lock +1 -1
  19. data/LICENSE +21 -0
  20. data/Rakefile +3 -2
  21. data/VERSION +1 -1
  22. data/beeps.gemspec +2 -2
  23. data/ext/beeps/analyser.cpp +1 -1
  24. data/ext/beeps/defs.h +2 -0
  25. data/ext/beeps/{adsr.cpp → envelope.cpp} +20 -20
  26. data/ext/beeps/extconf.rb +11 -3
  27. data/ext/beeps/file_in.cpp +1 -1
  28. data/ext/beeps/gain.cpp +1 -1
  29. data/ext/beeps/mic_in.cpp +1 -1
  30. data/ext/beeps/native.cpp +4 -2
  31. data/ext/beeps/oscillator.cpp +1 -1
  32. data/ext/beeps/pitch_shift.cpp +1 -1
  33. data/ext/beeps/processor.cpp +1 -1
  34. data/ext/beeps/sequencer.cpp +92 -0
  35. data/ext/beeps/sound.cpp +1 -1
  36. data/ext/beeps/sound_player.cpp +1 -1
  37. data/ext/beeps/time_stretch.cpp +1 -1
  38. data/include/beeps/defs.h +7 -0
  39. data/include/beeps/filter.h +4 -4
  40. data/include/beeps/generator.h +31 -0
  41. data/include/beeps/ruby/beeps.h +1 -1
  42. data/include/beeps/ruby/exception.h +2 -2
  43. data/include/beeps/ruby/filter.h +16 -10
  44. data/include/beeps/ruby/generator.h +18 -5
  45. data/include/beeps/ruby/processor.h +2 -2
  46. data/include/beeps/ruby/sound.h +4 -4
  47. data/lib/beeps/extension.rb +4 -0
  48. data/lib/beeps/processor.rb +2 -2
  49. data/src/beeps.cpp +3 -1
  50. data/src/beeps.h +22 -0
  51. data/src/{adsr.cpp → envelope.cpp} +21 -20
  52. data/src/openal.cpp +3 -1
  53. data/src/oscillator.cpp +75 -44
  54. data/src/osx/beeps.mm +19 -0
  55. data/src/processor.cpp +59 -49
  56. data/src/processor.h +30 -14
  57. data/src/sequencer.cpp +160 -0
  58. data/src/signals.cpp +45 -29
  59. data/src/signals.h +4 -4
  60. data/src/sound.cpp +32 -29
  61. data/src/win32/beeps.cpp +36 -0
  62. data/src/win32/exception.cpp +40 -0
  63. data/src/win32/exception.h +40 -0
  64. data/src/win32/signals.cpp +186 -0
  65. metadata +21 -10
data/src/oscillator.cpp CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include <assert.h>
5
5
  #include "SineWave.h"
6
+ #include "Blit.h"
6
7
  #include "BlitSquare.h"
7
8
  #include "BlitSaw.h"
8
9
  #include "beeps/exception.h"
@@ -13,6 +14,70 @@ namespace Beeps
13
14
  {
14
15
 
15
16
 
17
+ class Osc
18
+ {
19
+
20
+ public:
21
+
22
+ virtual ~Osc () {}
23
+
24
+ virtual void reset () = 0;
25
+
26
+ virtual void tick (Frames* frames) = 0;
27
+
28
+ virtual void set_frequency (float freq) = 0;
29
+
30
+ };// Osc
31
+
32
+
33
+ template <typename OSC>
34
+ class StkOsc : public Osc
35
+ {
36
+
37
+ public:
38
+
39
+ void reset () override
40
+ {
41
+ osc.reset();
42
+ }
43
+
44
+ void tick (Frames* frames) override
45
+ {
46
+ osc.tick(*frames);
47
+ }
48
+
49
+ void set_frequency (float freq) override
50
+ {
51
+ osc.setFrequency(freq);
52
+ }
53
+
54
+ protected:
55
+
56
+ OSC osc;
57
+
58
+ };// StkOsc
59
+
60
+
61
+ typedef StkOsc<stk::SineWave> SineOsc;
62
+
63
+ typedef StkOsc<stk::BlitSquare> SquareOsc;
64
+
65
+ typedef StkOsc<stk::BlitSaw> SawtoothOsc;
66
+
67
+
68
+ class TriangleOsc : public StkOsc<stk::Blit>
69
+ {
70
+
71
+ public:
72
+
73
+ TriangleOsc ()
74
+ {
75
+ osc.setHarmonics(10);
76
+ }
77
+
78
+ };// TriangleOsc
79
+
80
+
16
81
  struct Oscillator::Data
17
82
  {
18
83
 
@@ -20,11 +85,7 @@ namespace Beeps
20
85
 
21
86
  float frequency = 440;
22
87
 
23
- std::unique_ptr<stk::SineWave> sine;
24
-
25
- std::unique_ptr<stk::BlitSquare> square;
26
-
27
- std::unique_ptr<stk::BlitSaw> saw;
88
+ std::unique_ptr<Osc> osc;
28
89
 
29
90
  };// Oscillator::Data
30
91
 
@@ -42,10 +103,7 @@ namespace Beeps
42
103
  Oscillator::reset ()
43
104
  {
44
105
  Super::reset();
45
-
46
- if (self->sine) self->sine->reset();
47
- if (self->square) self->square->reset();
48
- if (self->saw) self->saw->reset();
106
+ self->osc->reset();
49
107
  }
50
108
 
51
109
  void
@@ -54,16 +112,14 @@ namespace Beeps
54
112
  if (type == self->type) return;
55
113
 
56
114
  self->type = type;
57
-
58
- self->sine .reset();
59
- self->square.reset();
60
- self->saw .reset();
115
+ self->osc.reset();
61
116
 
62
117
  switch (self->type)
63
118
  {
64
- case SINE: self->sine .reset(new stk::SineWave()); break;
65
- case SQUARE: self->square.reset(new stk::BlitSquare()); break;
66
- case SAWTOOTH: self->saw .reset(new stk::BlitSaw()); break;
119
+ case SINE: self->osc.reset(new SineOsc()); break;
120
+ case TRIANGLE: self->osc.reset(new TriangleOsc()); break;
121
+ case SQUARE: self->osc.reset(new SquareOsc()); break;
122
+ case SAWTOOTH: self->osc.reset(new SawtoothOsc()); break;
67
123
  default:
68
124
  argument_error(
69
125
  __FILE__, __LINE__, "unknown oscilator type '%d'", self->type);
@@ -105,31 +161,8 @@ namespace Beeps
105
161
  if (!frames)
106
162
  argument_error(__FILE__, __LINE__);
107
163
 
108
- switch (self->type)
109
- {
110
- case SINE:
111
- assert(self->sine);
112
- self->sine->setFrequency(self->frequency);
113
- self->sine->tick(*frames);
114
- break;
115
-
116
- case SQUARE:
117
- assert(self->square);
118
- self->square->setFrequency(self->frequency);
119
- self->square->tick(*frames);
120
- break;
121
-
122
- case SAWTOOTH:
123
- assert(self->saw);
124
- self->saw->setFrequency(self->frequency);
125
- self->saw->tick(*frames);
126
- break;
127
-
128
- default:
129
- invalid_state_error(
130
- __FILE__, __LINE__, "unknown oscilator type '%d'", self->type);
131
- break;
132
- }
164
+ self->osc->set_frequency(self->frequency);
165
+ self->osc->tick(frames);
133
166
 
134
167
  Signals_set_nsamples(signals, frames->nframes());
135
168
  }
@@ -137,9 +170,7 @@ namespace Beeps
137
170
  Oscillator::operator bool () const
138
171
  {
139
172
  if (!Super::operator bool()) return false;
140
- return
141
- self->type != TYPE_NONE && self->frequency > 0 &&
142
- (self->sine || self->square || self->saw);
173
+ return self->type != TYPE_NONE && self->frequency > 0 && self->osc;
143
174
  }
144
175
 
145
176
 
data/src/osx/beeps.mm ADDED
@@ -0,0 +1,19 @@
1
+ #include "../beeps.h"
2
+
3
+
4
+ namespace Beeps
5
+ {
6
+
7
+
8
+ void
9
+ Beeps_init ()
10
+ {
11
+ }
12
+
13
+ void
14
+ Beeps_fin ()
15
+ {
16
+ }
17
+
18
+
19
+ }// Beeps
data/src/processor.cpp CHANGED
@@ -4,19 +4,13 @@
4
4
  #include <assert.h>
5
5
  #include <xot/time.h>
6
6
  #include "beeps/exception.h"
7
+ #include "beeps/debug.h"
7
8
 
8
9
 
9
10
  namespace Beeps
10
11
  {
11
12
 
12
13
 
13
- static ProcessorContext*
14
- get_context(Processor::Context* context)
15
- {
16
- return (ProcessorContext*) context;
17
- }
18
-
19
-
20
14
  struct Processor::Data
21
15
  {
22
16
 
@@ -36,6 +30,12 @@ namespace Beeps
36
30
  };// Processor::Data
37
31
 
38
32
 
33
+ ProcessorContext*
34
+ Processor_get_context(Processor::Context* context)
35
+ {
36
+ return (ProcessorContext*) context;
37
+ }
38
+
39
39
  float
40
40
  Processor_get_buffering_seconds (Processor* processor)
41
41
  {
@@ -108,24 +108,40 @@ namespace Beeps
108
108
  void
109
109
  Processor::generate (Context* context, Signals* signals, uint* offset)
110
110
  {
111
- if (!context || !signals || !*signals || signals->nsamples() > 0 || !offset)
111
+ if (!context)
112
+ argument_error(__FILE__, __LINE__);
113
+ if (!signals)
114
+ argument_error(__FILE__, __LINE__);
115
+ if (!*signals)
116
+ argument_error(__FILE__, __LINE__);
117
+ if (signals->nsamples() > 0)
118
+ argument_error(__FILE__, __LINE__);
119
+ if (!offset)
112
120
  argument_error(__FILE__, __LINE__);
113
121
 
114
- if (!*this || self->input)
122
+ if (!*this)
123
+ invalid_state_error(__FILE__, __LINE__);
124
+ if (self->input)
115
125
  invalid_state_error(__FILE__, __LINE__);
116
126
  }
117
127
 
118
128
  void
119
129
  Processor::filter (Context* context, Signals* signals, uint* offset)
120
130
  {
121
- if (!context || !signals || !*signals || !offset)
131
+ if (!context)
132
+ argument_error(__FILE__, __LINE__);
133
+ if (!signals)
134
+ argument_error(__FILE__, __LINE__);
135
+ if (!*signals)
136
+ argument_error(__FILE__, __LINE__);
137
+ if (!offset)
122
138
  argument_error(__FILE__, __LINE__);
123
139
 
124
140
  if (!*this)
125
141
  invalid_state_error(__FILE__, __LINE__);
126
142
 
127
143
  if (self->input)
128
- get_context(context)->process(self->input, signals, offset);
144
+ Processor_get_context(context)->process(self->input, signals, offset);
129
145
  }
130
146
 
131
147
  void
@@ -235,25 +251,9 @@ namespace Beeps
235
251
  }
236
252
 
237
253
 
238
- ProcessorContext::ProcessorContext (
239
- uint nsamples_per_process, uint nchannels, double sample_rate)
240
- : signals(Signals_create(nsamples_per_process, nchannels, sample_rate))
241
- {
242
- assert(*this);
243
- }
244
-
245
- Signals
246
- ProcessorContext::process_signals (Processor* processor)
254
+ ProcessorContext::ProcessorContext (uint nchannels, double sample_rate)
255
+ : sample_rate(sample_rate), nchannels(nchannels)
247
256
  {
248
- assert(processor);
249
-
250
- Signals_clear(&signals);
251
- process(processor, &signals, &offset);
252
-
253
- if (signals.nsamples() < signals.capacity())
254
- finished = true;
255
-
256
- return signals.nsamples() > 0 ? signals : Signals();
257
257
  }
258
258
 
259
259
  void
@@ -269,23 +269,6 @@ namespace Beeps
269
269
  processor->process(this, signals, offset);
270
270
  }
271
271
 
272
- bool
273
- ProcessorContext::is_finished () const
274
- {
275
- return finished;
276
- }
277
-
278
- ProcessorContext::operator bool () const
279
- {
280
- return signals && !finished;
281
- }
282
-
283
- bool
284
- ProcessorContext::operator ! () const
285
- {
286
- return !operator bool();
287
- }
288
-
289
272
  static uintptr_t
290
273
  get_buffer_key (Processor* processor)
291
274
  {
@@ -304,13 +287,40 @@ namespace Beeps
304
287
  auto it = buffers.find(key);
305
288
  if (it != buffers.end()) return it->second.get();
306
289
 
307
- SignalsBuffer* buffer = new SignalsBuffer(
308
- buffering_sec * signals.sample_rate(),
309
- signals.nchannels(), signals.sample_rate());
290
+ SignalsBuffer* buffer =
291
+ new SignalsBuffer(buffering_sec * sample_rate, nchannels, sample_rate);
310
292
 
311
293
  buffers.emplace(key, buffer);
312
294
  return buffer;
313
295
  }
314
296
 
315
297
 
298
+ StreamContext::StreamContext (
299
+ uint nsamples_per_process, uint nchannels, double sample_rate)
300
+ : context(nchannels, sample_rate),
301
+ signals(Signals_create(nsamples_per_process, nchannels, sample_rate))
302
+ {
303
+ }
304
+
305
+ Signals
306
+ StreamContext::process_next (Processor* processor)
307
+ {
308
+ assert(processor);
309
+
310
+ Signals_clear(&signals);
311
+ context.process(processor, &signals, &offset);
312
+
313
+ if (signals.nsamples() < signals.capacity())
314
+ finished = true;
315
+
316
+ return signals;
317
+ }
318
+
319
+ bool
320
+ StreamContext::is_finished () const
321
+ {
322
+ return finished;
323
+ }
324
+
325
+
316
326
  }// Beeps
data/src/processor.h CHANGED
@@ -16,6 +16,8 @@ namespace Beeps
16
16
  class ProcessorContext;
17
17
 
18
18
 
19
+ ProcessorContext* Processor_get_context (Processor::Context* context);
20
+
19
21
  float Processor_get_buffering_seconds (Processor* processor);
20
22
 
21
23
 
@@ -56,40 +58,54 @@ namespace Beeps
56
58
 
57
59
  public:
58
60
 
59
- ProcessorContext (
60
- uint nsamples_per_process, uint nchannels, double sample_rate);
61
-
62
- Signals process_signals (Processor* processor);
61
+ ProcessorContext (uint nchannels, double sample_rate);
63
62
 
64
63
  void process (
65
64
  Processor* processor, Signals* signals, uint* offset,
66
65
  bool ignore_buffer = false);
67
66
 
67
+ private:
68
+
69
+ double sample_rate;
70
+
71
+ uint nchannels;
72
+
73
+ std::map<uintptr_t, std::unique_ptr<SignalsBuffer>> buffers;
74
+
75
+ SignalsBuffer* get_buffer (Processor* processor);
76
+
77
+ };// ProcessorContext
78
+
79
+
80
+ class StreamContext
81
+ {
82
+
83
+ public:
84
+
85
+ StreamContext (
86
+ uint nsamples_per_process, uint nchannels, double sample_rate);
87
+
88
+ Signals process_next (Processor* processor);
89
+
68
90
  //void push_offset (uint offset);
69
91
 
70
92
  //uint pop_offset ();
71
93
 
72
94
  bool is_finished () const;
73
95
 
74
- operator bool () const;
75
-
76
- bool operator ! () const;
77
-
78
96
  private:
79
97
 
98
+ ProcessorContext context;
99
+
80
100
  Signals signals;
81
101
 
82
- uint offset = 0;
102
+ uint offset = 0;
83
103
 
84
104
  bool finished = false;
85
105
 
86
106
  //std::vector<uint> offset_stack;
87
107
 
88
- std::map<uintptr_t, std::unique_ptr<SignalsBuffer>> buffers;
89
-
90
- SignalsBuffer* get_buffer (Processor* processor);
91
-
92
- };// ProcessorContext
108
+ };// StreamContext
93
109
 
94
110
 
95
111
  }// Beeps
data/src/sequencer.cpp ADDED
@@ -0,0 +1,160 @@
1
+ #include "beeps/generator.h"
2
+
3
+
4
+ #include <list>
5
+ #include <algorithm>
6
+ #include "beeps/exception.h"
7
+ #include "beeps/debug.h"
8
+ #include "processor.h"
9
+ #include "signals.h"
10
+
11
+
12
+ namespace Beeps
13
+ {
14
+
15
+
16
+ struct Sequencer::Data
17
+ {
18
+
19
+ struct Note
20
+ {
21
+
22
+ Processor::Ref processor;
23
+
24
+ float offset, duration;
25
+
26
+ Note (Processor* processor, float offset, float duration)
27
+ : processor(processor), offset(offset), duration(duration)
28
+ {
29
+ }
30
+
31
+ };// Note
32
+
33
+ std::list<Note> notes;
34
+
35
+ float time_scale = 1;
36
+
37
+ uint sec2nsample (const Signals& signals, float sec) const
38
+ {
39
+ return sec * signals.sample_rate() / time_scale;
40
+ }
41
+
42
+ };// Sequencer::Data
43
+
44
+
45
+ Sequencer::Sequencer ()
46
+ {
47
+ }
48
+
49
+ Sequencer::~Sequencer ()
50
+ {
51
+ }
52
+
53
+ void
54
+ Sequencer::add (Processor* processor, float offset, float duration)
55
+ {
56
+ auto& notes = self->notes;
57
+ for (auto it = notes.begin(), end = notes.end(); it != end; ++it)
58
+ {
59
+ if (offset < it->offset)
60
+ {
61
+ notes.emplace(it, processor, offset, duration);
62
+ return;
63
+ }
64
+ }
65
+ notes.emplace_back(processor, offset, duration);
66
+ }
67
+
68
+ void
69
+ Sequencer::remove (Processor* processor, float offset)
70
+ {
71
+ auto it =
72
+ std::find_if(self->notes.begin(), self->notes.end(), [&](auto& note)
73
+ {
74
+ return note.processor == processor && note.offset == offset;
75
+ });
76
+ if (it == self->notes.end()) return;
77
+
78
+ self->notes.erase(it);
79
+ }
80
+
81
+ void
82
+ Sequencer::set_time_scale (float time_scale)
83
+ {
84
+ if (time_scale <= 0)
85
+ argument_error(__FILE__, __LINE__, "time_scale must be greater than 0");
86
+
87
+ self->time_scale = time_scale;
88
+ }
89
+
90
+ float
91
+ Sequencer::time_scale () const
92
+ {
93
+ return self->time_scale;
94
+ }
95
+
96
+ static void
97
+ mix (Signals* mixed, const Signals& source, uint source_offset)
98
+ {
99
+ Frames* mixf = Signals_get_frames(mixed);
100
+ Frames* srcf = Signals_get_frames(const_cast<Signals*>(&source));
101
+ Float* mixp = &(*mixf)(source_offset, 0);
102
+ Float* srcp = &(*srcf)(0, 0);
103
+ uint size = source.nsamples() * source.nchannels();
104
+ for (uint i = 0; i < size; ++i, ++mixp, ++srcp)
105
+ *mixp += *srcp;
106
+ }
107
+
108
+ void
109
+ Sequencer::generate (Context* pcontext, Signals* psignals, uint* offset)
110
+ {
111
+ Super::generate(pcontext, psignals, offset);
112
+
113
+ auto& context = *Processor_get_context(pcontext);
114
+ auto& signals = *psignals;
115
+ Signals_resize(&signals, signals.capacity(), 0);
116
+
117
+ uint generate_begin = *offset;
118
+ uint generate_end = *offset + signals.capacity();
119
+ Signals note_signals = Signals_create(
120
+ signals.capacity(), signals.nchannels(), signals.sample_rate());
121
+
122
+ uint nsamples = 0;
123
+ for (auto& note : self->notes)
124
+ {
125
+ uint note_begin = self->sec2nsample(signals, note.offset);
126
+ uint note_end = self->sec2nsample(signals, note.offset + note.duration);
127
+ if (note_end <= generate_begin) continue;
128
+ if (generate_end <= note_begin)
129
+ {
130
+ nsamples = generate_end - generate_begin;
131
+ break;
132
+ }
133
+
134
+ uint begin = std::max(generate_begin, note_begin);
135
+ uint end = std::min(generate_end, note_end);
136
+ assert(begin != end);
137
+
138
+ uint note_offset = begin - note_begin;
139
+ Signals_clear(&note_signals, end - begin);
140
+ context.process(note.processor.get(), &note_signals, &note_offset);
141
+
142
+ uint mix_offset = begin - generate_begin;
143
+ mix(&signals, note_signals, mix_offset);
144
+
145
+ if (mix_offset + note_signals.nsamples() > nsamples)
146
+ nsamples = mix_offset + note_signals.nsamples();
147
+ }
148
+
149
+ Signals_set_nsamples(&signals, nsamples);
150
+ *offset += nsamples;
151
+ }
152
+
153
+ Sequencer::operator bool () const
154
+ {
155
+ if (!Super::operator bool()) return false;
156
+ return self->time_scale > 0;
157
+ }
158
+
159
+
160
+ }// Beeps