synth_blocks 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,275 @@
1
+ module SynthBlocks
2
+ module Fx
3
+ # Direct port from GVerb
4
+ # Source: https://github.com/swh/lv2/blob/master/gverb/gverbdsp.c
5
+ # (and other files from that repo)
6
+ #
7
+ # Here's the original (c) notice from https://github.com/swh/lv2/blob/master/gverb/gverbdsp.c
8
+ #
9
+ # Copyright (C) 1999 Juhana Sadeharju
10
+ # kouhia at nic.funet.fi
11
+ # This program is free software; you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation; either version 2 of the License, or
14
+ # (at your option) any later version.
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program; if not, write to the Free Software
21
+ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
+ #
23
+
24
+ require 'prime'
25
+
26
+ class FixedDelay # :nodoc:
27
+ def initialize(size)
28
+ @size = size
29
+ @buf = Array.new(size)
30
+ @idx = 0
31
+ @buf = @buf.map { |e| 0.0 }
32
+ end
33
+
34
+ def read(n)
35
+ i = (@idx - n + @size) % @size;
36
+ @buf[i]
37
+ end
38
+
39
+ def write(x)
40
+ @buf[@idx] = x
41
+ @idx = (@idx + 1) % @size
42
+ end
43
+ end
44
+
45
+ class Damper # :nodoc:
46
+ def initialize(damping)
47
+ @damping = damping
48
+ @delay = 0.0
49
+ end
50
+
51
+ def run(x)
52
+ y = x * (1.0-@damping) + @delay * @damping;
53
+ @delay = y
54
+ y
55
+ end
56
+ end
57
+
58
+ class Diffuser # :nodoc:
59
+ def initialize(size, coeff)
60
+ @size = size.floor
61
+ @coeff = coeff
62
+ @idx = 0
63
+ @buf = Array.new(@size)
64
+ @buf = @buf.map { |e| 0.0 }
65
+ end
66
+
67
+ def run(x)
68
+ w = x - @buf[@idx] * @coeff;
69
+ y = @buf[@idx] + w * @coeff;
70
+ @buf[@idx] = w
71
+ @idx = (@idx + 1) % @size;
72
+ y
73
+ end
74
+ end
75
+ ##
76
+ # GVerb is a relatively simple reverb implementation
77
+ class GVerb
78
+ FDNORDER = 4 # :nodoc:
79
+
80
+
81
+ ##
82
+ # Create new GVerb instance
83
+ #
84
+ # max_room_size is the maximum room size you'll use
85
+ #
86
+ # room_size is the current room size
87
+ #
88
+ def initialize(srate, max_room_size: 120.0, room_size: 50.0, rev_time: 2.0, damping: 0.3, spread: 15.0, input_bandwidth: 1.5, early_level: 0.8, tail_level: 0.5, mix: 0.2)
89
+ @rate = srate
90
+ @damping = damping
91
+ @max_room_size = max_room_size
92
+ @room_size = room_size
93
+ @rev_time = rev_time
94
+ @early_level = early_level
95
+ @tail_level = tail_level
96
+ @mix = mix
97
+ @max_delay = @rate * @max_room_size / 340.0
98
+ @largest_delay = @rate * @room_size / 340.0
99
+ @input_bandwidth = input_bandwidth;
100
+ @input_damper = Damper.new(1.0 - @input_bandwidth)
101
+
102
+
103
+ @fdndels = FDNORDER.times.map do |i|
104
+ FixedDelay.new(@max_delay + 1000)
105
+ end
106
+ @fdngains = Array.new(FDNORDER)
107
+ @fdnlens = Array.new(FDNORDER)
108
+
109
+ @fdndamps = FDNORDER.times.map do |i|
110
+ Damper.new(@damping)
111
+ end
112
+
113
+ ga = 60.0;
114
+ gt = @rev_time;
115
+ ga = 10.0 ** (-ga / 20.0)
116
+ n = @rate * gt
117
+ @alpha = ga ** (1.0 / n)
118
+ gb = 0.0;
119
+ FDNORDER.times do |i|
120
+ gb = 1.000000*@largest_delay if (i == 0)
121
+ gb = 0.816490*@largest_delay if (i == 1)
122
+ gb = 0.707100*@largest_delay if (i == 2)
123
+ gb = 0.632450*@largest_delay if (i == 3)
124
+
125
+ @fdnlens[i] = nearest_prime(gb, 0.5);
126
+ @fdnlens[i] = gb.round;
127
+ @fdngains[i] = -(@alpha ** @fdnlens[i])
128
+ end
129
+
130
+ @d = Array.new(FDNORDER)
131
+ @u = Array.new(FDNORDER)
132
+ @f = Array.new(FDNORDER)
133
+
134
+ # DIFFUSER SECTION
135
+
136
+ diffscale = @fdnlens[3].to_f/(210+159+562+410);
137
+ spread1 = spread.to_f
138
+ spread2 = 3.0*spread
139
+
140
+ b = 210
141
+ r = 0.125541
142
+ a = spread1*r
143
+ c = 210+159+a
144
+ cc = c-b
145
+ r = 0.854046
146
+ a = spread2*r
147
+ d = 210+159+562+a
148
+ dd = d-c
149
+ e = 1341-d
150
+
151
+ @ldifs = [
152
+ Diffuser.new((diffscale*b),0.75),
153
+ Diffuser.new((diffscale*cc),0.75),
154
+ Diffuser.new((diffscale*dd),0.625),
155
+ Diffuser.new((diffscale*e),0.625)
156
+ ]
157
+
158
+ b = 210
159
+ r = -0.568366
160
+ a = spread1*r
161
+ c = 210+159+a
162
+ cc = c-b
163
+ r = -0.126815;
164
+ a = spread2*r
165
+ d = 210+159+562+a
166
+ dd = d-c
167
+ e = 1341-d
168
+
169
+ @rdifs = [
170
+ Diffuser.new((diffscale*b),0.75),
171
+ Diffuser.new((diffscale*cc),0.75),
172
+ Diffuser.new((diffscale*dd),0.625),
173
+ Diffuser.new((diffscale*e),0.625)
174
+ ]
175
+
176
+
177
+ # Tapped delay section */
178
+
179
+ @tapdelay = FixedDelay.new(44000)
180
+ @taps = Array.new(FDNORDER)
181
+ @tapgains = Array.new(FDNORDER)
182
+
183
+ @taps[0] = 5+0.410*@largest_delay
184
+ @taps[1] = 5+0.300*@largest_delay
185
+ @taps[2] = 5+0.155*@largest_delay
186
+ @taps[3] = 5+0.000*@largest_delay
187
+
188
+ FDNORDER.times do |i|
189
+ @tapgains[i] = @alpha ** @taps[i]
190
+ end
191
+ end
192
+
193
+
194
+ ##
195
+ # runs a value through the reverb, returns the reverberated signal
196
+ # mixed with the original.
197
+ def run(x)
198
+ if x.nan? || x.abs > 100000.0
199
+ x = 0.0
200
+ end
201
+
202
+ z = @input_damper.run(x)
203
+ z = @ldifs[0].run(z)
204
+ FDNORDER.times do |i|
205
+ @u[i] = @tapgains[i] * @tapdelay.read(@taps[i])
206
+ end
207
+
208
+ @tapdelay.write(z)
209
+
210
+ FDNORDER.times do |i|
211
+ @d[i] = @fdndamps[i].run(@fdngains[i] * @fdndels[i].read(@fdnlens[i]))
212
+ end
213
+
214
+ sum = 0.0
215
+ sign = 1.0
216
+ FDNORDER.times do |i|
217
+ sum += sign * (@tail_level * @d[i] + @early_level * @u[i])
218
+ sign = -sign
219
+ end
220
+
221
+ sum += x* @early_level
222
+
223
+ lsum = sum
224
+ # rsum = sum
225
+
226
+ @f = fdn_matrix(@d)
227
+
228
+ FDNORDER.times do |i|
229
+ @fdndels[i].write(@u[i] + @f[i])
230
+ end
231
+
232
+ lsum = @ldifs[1].run(lsum)
233
+ lsum = @ldifs[2].run(lsum)
234
+ lsum = @ldifs[3].run(lsum)
235
+
236
+ # rsum = @rdifs[1].run(rsum)
237
+ # rsum = @rdifs[2].run(rsum)
238
+ # rsum = @rdifs[3].run(rsum)
239
+
240
+ lsum = x * (1.0 - @mix) + lsum * @mix
241
+ # rsum = x * (1.0 - mix) + rsum * mix
242
+ return lsum
243
+ end
244
+
245
+
246
+ private
247
+
248
+ def nearest_prime(n_f, rerror)
249
+ n = n_f.to_i
250
+ return n if Prime.prime?(n)
251
+ # assume n is large enough and n*rerror enough smaller than n */
252
+ bound = n*rerror;
253
+ 1.upto(bound) do |k|
254
+ return n+k if Prime.prime?(n+k)
255
+ return n-k if Prime.prime?(n-k)
256
+ end
257
+ return -1
258
+ end
259
+
260
+ def fdn_matrix(a)
261
+ b = Array.new(FDNORDER)
262
+ dl0 = a[0]
263
+ dl1 = a[1]
264
+ dl2 = a[2]
265
+ dl3 = a[3]
266
+
267
+ b[0] = 0.5*(dl0 + dl1 - dl2 - dl3);
268
+ b[1] = 0.5*(dl0 - dl1 - dl2 + dl3);
269
+ b[2] = 0.5*(-dl0 + dl1 - dl2 + dl3);
270
+ b[3] = 0.5*(dl0 + dl1 + dl2 + dl3);
271
+ b
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,15 @@
1
+ module SynthBlocks
2
+ module Fx
3
+ ##
4
+ # Simple soft limiter
5
+ # Taken from https://github.com/pichenettes/stmlib/blob/448babb082dfe7b0a1ffbf0b349eefde64691b49/dsp/dsp.h#L97
6
+ class Limiter
7
+
8
+ ##
9
+ # run limiter
10
+ def run(x)
11
+ x * (27.0 + x * x) / (27.0 + 9.0 * x * x)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module SynthBlocks
2
+ module Fx
3
+ ##
4
+ # waveshaper, source http://www.musicdsp.org/en/latest/Effects/41-waveshaper.html
5
+ # amount can go from 1 to ... oo
6
+ # the higher a the stronger is the distortion
7
+ class Waveshaper
8
+ ##
9
+ # Waveshaper amount
10
+ attr_reader :amount
11
+ ##
12
+ # Create waveshaper instance
13
+ # [amount] Amount can be from 0 to oo
14
+ def initialize(amount)
15
+ @amount = amount
16
+ end
17
+ ##
18
+ # run waveshaper
19
+ def run(input)
20
+ input * (input.abs + amount) / (input ** 2 + (amount - 1) * input.abs + 1)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,106 @@
1
+ require 'synth_blocks/core/sound'
2
+
3
+ module SynthBlocks
4
+ module Mixer
5
+ ##
6
+ # Emulation of a mixer channel on a mixing desk
7
+ # has a built in EQ, compressor and ducker, can run an arbitrary
8
+ # number of insert effects and send channels
9
+ class MixerChannel < SynthBlocks::Core::Sound
10
+ ##
11
+ # These params can be automated
12
+ LIVE_PARAMS = [:volume, :eq_low_gain, :eq_high_gain, :eq_mid_gain]
13
+ attr_accessor :preset # :nodoc:
14
+
15
+ def live_params # :nodoc:
16
+ LIVE_PARAMS
17
+ end
18
+
19
+ ##
20
+ # - source - Source sound generator
21
+ # - insert_effects - Array of effects instances
22
+ # - sends - Array of send values
23
+ # === Parameters
24
+ # - volume - channel volume
25
+ # - eq_low_freq, eq_high_freq - shelving frequencies for equalizer
26
+ # - eq_low_gain, eq_mid_gain, eq_high_gain - Equalizer gains per band
27
+ # - comp_threshold, comp_ratio, comp_attack, comp_release - Compressor params
28
+ # - duck - Duck amount (0-1)
29
+ # - duck attack, duck_release - Ducker envelope params in s
30
+ def initialize(srate, source, insert_effects: [], sends: [], preset: {})
31
+ @source = source
32
+ @insert_effects = insert_effects
33
+ @sends = sends
34
+ @preset = {
35
+ volume: 0.2,
36
+ eq_low_freq: 880,
37
+ eq_high_freq: 5000,
38
+ eq_low_gain: 1.0,
39
+ eq_mid_gain: 1.0,
40
+ eq_high_gain: 1.0,
41
+ comp_threshold: -50.0,
42
+ comp_ratio: 0.4,
43
+ comp_attack: 80.0,
44
+ comp_release: 200.0,
45
+ duck: 0.0,
46
+ duck_attack: 0.01,
47
+ duck_release: 0.5
48
+ }.merge(preset)
49
+
50
+ super(srate)
51
+ @ducks = []
52
+ @duck_env = SynthBlocks::Mod::Envelope.new(@preset[:duck_attack], @preset[:duck_release])
53
+ @eq = SynthBlocks::Fx::Eq.new(srate, lowfreq: @preset[:eq_low_freq], highfreq: @preset[:eq_high_freq])
54
+ @compressor = SynthBlocks::Fx::Compressor.new(srate, attack: @preset[:comp_attack], release: @preset[:comp_release], ratio: @preset[:comp_ratio], threshold: @preset[:comp_threshold])
55
+ update_live_params(0)
56
+ end
57
+
58
+ ##
59
+ # Schedule ducking at time t (in seconds)
60
+ def duck(t)
61
+ @ducks << t
62
+ @ducks.sort
63
+ end
64
+
65
+ ##
66
+ # returns send portion of output signal for send index
67
+ def send(index)
68
+ @output * (@sends[index] || 0.0)
69
+ end
70
+
71
+ ##
72
+ # runs channel
73
+ def run(offset)
74
+ t = time(offset)
75
+ update_live_params(t)
76
+ out = @eq.run(@source.run(offset))
77
+ @insert_effects.each do |effect|
78
+ out = effect.run(out)
79
+ end
80
+ if @preset[:duck] != 0.0
81
+ duck = current_duck(t)
82
+ if duck
83
+ local_duck = t - duck
84
+ out = out * (1.0 - @preset[:duck] * @duck_env.run(local_duck))
85
+ end
86
+ end
87
+ out = @compressor.run(out)
88
+ @output = out * @preset[:volume]
89
+ end
90
+
91
+ private
92
+
93
+ def update_live_params(t)
94
+ @eq.low_gain = get(:eq_low_gain, t)
95
+ @eq.mid_gain = get(:eq_low_gain, t)
96
+ @eq.high_gain = get(:eq_low_gain, t)
97
+ end
98
+
99
+ def current_duck(t)
100
+ past = @ducks.select {|duck| duck < t}
101
+ return past.last unless past.empty?
102
+ nil
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ require 'synth_blocks/mixer/mixer_channel'
2
+
3
+ module SynthBlocks
4
+ module Mixer
5
+ ##t
6
+ # Channel subclass specifically for SendChannels
7
+ class SendChannel < MixerChannel
8
+ ##
9
+ # creates new send channel. See MixerChannel#new for parameters
10
+ def initialize(srate, insert_effects: [], sends: [], preset: {})
11
+ super(srate, nil, insert_effects: insert_effects, sends: sends, preset: preset)
12
+ end
13
+
14
+ ##
15
+ # run the send channel
16
+ def run(offset, input)
17
+ out = @eq.run(input)
18
+ @insert_effects.each do |effect|
19
+ out = effect.run(out)
20
+ end
21
+ @output = out * @preset[:volume]
22
+ end
23
+ end
24
+ end
25
+ end