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.
- checksums.yaml +4 -4
- data/.doc/ext/beeps/adsr.cpp +139 -0
- data/.doc/ext/beeps/analyser.cpp +128 -0
- data/.doc/ext/beeps/beeps.cpp +9 -1
- data/.doc/ext/beeps/file_in.cpp +55 -9
- data/.doc/ext/beeps/gain.cpp +64 -0
- data/.doc/ext/beeps/mic_in.cpp +83 -0
- data/.doc/ext/beeps/native.cpp +20 -8
- data/.doc/ext/beeps/oscillator.cpp +88 -0
- data/.doc/ext/beeps/pitch_shift.cpp +64 -0
- data/.doc/ext/beeps/processor.cpp +31 -2
- data/.doc/ext/beeps/sound.cpp +90 -7
- data/.doc/ext/beeps/sound_player.cpp +156 -0
- data/.doc/ext/beeps/time_stretch.cpp +64 -0
- data/.github/workflows/release-gem.yml +62 -0
- data/.github/workflows/test.yml +0 -6
- data/.github/workflows/utils.rb +13 -5
- data/ChangeLog.md +13 -0
- data/Rakefile +27 -6
- data/VERSION +1 -1
- data/beeps.gemspec +2 -2
- data/ext/beeps/adsr.cpp +150 -0
- data/ext/beeps/analyser.cpp +134 -0
- data/ext/beeps/beeps.cpp +10 -1
- data/ext/beeps/extconf.rb +1 -2
- data/ext/beeps/file_in.cpp +60 -9
- data/ext/beeps/gain.cpp +67 -0
- data/ext/beeps/mic_in.cpp +88 -0
- data/ext/beeps/native.cpp +20 -8
- data/ext/beeps/oscillator.cpp +93 -0
- data/ext/beeps/pitch_shift.cpp +67 -0
- data/ext/beeps/processor.cpp +34 -2
- data/ext/beeps/sound.cpp +99 -7
- data/ext/beeps/sound_player.cpp +169 -0
- data/ext/beeps/time_stretch.cpp +67 -0
- data/include/beeps/beeps.h +2 -1
- data/include/beeps/filter.h +179 -0
- data/include/beeps/generator.h +120 -0
- data/include/beeps/processor.h +37 -68
- data/include/beeps/ruby/filter.h +78 -0
- data/include/beeps/ruby/generator.h +60 -0
- data/include/beeps/ruby/processor.h +5 -45
- data/include/beeps/ruby/sound.h +14 -3
- data/include/beeps/signals.h +10 -4
- data/include/beeps/sound.h +67 -2
- data/lib/beeps/beeps.rb +6 -1
- data/lib/beeps/processor.rb +95 -15
- data/lib/beeps/sound.rb +29 -2
- data/src/adsr.cpp +245 -0
- data/src/analyser.cpp +254 -0
- data/src/beeps.cpp +11 -2
- data/src/file_in.cpp +94 -0
- data/src/gain.cpp +55 -0
- data/src/mic_in.cpp +262 -0
- data/src/mic_in.h +20 -0
- data/src/openal.cpp +2 -1
- data/src/oscillator.cpp +145 -0
- data/src/osx/signals.mm +83 -0
- data/src/pitch_shift.cpp +82 -0
- data/src/processor.cpp +202 -88
- data/src/processor.h +98 -0
- data/src/signals.cpp +326 -20
- data/src/signals.h +192 -2
- data/src/sound.cpp +735 -113
- data/src/sound.h +6 -1
- data/src/time_stretch.cpp +91 -0
- data/test/helper.rb +2 -1
- data/test/test_beeps.rb +10 -7
- data/test/test_beeps_init.rb +18 -0
- data/test/test_file_in.rb +15 -0
- data/test/test_processor.rb +50 -0
- data/test/test_sound.rb +87 -11
- data/test/test_sound_player.rb +134 -0
- metadata +55 -17
- data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
- data/.doc/ext/beeps/sine_wave.cpp +0 -61
- data/.doc/ext/beeps/square_wave.cpp +0 -61
- data/.github/workflows/release.yml +0 -34
- data/ext/beeps/sawtooth_wave.cpp +0 -64
- data/ext/beeps/sine_wave.cpp +0 -64
- data/ext/beeps/square_wave.cpp +0 -64
data/lib/beeps/processor.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
require 'xot/setter'
|
5
|
+
require 'xot/const_symbol_accessor'
|
5
6
|
require 'xot/universal_accessor'
|
6
7
|
require 'xot/block_util'
|
7
8
|
require 'beeps/ext'
|
@@ -14,43 +15,122 @@ module Beeps
|
|
14
15
|
|
15
16
|
include Xot::Setter
|
16
17
|
|
17
|
-
def initialize(options
|
18
|
+
def initialize(**options, &block)
|
18
19
|
super()
|
19
|
-
set options
|
20
|
+
set options unless options.empty?
|
20
21
|
Xot::BlockUtil.instance_eval_or_block_call self, &block if block
|
21
22
|
end
|
22
23
|
|
24
|
+
def >>(o)
|
25
|
+
o.input = self
|
26
|
+
o
|
27
|
+
end
|
28
|
+
|
29
|
+
def <<(o)
|
30
|
+
self.input = o
|
31
|
+
o
|
32
|
+
end
|
33
|
+
|
34
|
+
universal_accessor :input
|
35
|
+
|
23
36
|
end# Processor
|
24
37
|
|
25
38
|
|
26
|
-
class
|
39
|
+
class Oscillator
|
40
|
+
|
41
|
+
const_symbol_accessor :type, **{
|
42
|
+
none: NONE,
|
43
|
+
sine: SINE,
|
44
|
+
triangle: TRIANGLE,
|
45
|
+
square: SQUARE,
|
46
|
+
sawtooth: SAWTOOTH
|
47
|
+
}
|
48
|
+
|
49
|
+
def initialize(type = :sine, *args, **kwargs, &block)
|
50
|
+
super(*args, **kwargs, &block)
|
51
|
+
self.type = type
|
52
|
+
end
|
27
53
|
|
28
54
|
alias freq= frequency=
|
29
55
|
alias freq frequency
|
30
56
|
|
31
|
-
universal_accessor :frequency, :freq
|
57
|
+
universal_accessor :type, :frequency, :freq
|
32
58
|
|
33
|
-
end#
|
59
|
+
end# Oscillator
|
34
60
|
|
35
61
|
|
36
|
-
class
|
62
|
+
class FileIn
|
37
63
|
|
38
|
-
|
39
|
-
|
64
|
+
def initialize(path = nil, *args, **kwargs, &block)
|
65
|
+
super(*args, **kwargs, &block)
|
66
|
+
self.path = path if path
|
67
|
+
end
|
40
68
|
|
41
|
-
universal_accessor :
|
69
|
+
universal_accessor :path
|
42
70
|
|
43
|
-
end#
|
71
|
+
end# FileIn
|
44
72
|
|
45
73
|
|
46
|
-
class
|
74
|
+
class Gain
|
47
75
|
|
48
|
-
|
49
|
-
|
76
|
+
universal_accessor :gain
|
77
|
+
|
78
|
+
end# Gain
|
79
|
+
|
80
|
+
|
81
|
+
class ADSR
|
82
|
+
|
83
|
+
def note_on(delay = 0)
|
84
|
+
note_on! delay
|
85
|
+
end
|
86
|
+
|
87
|
+
def note_off(delay = 0)
|
88
|
+
note_off! delay
|
89
|
+
end
|
90
|
+
|
91
|
+
universal_accessor :attack_time, :decay_time, :sustain_level, :release_time
|
92
|
+
|
93
|
+
alias attack= attack_time=
|
94
|
+
alias attack attack_time
|
95
|
+
alias decay= decay_time=
|
96
|
+
alias decay decay_time
|
97
|
+
alias sustain= sustain_level=
|
98
|
+
alias sustain sustain_level
|
99
|
+
alias release= release_time=
|
100
|
+
alias release release_time
|
101
|
+
|
102
|
+
end# ADSR
|
103
|
+
|
104
|
+
|
105
|
+
class TimeStretch
|
106
|
+
|
107
|
+
universal_accessor :scale
|
108
|
+
|
109
|
+
end# TimeStretch
|
110
|
+
|
111
|
+
|
112
|
+
class PitchShift
|
113
|
+
|
114
|
+
universal_accessor :shift
|
115
|
+
|
116
|
+
end# PitchShift
|
117
|
+
|
118
|
+
|
119
|
+
class Analyser
|
120
|
+
|
121
|
+
def each_signal(nsamples = fft_size, &block)
|
122
|
+
return enum_for(:each_signal, nsamples) unless block
|
123
|
+
each_signal!(nsamples, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def each_spectrum(&block)
|
127
|
+
return enum_for(:each_spectrum) unless block
|
128
|
+
each_spectrum!(&block)
|
129
|
+
end
|
50
130
|
|
51
|
-
universal_accessor :
|
131
|
+
universal_accessor :fft_size
|
52
132
|
|
53
|
-
end#
|
133
|
+
end# Analyser
|
54
134
|
|
55
135
|
|
56
136
|
end# Beeps
|
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
|
-
|
13
|
-
|
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
|
-
|
29
|
-
|
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
|
}
|