beeps 0.3.3 → 0.3.5
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/mixer.cpp +85 -0
- data/.doc/ext/beeps/native.cpp +2 -0
- data/.doc/ext/beeps/oscillator.cpp +54 -0
- data/.doc/ext/beeps/processor.cpp +10 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
- data/CONTRIBUTING.md +7 -0
- data/ChangeLog.md +29 -0
- data/README.md +45 -2
- data/VERSION +1 -1
- data/beeps.gemspec +4 -2
- data/ext/beeps/mixer.cpp +89 -0
- data/ext/beeps/native.cpp +2 -0
- data/ext/beeps/oscillator.cpp +58 -0
- data/ext/beeps/processor.cpp +11 -0
- data/include/beeps/filter.h +41 -0
- data/include/beeps/generator.h +16 -1
- data/include/beeps/processor.h +2 -0
- data/include/beeps/ruby/filter.h +11 -0
- data/include/beeps/ruby/processor.h +17 -1
- data/lib/beeps/processor.rb +76 -17
- data/src/envelope.cpp +18 -10
- data/src/mixer.cpp +112 -0
- data/src/oscillator.cpp +280 -6
- data/src/processor.cpp +14 -7
- data/src/signals.cpp +24 -6
- data/src/signals.h +13 -1
- data/test/test_processor.rb +1 -1
- metadata +18 -11
data/include/beeps/ruby/filter.h
CHANGED
@@ -11,6 +11,8 @@
|
|
11
11
|
|
12
12
|
RUCY_DECLARE_WRAPPER_VALUE_FROM_TO(BEEPS_EXPORT, Beeps::Gain)
|
13
13
|
|
14
|
+
RUCY_DECLARE_WRAPPER_VALUE_FROM_TO(BEEPS_EXPORT, Beeps::Mixer)
|
15
|
+
|
14
16
|
RUCY_DECLARE_WRAPPER_VALUE_FROM_TO(BEEPS_EXPORT, Beeps::Envelope)
|
15
17
|
|
16
18
|
RUCY_DECLARE_WRAPPER_VALUE_FROM_TO(BEEPS_EXPORT, Beeps::TimeStretch)
|
@@ -27,6 +29,9 @@ namespace Beeps
|
|
27
29
|
BEEPS_EXPORT Rucy::Class gain_class ();
|
28
30
|
// class Beeps::Gain
|
29
31
|
|
32
|
+
BEEPS_EXPORT Rucy::Class mixer_class ();
|
33
|
+
// class Beeps::Mixer
|
34
|
+
|
30
35
|
BEEPS_EXPORT Rucy::Class envelope_class ();
|
31
36
|
// class Beeps::Envelope
|
32
37
|
|
@@ -53,6 +58,12 @@ namespace Rucy
|
|
53
58
|
return Beeps::gain_class();
|
54
59
|
}
|
55
60
|
|
61
|
+
template <> inline Class
|
62
|
+
get_ruby_class<Beeps::Mixer> ()
|
63
|
+
{
|
64
|
+
return Beeps::mixer_class();
|
65
|
+
}
|
66
|
+
|
56
67
|
template <> inline Class
|
57
68
|
get_ruby_class<Beeps::Envelope> ()
|
58
69
|
{
|
@@ -21,7 +21,23 @@ namespace Beeps
|
|
21
21
|
|
22
22
|
|
23
23
|
template <typename T>
|
24
|
-
class RubyProcessor : public Rucy::ClassWrapper<T>
|
24
|
+
class RubyProcessor : public Rucy::ClassWrapper<T>
|
25
|
+
{
|
26
|
+
|
27
|
+
typedef Rucy::ClassWrapper<T> Super;
|
28
|
+
|
29
|
+
public:
|
30
|
+
|
31
|
+
virtual void on_start ()
|
32
|
+
{
|
33
|
+
RUCY_SYM(on_start);
|
34
|
+
if (this->is_overridable())
|
35
|
+
this->value.call(on_start);
|
36
|
+
else
|
37
|
+
Super::on_start();
|
38
|
+
}
|
39
|
+
|
40
|
+
};// RubyProcessor
|
25
41
|
|
26
42
|
|
27
43
|
}// Beeps
|
data/lib/beeps/processor.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'xot/setter'
|
2
2
|
require 'xot/const_symbol_accessor'
|
3
3
|
require 'xot/universal_accessor'
|
4
|
+
require 'xot/hookable'
|
4
5
|
require 'xot/block_util'
|
5
6
|
require 'beeps/ext'
|
6
7
|
|
@@ -11,55 +12,76 @@ module Beeps
|
|
11
12
|
class Processor
|
12
13
|
|
13
14
|
include Xot::Setter
|
15
|
+
include Xot::Hookable
|
14
16
|
|
15
|
-
def initialize(**options, &block)
|
17
|
+
def initialize(*inputs, **options, &block)
|
16
18
|
super()
|
19
|
+
add_input(*inputs)
|
17
20
|
set options unless options.empty?
|
18
21
|
Xot::BlockUtil.instance_eval_or_block_call self, &block if block
|
19
22
|
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
universal_accessor :input
|
25
|
+
|
26
|
+
def add_input(*inputs)
|
27
|
+
last = inputs.flatten.compact.last
|
28
|
+
self.input = last if last
|
24
29
|
end
|
25
30
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
31
|
+
def >>(processor)
|
32
|
+
processor.add_input self
|
33
|
+
processor
|
29
34
|
end
|
30
35
|
|
31
|
-
|
36
|
+
#def <<(processors)
|
37
|
+
# self.add_input(*processors)
|
38
|
+
# self
|
39
|
+
#end
|
32
40
|
|
33
41
|
end# Processor
|
34
42
|
|
35
43
|
|
36
44
|
class Oscillator
|
37
45
|
|
46
|
+
include Enumerable
|
47
|
+
|
38
48
|
const_symbol_accessor :type, **{
|
39
49
|
none: TYPE_NONE,
|
40
50
|
sine: SINE,
|
41
51
|
triangle: TRIANGLE,
|
42
52
|
square: SQUARE,
|
43
|
-
sawtooth: SAWTOOTH
|
53
|
+
sawtooth: SAWTOOTH,
|
54
|
+
noise: NOISE,
|
55
|
+
samples: SAMPLES
|
44
56
|
}
|
45
57
|
|
46
|
-
def initialize(type = :sine,
|
47
|
-
super(
|
48
|
-
|
58
|
+
def initialize(type = :sine, samples: nil, **kwargs, &block)
|
59
|
+
super(**kwargs, &block)
|
60
|
+
if samples
|
61
|
+
self.samples = samples
|
62
|
+
else
|
63
|
+
self.type = type
|
64
|
+
end
|
49
65
|
end
|
50
66
|
|
67
|
+
universal_accessor :type, :frequency, :phase
|
68
|
+
|
51
69
|
alias freq= frequency=
|
52
70
|
alias freq frequency
|
53
71
|
|
54
|
-
|
72
|
+
def each_sample(&block)
|
73
|
+
block ? each_sample!(&block) : enum_for(:each_sample!)
|
74
|
+
end
|
75
|
+
|
76
|
+
alias each each_sample
|
55
77
|
|
56
78
|
end# Oscillator
|
57
79
|
|
58
80
|
|
59
81
|
class FileIn
|
60
82
|
|
61
|
-
def initialize(path = nil,
|
62
|
-
super(
|
83
|
+
def initialize(path = nil, **kwargs, &block)
|
84
|
+
super(**kwargs, &block)
|
63
85
|
self.path = path if path
|
64
86
|
end
|
65
87
|
|
@@ -70,13 +92,50 @@ module Beeps
|
|
70
92
|
|
71
93
|
class Gain
|
72
94
|
|
95
|
+
def initialize(gain = 1, **kwargs, &block)
|
96
|
+
super(gain: gain, **kwargs, &block)
|
97
|
+
end
|
98
|
+
|
73
99
|
universal_accessor :gain
|
74
100
|
|
75
101
|
end# Gain
|
76
102
|
|
77
103
|
|
104
|
+
class Mixer
|
105
|
+
|
106
|
+
include Enumerable
|
107
|
+
|
108
|
+
def add_input(*inputs)
|
109
|
+
add_input! inputs.flatten.compact
|
110
|
+
end
|
111
|
+
|
112
|
+
def remove_input(*inputs)
|
113
|
+
remove_input! inputs.flatten.compact
|
114
|
+
end
|
115
|
+
|
116
|
+
def each_input(&block)
|
117
|
+
block ? each_input!(&block) : enum_for(:each_input!)
|
118
|
+
end
|
119
|
+
|
120
|
+
alias each each_input
|
121
|
+
|
122
|
+
end# Mixer
|
123
|
+
|
124
|
+
|
78
125
|
class Envelope
|
79
126
|
|
127
|
+
def initialize(
|
128
|
+
attack = nil, decay = nil, sustain = nil, release = nil,
|
129
|
+
*args, **kwargs, &block)
|
130
|
+
|
131
|
+
attack_time attack if attack
|
132
|
+
decay_time decay if decay
|
133
|
+
sustain_level sustain if sustain
|
134
|
+
release_time release if release
|
135
|
+
|
136
|
+
super(*args, **kwargs, &block)
|
137
|
+
end
|
138
|
+
|
80
139
|
def note_on(delay = 0)
|
81
140
|
note_on! delay
|
82
141
|
end
|
@@ -115,6 +174,8 @@ module Beeps
|
|
115
174
|
|
116
175
|
class Analyser
|
117
176
|
|
177
|
+
universal_accessor :fft_size
|
178
|
+
|
118
179
|
def each_signal(nsamples = fft_size, &block)
|
119
180
|
return enum_for(:each_signal, nsamples) unless block
|
120
181
|
each_signal!(nsamples, &block)
|
@@ -125,8 +186,6 @@ module Beeps
|
|
125
186
|
each_spectrum!(&block)
|
126
187
|
end
|
127
188
|
|
128
|
-
universal_accessor :fft_size
|
129
|
-
|
130
189
|
end# Analyser
|
131
190
|
|
132
191
|
|
data/src/envelope.cpp
CHANGED
@@ -11,6 +11,9 @@ namespace Beeps
|
|
11
11
|
{
|
12
12
|
|
13
13
|
|
14
|
+
static const float TIME_ZERO = 0.0000001;
|
15
|
+
|
16
|
+
|
14
17
|
struct Envelope::Data
|
15
18
|
{
|
16
19
|
|
@@ -18,16 +21,19 @@ namespace Beeps
|
|
18
21
|
|
19
22
|
Signals adsr_signals;
|
20
23
|
|
21
|
-
float attack_time
|
24
|
+
float attack_time = 0.005;
|
25
|
+
float decay_time = 0.005;
|
26
|
+
float sustain_level = 1;
|
27
|
+
float release_time = 0.005;
|
22
28
|
|
23
29
|
float time = 0, note_on_time = -1, note_off_time = -1;
|
24
30
|
|
25
31
|
void update_envelope ()
|
26
32
|
{
|
27
|
-
adsr.setAttackTime( attack_time == 0 ? 0.01 : attack_time);
|
28
33
|
adsr.setSustainLevel(sustain_level);
|
29
|
-
adsr.
|
30
|
-
adsr.
|
34
|
+
adsr.setAttackTime( attack_time == 0 ? TIME_ZERO : attack_time);
|
35
|
+
adsr.setDecayTime( decay_time == 0 ? TIME_ZERO : decay_time);
|
36
|
+
adsr.setReleaseTime(release_time == 0 ? TIME_ZERO : release_time);
|
31
37
|
}
|
32
38
|
|
33
39
|
};// Envelope::Data
|
@@ -148,11 +154,10 @@ namespace Beeps
|
|
148
154
|
static size_t
|
149
155
|
slice (Frames* frames, size_t start, float length_sec = -1)
|
150
156
|
{
|
151
|
-
|
152
|
-
size_t len
|
153
|
-
|
154
|
-
|
155
|
-
assert(0 < len && (start + len) < frames->nframes());
|
157
|
+
size_t max = frames->nframes() - start;
|
158
|
+
size_t len = length_sec >= 0 ? length_sec * frames->sample_rate() : max;
|
159
|
+
if (len > max) len = max;
|
160
|
+
assert(0 < len && (start + len) <= frames->nframes());
|
156
161
|
|
157
162
|
return frames->slice(start, len);
|
158
163
|
}
|
@@ -167,6 +172,9 @@ namespace Beeps
|
|
167
172
|
Frames* frames = Signals_get_frames(signals);
|
168
173
|
assert(frames);
|
169
174
|
|
175
|
+
if (self->time == 0 && self->attack_time == 0)
|
176
|
+
self->adsr.setValue(self->sustain_level);// skip attack phase
|
177
|
+
|
170
178
|
float start = self->time;
|
171
179
|
float end = start + Signals_get_seconds(*signals);
|
172
180
|
self->time = end;
|
@@ -175,7 +183,7 @@ namespace Beeps
|
|
175
183
|
float off = self->note_off_time;
|
176
184
|
assert(on <= off);
|
177
185
|
|
178
|
-
bool has_on
|
186
|
+
bool has_on = 0 <= on && start <= on && on < end;
|
179
187
|
bool has_off = 0 <= off && start <= off && off < end;
|
180
188
|
|
181
189
|
if (!has_on && !has_off)
|
data/src/mixer.cpp
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
#include "beeps/filter.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include <algorithm>
|
5
|
+
#include <vector>
|
6
|
+
#include "processor.h"
|
7
|
+
#include "signals.h"
|
8
|
+
|
9
|
+
|
10
|
+
namespace Beeps
|
11
|
+
{
|
12
|
+
|
13
|
+
|
14
|
+
struct Mixer::Data
|
15
|
+
{
|
16
|
+
|
17
|
+
std::vector<Processor::Ref> inputs;
|
18
|
+
|
19
|
+
};// Mixer::Data
|
20
|
+
|
21
|
+
|
22
|
+
Mixer::Mixer (Processor* input)
|
23
|
+
{
|
24
|
+
if (input) add_input(input);
|
25
|
+
}
|
26
|
+
|
27
|
+
Mixer::~Mixer ()
|
28
|
+
{
|
29
|
+
}
|
30
|
+
|
31
|
+
void
|
32
|
+
Mixer::add_input (Processor* input)
|
33
|
+
{
|
34
|
+
auto it = std::find(self->inputs.begin(), self->inputs.end(), input);
|
35
|
+
if (it != self->inputs.end()) return;
|
36
|
+
|
37
|
+
self->inputs.emplace_back(input);
|
38
|
+
|
39
|
+
set_updated();
|
40
|
+
}
|
41
|
+
|
42
|
+
void
|
43
|
+
Mixer::remove_input (Processor* input)
|
44
|
+
{
|
45
|
+
auto it = std::find(self->inputs.begin(), self->inputs.end(), input);
|
46
|
+
if (it == self->inputs.end()) return;
|
47
|
+
|
48
|
+
self->inputs.erase(it);
|
49
|
+
|
50
|
+
set_updated();
|
51
|
+
}
|
52
|
+
|
53
|
+
Mixer::iterator
|
54
|
+
Mixer::begin ()
|
55
|
+
{
|
56
|
+
return self->inputs.begin();
|
57
|
+
}
|
58
|
+
|
59
|
+
Mixer::const_iterator
|
60
|
+
Mixer::begin () const
|
61
|
+
{
|
62
|
+
return self->inputs.begin();
|
63
|
+
}
|
64
|
+
|
65
|
+
Mixer::iterator
|
66
|
+
Mixer::end ()
|
67
|
+
{
|
68
|
+
return self->inputs.end();
|
69
|
+
}
|
70
|
+
|
71
|
+
Mixer::const_iterator
|
72
|
+
Mixer::end () const
|
73
|
+
{
|
74
|
+
return self->inputs.end();
|
75
|
+
}
|
76
|
+
|
77
|
+
void
|
78
|
+
Mixer::filter (Context* context, Signals* signals, uint* offset)
|
79
|
+
{
|
80
|
+
Super::filter(context, signals, offset);
|
81
|
+
|
82
|
+
Signals_resize(signals, signals->capacity(), 0);
|
83
|
+
|
84
|
+
Signals sig = Signals_create(signals->capacity(), signals->nchannels());
|
85
|
+
uint min_size = signals->capacity();
|
86
|
+
for (auto& input : self->inputs)
|
87
|
+
{
|
88
|
+
Signals_clear(&sig);
|
89
|
+
|
90
|
+
uint sig_offset = *offset;
|
91
|
+
Processor_get_context(context)->process(input, &sig, &sig_offset);
|
92
|
+
|
93
|
+
uint size = sig_offset - *offset;
|
94
|
+
if (size < min_size) min_size = size;
|
95
|
+
|
96
|
+
Signals_add(signals, sig);
|
97
|
+
}
|
98
|
+
|
99
|
+
*offset += min_size;
|
100
|
+
}
|
101
|
+
|
102
|
+
Mixer::operator bool () const
|
103
|
+
{
|
104
|
+
if (self->inputs.empty()) return false;
|
105
|
+
|
106
|
+
for (auto& input : self->inputs)
|
107
|
+
if (!*input) return false;
|
108
|
+
return true;
|
109
|
+
}
|
110
|
+
|
111
|
+
|
112
|
+
}// Beeps
|