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