beeps 0.1.32 → 0.1.33

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