synthesizer 3.1.0 → 3.4.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 +4 -4
- data/.gitignore +4 -0
- data/examples/{metronome.rb → audio_input_metronome.rb} +8 -2
- data/examples/{step_editor.rb → audio_input_step_editor.rb} +10 -2
- data/examples/filter_band_pass.rb +73 -0
- data/examples/{lpf.rb → filter_serial.rb} +9 -2
- data/examples/oscillator_pan.rb +70 -0
- data/examples/{formant_vocoder.rb → oscillator_source_formant_vocoder.rb} +10 -3
- data/examples/{formant_vocoder_sweep.rb → oscillator_source_formant_vocoder_sweep.rb} +13 -6
- data/examples/oscillator_sync.rb +71 -0
- data/examples/oscillator_unison.rb +64 -0
- data/examples/oscillator_volume.rb +70 -0
- data/examples/{mono.rb → synth_mono.rb} +10 -2
- data/examples/{poly.rb → synth_poly.rb} +10 -2
- data/{examples → jupyter}/adsr.ipynb +20 -17
- data/jupyter/curves.ipynb +103 -0
- data/jupyter/shapes.ipynb +111 -0
- data/lib/audio_stream/audio_input_metronome.rb +1 -1
- data/lib/synthesizer/amplifier.rb +4 -1
- data/lib/synthesizer/filter/band_pass_filter.rb +3 -3
- data/lib/synthesizer/filter/high_pass_filter.rb +3 -3
- data/lib/synthesizer/filter/high_shelf_filter.rb +4 -4
- data/lib/synthesizer/filter/low_pass_filter.rb +3 -3
- data/lib/synthesizer/filter/low_shelf_filter.rb +4 -4
- data/lib/synthesizer/filter/parallel.rb +2 -2
- data/lib/synthesizer/filter/peaking_filter.rb +5 -5
- data/lib/synthesizer/filter/serial.rb +2 -2
- data/lib/synthesizer/modulation/adsr.rb +37 -35
- data/lib/synthesizer/modulation/curve.rb +3 -0
- data/lib/synthesizer/modulation/glide.rb +9 -8
- data/lib/synthesizer/modulation/lfo.rb +23 -23
- data/lib/synthesizer/modulation_value.rb +5 -5
- data/lib/synthesizer/mono_synth.rb +7 -2
- data/lib/synthesizer/oscillator.rb +11 -9
- data/lib/synthesizer/oscillator_source/base.rb +35 -5
- data/lib/synthesizer/oscillator_source/formant_vocoder.rb +27 -19
- data/lib/synthesizer/oscillator_source/pulse.rb +11 -3
- data/lib/synthesizer/poly_synth.rb +5 -2
- data/lib/synthesizer/processor/high.rb +63 -0
- data/lib/synthesizer/processor/low.rb +59 -0
- data/lib/synthesizer/processor.rb +6 -44
- data/lib/synthesizer/quality.rb +6 -0
- data/lib/synthesizer/shape.rb +4 -4
- data/lib/synthesizer/shape_pos.rb +22 -19
- data/lib/synthesizer/unison.rb +28 -16
- data/lib/synthesizer/version.rb +1 -1
- data/lib/synthesizer.rb +1 -0
- data/samples/cinema.rb +730 -0
- data/samples/cinema_drum.wav +0 -0
- data/samples/daijoubu.rb +811 -0
- data/samples/daijoubu_drum.wav +0 -0
- data/samples/kira_power.rb +987 -0
- data/samples/kira_power_drum.wav +0 -0
- data/synthesizer.gemspec +1 -1
- metadata +32 -18
- data/examples/curves.ipynb +0 -105
- data/examples/shapes.ipynb +0 -116
@@ -5,9 +5,9 @@ module Synthesizer
|
|
5
5
|
@filters = filters
|
6
6
|
end
|
7
7
|
|
8
|
-
def generator(note_perform,
|
8
|
+
def generator(note_perform, samplecount)
|
9
9
|
filter_mods = @filters.map {|filter|
|
10
|
-
filter.generator(note_perform,
|
10
|
+
filter.generator(note_perform, samplecount)
|
11
11
|
}
|
12
12
|
|
13
13
|
-> {
|
@@ -7,13 +7,13 @@ module Synthesizer
|
|
7
7
|
@gain = ModulationValue.create(gain)
|
8
8
|
end
|
9
9
|
|
10
|
-
def generator(note_perform,
|
10
|
+
def generator(note_perform, samplecount)
|
11
11
|
soundinfo = note_perform.synth.soundinfo
|
12
|
-
filter = AudioStream::Fx::
|
12
|
+
filter = AudioStream::Fx::PeakingFilter.new(soundinfo)
|
13
13
|
|
14
|
-
freq_mod = ModulationValue.balance_generator(note_perform,
|
15
|
-
bandwidth_mod = ModulationValue.balance_generator(note_perform,
|
16
|
-
gain_mod = ModulationValue.balance_generator(note_perform,
|
14
|
+
freq_mod = ModulationValue.balance_generator(note_perform, samplecount, @freq)
|
15
|
+
bandwidth_mod = ModulationValue.balance_generator(note_perform, samplecount, @bandwidth)
|
16
|
+
gain_mod = ModulationValue.balance_generator(note_perform, samplecount, @gain)
|
17
17
|
|
18
18
|
-> {
|
19
19
|
filter.update_coef(freq: freq_mod[], bandwidth: bandwidth_mod[], gain: gain_mod[])
|
@@ -5,9 +5,9 @@ module Synthesizer
|
|
5
5
|
@filters = filters
|
6
6
|
end
|
7
7
|
|
8
|
-
def generator(note_perform,
|
8
|
+
def generator(note_perform, samplecount)
|
9
9
|
filter_mods = @filters.map {|filter|
|
10
|
-
filter.generator(note_perform,
|
10
|
+
filter.generator(note_perform, samplecount)
|
11
11
|
}
|
12
12
|
|
13
13
|
-> {
|
@@ -2,46 +2,45 @@ module Synthesizer
|
|
2
2
|
module Modulation
|
3
3
|
class Adsr
|
4
4
|
|
5
|
-
# @param attack [Float] attack sec (0.0~)
|
5
|
+
# @param attack [AudioStream::Rate | Float] attack sec (0.0~)
|
6
6
|
# @param attack_curve [Synthesizer::Curve]
|
7
|
-
# @param hold [Float] hold sec (0.0~)
|
8
|
-
# @param decay [Float] decay sec (0.0~)
|
7
|
+
# @param hold [AudioStream::Rate | Float] hold sec (0.0~)
|
8
|
+
# @param decay [AudioStream::Rate | Float] decay sec (0.0~)
|
9
9
|
# @param sustain_curve [Synthesizer::Curve]
|
10
|
-
# @param sustain [Float] sustain
|
11
|
-
# @param release [Float] release sec (0.0~)
|
10
|
+
# @param sustain [Float] sustain level (0.0~1.0)
|
11
|
+
# @param release [AudioStream::Rate | Float] release sec (0.0~)
|
12
12
|
# @param release_curve [Synthesizer::Curve]
|
13
13
|
def initialize(attack:, attack_curve: Curve::EaseOut, hold: 0.0, decay:, sustain_curve: Curve::EaseOut, sustain:, release:, release_curve: Curve::EaseOut)
|
14
|
-
@attack = attack
|
14
|
+
@attack = AudioStream::Rate.sec(attack)
|
15
15
|
@attack_curve = attack_curve
|
16
|
-
@hold = hold
|
17
|
-
@decay = decay
|
16
|
+
@hold = AudioStream::Rate.sec(hold)
|
17
|
+
@decay = AudioStream::Rate.sec(decay)
|
18
18
|
@sustain_curve = sustain_curve
|
19
19
|
@sustain = sustain
|
20
|
-
@release = release
|
20
|
+
@release = AudioStream::Rate.sec(release)
|
21
21
|
@release_curve = release_curve
|
22
22
|
end
|
23
23
|
|
24
|
-
def note_on_envelope(
|
24
|
+
def note_on_envelope(soundinfo, samplecount, sustain: false, &block)
|
25
25
|
Enumerator.new do |yld|
|
26
26
|
# attack
|
27
|
-
|
28
|
-
|
29
|
-
x = i.to_f /
|
27
|
+
attack_len = (@attack.sample(soundinfo) / samplecount).to_i
|
28
|
+
attack_len.times {|i|
|
29
|
+
x = i.to_f / attack_len
|
30
30
|
y = @attack_curve[x]
|
31
31
|
yld << y
|
32
32
|
}
|
33
33
|
|
34
34
|
# hold
|
35
|
-
|
36
|
-
rate.to_i.times {|i|
|
35
|
+
(@hold.sample(soundinfo) / samplecount).to_i.times {|i|
|
37
36
|
yld << 1.0
|
38
37
|
}
|
39
38
|
|
40
39
|
# decay
|
41
|
-
|
42
|
-
|
43
|
-
x = i.to_f /
|
44
|
-
y = 1.0 - @sustain_curve[x]
|
40
|
+
decay_len = (@decay.sample(soundinfo) / samplecount).to_i
|
41
|
+
decay_len.times {|i|
|
42
|
+
x = i.to_f / decay_len
|
43
|
+
y = 1.0 - @sustain_curve[x] * (1.0 - @sustain)
|
45
44
|
yld << y
|
46
45
|
}
|
47
46
|
|
@@ -54,12 +53,12 @@ module Synthesizer
|
|
54
53
|
end.each(&block)
|
55
54
|
end
|
56
55
|
|
57
|
-
def note_off_envelope(
|
56
|
+
def note_off_envelope(soundinfo, samplecount, sustain: false, &block)
|
58
57
|
Enumerator.new do |yld|
|
59
58
|
# release
|
60
|
-
|
61
|
-
|
62
|
-
x = i.to_f /
|
59
|
+
release_len = (@release.sample(soundinfo) / samplecount).to_i
|
60
|
+
release_len.times {|i|
|
61
|
+
x = i.to_f / release_len
|
63
62
|
y = 1.0 - @release_curve[x]
|
64
63
|
yld << y
|
65
64
|
}
|
@@ -73,9 +72,11 @@ module Synthesizer
|
|
73
72
|
end.each(&block)
|
74
73
|
end
|
75
74
|
|
76
|
-
def generator(note_perform,
|
77
|
-
|
78
|
-
|
75
|
+
def generator(note_perform, samplecount, release_sustain:)
|
76
|
+
soundinfo = note_perform.synth.soundinfo
|
77
|
+
|
78
|
+
note_on = note_on_envelope(soundinfo, samplecount, sustain: true)
|
79
|
+
note_off = note_off_envelope(soundinfo, samplecount, sustain: release_sustain)
|
79
80
|
last = 0.0
|
80
81
|
|
81
82
|
-> {
|
@@ -88,26 +89,27 @@ module Synthesizer
|
|
88
89
|
end
|
89
90
|
|
90
91
|
|
91
|
-
def amp_generator(note_perform,
|
92
|
+
def amp_generator(note_perform, samplecount, depth, &block)
|
92
93
|
bottom = 1.0 - depth
|
93
|
-
gen = generator(note_perform,
|
94
|
+
gen = generator(note_perform, samplecount, release_sustain: 0.0<bottom)
|
94
95
|
|
95
96
|
-> {
|
96
97
|
gen[] * depth + bottom
|
97
98
|
}
|
98
99
|
end
|
99
100
|
|
100
|
-
def balance_generator(note_perform,
|
101
|
-
gen = generator(note_perform,
|
101
|
+
def balance_generator(note_perform, samplecount, depth, &block)
|
102
|
+
gen = generator(note_perform, samplecount, release_sustain: true)
|
102
103
|
|
103
104
|
-> {
|
104
105
|
gen[] * depth
|
105
106
|
}
|
106
107
|
end
|
107
108
|
|
108
|
-
def plot_data(
|
109
|
-
|
110
|
-
|
109
|
+
def plot_data(soundinfo)
|
110
|
+
samplecount = soundinfo.window_size.to_f
|
111
|
+
note_on = note_on_envelope(soundinfo, samplecount, sustain: false)
|
112
|
+
note_off = note_off_envelope(soundinfo, samplecount, sustain: false)
|
111
113
|
last = 0.0
|
112
114
|
|
113
115
|
xs = []
|
@@ -127,8 +129,8 @@ module Synthesizer
|
|
127
129
|
{x: xs, y: ys}
|
128
130
|
end
|
129
131
|
|
130
|
-
def plot(
|
131
|
-
data = plot_data(
|
132
|
+
def plot(soundinfo)
|
133
|
+
data = plot_data(soundinfo)
|
132
134
|
Plotly::Plot.new(data: [data])
|
133
135
|
end
|
134
136
|
end
|
@@ -2,9 +2,9 @@ module Synthesizer
|
|
2
2
|
module Modulation
|
3
3
|
class Glide
|
4
4
|
|
5
|
-
# @param time [Float] glide time sec (0.0~)
|
5
|
+
# @param time [AudioStream::Rate | Float] glide time sec (0.0~)
|
6
6
|
def initialize(time:)
|
7
|
-
@time = time
|
7
|
+
@time = AudioStream::Rate.sec(time)
|
8
8
|
|
9
9
|
@base = 0.0
|
10
10
|
@current = 0.0
|
@@ -26,15 +26,16 @@ module Synthesizer
|
|
26
26
|
@diff = target - @current
|
27
27
|
end
|
28
28
|
|
29
|
-
def generator(note_perform,
|
30
|
-
|
29
|
+
def generator(note_perform, samplecount)
|
30
|
+
soundinfo = note_perform.synth.soundinfo
|
31
|
+
rate = @time.sample(soundinfo) / samplecount
|
31
32
|
|
32
33
|
-> {
|
33
34
|
ret = nil
|
34
35
|
|
35
|
-
if note_perform.
|
36
|
+
if !note_perform.released?
|
36
37
|
# Note On
|
37
|
-
if 0
|
38
|
+
if 0<rate && @target!=@current
|
38
39
|
# Gliding
|
39
40
|
x = @diff / rate
|
40
41
|
if x.abs<(@target-@current).abs
|
@@ -60,8 +61,8 @@ module Synthesizer
|
|
60
61
|
}
|
61
62
|
end
|
62
63
|
|
63
|
-
def balance_generator(note_perform,
|
64
|
-
gen = generator(note_perform,
|
64
|
+
def balance_generator(note_perform, samplecount, depth)
|
65
|
+
gen = generator(note_perform, samplecount)
|
65
66
|
|
66
67
|
-> {
|
67
68
|
gen[] * depth
|
@@ -3,51 +3,51 @@ module Synthesizer
|
|
3
3
|
class Lfo
|
4
4
|
|
5
5
|
# @param shape [Synthesizer::Shape]
|
6
|
-
# @param delay [Float] delay sec (0.0~)
|
7
|
-
# @param attack [Float] attack sec (0.0~)
|
6
|
+
# @param delay [AudioStream::Rate | Float] delay sec (0.0~)
|
7
|
+
# @param attack [AudioStream::Rate | Float] attack sec (0.0~)
|
8
8
|
# @param attack_curve [Synthesizer::Curve]
|
9
9
|
# @param phase [Float] phase percent (0.0~1.0)
|
10
|
-
# @param rate [Float] wave freq (0.0~)
|
11
|
-
def initialize(shape: Shape::Sine, delay: 0.0, attack: 0.0, attack_curve: Curve::Straight, phase: 0.0, rate: 3
|
10
|
+
# @param rate [AudioStream::Rate | Float] wave freq (0.0~)
|
11
|
+
def initialize(shape: Shape::Sine, delay: 0.0, attack: 0.0, attack_curve: Curve::Straight, phase: 0.0, rate: 0.3)
|
12
12
|
@shape = shape
|
13
|
-
@delay = delay
|
14
|
-
@attack = attack
|
13
|
+
@delay = AudioStream::Rate.sec(delay)
|
14
|
+
@attack = AudioStream::Rate.sec(attack)
|
15
15
|
@attack_curve = attack_curve
|
16
|
-
@phase = phase
|
17
|
-
@rate = rate
|
16
|
+
@phase = phase.to_f
|
17
|
+
@rate = AudioStream::Rate.sec(rate)
|
18
18
|
end
|
19
19
|
|
20
|
-
def generator(note_perform,
|
21
|
-
|
22
|
-
|
20
|
+
def generator(note_perform, samplecount, &block)
|
21
|
+
soundinfo = note_perform.synth.soundinfo
|
22
|
+
hz = @rate.freq(soundinfo)
|
23
23
|
|
24
|
-
|
24
|
+
Enumerator.new do |yld|
|
25
|
+
pos = ShapePos.new(soundinfo.samplerate / samplecount, @phase)
|
25
26
|
|
26
27
|
# delay
|
27
|
-
|
28
|
-
rate.to_i.times {|i|
|
28
|
+
(@delay.sample(soundinfo) / samplecount).to_i.times {|i|
|
29
29
|
yld << 0.0
|
30
30
|
}
|
31
31
|
|
32
32
|
# attack
|
33
|
-
|
34
|
-
|
35
|
-
x = i.to_f /
|
33
|
+
attack_len = (@attack.sample(soundinfo) / samplecount).to_i
|
34
|
+
attack_len.times {|i|
|
35
|
+
x = i.to_f / attack_len
|
36
36
|
y = @attack_curve[x]
|
37
|
-
yld << @shape[pos.next(
|
37
|
+
yld << @shape[pos.next(hz, 0.0, 0.0)] * y
|
38
38
|
}
|
39
39
|
|
40
40
|
# sustain
|
41
41
|
loop {
|
42
|
-
val = @shape[pos.next(
|
42
|
+
val = @shape[pos.next(hz, 0.0, 0.0)]
|
43
43
|
yld << val
|
44
44
|
}
|
45
45
|
end.each(&block)
|
46
46
|
end
|
47
47
|
|
48
|
-
def amp_generator(note_perform,
|
48
|
+
def amp_generator(note_perform, samplecount, depth, &block)
|
49
49
|
bottom = 1.0 - depth
|
50
|
-
gen = generator(note_perform,
|
50
|
+
gen = generator(note_perform, samplecount)
|
51
51
|
|
52
52
|
-> {
|
53
53
|
val = (gen.next + 1) / 2
|
@@ -55,8 +55,8 @@ module Synthesizer
|
|
55
55
|
}
|
56
56
|
end
|
57
57
|
|
58
|
-
def balance_generator(note_perform,
|
59
|
-
gen = generator(note_perform,
|
58
|
+
def balance_generator(note_perform, samplecount, depth, &block)
|
59
|
+
gen = generator(note_perform, samplecount)
|
60
60
|
|
61
61
|
-> {
|
62
62
|
gen.next * depth
|
@@ -9,7 +9,7 @@ module Synthesizer
|
|
9
9
|
@mods = []
|
10
10
|
|
11
11
|
mods.each {|mod, depth|
|
12
|
-
add(mod, depth: depth)
|
12
|
+
add(mod, depth: depth || 1.0)
|
13
13
|
}
|
14
14
|
end
|
15
15
|
|
@@ -35,7 +35,7 @@ module Synthesizer
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def self.amp_generator(note_perform,
|
38
|
+
def self.amp_generator(note_perform, samplecount, *modvals)
|
39
39
|
modvals = modvals.flatten.compact
|
40
40
|
|
41
41
|
# value
|
@@ -45,7 +45,7 @@ module Synthesizer
|
|
45
45
|
mods = []
|
46
46
|
modvals.each {|modval|
|
47
47
|
modval.mods.each {|mod, depth|
|
48
|
-
mods << mod.amp_generator(note_perform,
|
48
|
+
mods << mod.amp_generator(note_perform, samplecount, depth)
|
49
49
|
}
|
50
50
|
}
|
51
51
|
|
@@ -55,7 +55,7 @@ module Synthesizer
|
|
55
55
|
}
|
56
56
|
end
|
57
57
|
|
58
|
-
def self.balance_generator(note_perform,
|
58
|
+
def self.balance_generator(note_perform, samplecount, *modvals, center: 0)
|
59
59
|
modvals = modvals.flatten.compact
|
60
60
|
|
61
61
|
# value
|
@@ -66,7 +66,7 @@ module Synthesizer
|
|
66
66
|
mods = []
|
67
67
|
modvals.each {|modval|
|
68
68
|
modval.mods.each {|mod, depth|
|
69
|
-
mods << mod.balance_generator(note_perform,
|
69
|
+
mods << mod.balance_generator(note_perform, samplecount, depth)
|
70
70
|
}
|
71
71
|
}
|
72
72
|
|
@@ -6,22 +6,27 @@ module Synthesizer
|
|
6
6
|
attr_reader :amplifier
|
7
7
|
attr_reader :processor
|
8
8
|
|
9
|
+
attr_reader :quality
|
9
10
|
attr_reader :soundinfo
|
10
11
|
|
11
12
|
attr_reader :glide
|
12
13
|
attr_accessor :pitch_bend
|
13
14
|
|
14
15
|
# @param oscillators [Synthesizer::Oscillator] oscillator
|
16
|
+
# @param filter [Synthesizer::Filter] filter
|
15
17
|
# @param amplifier [Synthesizer::Amplifier] amplifier
|
18
|
+
# @param glide [AudioStream::Rate] glide time sec (0.0~)
|
19
|
+
# @param quality [Synthesizer::Quality] processor quality
|
16
20
|
# @param soundinfo [AudioStream::SoundInfo]
|
17
|
-
def initialize(oscillators:, filter: nil, amplifier:, glide: 0.1, soundinfo:)
|
21
|
+
def initialize(oscillators:, filter: nil, amplifier:, glide: AudioStream::Rate.sec(0.1), quality: Quality::LOW, soundinfo:)
|
18
22
|
@oscillators = [oscillators].flatten.compact
|
19
23
|
@filter = filter
|
20
24
|
@amplifier = amplifier
|
21
25
|
|
26
|
+
@quality = quality
|
22
27
|
@soundinfo = soundinfo
|
23
28
|
|
24
|
-
@processor = Processor.
|
29
|
+
@processor = Processor.create(quality)
|
25
30
|
@note_nums = []
|
26
31
|
@perform = nil
|
27
32
|
@glide = Modulation::Glide.new(time: glide)
|
@@ -11,18 +11,20 @@ module Synthesizer
|
|
11
11
|
attr_reader :sync
|
12
12
|
attr_reader :uni_num
|
13
13
|
attr_reader :uni_detune
|
14
|
+
attr_reader :uni_stereo
|
14
15
|
|
15
16
|
# @param source [Synthesizer::OscillatorSource] oscillator waveform source
|
16
|
-
# @param volume [Float] oscillator volume. mute=0.0 max=1.0
|
17
|
-
# @param pan [Float] oscillator pan. left=-1.0 center=0.0 right=1.0 (-1.0~1.0)
|
18
|
-
# @param tune_semis [Integer] oscillator pitch semitone
|
19
|
-
# @param tune_cents [Integer] oscillator pitch cent
|
17
|
+
# @param volume [ModulationValue | Float] oscillator volume. mute=0.0 max=1.0
|
18
|
+
# @param pan [ModulationValue | Float] oscillator pan. left=-1.0 center=0.0 right=1.0 (-1.0~1.0)
|
19
|
+
# @param tune_semis [ModulationValue | Integer] oscillator pitch semitone
|
20
|
+
# @param tune_cents [ModulationValue | Integer] oscillator pitch cent
|
20
21
|
# @param sym [nil] TODO not implemented
|
21
|
-
# @param phase [Float] oscillator waveform shape start phase percent (0.0~1.0,nil) nil=random
|
22
|
-
# @param sync [Integer]
|
23
|
-
# @param uni_num [Float] oscillator voicing number (1.0~16.0)
|
24
|
-
# @param uni_detune [Float] oscillator voicing detune percent. 0.01=1cent 1.0=semitone (0.0~1.0)
|
25
|
-
|
22
|
+
# @param phase [ModulationValue | Float] oscillator waveform shape start phase percent (0.0~1.0,nil) nil=random
|
23
|
+
# @param sync [ModulationValue | Integer] oscillator sync pitch 1.0=semitone 12.0=octave (0.0~48.0)
|
24
|
+
# @param uni_num [ModulationValue | Float] oscillator voicing number (1.0~16.0)
|
25
|
+
# @param uni_detune [ModulationValue | Float] oscillator voicing detune percent. 0.01=1cent 1.0=semitone (0.0~1.0)
|
26
|
+
# @param uni_stereo [ModulationValue | Float] oscillator voicing spread pan. -1.0=full inv 0.0=mono 1.0=full (-1.0~1.0)
|
27
|
+
def initialize(source: OscillatorSource::Sine.instance, volume: 1.0, pan: 0.0, tune_semis: 0, tune_cents: 0, sym: 0, phase: nil, sync: 0, uni_num: 1.0, uni_detune: 0.0, uni_stereo: 0.0)
|
26
28
|
@source = source
|
27
29
|
|
28
30
|
@volume = ModulationValue.create(volume)
|
@@ -4,13 +4,15 @@ module Synthesizer
|
|
4
4
|
def initialize
|
5
5
|
end
|
6
6
|
|
7
|
-
def next(context,
|
7
|
+
def next(context, rate, sym, sync, l_gain, r_gain)
|
8
|
+
soundinfo = context.soundinfo
|
8
9
|
channels = context.channels
|
9
10
|
window_size = context.window_size
|
10
11
|
pos = context.pos
|
12
|
+
hz = rate.freq(soundinfo)
|
11
13
|
|
12
14
|
dst = window_size.times.map {|i|
|
13
|
-
sample(context, pos.next(
|
15
|
+
sample(context, pos.next(hz, sym, sync))
|
14
16
|
}
|
15
17
|
dst = Vdsp::DoubleArray.create(dst)
|
16
18
|
|
@@ -26,11 +28,39 @@ module Synthesizer
|
|
26
28
|
raise Error, "not implemented abstruct method: #{self.class.name}.sample(context, phase)"
|
27
29
|
end
|
28
30
|
|
29
|
-
def generate_context(soundinfo, note_perform,
|
30
|
-
Context.new(soundinfo
|
31
|
+
def generate_context(soundinfo, note_perform, init_phase)
|
32
|
+
Context.new(soundinfo, note_perform, init_phase)
|
31
33
|
end
|
32
34
|
|
33
|
-
|
35
|
+
class Context
|
36
|
+
attr_reader :soundinfo
|
37
|
+
attr_reader :note_perform
|
38
|
+
attr_reader :init_phase
|
39
|
+
attr_reader :pos
|
40
|
+
|
41
|
+
def initialize(soundinfo, note_perform, init_phase)
|
42
|
+
@soundinfo = soundinfo
|
43
|
+
@note_perform = note_perform
|
44
|
+
@init_phase = init_phase
|
45
|
+
@pos = ShapePos.new(@soundinfo.samplerate, init_phase)
|
46
|
+
end
|
47
|
+
|
48
|
+
def window_size
|
49
|
+
@window_size ||= soundinfo.window_size
|
50
|
+
end
|
51
|
+
|
52
|
+
def channels
|
53
|
+
@channels ||= soundinfo.channels
|
54
|
+
end
|
55
|
+
|
56
|
+
def samplerate
|
57
|
+
@samplerate ||= soundinfo.samplerate
|
58
|
+
end
|
59
|
+
|
60
|
+
def framerate
|
61
|
+
@framerate ||= soundinfo.framerate
|
62
|
+
end
|
63
|
+
end
|
34
64
|
end
|
35
65
|
end
|
36
66
|
end
|
@@ -21,7 +21,7 @@ module Synthesizer
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def vowels=(vowels)
|
24
|
-
vowels = vowels + [vowels[0]]
|
24
|
+
#vowels = vowels + [vowels[0]]
|
25
25
|
@vowels = vowels.map {|v| @@f[v]}
|
26
26
|
@vowels_len = @vowels.length
|
27
27
|
end
|
@@ -30,15 +30,25 @@ module Synthesizer
|
|
30
30
|
@pronunciation = ModulationValue.create(pronunciation)
|
31
31
|
end
|
32
32
|
|
33
|
-
def next(context,
|
33
|
+
def next(context, rate, sym, sync, l_gain, r_gain)
|
34
|
+
soundinfo = context.soundinfo
|
34
35
|
channels = context.channels
|
35
36
|
window_size = context.window_size
|
36
37
|
samplerate = context.samplerate
|
37
38
|
tmpbufs = context.tmpbufs
|
38
39
|
pronunciation_mod = context.pronunciation_mod
|
40
|
+
pulse_context = context.pulse_context
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
+
pulse = Pulse.instance.next(pulse_context, rate, sym, sync, 0.5, 0.5).streams[0]
|
43
|
+
|
44
|
+
notediff = Math.log2(rate.freq(soundinfo) / 440.0) * 12 + 69 - 36
|
45
|
+
if notediff<0.0
|
46
|
+
notediff = 0.0
|
47
|
+
end
|
48
|
+
notediff = Math.sqrt(notediff)
|
49
|
+
vowels = @vowels.map {|vowel|
|
50
|
+
vowel.map{|f| f * (ShapePos::SEMITONE_RATIO ** notediff)}
|
51
|
+
}
|
42
52
|
|
43
53
|
r_index = pronunciation_mod[]
|
44
54
|
index = r_index.to_i
|
@@ -46,7 +56,7 @@ module Synthesizer
|
|
46
56
|
dst = 5.times.map {|i|
|
47
57
|
#dst = (1...5).each.map {|i|
|
48
58
|
tmpbuf = tmpbufs[i]
|
49
|
-
freq =
|
59
|
+
freq = vowels[index % @vowels_len][i]+(vowels[(index+1) % @vowels_len][i]-vowels[index % @vowels_len][i])*(r_index-index)
|
50
60
|
w = Math::PI * 2 * freq
|
51
61
|
resfil(pulse, tmpbufs[i], @@gain[i], w, 1.0/samplerate, window_size)
|
52
62
|
}.inject(:+)
|
@@ -82,24 +92,22 @@ module Synthesizer
|
|
82
92
|
Vdsp::DoubleArray.create(y)
|
83
93
|
end
|
84
94
|
|
85
|
-
def generate_context(soundinfo, note_perform,
|
86
|
-
|
87
|
-
tmpbufs = Array.new(5) {|i| Vdsp::DoubleArray.new(soundinfo.window_size+2)}
|
88
|
-
|
89
|
-
FvContext.new(soundinfo, note_perform, phase, tmpbufs, pronunciation_mod)
|
95
|
+
def generate_context(soundinfo, note_perform, init_phase)
|
96
|
+
Context.new(soundinfo, note_perform, init_phase, @pronunciation)
|
90
97
|
end
|
91
98
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
99
|
+
class Context < Base::Context
|
100
|
+
attr_reader :pronunciation_mod
|
101
|
+
attr_reader :tmpbufs
|
102
|
+
attr_reader :pulse_context
|
96
103
|
|
97
|
-
def
|
98
|
-
soundinfo
|
99
|
-
end
|
104
|
+
def initialize(soundinfo, note_perform, init_phase, pronunciation)
|
105
|
+
super(soundinfo, note_perform, init_phase)
|
100
106
|
|
101
|
-
|
102
|
-
soundinfo.
|
107
|
+
synth = note_perform.synth
|
108
|
+
@pronunciation_mod = ModulationValue.balance_generator(note_perform, synth.soundinfo.window_size.to_f, pronunciation)
|
109
|
+
@tmpbufs = Array.new(5) {|i| Vdsp::DoubleArray.new(soundinfo.window_size+2)}
|
110
|
+
@pulse_context = Pulse.instance.generate_context(soundinfo, note_perform, init_phase)
|
103
111
|
end
|
104
112
|
end
|
105
113
|
end
|
@@ -13,11 +13,19 @@ module Synthesizer
|
|
13
13
|
result
|
14
14
|
end
|
15
15
|
|
16
|
-
def generate_context(soundinfo, note_perform,
|
17
|
-
|
16
|
+
def generate_context(soundinfo, note_perform, init_phase)
|
17
|
+
Context.new(soundinfo, note_perform, init_phase)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
class Context < Base::Context
|
21
|
+
attr_accessor :prev
|
22
|
+
|
23
|
+
def initialize(soundinfo, note_perform, init_phase)
|
24
|
+
super(soundinfo, note_perform, init_phase)
|
25
|
+
|
26
|
+
@prev = -1.0
|
27
|
+
end
|
28
|
+
end
|
21
29
|
end
|
22
30
|
end
|
23
31
|
end
|
@@ -6,6 +6,7 @@ module Synthesizer
|
|
6
6
|
attr_reader :amplifier
|
7
7
|
attr_reader :processor
|
8
8
|
|
9
|
+
attr_reader :quality
|
9
10
|
attr_reader :soundinfo
|
10
11
|
|
11
12
|
attr_reader :glide
|
@@ -14,15 +15,17 @@ module Synthesizer
|
|
14
15
|
# @param oscillators [Synthesizer::Oscillator] Oscillator
|
15
16
|
# @param filter [Synthesizer::Filter] filter
|
16
17
|
# @param amplifier [Synthesizer::Amplifier] amplifier
|
18
|
+
# @param quality [Synthesizer::Quality] processor quality
|
17
19
|
# @param soundinfo [AudioStream::SoundInfo]
|
18
|
-
def initialize(oscillators:, filter: nil, amplifier:, soundinfo:)
|
20
|
+
def initialize(oscillators:, filter: nil, amplifier:, quality: Quality::LOW, soundinfo:)
|
19
21
|
@oscillators = [oscillators].flatten.compact
|
20
22
|
@filter = filter
|
21
23
|
@amplifier = amplifier
|
22
24
|
|
25
|
+
@quality = quality
|
23
26
|
@soundinfo = soundinfo
|
24
27
|
|
25
|
-
@processor = Processor.
|
28
|
+
@processor = Processor.create(quality)
|
26
29
|
@performs = {}
|
27
30
|
@pitch_bend = 0.0
|
28
31
|
end
|