synth_blocks 1.0.0
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 +7 -0
- data/LICENSE +661 -0
- data/README.md +40 -0
- data/lib/synth_blocks.rb +24 -0
- data/lib/synth_blocks/core/moog_filter.rb +35 -0
- data/lib/synth_blocks/core/one_pole_lowpass.rb +14 -0
- data/lib/synth_blocks/core/oscillator.rb +36 -0
- data/lib/synth_blocks/core/sound.rb +187 -0
- data/lib/synth_blocks/core/state_variable_filter.rb +41 -0
- data/lib/synth_blocks/core/wave_writer.rb +28 -0
- data/lib/synth_blocks/drum/hihat.rb +54 -0
- data/lib/synth_blocks/drum/kick_drum.rb +54 -0
- data/lib/synth_blocks/drum/snare_drum.rb +87 -0
- data/lib/synth_blocks/drum/tuned_drum.rb +25 -0
- data/lib/synth_blocks/fx/chorus.rb +85 -0
- data/lib/synth_blocks/fx/compressor.rb +121 -0
- data/lib/synth_blocks/fx/delay.rb +42 -0
- data/lib/synth_blocks/fx/eq.rb +92 -0
- data/lib/synth_blocks/fx/g_verb.rb +275 -0
- data/lib/synth_blocks/fx/limiter.rb +15 -0
- data/lib/synth_blocks/fx/waveshaper.rb +24 -0
- data/lib/synth_blocks/mixer/mixer_channel.rb +106 -0
- data/lib/synth_blocks/mixer/send_channel.rb +25 -0
- data/lib/synth_blocks/mod/adsr.rb +83 -0
- data/lib/synth_blocks/mod/envelope.rb +35 -0
- data/lib/synth_blocks/sequencer/sequencer_dsl.rb +219 -0
- data/lib/synth_blocks/synth/monosynth.rb +77 -0
- data/lib/synth_blocks/synth/polysynth.rb +89 -0
- data/lib/synth_blocks/utils.rb +17 -0
- metadata +113 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'synth_blocks/core/sound'
|
2
|
+
require 'synth_blocks/core/oscillator'
|
3
|
+
require 'synth_blocks/mod/envelope'
|
4
|
+
|
5
|
+
module SynthBlocks
|
6
|
+
module Drum
|
7
|
+
##
|
8
|
+
# A simple kick drum generator
|
9
|
+
class KickDrum < SynthBlocks::Core::Sound
|
10
|
+
|
11
|
+
##
|
12
|
+
# === Structure
|
13
|
+
# Pitch Env > Sine wave OSC >
|
14
|
+
#
|
15
|
+
# === parameter:
|
16
|
+
# - pitch_attack, pitch_decay - Pitch envelope params in s
|
17
|
+
# - amp_attack, amp_decay - Amp envelope params in s
|
18
|
+
# - base_frequency - base frequency in Hz
|
19
|
+
# - pitch_mod - frequency modulation amount in Hz
|
20
|
+
def initialize(sfreq, preset = {})
|
21
|
+
super(sfreq, mode: :polyphonic)
|
22
|
+
@preset = {
|
23
|
+
pitch_attack: 0.001,
|
24
|
+
pitch_decay: 0.05,
|
25
|
+
amp_attack: 0.001,
|
26
|
+
amp_decay: 0.1,
|
27
|
+
base_frequency: 50,
|
28
|
+
pitch_mod: 200
|
29
|
+
}.merge(preset)
|
30
|
+
@oscillator = SynthBlocks::Core::Oscillator.new(@sampling_frequency)
|
31
|
+
@pitch_env = SynthBlocks::Mod::Envelope.new(@preset[:pitch_attack], @preset[:pitch_decay])
|
32
|
+
@amp_env = SynthBlocks::Mod::Envelope.new(@preset[:amp_attack], @preset[:amp_decay])
|
33
|
+
end
|
34
|
+
|
35
|
+
def duration(_) # :nodoc:
|
36
|
+
@preset[:amp_attack] + @preset[:amp_decay]
|
37
|
+
end
|
38
|
+
##
|
39
|
+
# Run generator
|
40
|
+
def run(offset)
|
41
|
+
t = time(offset)
|
42
|
+
events = active_events(t)
|
43
|
+
if events.empty?
|
44
|
+
0.0
|
45
|
+
else
|
46
|
+
event = events[events.keys.last]
|
47
|
+
local_started = t - event[:started]
|
48
|
+
osc_out = @oscillator.run(@preset[:base_frequency].to_f + @pitch_env.run(local_started) * @preset[:pitch_mod].to_f, waveform: :sine)
|
49
|
+
osc_out = osc_out * 1.0 * @amp_env.run(local_started)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'synth_blocks/core/sound'
|
2
|
+
require 'synth_blocks/core/state_variable_filter'
|
3
|
+
require 'synth_blocks/drum/kick_drum'
|
4
|
+
require 'synth_blocks/mod/envelope'
|
5
|
+
|
6
|
+
module SynthBlocks
|
7
|
+
module Drum
|
8
|
+
##
|
9
|
+
# Simple snare drum generator
|
10
|
+
class SnareDrum < SynthBlocks::Core::Sound
|
11
|
+
##
|
12
|
+
# === Parameters
|
13
|
+
# [flt_frequency] Noise filter frequency
|
14
|
+
# [flt_envmod] Noise filter frequency modulation by envelope
|
15
|
+
# [flt_attack, flt_decay] Noise filter envelope params
|
16
|
+
# [flt_Q] Noise filter Q/resonance
|
17
|
+
# [noise_amp_attack, noise_amp_decay] Noise amp envelope params
|
18
|
+
# [noise_vol, drum_vol] Noise and Drum body volumes
|
19
|
+
# [base_frequency] Drum body base frequency (see KickDrum#initialize)
|
20
|
+
# [pitch_mod] Drum body pitch mod (see KickDrum#initialize)
|
21
|
+
# [pitch_attack, pitch_decay] Drum body pitch env params
|
22
|
+
#
|
23
|
+
def initialize(sfreq, preset = {})
|
24
|
+
super(sfreq, mode: :polyphonic)
|
25
|
+
@preset = {
|
26
|
+
flt_frequency: 4000,
|
27
|
+
flt_envmod: 6000,
|
28
|
+
flt_attack: 0.001,
|
29
|
+
flt_decay: 0.1,
|
30
|
+
flt_Q: 2,
|
31
|
+
noise_amp_attack: 0.001,
|
32
|
+
noise_amp_decay: 0.15,
|
33
|
+
noise_vol: 0.5,
|
34
|
+
drum_vol: 0.3,
|
35
|
+
base_frequency: 200,
|
36
|
+
pitch_mod: 200,
|
37
|
+
pitch_decay: 0.07
|
38
|
+
|
39
|
+
}.merge(preset)
|
40
|
+
@drum = SynthBlocks::Drum::KickDrum.new(sfreq, @preset)
|
41
|
+
@filter = SynthBlocks::Core::StateVariableFilter.new(sfreq)
|
42
|
+
@flt_env = SynthBlocks::Mod::Envelope.new(@preset[:flt_attack], @preset[:flt_decay])
|
43
|
+
@amp_env = SynthBlocks::Mod::Envelope.new(@preset[:noise_amp_attack], @preset[:noise_amp_decay])
|
44
|
+
end
|
45
|
+
|
46
|
+
# create a note on event
|
47
|
+
# [t] time in seconds since song start
|
48
|
+
# [note] MIDI note number
|
49
|
+
# [velocity] velocity (currently unused)
|
50
|
+
def start(t, note = 36, velocity = 1.0)
|
51
|
+
super(t, note, velocity)
|
52
|
+
@drum.start(t, note, velocity)
|
53
|
+
end
|
54
|
+
|
55
|
+
# create a note off event
|
56
|
+
# [t] time in seconds since song start
|
57
|
+
# [note] MIDI note number
|
58
|
+
def stop(t, note = 36)
|
59
|
+
super(t, note)
|
60
|
+
@drum.stop(t, note)
|
61
|
+
end
|
62
|
+
|
63
|
+
def duration(t) # :nodoc:
|
64
|
+
[@preset[:noise_amp_attack] + @preset[:noise_amp_decay], @drum.duration(t)].max
|
65
|
+
end
|
66
|
+
|
67
|
+
# run the generator
|
68
|
+
def run(offset)
|
69
|
+
drum_out = @drum.run(offset)
|
70
|
+
# time in seconds
|
71
|
+
t = time(offset)
|
72
|
+
events = active_events(t)
|
73
|
+
if events.empty?
|
74
|
+
0.0
|
75
|
+
else
|
76
|
+
event = events[events.keys.last]
|
77
|
+
# lfo_out = (@lfo.run(@preset[:lfo_frequency], waveform: @preset[:lfo_waveform]) + 1) / 8 + 0.5
|
78
|
+
noise_out = rand * 2.0 - 1.0
|
79
|
+
local_started = t - event[:started]
|
80
|
+
noise_out = @filter.run(noise_out, @preset[:flt_frequency] + @flt_env.run(local_started) * @preset[:flt_envmod], @preset[:flt_Q])
|
81
|
+
noise_out = 0.3 * noise_out * @amp_env.run(local_started)
|
82
|
+
noise_out * @preset[:noise_vol] + drum_out * @preset[:drum_vol]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'synth_blocks/drum/kick_drum'
|
2
|
+
|
3
|
+
module SynthBlocks
|
4
|
+
module Drum
|
5
|
+
##
|
6
|
+
# Special case of the kick drum that allows to run it from a note pattern to
|
7
|
+
# create percussive sounds
|
8
|
+
class TunedDrum < KickDrum
|
9
|
+
def run(offset)
|
10
|
+
t = time(offset)
|
11
|
+
events = active_events(t)
|
12
|
+
if events.empty?
|
13
|
+
0.0
|
14
|
+
else
|
15
|
+
event = events[events.keys.last]
|
16
|
+
note = events.keys.last
|
17
|
+
base_freq = frequency(note)
|
18
|
+
local_started = t - event[:started]
|
19
|
+
osc_out = @oscillator.run(base_freq + @pitch_env.run(local_started) * @preset[:pitch_mod].to_f, waveform: :sine)
|
20
|
+
osc_out = osc_out * 1.0 * @amp_env.run(local_started)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'synth_blocks/core/one_pole_lowpass'
|
2
|
+
|
3
|
+
module SynthBlocks
|
4
|
+
module Fx
|
5
|
+
##
|
6
|
+
# A simple chorus
|
7
|
+
class Chorus
|
8
|
+
##
|
9
|
+
attr_writer :phase, :rate, :delay_time, :mix # :nodoc:
|
10
|
+
##
|
11
|
+
# Create new Chorus instance
|
12
|
+
#
|
13
|
+
# phase allows you to shift the phase of the delayed signal additionally
|
14
|
+
#
|
15
|
+
# rate is the LFO rate in Hz
|
16
|
+
#
|
17
|
+
# delay_time is the maximum delay time in ms
|
18
|
+
#
|
19
|
+
# mix is the ratio between original and delayed signal. 1.0 would mean only
|
20
|
+
# delayed signal (which wouldn't make any sense)
|
21
|
+
def initialize(sample_rate, phase: 0.0, rate: 0.5, delay_time: 7.0, mix: 0.5)
|
22
|
+
@sample_rate = sample_rate
|
23
|
+
@rate = rate
|
24
|
+
@delay_time = delay_time
|
25
|
+
@mix = mix
|
26
|
+
|
27
|
+
@z1 = 0.0
|
28
|
+
@sign = 0
|
29
|
+
@lfo_phase = phase * 2.0 - 1.0
|
30
|
+
@lfo_step_size = (4.0 * @rate / @sample_rate)
|
31
|
+
@lfo_sign = 1.0
|
32
|
+
|
33
|
+
# Compute required buffer size for desired delay and allocate it
|
34
|
+
# Add extra point to aid in interpolation later
|
35
|
+
@delay_line_length = ((@delay_time * @sample_rate * 0.001).floor * 2).to_i
|
36
|
+
@delay_line = [0.0] * @delay_line_length
|
37
|
+
@write_ptr = @delay_line_length - 1
|
38
|
+
@lp = SynthBlocks::Core::OnePoleLP.new
|
39
|
+
@output = 0.0
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# run the chorus
|
44
|
+
def run(input)
|
45
|
+
# Get delay time
|
46
|
+
offset = (next_lfo() * 0.3 + 0.4) * @delay_time * @sample_rate * 0.001
|
47
|
+
|
48
|
+
# Compute the largest read pointer based on the offset. If ptr
|
49
|
+
# is before the first delayline location, wrap around end point
|
50
|
+
ptr = @write_ptr - offset.floor;
|
51
|
+
ptr += @delay_line_length - 1 if ptr < 0
|
52
|
+
|
53
|
+
|
54
|
+
ptr2 = ptr - 1
|
55
|
+
ptr2 += @delay_line_length - 1 if ptr2 < 0
|
56
|
+
|
57
|
+
frac = offset - offset.floor.to_f
|
58
|
+
@output = @delay_line[ptr2] + @delay_line[ptr] * (1.0 - frac) - (1.0 - frac) * @z1
|
59
|
+
@z1 = @output
|
60
|
+
|
61
|
+
# Low pass
|
62
|
+
@lp.run(@output, 0.95)
|
63
|
+
|
64
|
+
# Write the input sample and any feedback to delayline
|
65
|
+
@delay_line[@write_ptr] = input
|
66
|
+
|
67
|
+
# Increment buffer index and wrap if necesary
|
68
|
+
@write_ptr += 1
|
69
|
+
@write_ptr = 0 if @write_ptr >= @delay_line_length
|
70
|
+
return (@output * @mix) + (input * (1.0-@mix))
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def next_lfo()
|
76
|
+
if @lfo_phase >= 1.0
|
77
|
+
@lfo_sign = -1.0
|
78
|
+
elsif @lfo_phase <= -1.0
|
79
|
+
@lfo_sign = 1.0
|
80
|
+
end
|
81
|
+
@lfo_phase += @lfo_step_size * @lfo_sign
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module SynthBlocks
|
2
|
+
module Fx
|
3
|
+
|
4
|
+
class EnvelopeDetector # :nodoc:
|
5
|
+
def initialize(srate, tc: ms)
|
6
|
+
@sample_rate = srate
|
7
|
+
@ms = tc
|
8
|
+
set_coef()
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_coef
|
12
|
+
@coef = Math.exp( -1000.0 / ( @ms * @sample_rate) )
|
13
|
+
end
|
14
|
+
|
15
|
+
def tc=(tc)
|
16
|
+
@ms = tc
|
17
|
+
set_coef()
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(input, state)
|
21
|
+
input + @coef * ( state - input )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class AttRelEnvelope # :nodoc:
|
27
|
+
def initialize(srate, attack:, release:)
|
28
|
+
@attack = EnvelopeDetector.new(srate, tc: attack)
|
29
|
+
@release = EnvelopeDetector.new(srate, tc: release)
|
30
|
+
end
|
31
|
+
|
32
|
+
def attack=(attack)
|
33
|
+
@attack.tc = attack
|
34
|
+
end
|
35
|
+
|
36
|
+
def release=(decay)
|
37
|
+
@release.tc = decay
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(input, state)
|
41
|
+
if input > state
|
42
|
+
@attack.run( input, state )
|
43
|
+
else
|
44
|
+
@release.run( input, state )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# simple compresor
|
51
|
+
# taken from http://www.musicdsp.org/en/latest/Effects/204-simple-compressor-class-c.html
|
52
|
+
|
53
|
+
class Compressor
|
54
|
+
DC_OFFSET = 1.0E-25 # :nodoc:
|
55
|
+
LOG_2_DB = 8.6858896380650365530225783783321 # :nodoc:
|
56
|
+
DB_2_LOG = 0.11512925464970228420089957273422 # :nodoc:
|
57
|
+
attr_writer :ratio, :threshold, :window # :nodoc:
|
58
|
+
##
|
59
|
+
# Create compressor instance
|
60
|
+
#
|
61
|
+
# attack is the attack time in ms
|
62
|
+
#
|
63
|
+
# release is the release time in ms
|
64
|
+
#
|
65
|
+
# ratio is the compresor ratio
|
66
|
+
#
|
67
|
+
# threshold is the knee threshold
|
68
|
+
def initialize(srate, attack: 10.0, release: 100.0, ratio: 1.0, threshold: 0.0)
|
69
|
+
@sample_rate = srate
|
70
|
+
@envelope = AttRelEnvelope.new(srate, attack: attack, release: release)
|
71
|
+
@env_db = DC_OFFSET
|
72
|
+
@ratio = ratio
|
73
|
+
@threshold = threshold
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# set attack
|
78
|
+
def attack=(attack)
|
79
|
+
@envelope.attack = attack
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# set release
|
84
|
+
def release=(release)
|
85
|
+
@envelope.release = release
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
##
|
90
|
+
# run compressor
|
91
|
+
def run(input)
|
92
|
+
rect = input.abs
|
93
|
+
rect += DC_OFFSET
|
94
|
+
key_db = lin2db(rect)
|
95
|
+
|
96
|
+
over_db = key_db - @threshold
|
97
|
+
over_db = 0.0 if over_db < 0.0
|
98
|
+
|
99
|
+
# attack/release
|
100
|
+
over_db += DC_OFFSET
|
101
|
+
@env_db = @envelope.run(over_db, @env_db)
|
102
|
+
over_db = @env_db - DC_OFFSET
|
103
|
+
|
104
|
+
gr = over_db * @ratio - 1.0
|
105
|
+
gr = db2lin(gr)
|
106
|
+
input * gr
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def lin2db(lin)
|
112
|
+
return Math.log( lin ) * LOG_2_DB
|
113
|
+
end
|
114
|
+
|
115
|
+
def db2lin(db)
|
116
|
+
return Math.exp( db * DB_2_LOG )
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SynthBlocks
|
2
|
+
module Fx
|
3
|
+
##
|
4
|
+
# Simple delay with mix and feedback parameters
|
5
|
+
# Currently doesn't really have a variable delay time, I'll tackle that when I
|
6
|
+
# need it. It uses a simple ring buffer implementation and delay time is only
|
7
|
+
# exact down to the sample.
|
8
|
+
class Delay
|
9
|
+
attr_reader :mix, :feedback # :nodoc:
|
10
|
+
|
11
|
+
##
|
12
|
+
# time is given in seconds
|
13
|
+
#
|
14
|
+
# mix (0 = no delay, 1 = only delay)
|
15
|
+
#
|
16
|
+
# feedback (0 = zero feedback, 1 = full feedback (not advised))
|
17
|
+
#
|
18
|
+
# if given a block it will call the block from the run method to process the
|
19
|
+
# feedback signal
|
20
|
+
def initialize(sample_rate, time: 0.2, mix: 0.5, feedback: 0.4, &block)
|
21
|
+
@buffer = Array.new((sample_rate.to_f * time).floor)
|
22
|
+
@block = block
|
23
|
+
@pointer = 0
|
24
|
+
@mix = mix
|
25
|
+
@feedback = feedback
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# run delay
|
30
|
+
def run(input)
|
31
|
+
old_pointer = @pointer
|
32
|
+
@pointer = (@pointer + 1) % @buffer.length
|
33
|
+
delayed = (@buffer[@pointer] || 0.0)
|
34
|
+
if @block
|
35
|
+
delayed = @block.call(delayed)
|
36
|
+
end
|
37
|
+
@buffer[old_pointer] = input + (feedback * delayed)
|
38
|
+
input * (1.0 - mix) + delayed * mix
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module SynthBlocks
|
2
|
+
module Fx
|
3
|
+
##
|
4
|
+
# Simple 3 band Equalizer with variable shelving frequencies
|
5
|
+
#
|
6
|
+
# Source: http://www.musicdsp.org/en/latest/Filters/236-3-band-equaliser.html
|
7
|
+
class Eq
|
8
|
+
VSA = 1.0 / 4294967295.0 # very small amount (Denormal fix) :nodoc:
|
9
|
+
|
10
|
+
attr_accessor :low_gain, :mid_gain, :high_gain # :nodoc: Low Gain, Mid Gain, High Gain
|
11
|
+
|
12
|
+
##
|
13
|
+
# Create new Equalizer instance
|
14
|
+
#
|
15
|
+
# lowfreq and highfreq are the shelving frequencies in Hz
|
16
|
+
def initialize(sample_rate=44100, lowfreq: 880, highfreq: 5000)
|
17
|
+
|
18
|
+
# Poles Lowpass
|
19
|
+
@f1p0 = 0.0
|
20
|
+
@f1p1 = 0.0
|
21
|
+
@f1p2 = 0.0
|
22
|
+
@f1p3 = 0.0
|
23
|
+
|
24
|
+
# Poles Highpass
|
25
|
+
@f2p0 = 0.0
|
26
|
+
@f2p1 = 0.0
|
27
|
+
@f2p2 = 0.0
|
28
|
+
@f2p3 = 0.0
|
29
|
+
|
30
|
+
# Sample history buffer
|
31
|
+
|
32
|
+
@sdm1 = 0.0 # Sample data minus 1
|
33
|
+
@sdm2 = 0.0 # 2
|
34
|
+
@sdm3 = 0.0 # 3
|
35
|
+
|
36
|
+
# Gain Controls
|
37
|
+
# Set Low/Mid/High gains to unity
|
38
|
+
|
39
|
+
@low_gain = 1.0 # low gain
|
40
|
+
@mid_gain = 1.0 # mid gain
|
41
|
+
@high_gain = 1.0 # high gain
|
42
|
+
|
43
|
+
# Calculate filter cutoff frequencies
|
44
|
+
|
45
|
+
@lf = 2 * Math.sin(Math::PI * (lowfreq.to_f / sample_rate.to_f))
|
46
|
+
@hf = 2 * Math.sin(Math::PI * (highfreq.to_f / sample_rate.to_f))
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# run equalizer
|
51
|
+
def run(sample)
|
52
|
+
# Filter #1 (lowpass)
|
53
|
+
|
54
|
+
@f1p0 += (@lf * (sample - @f1p0)) + VSA;
|
55
|
+
@f1p1 += (@lf * (@f1p0 - @f1p1));
|
56
|
+
@f1p2 += (@lf * (@f1p1 - @f1p2));
|
57
|
+
@f1p3 += (@lf * (@f1p2 - @f1p3));
|
58
|
+
|
59
|
+
l = @f1p3;
|
60
|
+
|
61
|
+
# Filter #2 (highpass)
|
62
|
+
|
63
|
+
@f2p0 += (@hf * (sample - @f2p0)) + VSA;
|
64
|
+
@f2p1 += (@hf * (@f2p0 - @f2p1));
|
65
|
+
@f2p2 += (@hf * (@f2p1 - @f2p2));
|
66
|
+
@f2p3 += (@hf * (@f2p2 - @f2p3));
|
67
|
+
|
68
|
+
h = @sdm3 - @f2p3;
|
69
|
+
|
70
|
+
# Calculate midrange (signal - (low + high))
|
71
|
+
|
72
|
+
m = @sdm3 - (h + l);
|
73
|
+
|
74
|
+
# Scale, Combine and store
|
75
|
+
|
76
|
+
l *= @low_gain
|
77
|
+
m *= @mid_gain
|
78
|
+
h *= @high_gain
|
79
|
+
|
80
|
+
# Shuffle history buffer
|
81
|
+
|
82
|
+
@sdm3 = @sdm2;
|
83
|
+
@sdm2 = @sdm1;
|
84
|
+
@sdm1 = sample;
|
85
|
+
|
86
|
+
# Return result
|
87
|
+
|
88
|
+
l + m + h
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|