beeps 0.2.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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