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.
Files changed (78) 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 +4 -1
  16. data/ChangeLog.md +8 -0
  17. data/Rakefile +26 -4
  18. data/VERSION +1 -1
  19. data/beeps.gemspec +2 -2
  20. data/ext/beeps/adsr.cpp +150 -0
  21. data/ext/beeps/analyser.cpp +134 -0
  22. data/ext/beeps/beeps.cpp +10 -1
  23. data/ext/beeps/extconf.rb +1 -2
  24. data/ext/beeps/file_in.cpp +60 -9
  25. data/ext/beeps/gain.cpp +67 -0
  26. data/ext/beeps/mic_in.cpp +88 -0
  27. data/ext/beeps/native.cpp +20 -8
  28. data/ext/beeps/oscillator.cpp +93 -0
  29. data/ext/beeps/pitch_shift.cpp +67 -0
  30. data/ext/beeps/processor.cpp +34 -2
  31. data/ext/beeps/sound.cpp +99 -7
  32. data/ext/beeps/sound_player.cpp +169 -0
  33. data/ext/beeps/time_stretch.cpp +67 -0
  34. data/include/beeps/beeps.h +2 -1
  35. data/include/beeps/filter.h +179 -0
  36. data/include/beeps/generator.h +120 -0
  37. data/include/beeps/processor.h +37 -68
  38. data/include/beeps/ruby/filter.h +78 -0
  39. data/include/beeps/ruby/generator.h +60 -0
  40. data/include/beeps/ruby/processor.h +5 -45
  41. data/include/beeps/ruby/sound.h +14 -3
  42. data/include/beeps/signals.h +10 -4
  43. data/include/beeps/sound.h +67 -2
  44. data/lib/beeps/beeps.rb +6 -1
  45. data/lib/beeps/processor.rb +95 -15
  46. data/lib/beeps/sound.rb +29 -2
  47. data/src/adsr.cpp +245 -0
  48. data/src/analyser.cpp +254 -0
  49. data/src/beeps.cpp +11 -2
  50. data/src/file_in.cpp +94 -0
  51. data/src/gain.cpp +55 -0
  52. data/src/mic_in.cpp +262 -0
  53. data/src/mic_in.h +20 -0
  54. data/src/openal.cpp +2 -1
  55. data/src/oscillator.cpp +145 -0
  56. data/src/osx/signals.mm +83 -0
  57. data/src/pitch_shift.cpp +82 -0
  58. data/src/processor.cpp +202 -88
  59. data/src/processor.h +98 -0
  60. data/src/signals.cpp +326 -20
  61. data/src/signals.h +192 -2
  62. data/src/sound.cpp +735 -113
  63. data/src/sound.h +6 -1
  64. data/src/time_stretch.cpp +91 -0
  65. data/test/helper.rb +2 -1
  66. data/test/test_beeps.rb +10 -7
  67. data/test/test_beeps_init.rb +18 -0
  68. data/test/test_file_in.rb +15 -0
  69. data/test/test_processor.rb +50 -0
  70. data/test/test_sound.rb +87 -11
  71. data/test/test_sound_player.rb +134 -0
  72. metadata +54 -16
  73. data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
  74. data/.doc/ext/beeps/sine_wave.cpp +0 -61
  75. data/.doc/ext/beeps/square_wave.cpp +0 -61
  76. data/ext/beeps/sawtooth_wave.cpp +0 -64
  77. data/ext/beeps/sine_wave.cpp +0 -64
  78. data/ext/beeps/square_wave.cpp +0 -64
data/lib/beeps/sound.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
 
4
+ require 'xot/setter'
5
+ require 'xot/universal_accessor'
6
+ require 'xot/block_util'
4
7
  require 'beeps/ext'
5
8
 
6
9
 
@@ -9,11 +12,35 @@ module Beeps
9
12
 
10
13
  class Sound
11
14
 
12
- def self.load(path)
13
- Sound.new FileIn.new(path), 1
15
+ include Xot::Setter
16
+
17
+ def initialize(
18
+ processor, seconds = 0, nchannels: 1, sample_rate: 0, **options, &block)
19
+
20
+ setup processor, seconds, nchannels, sample_rate
21
+ set(**options) unless options.empty?
22
+ Xot::BlockUtil.instance_eval_or_block_call self, &block if block
14
23
  end
15
24
 
25
+ def play(**options, &block)
26
+ play!.tap do |player|
27
+ player.set(**options) unless options.empty?
28
+ Xot::BlockUtil.instance_eval_or_block_call player, &block if block
29
+ end
30
+ end
31
+
32
+ universal_accessor :gain, :loop
33
+
16
34
  end# Sound
17
35
 
18
36
 
37
+ class SoundPlayer
38
+
39
+ include Xot::Setter
40
+
41
+ universal_accessor :gain, :loop
42
+
43
+ end# SoundPlayer
44
+
45
+
19
46
  end# Beeps
data/src/adsr.cpp ADDED
@@ -0,0 +1,245 @@
1
+ #include "beeps/filter.h"
2
+
3
+
4
+ #include <ADSR.h>
5
+ #include "signals.h"
6
+
7
+
8
+ namespace Beeps
9
+ {
10
+
11
+
12
+ struct ADSR::Data
13
+ {
14
+
15
+ stk::ADSR adsr;
16
+
17
+ Signals adsr_signals;
18
+
19
+ float attack_time = 0, decay_time = 0, sustain_level = 1, release_time = 0;
20
+
21
+ float time = 0, note_on_time = -1, note_off_time = -1;
22
+
23
+ void update_envelope ()
24
+ {
25
+ adsr.setAttackTime( attack_time == 0 ? 0.01 : attack_time);
26
+ adsr.setSustainLevel(sustain_level);
27
+ adsr.setDecayTime( decay_time == 0 ? 0.01 : decay_time);
28
+ adsr.setReleaseTime( release_time == 0 ? 0.01 : release_time);
29
+ }
30
+
31
+ };// ADSR::Data
32
+
33
+
34
+ ADSR::ADSR (Processor* input)
35
+ : Super(input)
36
+ {
37
+ self->update_envelope();
38
+ }
39
+
40
+ ADSR::~ADSR ()
41
+ {
42
+ }
43
+
44
+ void
45
+ ADSR::note_on (float delay)
46
+ {
47
+ if (delay < 0)
48
+ argument_error(__FILE__, __LINE__);
49
+
50
+ float on = self->time + delay, off = self->note_off_time;
51
+ if (0 <= off && off < on)
52
+ self->note_off_time = -1;
53
+
54
+ self->note_on_time = on;
55
+
56
+ set_updated();
57
+ }
58
+
59
+ void
60
+ ADSR::note_off (float delay)
61
+ {
62
+ if (delay < 0)
63
+ argument_error(__FILE__, __LINE__);
64
+
65
+ float off = self->time + delay, on = self->note_on_time;
66
+ if (0 <= on && off < on)
67
+ argument_error(__FILE__, __LINE__);
68
+
69
+ self->note_off_time = off;
70
+
71
+ set_updated();
72
+ }
73
+
74
+ void
75
+ ADSR::set_attack_time (float time)
76
+ {
77
+ if (time < 0)
78
+ argument_error(__FILE__, __LINE__);
79
+
80
+ self->attack_time = time;
81
+ self->update_envelope();
82
+
83
+ set_updated();
84
+ }
85
+
86
+ float
87
+ ADSR::attack_time () const
88
+ {
89
+ return self->attack_time;
90
+ }
91
+
92
+ void
93
+ ADSR::set_decay_time (float time)
94
+ {
95
+ if (time < 0)
96
+ argument_error(__FILE__, __LINE__);
97
+
98
+ self->decay_time = time;
99
+ self->update_envelope();
100
+
101
+ set_updated();
102
+ }
103
+
104
+ float
105
+ ADSR::decay_time () const
106
+ {
107
+ return self->decay_time;
108
+ }
109
+
110
+ void
111
+ ADSR::set_sustain_level (float level)
112
+ {
113
+ if (level < 0)
114
+ argument_error(__FILE__, __LINE__);
115
+
116
+ self->sustain_level = level;
117
+ self->update_envelope();
118
+
119
+ set_updated();
120
+ }
121
+
122
+ float
123
+ ADSR::sustain_level () const
124
+ {
125
+ return self->sustain_level;
126
+ }
127
+
128
+ void
129
+ ADSR::set_release_time (float time)
130
+ {
131
+ if (time < 0)
132
+ argument_error(__FILE__, __LINE__);
133
+
134
+ self->release_time = time;
135
+ self->update_envelope();
136
+
137
+ set_updated();
138
+ }
139
+
140
+ float
141
+ ADSR::release_time () const
142
+ {
143
+ return self->release_time;
144
+ }
145
+
146
+ static size_t
147
+ slice (Frames* frames, size_t start, float length_sec = -1)
148
+ {
149
+ Float sample_rate = frames->dataRate();
150
+ size_t len = length_sec >= 0
151
+ ? length_sec * sample_rate
152
+ : frames->nframes() - start;
153
+ assert(0 < len && (start + len) < frames->nsamples());
154
+
155
+ return frames->slice(start, len);
156
+ }
157
+
158
+ static void
159
+ process_envelope_signals (ADSR* adsr, Signals* signals)
160
+ {
161
+ assert(adsr && signals && signals->nchannels() == 1);
162
+
163
+ ADSR::Data* self = adsr->self.get();
164
+
165
+ Frames* frames = Signals_get_frames(signals);
166
+ assert(frames);
167
+
168
+ float start = self->time;
169
+ float end = start + Signals_get_seconds(*signals);
170
+ self->time = end;
171
+
172
+ float on = self->note_on_time;
173
+ float off = self->note_off_time;
174
+ assert(on <= off);
175
+
176
+ bool has_on = 0 <= on && start <= on && on < end;
177
+ bool has_off = 0 <= off && start <= off && off < end;
178
+
179
+ if (!has_on && !has_off)
180
+ {
181
+ self->adsr.tick(*frames);
182
+ return;
183
+ }
184
+
185
+ size_t last = 0;
186
+ if (has_on)
187
+ {
188
+ if (start < on)
189
+ {
190
+ last = slice(frames, 0, on - start);
191
+ self->adsr.tick(*frames);
192
+ frames->unslice();
193
+ }
194
+ self->adsr.keyOn();
195
+ }
196
+ if (has_on || has_off)
197
+ {
198
+ float len = has_off ? off - (has_on ? on : start) : -1;
199
+ last = slice(frames, last, len);
200
+ self->adsr.tick(*frames);
201
+ frames->unslice();
202
+ }
203
+ if (has_off)
204
+ {
205
+ self->adsr.keyOff();
206
+ if (off < end)
207
+ {
208
+ slice(frames, last, -1);
209
+ self->adsr.tick(*frames);
210
+ frames->unslice();
211
+ }
212
+ }
213
+ }
214
+
215
+ void
216
+ ADSR::filter (Context* context, Signals* signals, uint* offset)
217
+ {
218
+ Super::filter(context, signals, offset);
219
+
220
+ if (!self->adsr_signals)
221
+ {
222
+ self->adsr_signals =
223
+ Signals_create(signals->nsamples(), 1, signals->sample_rate());
224
+ }
225
+
226
+ if (self->adsr_signals.nsamples() != signals->nsamples())
227
+ Signals_resize(&self->adsr_signals, signals->nsamples(), 0);
228
+
229
+ process_envelope_signals(this, &self->adsr_signals);
230
+ Signals_apply(signals, self->adsr_signals);
231
+ }
232
+
233
+ ADSR::operator bool () const
234
+ {
235
+ if (!Super::operator bool()) return false;
236
+ return
237
+ self->attack_time >= 0 &&
238
+ self->decay_time >= 0 &&
239
+ self->sustain_level >= 0 &&
240
+ self->release_time >= 0 &&
241
+ self->time >= 0;
242
+ }
243
+
244
+
245
+ }// Beeps
data/src/analyser.cpp ADDED
@@ -0,0 +1,254 @@
1
+ #include "beeps/filter.h"
2
+
3
+
4
+ #include <math.h>
5
+ #include <algorithm>
6
+ #include "pffft.h"
7
+ #include "beeps/beeps.h"
8
+ #include "beeps/exception.h"
9
+ #include "signals.h"
10
+
11
+
12
+ namespace Beeps
13
+ {
14
+
15
+
16
+ struct PFFFTDestroySetup
17
+ {
18
+ void operator () (PFFFT_Setup* ptr)
19
+ {
20
+ pffft_destroy_setup(ptr);
21
+ }
22
+ };
23
+
24
+ struct PFFFTAlignedFree
25
+ {
26
+ void operator () (float* ptr)
27
+ {
28
+ pffft_aligned_free(ptr);
29
+ }
30
+ };
31
+
32
+
33
+ struct Analyser::Data
34
+ {
35
+
36
+ Signals signals;
37
+
38
+ Spectrum spectrum;
39
+
40
+ std::unique_ptr<PFFFT_Setup, PFFFTDestroySetup> pffft;
41
+
42
+ std::unique_ptr<float[], PFFFTAlignedFree> fft_buffer;
43
+
44
+ uint fft_size = 0;
45
+
46
+ ~Data ()
47
+ {
48
+ clear();
49
+ }
50
+
51
+ void setup (uint size)
52
+ {
53
+ if (size == fft_size) return;
54
+
55
+ clear();
56
+
57
+ signals = Signals_create(std::min(size, (uint) Beeps::sample_rate()));
58
+ pffft .reset(pffft_new_setup(size, PFFFT_REAL));
59
+ fft_buffer.reset((float*) pffft_aligned_malloc(sizeof(float) * size));
60
+ fft_size = size;
61
+ }
62
+
63
+ void clear ()
64
+ {
65
+ if (!pffft) return;
66
+
67
+ signals = Signals();
68
+ spectrum .clear();
69
+ pffft .reset();
70
+ fft_buffer.reset();
71
+ fft_size = 0;
72
+ }
73
+
74
+ bool is_valid () const
75
+ {
76
+ return signals && pffft && fft_buffer && fft_size > 0;
77
+ }
78
+
79
+ };// Analyser::Data
80
+
81
+
82
+ Analyser::Analyser (uint fft_size, Processor* input)
83
+ : Super(input)
84
+ {
85
+ set_fft_size(fft_size);
86
+ }
87
+
88
+ Analyser::~Analyser ()
89
+ {
90
+ }
91
+
92
+ static bool
93
+ is_valid_fft_size (uint size)
94
+ {
95
+ if (size < 32) return false;
96
+
97
+ while (size > 1)
98
+ {
99
+ if (size % 2 == 0) size /= 2;
100
+ else if (size % 3 == 0) size /= 3;
101
+ else if (size % 5 == 0) size /= 5;
102
+ else break;
103
+ }
104
+ return size == 1;
105
+ }
106
+
107
+ void
108
+ Analyser::set_fft_size (uint size)
109
+ {
110
+ if (!is_valid_fft_size(size))
111
+ beeps_error(__FILE__, __LINE__, "fft_size must be divisible by 2, 3, or 5");
112
+
113
+ self->setup(size);
114
+ }
115
+
116
+ uint
117
+ Analyser::fft_size () const
118
+ {
119
+ return self->fft_size;
120
+ }
121
+
122
+ float
123
+ Analyser::resolution () const
124
+ {
125
+ if (self->fft_size == 0) return 0;
126
+ return self->signals.sample_rate() / self->fft_size;
127
+ }
128
+
129
+ const Signals&
130
+ Analyser::signals () const
131
+ {
132
+ return self->signals;
133
+ }
134
+
135
+ static void
136
+ copy_signals_to_fft_buffer (float* buffer, uint size, const Signals& signals)
137
+ {
138
+ const double* samples = signals.samples();
139
+ uint nsamples = size > signals.nsamples() ? signals.nsamples() : size;
140
+
141
+ switch (signals.nchannels())
142
+ {
143
+ case 1:
144
+ for (uint i = 0; i < nsamples; ++i)
145
+ buffer[i] = samples[i];
146
+ break;
147
+
148
+ case 2:
149
+ for (uint i = 0; i < nsamples; ++i, samples += 2)
150
+ buffer[i] = (samples[0] + samples[1]) * 0.5;
151
+ break;
152
+
153
+ default:
154
+ beeps_error(__FILE__, __LINE__);
155
+ }
156
+
157
+ for (uint i = nsamples; i < size; ++i)
158
+ buffer[i] = 0;
159
+ }
160
+
161
+ static void
162
+ update_spectrum (Analyser::Spectrum* spectrum, float* buffer, uint buffer_size)
163
+ {
164
+ assert(spectrum && spectrum->empty() && buffer && (buffer_size % 2) == 0);
165
+
166
+ uint size = buffer_size / 2;
167
+ float div = 1.0 / size;
168
+ float* p = buffer;
169
+
170
+ spectrum->reserve(size);
171
+ for (uint i = 0; i < size; ++i, p += 2)
172
+ spectrum->push_back(sqrt(p[0] * p[0] + p[1] * p[1]) * div);
173
+ }
174
+
175
+ const Analyser::Spectrum&
176
+ Analyser::spectrum () const
177
+ {
178
+ if (self->spectrum.empty() && *this)
179
+ {
180
+ float* p = &self->fft_buffer[0];
181
+ copy_signals_to_fft_buffer(p, self->fft_size, self->signals);
182
+ pffft_transform_ordered(self->pffft.get(), p, p, NULL, PFFFT_FORWARD);
183
+ update_spectrum(&self->spectrum, p, self->fft_size);
184
+ }
185
+ return self->spectrum;
186
+ }
187
+
188
+ static void
189
+ shift (Signals* signals, uint nsamples)
190
+ {
191
+ if (nsamples > signals->nsamples())
192
+ return Signals_clear(signals);
193
+
194
+ Frames* frames = Signals_get_frames(signals);
195
+ assert(frames);
196
+
197
+ Float* from = &(*frames)(nsamples, 0);
198
+ Float* to = &(*frames)(0, 0);
199
+ uint size = (signals->nsamples() - nsamples) * signals->nchannels();
200
+ for (uint i = 0; i < size; ++i)
201
+ *to++ = *from++;
202
+
203
+ Signals_set_nsamples(signals, signals->nsamples() - nsamples);
204
+ }
205
+
206
+ static void
207
+ append (Signals* to, const Signals& from)
208
+ {
209
+ assert(to);
210
+
211
+ Frames* tof = Signals_get_frames(to);
212
+ const Frames* fromf = Signals_get_frames(&from);
213
+ assert(fromf && tof);
214
+
215
+ uint to_cap = to->capacity();
216
+ uint to_nsamples = to->nsamples();
217
+ uint from_nsamples = from.nsamples();
218
+ uint from_start = from_nsamples > to_cap ? from_nsamples - to_cap : 0;
219
+ uint nsamples = from_nsamples - from_start;
220
+ assert(to_nsamples + nsamples <= to_capacity);
221
+
222
+ for (uint ch = 0; ch < tof->nchannels(); ++ch)
223
+ {
224
+ uint from_ch = ch < fromf->nchannels() ? ch : 0;
225
+ for (uint i = 0; i < nsamples; ++i)
226
+ (*tof)(to_nsamples + i, ch) = (*fromf)(from_start + i, from_ch);
227
+ }
228
+
229
+ Signals_set_nsamples(to, to_nsamples + nsamples);
230
+ }
231
+
232
+ void
233
+ Analyser::filter (Context* context, Signals* signals, uint* offset)
234
+ {
235
+ Super::filter(context, signals, offset);
236
+
237
+ Signals& sig = self->signals;
238
+ uint nsamples = sig.nsamples() + signals->nsamples();
239
+ if (nsamples > sig.capacity())
240
+ shift(&sig, nsamples - sig.capacity());
241
+
242
+ append(&sig, *signals);
243
+
244
+ self->spectrum.clear();
245
+ }
246
+
247
+ Analyser::operator bool () const
248
+ {
249
+ if (!Super::operator bool()) return false;
250
+ return self->is_valid();
251
+ }
252
+
253
+
254
+ }// Beeps
data/src/beeps.cpp CHANGED
@@ -5,6 +5,8 @@
5
5
  #include "Stk.h"
6
6
  #include "beeps/exception.h"
7
7
  #include "openal.h"
8
+ #include "sound.h"
9
+ #include "mic_in.h"
8
10
 
9
11
 
10
12
  namespace Beeps
@@ -25,8 +27,15 @@ namespace Beeps
25
27
  OpenAL_fin();
26
28
  }
27
29
 
28
- uint
29
- sampling_rate ()
30
+ void
31
+ process_streams ()
32
+ {
33
+ MicIn_process_streams();
34
+ SoundPlayer_process_streams();
35
+ }
36
+
37
+ double
38
+ sample_rate ()
30
39
  {
31
40
  return stk::Stk::sampleRate();
32
41
  }
data/src/file_in.cpp ADDED
@@ -0,0 +1,94 @@
1
+ #include "beeps/generator.h"
2
+
3
+
4
+ #include "beeps/exception.h"
5
+ #include "signals.h"
6
+
7
+
8
+ namespace Beeps
9
+ {
10
+
11
+
12
+ struct FileIn::Data
13
+ {
14
+
15
+ String path;
16
+
17
+ Signals signals;
18
+
19
+ };// FileIn::Data
20
+
21
+
22
+ FileIn::FileIn (const char* path)
23
+ {
24
+ if (path) set_path(path);
25
+ }
26
+
27
+ FileIn::~FileIn ()
28
+ {
29
+ }
30
+
31
+ void
32
+ FileIn::reset ()
33
+ {
34
+ Super::reset();
35
+ }
36
+
37
+ void
38
+ FileIn::set_path (const char* path)
39
+ {
40
+ if (*this)
41
+ invalid_state_error(__FILE__, __LINE__, "path is already set");
42
+
43
+ self->signals = Signals_load(path);
44
+ self->path = path;
45
+
46
+ set_updated();
47
+ }
48
+
49
+ const char*
50
+ FileIn::path () const
51
+ {
52
+ return self->path;
53
+ }
54
+
55
+ double
56
+ FileIn::sample_rate () const
57
+ {
58
+ return self->signals.sample_rate();
59
+ }
60
+
61
+ uint
62
+ FileIn::nchannels () const
63
+ {
64
+ return self->signals.nchannels();
65
+ }
66
+
67
+ uint
68
+ FileIn::nsamples () const
69
+ {
70
+ return self->signals.nsamples();
71
+ }
72
+
73
+ float
74
+ FileIn::seconds () const
75
+ {
76
+ return Signals_get_seconds(self->signals);
77
+ }
78
+
79
+ void
80
+ FileIn::generate (Context* context, Signals* signals, uint* offset)
81
+ {
82
+ Super::generate(context, signals, offset);
83
+
84
+ *offset += Signals_copy(signals, self->signals, *offset);
85
+ }
86
+
87
+ FileIn::operator bool () const
88
+ {
89
+ if (!Super::operator bool()) return false;
90
+ return self->signals;
91
+ }
92
+
93
+
94
+ }// Beeps
data/src/gain.cpp ADDED
@@ -0,0 +1,55 @@
1
+ #include "beeps/filter.h"
2
+
3
+
4
+ #include "signals.h"
5
+
6
+
7
+ namespace Beeps
8
+ {
9
+
10
+
11
+ struct Gain::Data
12
+ {
13
+
14
+ float gain = 1;
15
+
16
+ };// Gain::Data
17
+
18
+
19
+ Gain::Gain (Processor* input)
20
+ : Super(input)
21
+ {
22
+ }
23
+
24
+ Gain::~Gain ()
25
+ {
26
+ }
27
+
28
+ void
29
+ Gain::set_gain (float gain)
30
+ {
31
+ self->gain = gain;
32
+
33
+ set_updated();
34
+ }
35
+
36
+ float
37
+ Gain::gain () const
38
+ {
39
+ return self->gain;
40
+ }
41
+
42
+ void
43
+ Gain::filter (Context* context, Signals* signals, uint* offset)
44
+ {
45
+ Super::filter(context, signals, offset);
46
+
47
+ Frames* frames = Signals_get_frames(signals);
48
+ if (!frames)
49
+ argument_error(__FILE__, __LINE__);
50
+
51
+ *frames *= self->gain;
52
+ }
53
+
54
+
55
+ }// Beeps